summaryrefslogtreecommitdiff
path: root/arch/alpha/lib/strlen_user.S
diff options
context:
space:
mode:
Diffstat (limited to 'arch/alpha/lib/strlen_user.S')
-rw-r--r--arch/alpha/lib/strlen_user.S91
1 files changed, 91 insertions, 0 deletions
diff --git a/arch/alpha/lib/strlen_user.S b/arch/alpha/lib/strlen_user.S
new file mode 100644
index 00000000..508a18e9
--- /dev/null
+++ b/arch/alpha/lib/strlen_user.S
@@ -0,0 +1,91 @@
+/*
+ * arch/alpha/lib/strlen_user.S
+ *
+ * Return the length of the string including the NUL terminator
+ * (strlen+1) or zero if an error occurred.
+ *
+ * In places where it is critical to limit the processing time,
+ * and the data is not trusted, strnlen_user() should be used.
+ * It will return a value greater than its second argument if
+ * that limit would be exceeded. This implementation is allowed
+ * to access memory beyond the limit, but will not cross a page
+ * boundary when doing so.
+ */
+
+#include <asm/regdef.h>
+
+
+/* Allow an exception for an insn; exit if we get one. */
+#define EX(x,y...) \
+ 99: x,##y; \
+ .section __ex_table,"a"; \
+ .long 99b - .; \
+ lda v0, $exception-99b(zero); \
+ .previous
+
+
+ .set noreorder
+ .set noat
+ .text
+
+ .globl __strlen_user
+ .ent __strlen_user
+ .frame sp, 0, ra
+
+ .align 3
+__strlen_user:
+ ldah a1, 32767(zero) # do not use plain strlen_user() for strings
+ # that might be almost 2 GB long; you should
+ # be using strnlen_user() instead
+
+ .globl __strnlen_user
+
+ .align 3
+__strnlen_user:
+ .prologue 0
+
+ EX( ldq_u t0, 0(a0) ) # load first quadword (a0 may be misaligned)
+ lda t1, -1(zero)
+ insqh t1, a0, t1
+ andnot a0, 7, v0
+ or t1, t0, t0
+ subq a0, 1, a0 # get our +1 for the return
+ cmpbge zero, t0, t1 # t1 <- bitmask: bit i == 1 <==> i-th byte == 0
+ subq a1, 7, t2
+ subq a0, v0, t0
+ bne t1, $found
+
+ addq t2, t0, t2
+ addq a1, 1, a1
+
+ .align 3
+$loop: ble t2, $limit
+ EX( ldq t0, 8(v0) )
+ subq t2, 8, t2
+ addq v0, 8, v0 # addr += 8
+ cmpbge zero, t0, t1
+ beq t1, $loop
+
+$found: negq t1, t2 # clear all but least set bit
+ and t1, t2, t1
+
+ and t1, 0xf0, t2 # binary search for that set bit
+ and t1, 0xcc, t3
+ and t1, 0xaa, t4
+ cmovne t2, 4, t2
+ cmovne t3, 2, t3
+ cmovne t4, 1, t4
+ addq t2, t3, t2
+ addq v0, t4, v0
+ addq v0, t2, v0
+ nop # dual issue next two on ev4 and ev5
+ subq v0, a0, v0
+$exception:
+ ret
+
+ .align 3 # currently redundant
+$limit:
+ subq a1, t2, v0
+ ret
+
+ .end __strlen_user