summaryrefslogtreecommitdiff
path: root/arch/cris/arch-v32/lib
diff options
context:
space:
mode:
Diffstat (limited to 'arch/cris/arch-v32/lib')
-rw-r--r--arch/cris/arch-v32/lib/Makefile7
-rw-r--r--arch/cris/arch-v32/lib/checksum.S88
-rw-r--r--arch/cris/arch-v32/lib/checksumcopy.S94
-rw-r--r--arch/cris/arch-v32/lib/csumcpfruser.S69
-rw-r--r--arch/cris/arch-v32/lib/delay.c28
-rw-r--r--arch/cris/arch-v32/lib/memset.c259
-rw-r--r--arch/cris/arch-v32/lib/strcmp.S21
-rw-r--r--arch/cris/arch-v32/lib/string.c236
-rw-r--r--arch/cris/arch-v32/lib/usercopy.c469
9 files changed, 1271 insertions, 0 deletions
diff --git a/arch/cris/arch-v32/lib/Makefile b/arch/cris/arch-v32/lib/Makefile
new file mode 100644
index 000000000..e91cf02f6
--- /dev/null
+++ b/arch/cris/arch-v32/lib/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for Etrax-specific library files..
+#
+
+lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o \
+ csumcpfruser.o delay.o strcmp.o
+
diff --git a/arch/cris/arch-v32/lib/checksum.S b/arch/cris/arch-v32/lib/checksum.S
new file mode 100644
index 000000000..4a72a94a4
--- /dev/null
+++ b/arch/cris/arch-v32/lib/checksum.S
@@ -0,0 +1,88 @@
+/*
+ * A fast checksum routine using movem
+ * Copyright (c) 1998-2007 Axis Communications AB
+ *
+ * csum_partial(const unsigned char * buff, int len, unsigned int sum)
+ */
+
+ .globl csum_partial
+ .type csum_partial,@function
+csum_partial:
+
+ ;; r10 - src
+ ;; r11 - length
+ ;; r12 - checksum
+
+ ;; Optimized for large packets
+ subq 10*4, $r11
+ blt _word_loop
+ move.d $r11, $acr
+
+ subq 9*4,$sp
+ clearf c
+ movem $r8,[$sp]
+
+ ;; do a movem checksum
+
+_mloop: movem [$r10+],$r9 ; read 10 longwords
+ ;; Loop count without touching the c flag.
+ addoq -10*4, $acr, $acr
+ ;; perform dword checksumming on the 10 longwords
+
+ addc $r0,$r12
+ addc $r1,$r12
+ addc $r2,$r12
+ addc $r3,$r12
+ addc $r4,$r12
+ addc $r5,$r12
+ addc $r6,$r12
+ addc $r7,$r12
+ addc $r8,$r12
+ addc $r9,$r12
+
+ ;; test $acr without trashing carry.
+ move.d $acr, $acr
+ bpl _mloop
+ ;; r11 <= acr is not really needed in the mloop, just using the dslot
+ ;; to prepare for what is needed after mloop.
+ move.d $acr, $r11
+
+ ;; fold the last carry into r13
+ addc 0, $r12
+ movem [$sp+],$r8 ; restore regs
+
+_word_loop:
+ addq 10*4,$r11 ; compensate for last loop underflowing length
+
+ moveq -1,$r9 ; put 0xffff in r9, faster than move.d 0xffff,r9
+ lsrq 16,$r9
+
+ move.d $r12,$r13
+ lsrq 16,$r13 ; r13 = checksum >> 16
+ and.d $r9,$r12 ; checksum = checksum & 0xffff
+
+_no_fold:
+ subq 2,$r11
+ blt _no_words
+ add.d $r13,$r12 ; checksum += r13
+
+ ;; checksum the rest of the words
+_wloop: subq 2,$r11
+ bge _wloop
+ addu.w [$r10+],$r12
+
+_no_words:
+ addq 2,$r11
+ ;; see if we have one odd byte more
+ bne _do_byte
+ nop
+ ret
+ move.d $r12,$r10
+
+_do_byte:
+ ;; copy and checksum the last byte
+ addu.b [$r10],$r12
+ ret
+ move.d $r12,$r10
+
+ .size csum_partial, .-csum_partial
diff --git a/arch/cris/arch-v32/lib/checksumcopy.S b/arch/cris/arch-v32/lib/checksumcopy.S
new file mode 100644
index 000000000..54e209f18
--- /dev/null
+++ b/arch/cris/arch-v32/lib/checksumcopy.S
@@ -0,0 +1,94 @@
+/*
+ * A fast checksum+copy routine using movem
+ * Copyright (c) 1998-2007 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen
+ *
+ * csum_partial_copy_nocheck(const char *src, char *dst,
+ * int len, unsigned int sum)
+ */
+
+ .globl csum_partial_copy_nocheck
+ .type csum_partial_copy_nocheck,@function
+csum_partial_copy_nocheck:
+
+ ;; r10 - src
+ ;; r11 - dst
+ ;; r12 - length
+ ;; r13 - checksum
+
+ ;; Optimized for large packets
+ subq 10*4, $r12
+ blt _word_loop
+ move.d $r12, $acr
+
+ subq 9*4,$sp
+ clearf c
+ movem $r8,[$sp]
+
+ ;; do a movem copy and checksum
+1: ;; A failing userspace access (the read) will have this as PC.
+_mloop: movem [$r10+],$r9 ; read 10 longwords
+ addoq -10*4, $acr, $acr ; loop counter in latency cycle
+ movem $r9,[$r11+] ; write 10 longwords
+
+ ;; perform dword checksumming on the 10 longwords
+ addc $r0,$r13
+ addc $r1,$r13
+ addc $r2,$r13
+ addc $r3,$r13
+ addc $r4,$r13
+ addc $r5,$r13
+ addc $r6,$r13
+ addc $r7,$r13
+ addc $r8,$r13
+ addc $r9,$r13
+
+ ;; test $acr, without trashing carry.
+ move.d $acr, $acr
+ bpl _mloop
+ ;; r12 <= acr is needed after mloop and in the exception handlers.
+ move.d $acr, $r12
+
+ ;; fold the last carry into r13
+ addc 0, $r13
+ movem [$sp+],$r8 ; restore regs
+
+_word_loop:
+ addq 10*4,$r12 ; compensate for last loop underflowing length
+
+ ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below
+ ;; r9 can be used as temporary.
+ move.d $r13,$r9
+ lsrq 16,$r9 ; r0 = checksum >> 16
+ and.d 0xffff,$r13 ; checksum = checksum & 0xffff
+
+ subq 2, $r12
+ blt _no_words
+ add.d $r9,$r13 ; checksum += r0
+
+ ;; copy and checksum the rest of the words
+2: ;; A failing userspace access for the read below will have this as PC.
+_wloop: move.w [$r10+],$r9
+ addu.w $r9,$r13
+ subq 2,$r12
+ bge _wloop
+ move.w $r9,[$r11+]
+
+_no_words:
+ addq 2,$r12
+ bne _do_byte
+ nop
+ ret
+ move.d $r13,$r10
+
+_do_byte:
+ ;; copy and checksum the last byte
+3: ;; A failing userspace access for the read below will have this as PC.
+ move.b [$r10],$r9
+ addu.b $r9,$r13
+ move.b $r9,[$r11]
+ ret
+ move.d $r13,$r10
+
+ .size csum_partial_copy_nocheck, . - csum_partial_copy_nocheck
diff --git a/arch/cris/arch-v32/lib/csumcpfruser.S b/arch/cris/arch-v32/lib/csumcpfruser.S
new file mode 100644
index 000000000..600ec16b9
--- /dev/null
+++ b/arch/cris/arch-v32/lib/csumcpfruser.S
@@ -0,0 +1,69 @@
+/*
+ * Add-on to transform csum_partial_copy_nocheck in checksumcopy.S into
+ * csum_partial_copy_from_user by adding exception records.
+ *
+ * Copyright (C) 2001, 2003 Axis Communications AB.
+ *
+ * Author: Hans-Peter Nilsson.
+ */
+
+#include <asm/errno.h>
+
+/* Same function body, but a different name. If we just added exception
+ records to _csum_partial_copy_nocheck and made it generic, we wouldn't
+ know a user fault from a kernel fault and we would have overhead in
+ each kernel caller for the error-pointer argument.
+
+ unsigned int csum_partial_copy_from_user
+ (const char *src, char *dst, int len, unsigned int sum, int *errptr);
+
+ Note that the errptr argument is only set if we encounter an error.
+ It is conveniently located on the stack, so the normal function body
+ does not have to handle it. */
+
+#define csum_partial_copy_nocheck csum_partial_copy_from_user
+
+/* There are local labels numbered 1, 2 and 3 present to mark the
+ different from-user accesses. */
+#include "checksumcopy.S"
+
+ .section .fixup,"ax"
+
+;; Here from the movem loop; restore stack.
+4:
+ movem [$sp+],$r8
+;; r12 is already decremented. Add back chunk_size-2.
+ addq 40-2,$r12
+
+;; Here from the word loop; r12 is off by 2; add it back.
+5:
+ addq 2,$r12
+
+;; Here from a failing single byte.
+6:
+
+;; Signal in *errptr that we had a failing access.
+ move.d [$sp],$acr
+ moveq -EFAULT,$r9
+ subq 4,$sp
+ move.d $r9,[$acr]
+
+;; Clear the rest of the destination area using memset. Preserve the
+;; checksum for the readable bytes.
+ move.d $r13,[$sp]
+ subq 4,$sp
+ move.d $r11,$r10
+ move $srp,[$sp]
+ jsr memset
+ clear.d $r11
+
+ move [$sp+],$srp
+ ret
+ move.d [$sp+],$r10
+
+ .previous
+ .section __ex_table,"a"
+ .dword 1b,4b
+ .dword 2b,5b
+ .dword 3b,6b
+ .previous
diff --git a/arch/cris/arch-v32/lib/delay.c b/arch/cris/arch-v32/lib/delay.c
new file mode 100644
index 000000000..39f1ac999
--- /dev/null
+++ b/arch/cris/arch-v32/lib/delay.c
@@ -0,0 +1,28 @@
+/*
+ * Precise Delay Loops for ETRAX FS
+ *
+ * Copyright (C) 2006 Axis Communications AB.
+ *
+ */
+
+#include <hwregs/reg_map.h>
+#include <hwregs/reg_rdwr.h>
+#include <hwregs/timer_defs.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+/*
+ * On ETRAX FS, we can check the free-running read-only 100MHz timer
+ * getting 32-bit 10ns precision, theoretically good for 42.94967295
+ * seconds. Unsigned arithmetic and careful expression handles
+ * wrapping.
+ */
+
+void cris_delay10ns(u32 n10ns)
+{
+ u32 t0 = REG_RD(timer, regi_timer0, r_time);
+ while (REG_RD(timer, regi_timer0, r_time) - t0 < n10ns)
+ ;
+}
+EXPORT_SYMBOL(cris_delay10ns);
diff --git a/arch/cris/arch-v32/lib/memset.c b/arch/cris/arch-v32/lib/memset.c
new file mode 100644
index 000000000..c94ea9b3e
--- /dev/null
+++ b/arch/cris/arch-v32/lib/memset.c
@@ -0,0 +1,259 @@
+/* A memset for CRIS.
+ Copyright (C) 1999-2005 Axis Communications.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Neither the name of Axis Communications nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AXIS COMMUNICATIONS AND ITS CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AXIS
+ COMMUNICATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE. */
+
+/* FIXME: This file should really only be used for reference, as the
+ result is somewhat depending on gcc generating what we expect rather
+ than what we describe. An assembly file should be used instead. */
+
+/* Note the multiple occurrence of the expression "12*4", including the
+ asm. It is hard to get it into the asm in a good way. Thus better to
+ expose the problem everywhere: no macro. */
+
+/* Assuming one cycle per dword written or read (ok, not really true; the
+ world is not ideal), and one cycle per instruction, then 43+3*(n/48-1)
+ <= 24+24*(n/48-1) so n >= 45.7; n >= 0.9; we win on the first full
+ 48-byte block to set. */
+
+#define MEMSET_BY_BLOCK_THRESHOLD (1 * 48)
+
+/* No name ambiguities in this file. */
+__asm__ (".syntax no_register_prefix");
+
+void *memset(void *pdst, int c, unsigned int plen)
+{
+ /* Now we want the parameters in special registers. Make sure the
+ compiler does something usable with this. */
+
+ register char *return_dst __asm__ ("r10") = pdst;
+ register int n __asm__ ("r12") = plen;
+ register int lc __asm__ ("r11") = c;
+
+ /* Most apps use memset sanely. Memsetting about 3..4 bytes or less get
+ penalized here compared to the generic implementation. */
+
+ /* This is fragile performancewise at best. Check with newer GCC
+ releases, if they compile cascaded "x |= x << 8" to sane code. */
+ __asm__("movu.b %0,r13 \n\
+ lslq 8,r13 \n\
+ move.b %0,r13 \n\
+ move.d r13,%0 \n\
+ lslq 16,r13 \n\
+ or.d r13,%0"
+ : "=r" (lc) /* Inputs. */
+ : "0" (lc) /* Outputs. */
+ : "r13"); /* Trash. */
+
+ {
+ register char *dst __asm__ ("r13") = pdst;
+
+ if (((unsigned long) pdst & 3) != 0
+ /* Oops! n = 0 must be a valid call, regardless of alignment. */
+ && n >= 3)
+ {
+ if ((unsigned long) dst & 1)
+ {
+ *dst = (char) lc;
+ n--;
+ dst++;
+ }
+
+ if ((unsigned long) dst & 2)
+ {
+ *(short *) dst = lc;
+ n -= 2;
+ dst += 2;
+ }
+ }
+
+ /* Decide which setting method to use. */
+ if (n >= MEMSET_BY_BLOCK_THRESHOLD)
+ {
+ /* It is not optimal to tell the compiler about clobbering any
+ registers; that will move the saving/restoring of those registers
+ to the function prologue/epilogue, and make non-block sizes
+ suboptimal. */
+ __asm__ volatile
+ ("\
+ ;; GCC does promise correct register allocations, but let's \n\
+ ;; make sure it keeps its promises. \n\
+ .ifnc %0-%1-%4,$r13-$r12-$r11 \n\
+ .error \"GCC reg alloc bug: %0-%1-%4 != $r13-$r12-$r11\" \n\
+ .endif \n\
+ \n\
+ ;; Save the registers we'll clobber in the movem process \n\
+ ;; on the stack. Don't mention them to gcc, it will only be \n\
+ ;; upset. \n\
+ subq 11*4,sp \n\
+ movem r10,[sp] \n\
+ \n\
+ move.d r11,r0 \n\
+ move.d r11,r1 \n\
+ move.d r11,r2 \n\
+ move.d r11,r3 \n\
+ move.d r11,r4 \n\
+ move.d r11,r5 \n\
+ move.d r11,r6 \n\
+ move.d r11,r7 \n\
+ move.d r11,r8 \n\
+ move.d r11,r9 \n\
+ move.d r11,r10 \n\
+ \n\
+ ;; Now we've got this: \n\
+ ;; r13 - dst \n\
+ ;; r12 - n \n\
+ \n\
+ ;; Update n for the first loop \n\
+ subq 12*4,r12 \n\
+0: \n\
+"
+#ifdef __arch_common_v10_v32
+ /* Cater to branch offset difference between v32 and v10. We
+ assume the branch below has an 8-bit offset. */
+" setf\n"
+#endif
+" subq 12*4,r12 \n\
+ bge 0b \n\
+ movem r11,[r13+] \n\
+ \n\
+ ;; Compensate for last loop underflowing n. \n\
+ addq 12*4,r12 \n\
+ \n\
+ ;; Restore registers from stack. \n\
+ movem [sp+],r10"
+
+ /* Outputs. */
+ : "=r" (dst), "=r" (n)
+
+ /* Inputs. */
+ : "0" (dst), "1" (n), "r" (lc));
+ }
+
+ /* An ad-hoc unroll, used for 4*12-1..16 bytes. */
+ while (n >= 16)
+ {
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc; dst += 4;
+ n -= 16;
+ }
+
+ switch (n)
+ {
+ case 0:
+ break;
+
+ case 1:
+ *dst = (char) lc;
+ break;
+
+ case 2:
+ *(short *) dst = (short) lc;
+ break;
+
+ case 3:
+ *(short *) dst = (short) lc; dst += 2;
+ *dst = (char) lc;
+ break;
+
+ case 4:
+ *(long *) dst = lc;
+ break;
+
+ case 5:
+ *(long *) dst = lc; dst += 4;
+ *dst = (char) lc;
+ break;
+
+ case 6:
+ *(long *) dst = lc; dst += 4;
+ *(short *) dst = (short) lc;
+ break;
+
+ case 7:
+ *(long *) dst = lc; dst += 4;
+ *(short *) dst = (short) lc; dst += 2;
+ *dst = (char) lc;
+ break;
+
+ case 8:
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc;
+ break;
+
+ case 9:
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc; dst += 4;
+ *dst = (char) lc;
+ break;
+
+ case 10:
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc; dst += 4;
+ *(short *) dst = (short) lc;
+ break;
+
+ case 11:
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc; dst += 4;
+ *(short *) dst = (short) lc; dst += 2;
+ *dst = (char) lc;
+ break;
+
+ case 12:
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc;
+ break;
+
+ case 13:
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc; dst += 4;
+ *dst = (char) lc;
+ break;
+
+ case 14:
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc; dst += 4;
+ *(short *) dst = (short) lc;
+ break;
+
+ case 15:
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc; dst += 4;
+ *(long *) dst = lc; dst += 4;
+ *(short *) dst = (short) lc; dst += 2;
+ *dst = (char) lc;
+ break;
+ }
+ }
+
+ return return_dst;
+}
diff --git a/arch/cris/arch-v32/lib/strcmp.S b/arch/cris/arch-v32/lib/strcmp.S
new file mode 100644
index 000000000..8f7a1ee62
--- /dev/null
+++ b/arch/cris/arch-v32/lib/strcmp.S
@@ -0,0 +1,21 @@
+; strcmp.S -- CRISv32 version.
+; Copyright (C) 2008 AXIS Communications AB
+; Written by Edgar E. Iglesias
+;
+; This source code is licensed under the GNU General Public License,
+; Version 2. See the file COPYING for more details.
+
+ .global strcmp
+ .type strcmp,@function
+strcmp:
+1:
+ move.b [$r10+], $r12
+ seq $r13
+ sub.b [$r11+], $r12
+ or.b $r12, $r13
+ beq 1b
+ nop
+
+ ret
+ movs.b $r12, $r10
+ .size strcmp, . - strcmp
diff --git a/arch/cris/arch-v32/lib/string.c b/arch/cris/arch-v32/lib/string.c
new file mode 100644
index 000000000..c7bd6ebdc
--- /dev/null
+++ b/arch/cris/arch-v32/lib/string.c
@@ -0,0 +1,236 @@
+/* A memcpy for CRIS.
+ Copyright (C) 1994-2005 Axis Communications.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. Neither the name of Axis Communications nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AXIS COMMUNICATIONS AND ITS CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AXIS
+ COMMUNICATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE. */
+
+/* FIXME: This file should really only be used for reference, as the
+ result is somewhat depending on gcc generating what we expect rather
+ than what we describe. An assembly file should be used instead. */
+
+#include <stddef.h>
+
+/* Break even between movem and move16 is really at 38.7 * 2, but
+ modulo 44, so up to the next multiple of 44, we use ordinary code. */
+#define MEMCPY_BY_BLOCK_THRESHOLD (44 * 2)
+
+/* No name ambiguities in this file. */
+__asm__ (".syntax no_register_prefix");
+
+void *
+memcpy(void *pdst, const void *psrc, size_t pn)
+{
+ /* Now we want the parameters put in special registers.
+ Make sure the compiler is able to make something useful of this.
+ As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop).
+
+ If gcc was allright, it really would need no temporaries, and no
+ stack space to save stuff on. */
+
+ register void *return_dst __asm__ ("r10") = pdst;
+ register unsigned char *dst __asm__ ("r13") = pdst;
+ register unsigned const char *src __asm__ ("r11") = psrc;
+ register int n __asm__ ("r12") = pn;
+
+ /* When src is aligned but not dst, this makes a few extra needless
+ cycles. I believe it would take as many to check that the
+ re-alignment was unnecessary. */
+ if (((unsigned long) dst & 3) != 0
+ /* Don't align if we wouldn't copy more than a few bytes; so we
+ don't have to check further for overflows. */
+ && n >= 3)
+ {
+ if ((unsigned long) dst & 1)
+ {
+ n--;
+ *dst = *src;
+ src++;
+ dst++;
+ }
+
+ if ((unsigned long) dst & 2)
+ {
+ n -= 2;
+ *(short *) dst = *(short *) src;
+ src += 2;
+ dst += 2;
+ }
+ }
+
+ /* Decide which copying method to use. */
+ if (n >= MEMCPY_BY_BLOCK_THRESHOLD)
+ {
+ /* It is not optimal to tell the compiler about clobbering any
+ registers; that will move the saving/restoring of those registers
+ to the function prologue/epilogue, and make non-movem sizes
+ suboptimal. */
+ __asm__ volatile
+ ("\
+ ;; GCC does promise correct register allocations, but let's \n\
+ ;; make sure it keeps its promises. \n\
+ .ifnc %0-%1-%2,$r13-$r11-$r12 \n\
+ .error \"GCC reg alloc bug: %0-%1-%4 != $r13-$r12-$r11\" \n\
+ .endif \n\
+ \n\
+ ;; Save the registers we'll use in the movem process \n\
+ ;; on the stack. \n\
+ subq 11*4,sp \n\
+ movem r10,[sp] \n\
+ \n\
+ ;; Now we've got this: \n\
+ ;; r11 - src \n\
+ ;; r13 - dst \n\
+ ;; r12 - n \n\
+ \n\
+ ;; Update n for the first loop. \n\
+ subq 44,r12 \n\
+0: \n\
+"
+#ifdef __arch_common_v10_v32
+ /* Cater to branch offset difference between v32 and v10. We
+ assume the branch below has an 8-bit offset. */
+" setf\n"
+#endif
+" movem [r11+],r10 \n\
+ subq 44,r12 \n\
+ bge 0b \n\
+ movem r10,[r13+] \n\
+ \n\
+ ;; Compensate for last loop underflowing n. \n\
+ addq 44,r12 \n\
+ \n\
+ ;; Restore registers from stack. \n\
+ movem [sp+],r10"
+
+ /* Outputs. */
+ : "=r" (dst), "=r" (src), "=r" (n)
+
+ /* Inputs. */
+ : "0" (dst), "1" (src), "2" (n));
+ }
+
+ while (n >= 16)
+ {
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+
+ n -= 16;
+ }
+
+ switch (n)
+ {
+ case 0:
+ break;
+
+ case 1:
+ *dst = *src;
+ break;
+
+ case 2:
+ *(short *) dst = *(short *) src;
+ break;
+
+ case 3:
+ *(short *) dst = *(short *) src; dst += 2; src += 2;
+ *dst = *src;
+ break;
+
+ case 4:
+ *(long *) dst = *(long *) src;
+ break;
+
+ case 5:
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *dst = *src;
+ break;
+
+ case 6:
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(short *) dst = *(short *) src;
+ break;
+
+ case 7:
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(short *) dst = *(short *) src; dst += 2; src += 2;
+ *dst = *src;
+ break;
+
+ case 8:
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src;
+ break;
+
+ case 9:
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *dst = *src;
+ break;
+
+ case 10:
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(short *) dst = *(short *) src;
+ break;
+
+ case 11:
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(short *) dst = *(short *) src; dst += 2; src += 2;
+ *dst = *src;
+ break;
+
+ case 12:
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src;
+ break;
+
+ case 13:
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *dst = *src;
+ break;
+
+ case 14:
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(short *) dst = *(short *) src;
+ break;
+
+ case 15:
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(long *) dst = *(long *) src; dst += 4; src += 4;
+ *(short *) dst = *(short *) src; dst += 2; src += 2;
+ *dst = *src;
+ break;
+ }
+
+ return return_dst;
+}
diff --git a/arch/cris/arch-v32/lib/usercopy.c b/arch/cris/arch-v32/lib/usercopy.c
new file mode 100644
index 000000000..f0f335d8a
--- /dev/null
+++ b/arch/cris/arch-v32/lib/usercopy.c
@@ -0,0 +1,469 @@
+/*
+ * User address space access functions.
+ * The non-inlined parts of asm-cris/uaccess.h are here.
+ *
+ * Copyright (C) 2000, 2003 Axis Communications AB.
+ *
+ * Written by Hans-Peter Nilsson.
+ * Pieces used from memcpy, originally by Kenny Ranerup long time ago.
+ */
+
+#include <asm/uaccess.h>
+
+/* Asm:s have been tweaked (within the domain of correctness) to give
+ satisfactory results for "gcc version 3.2.1 Axis release R53/1.53-v32".
+
+ Check regularly...
+
+ Note that for CRISv32, the PC saved at a bus-fault is the address
+ *at* the faulting instruction, with a special case for instructions
+ in delay slots: then it's the address of the branch. Note also that
+ in contrast to v10, a postincrement in the instruction is *not*
+ performed at a bus-fault; the register is seen having the original
+ value in fault handlers. */
+
+
+/* Copy to userspace. This is based on the memcpy used for
+ kernel-to-kernel copying; see "string.c". */
+
+unsigned long __copy_user(void __user *pdst, const void *psrc, unsigned long pn)
+{
+ /* We want the parameters put in special registers.
+ Make sure the compiler is able to make something useful of this.
+ As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop).
+
+ FIXME: Comment for old gcc version. Check.
+ If gcc was alright, it really would need no temporaries, and no
+ stack space to save stuff on. */
+
+ register char *dst __asm__ ("r13") = pdst;
+ register const char *src __asm__ ("r11") = psrc;
+ register int n __asm__ ("r12") = pn;
+ register int retn __asm__ ("r10") = 0;
+
+
+ /* When src is aligned but not dst, this makes a few extra needless
+ cycles. I believe it would take as many to check that the
+ re-alignment was unnecessary. */
+ if (((unsigned long) dst & 3) != 0
+ /* Don't align if we wouldn't copy more than a few bytes; so we
+ don't have to check further for overflows. */
+ && n >= 3)
+ {
+ if ((unsigned long) dst & 1)
+ {
+ __asm_copy_to_user_1 (dst, src, retn);
+ n--;
+ }
+
+ if ((unsigned long) dst & 2)
+ {
+ __asm_copy_to_user_2 (dst, src, retn);
+ n -= 2;
+ }
+ }
+
+ /* Movem is dirt cheap. The overheap is low enough to always use the
+ minimum possible block size as the threshold. */
+ if (n >= 44)
+ {
+ /* For large copies we use 'movem'. */
+
+ /* It is not optimal to tell the compiler about clobbering any
+ registers; that will move the saving/restoring of those registers
+ to the function prologue/epilogue, and make non-movem sizes
+ suboptimal. */
+ __asm__ volatile ("\
+ ;; Check that the register asm declaration got right. \n\
+ ;; The GCC manual explicitly says TRT will happen. \n\
+ .ifnc %0%1%2%3,$r13$r11$r12$r10 \n\
+ .err \n\
+ .endif \n\
+ \n\
+ ;; Save the registers we'll use in the movem process \n\
+ ;; on the stack. \n\
+ subq 11*4,$sp \n\
+ movem $r10,[$sp] \n\
+ \n\
+ ;; Now we've got this: \n\
+ ;; r11 - src \n\
+ ;; r13 - dst \n\
+ ;; r12 - n \n\
+ \n\
+ ;; Update n for the first loop \n\
+ subq 44,$r12 \n\
+0: \n\
+ movem [$r11+],$r10 \n\
+ subq 44,$r12 \n\
+1: bge 0b \n\
+ movem $r10,[$r13+] \n\
+3: \n\
+ addq 44,$r12 ;; compensate for last loop underflowing n \n\
+ \n\
+ ;; Restore registers from stack \n\
+ movem [$sp+],$r10 \n\
+2: \n\
+ .section .fixup,\"ax\" \n\
+4: \n\
+; When failing on any of the 1..44 bytes in a chunk, we adjust back the \n\
+; source pointer and just drop through to the by-16 and by-4 loops to \n\
+; get the correct number of failing bytes. This necessarily means a \n\
+; few extra exceptions, but invalid user pointers shouldn't happen in \n\
+; time-critical code anyway. \n\
+ jump 3b \n\
+ subq 44,$r11 \n\
+ \n\
+ .previous \n\
+ .section __ex_table,\"a\" \n\
+ .dword 1b,4b \n\
+ .previous"
+
+ /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n), "=r" (retn)
+ /* Inputs */ : "0" (dst), "1" (src), "2" (n), "3" (retn));
+
+ }
+
+ while (n >= 16)
+ {
+ __asm_copy_to_user_16 (dst, src, retn);
+ n -= 16;
+ }
+
+ /* Having a separate by-four loops cuts down on cache footprint.
+ FIXME: Test with and without; increasing switch to be 0..15. */
+ while (n >= 4)
+ {
+ __asm_copy_to_user_4 (dst, src, retn);
+ n -= 4;
+ }
+
+ switch (n)
+ {
+ case 0:
+ break;
+ case 1:
+ __asm_copy_to_user_1 (dst, src, retn);
+ break;
+ case 2:
+ __asm_copy_to_user_2 (dst, src, retn);
+ break;
+ case 3:
+ __asm_copy_to_user_3 (dst, src, retn);
+ break;
+ }
+
+ return retn;
+}
+EXPORT_SYMBOL(__copy_user);
+
+/* Copy from user to kernel, zeroing the bytes that were inaccessible in
+ userland. The return-value is the number of bytes that were
+ inaccessible. */
+unsigned long __copy_user_zeroing(void *pdst, const void __user *psrc,
+ unsigned long pn)
+{
+ /* We want the parameters put in special registers.
+ Make sure the compiler is able to make something useful of this.
+ As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop).
+
+ FIXME: Comment for old gcc version. Check.
+ If gcc was alright, it really would need no temporaries, and no
+ stack space to save stuff on. */
+
+ register char *dst __asm__ ("r13") = pdst;
+ register const char *src __asm__ ("r11") = psrc;
+ register int n __asm__ ("r12") = pn;
+ register int retn __asm__ ("r10") = 0;
+
+ /* The best reason to align src is that we then know that a read-fault
+ was for aligned bytes; there's no 1..3 remaining good bytes to
+ pickle. */
+ if (((unsigned long) src & 3) != 0)
+ {
+ if (((unsigned long) src & 1) && n != 0)
+ {
+ __asm_copy_from_user_1 (dst, src, retn);
+ n--;
+ }
+
+ if (((unsigned long) src & 2) && n >= 2)
+ {
+ __asm_copy_from_user_2 (dst, src, retn);
+ n -= 2;
+ }
+
+ /* We only need one check after the unalignment-adjustments, because
+ if both adjustments were done, either both or neither reference
+ had an exception. */
+ if (retn != 0)
+ goto copy_exception_bytes;
+ }
+
+ /* Movem is dirt cheap. The overheap is low enough to always use the
+ minimum possible block size as the threshold. */
+ if (n >= 44)
+ {
+ /* It is not optimal to tell the compiler about clobbering any
+ registers; that will move the saving/restoring of those registers
+ to the function prologue/epilogue, and make non-movem sizes
+ suboptimal. */
+ __asm__ volatile ("\
+ .ifnc %0%1%2%3,$r13$r11$r12$r10 \n\
+ .err \n\
+ .endif \n\
+ \n\
+ ;; Save the registers we'll use in the movem process \n\
+ ;; on the stack. \n\
+ subq 11*4,$sp \n\
+ movem $r10,[$sp] \n\
+ \n\
+ ;; Now we've got this: \n\
+ ;; r11 - src \n\
+ ;; r13 - dst \n\
+ ;; r12 - n \n\
+ \n\
+ ;; Update n for the first loop \n\
+ subq 44,$r12 \n\
+0: \n\
+ movem [$r11+],$r10 \n\
+ \n\
+ subq 44,$r12 \n\
+ bge 0b \n\
+ movem $r10,[$r13+] \n\
+ \n\
+4: \n\
+ addq 44,$r12 ;; compensate for last loop underflowing n \n\
+ \n\
+ ;; Restore registers from stack \n\
+ movem [$sp+],$r10 \n\
+ .section .fixup,\"ax\" \n\
+ \n\
+;; Do not jump back into the loop if we fail. For some uses, we get a \n\
+;; page fault somewhere on the line. Without checking for page limits, \n\
+;; we don't know where, but we need to copy accurately and keep an \n\
+;; accurate count; not just clear the whole line. To do that, we fall \n\
+;; down in the code below, proceeding with smaller amounts. It should \n\
+;; be kept in mind that we have to cater to code like what at one time \n\
+;; was in fs/super.c: \n\
+;; i = size - copy_from_user((void *)page, data, size); \n\
+;; which would cause repeated faults while clearing the remainder of \n\
+;; the SIZE bytes at PAGE after the first fault. \n\
+;; A caveat here is that we must not fall through from a failing page \n\
+;; to a valid page. \n\
+ \n\
+3: \n\
+ jump 4b ;; Fall through, pretending the fault didn't happen. \n\
+ nop \n\
+ \n\
+ .previous \n\
+ .section __ex_table,\"a\" \n\
+ .dword 0b,3b \n\
+ .previous"
+
+ /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n), "=r" (retn)
+ /* Inputs */ : "0" (dst), "1" (src), "2" (n), "3" (retn));
+ }
+
+ /* Either we directly start copying here, using dword copying in a loop,
+ or we copy as much as possible with 'movem' and then the last block
+ (<44 bytes) is copied here. This will work since 'movem' will have
+ updated src, dst and n. (Except with failing src.)
+
+ Since we want to keep src accurate, we can't use
+ __asm_copy_from_user_N with N != (1, 2, 4); it updates dst and
+ retn, but not src (by design; it's value is ignored elsewhere). */
+
+ while (n >= 4)
+ {
+ __asm_copy_from_user_4 (dst, src, retn);
+ n -= 4;
+
+ if (retn)
+ goto copy_exception_bytes;
+ }
+
+ /* If we get here, there were no memory read faults. */
+ switch (n)
+ {
+ /* These copies are at least "naturally aligned" (so we don't have
+ to check each byte), due to the src alignment code before the
+ movem loop. The *_3 case *will* get the correct count for retn. */
+ case 0:
+ /* This case deliberately left in (if you have doubts check the
+ generated assembly code). */
+ break;
+ case 1:
+ __asm_copy_from_user_1 (dst, src, retn);
+ break;
+ case 2:
+ __asm_copy_from_user_2 (dst, src, retn);
+ break;
+ case 3:
+ __asm_copy_from_user_3 (dst, src, retn);
+ break;
+ }
+
+ /* If we get here, retn correctly reflects the number of failing
+ bytes. */
+ return retn;
+
+copy_exception_bytes:
+ /* We already have "retn" bytes cleared, and need to clear the
+ remaining "n" bytes. A non-optimized simple byte-for-byte in-line
+ memset is preferred here, since this isn't speed-critical code and
+ we'd rather have this a leaf-function than calling memset. */
+ {
+ char *endp;
+ for (endp = dst + n; dst < endp; dst++)
+ *dst = 0;
+ }
+
+ return retn + n;
+}
+EXPORT_SYMBOL(__copy_user_zeroing);
+
+/* Zero userspace. */
+unsigned long __do_clear_user(void __user *pto, unsigned long pn)
+{
+ /* We want the parameters put in special registers.
+ Make sure the compiler is able to make something useful of this.
+ As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop).
+
+ FIXME: Comment for old gcc version. Check.
+ If gcc was alright, it really would need no temporaries, and no
+ stack space to save stuff on. */
+
+ register char *dst __asm__ ("r13") = pto;
+ register int n __asm__ ("r12") = pn;
+ register int retn __asm__ ("r10") = 0;
+
+
+ if (((unsigned long) dst & 3) != 0
+ /* Don't align if we wouldn't copy more than a few bytes. */
+ && n >= 3)
+ {
+ if ((unsigned long) dst & 1)
+ {
+ __asm_clear_1 (dst, retn);
+ n--;
+ }
+
+ if ((unsigned long) dst & 2)
+ {
+ __asm_clear_2 (dst, retn);
+ n -= 2;
+ }
+ }
+
+ /* Decide which copying method to use.
+ FIXME: This number is from the "ordinary" kernel memset. */
+ if (n >= 48)
+ {
+ /* For large clears we use 'movem' */
+
+ /* It is not optimal to tell the compiler about clobbering any
+ call-saved registers; that will move the saving/restoring of
+ those registers to the function prologue/epilogue, and make
+ non-movem sizes suboptimal.
+
+ This method is not foolproof; it assumes that the "asm reg"
+ declarations at the beginning of the function really are used
+ here (beware: they may be moved to temporary registers).
+ This way, we do not have to save/move the registers around into
+ temporaries; we can safely use them straight away.
+
+ If you want to check that the allocation was right; then
+ check the equalities in the first comment. It should say
+ something like "r13=r13, r11=r11, r12=r12". */
+ __asm__ volatile ("\
+ .ifnc %0%1%2,$r13$r12$r10 \n\
+ .err \n\
+ .endif \n\
+ \n\
+ ;; Save the registers we'll clobber in the movem process \n\
+ ;; on the stack. Don't mention them to gcc, it will only be \n\
+ ;; upset. \n\
+ subq 11*4,$sp \n\
+ movem $r10,[$sp] \n\
+ \n\
+ clear.d $r0 \n\
+ clear.d $r1 \n\
+ clear.d $r2 \n\
+ clear.d $r3 \n\
+ clear.d $r4 \n\
+ clear.d $r5 \n\
+ clear.d $r6 \n\
+ clear.d $r7 \n\
+ clear.d $r8 \n\
+ clear.d $r9 \n\
+ clear.d $r10 \n\
+ clear.d $r11 \n\
+ \n\
+ ;; Now we've got this: \n\
+ ;; r13 - dst \n\
+ ;; r12 - n \n\
+ \n\
+ ;; Update n for the first loop \n\
+ subq 12*4,$r12 \n\
+0: \n\
+ subq 12*4,$r12 \n\
+1: \n\
+ bge 0b \n\
+ movem $r11,[$r13+] \n\
+ \n\
+ addq 12*4,$r12 ;; compensate for last loop underflowing n \n\
+ \n\
+ ;; Restore registers from stack \n\
+ movem [$sp+],$r10 \n\
+2: \n\
+ .section .fixup,\"ax\" \n\
+3: \n\
+ movem [$sp],$r10 \n\
+ addq 12*4,$r10 \n\
+ addq 12*4,$r13 \n\
+ movem $r10,[$sp] \n\
+ jump 0b \n\
+ clear.d $r10 \n\
+ \n\
+ .previous \n\
+ .section __ex_table,\"a\" \n\
+ .dword 1b,3b \n\
+ .previous"
+
+ /* Outputs */ : "=r" (dst), "=r" (n), "=r" (retn)
+ /* Inputs */ : "0" (dst), "1" (n), "2" (retn)
+ /* Clobber */ : "r11");
+ }
+
+ while (n >= 16)
+ {
+ __asm_clear_16 (dst, retn);
+ n -= 16;
+ }
+
+ /* Having a separate by-four loops cuts down on cache footprint.
+ FIXME: Test with and without; increasing switch to be 0..15. */
+ while (n >= 4)
+ {
+ __asm_clear_4 (dst, retn);
+ n -= 4;
+ }
+
+ switch (n)
+ {
+ case 0:
+ break;
+ case 1:
+ __asm_clear_1 (dst, retn);
+ break;
+ case 2:
+ __asm_clear_2 (dst, retn);
+ break;
+ case 3:
+ __asm_clear_3 (dst, retn);
+ break;
+ }
+
+ return retn;
+}
+EXPORT_SYMBOL(__do_clear_user);