summaryrefslogtreecommitdiff
path: root/arch/cris/arch-v10/lib
diff options
context:
space:
mode:
Diffstat (limited to 'arch/cris/arch-v10/lib')
-rw-r--r--arch/cris/arch-v10/lib/Makefile9
-rw-r--r--arch/cris/arch-v10/lib/checksum.S124
-rw-r--r--arch/cris/arch-v10/lib/checksumcopy.S132
-rw-r--r--arch/cris/arch-v10/lib/csumcpfruser.S64
-rw-r--r--arch/cris/arch-v10/lib/dmacopy.c43
-rw-r--r--arch/cris/arch-v10/lib/dram_init.S205
-rw-r--r--arch/cris/arch-v10/lib/hw_settings.S62
-rw-r--r--arch/cris/arch-v10/lib/memset.c252
-rw-r--r--arch/cris/arch-v10/lib/old_checksum.c85
-rw-r--r--arch/cris/arch-v10/lib/string.c225
-rw-r--r--arch/cris/arch-v10/lib/usercopy.c523
11 files changed, 1724 insertions, 0 deletions
diff --git a/arch/cris/arch-v10/lib/Makefile b/arch/cris/arch-v10/lib/Makefile
new file mode 100644
index 000000000000..36e9a9c5239b
--- /dev/null
+++ b/arch/cris/arch-v10/lib/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for Etrax-specific library files..
+#
+
+
+EXTRA_AFLAGS := -traditional
+
+lib-y = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o
+
diff --git a/arch/cris/arch-v10/lib/checksum.S b/arch/cris/arch-v10/lib/checksum.S
new file mode 100644
index 000000000000..85c48f0a9ec2
--- /dev/null
+++ b/arch/cris/arch-v10/lib/checksum.S
@@ -0,0 +1,124 @@
+/* $Id: checksum.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $
+ * A fast checksum routine using movem
+ * Copyright (c) 1998-2001 Axis Communications AB
+ *
+ * csum_partial(const unsigned char * buff, int len, unsigned int sum)
+ */
+
+ .globl csum_partial
+csum_partial:
+
+ ;; r10 - src
+ ;; r11 - length
+ ;; r12 - checksum
+
+ ;; check for breakeven length between movem and normal word looping versions
+ ;; we also do _NOT_ want to compute a checksum over more than the
+ ;; actual length when length < 40
+
+ cmpu.w 80,$r11
+ blo _word_loop
+ nop
+
+ ;; need to save the registers we use below in the movem loop
+ ;; this overhead is why we have a check above for breakeven length
+ ;; only r0 - r8 have to be saved, the other ones are clobber-able
+ ;; according to the ABI
+
+ subq 9*4,$sp
+ movem $r8,[$sp]
+
+ ;; do a movem checksum
+
+ subq 10*4,$r11 ; update length for the first loop
+
+_mloop: movem [$r10+],$r9 ; read 10 longwords
+
+ ;; perform dword checksumming on the 10 longwords
+
+ add.d $r0,$r12
+ ax
+ add.d $r1,$r12
+ ax
+ add.d $r2,$r12
+ ax
+ add.d $r3,$r12
+ ax
+ add.d $r4,$r12
+ ax
+ add.d $r5,$r12
+ ax
+ add.d $r6,$r12
+ ax
+ add.d $r7,$r12
+ ax
+ add.d $r8,$r12
+ ax
+ add.d $r9,$r12
+
+ ;; fold the carry into the checksum, to avoid having to loop the carry
+ ;; back into the top
+
+ ax
+ addq 0,$r12
+ ax ; do it again, since we might have generated a carry
+ addq 0,$r12
+
+ subq 10*4,$r11
+ bge _mloop
+ nop
+
+ addq 10*4,$r11 ; compensate for last loop underflowing length
+
+ movem [$sp+],$r8 ; restore regs
+
+_word_loop:
+ ;; only fold if there is anything to fold.
+
+ cmpq 0,$r12
+ beq _no_fold
+
+ ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below.
+ ;; r9 and r13 can be used as temporaries.
+
+ 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
+ add.d $r13,$r12 ; checksum += r13
+ move.d $r12,$r13 ; do the same again, maybe we got a carry last add
+ lsrq 16,$r13
+ and.d $r9,$r12
+ add.d $r13,$r12
+
+_no_fold:
+ cmpq 2,$r11
+ blt _no_words
+ nop
+
+ ;; checksum the rest of the words
+
+ subq 2,$r11
+
+_wloop: subq 2,$r11
+ bge _wloop
+ addu.w [$r10+],$r12
+
+ addq 2,$r11
+
+_no_words:
+ ;; see if we have one odd byte more
+ cmpq 1,$r11
+ beq _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
+
diff --git a/arch/cris/arch-v10/lib/checksumcopy.S b/arch/cris/arch-v10/lib/checksumcopy.S
new file mode 100644
index 000000000000..35cbffb306fd
--- /dev/null
+++ b/arch/cris/arch-v10/lib/checksumcopy.S
@@ -0,0 +1,132 @@
+/* $Id: checksumcopy.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $
+ * A fast checksum+copy routine using movem
+ * Copyright (c) 1998, 2001 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
+csum_partial_copy_nocheck:
+
+ ;; r10 - src
+ ;; r11 - dst
+ ;; r12 - length
+ ;; r13 - checksum
+
+ ;; check for breakeven length between movem and normal word looping versions
+ ;; we also do _NOT_ want to compute a checksum over more than the
+ ;; actual length when length < 40
+
+ cmpu.w 80, $r12
+ blo _word_loop
+ nop
+
+ ;; need to save the registers we use below in the movem loop
+ ;; this overhead is why we have a check above for breakeven length
+ ;; only r0 - r8 have to be saved, the other ones are clobber-able
+ ;; according to the ABI
+
+ subq 9*4, $sp
+ movem $r8, [$sp]
+
+ ;; do a movem copy and checksum
+
+ subq 10*4, $r12 ; update length for the first loop
+
+_mloop: movem [$r10+],$r9 ; read 10 longwords
+1: ;; A failing userspace access will have this as PC.
+ movem $r9,[$r11+] ; write 10 longwords
+
+ ;; perform dword checksumming on the 10 longwords
+
+ add.d $r0,$r13
+ ax
+ add.d $r1,$r13
+ ax
+ add.d $r2,$r13
+ ax
+ add.d $r3,$r13
+ ax
+ add.d $r4,$r13
+ ax
+ add.d $r5,$r13
+ ax
+ add.d $r6,$r13
+ ax
+ add.d $r7,$r13
+ ax
+ add.d $r8,$r13
+ ax
+ add.d $r9,$r13
+
+ ;; fold the carry into the checksum, to avoid having to loop the carry
+ ;; back into the top
+
+ ax
+ addq 0,$r13
+ ax ; do it again, since we might have generated a carry
+ addq 0,$r13
+
+ subq 10*4,$r12
+ bge _mloop
+ nop
+
+ addq 10*4,$r12 ; compensate for last loop underflowing length
+
+ movem [$sp+],$r8 ; restore regs
+
+_word_loop:
+ ;; only fold if there is anything to fold.
+
+ cmpq 0,$r13
+ beq _no_fold
+
+ ;; 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
+ add.d $r9,$r13 ; checksum += r0
+ move.d $r13,$r9 ; do the same again, maybe we got a carry last add
+ lsrq 16,$r9
+ and.d 0xffff,$r13
+ add.d $r9,$r13
+
+_no_fold:
+ cmpq 2,$r12
+ blt _no_words
+ nop
+
+ ;; copy and checksum the rest of the words
+
+ subq 2,$r12
+
+_wloop: move.w [$r10+],$r9
+2: ;; A failing userspace access will have this as PC.
+ addu.w $r9,$r13
+ subq 2,$r12
+ bge _wloop
+ move.w $r9,[$r11+]
+
+ addq 2,$r12
+
+_no_words:
+ ;; see if we have one odd byte more
+ cmpq 1,$r12
+ beq _do_byte
+ nop
+ ret
+ move.d $r13, $r10
+
+_do_byte:
+ ;; copy and checksum the last byte
+ move.b [$r10],$r9
+3: ;; A failing userspace access will have this as PC.
+ addu.b $r9,$r13
+ move.b $r9,[$r11]
+ ret
+ move.d $r13, $r10
diff --git a/arch/cris/arch-v10/lib/csumcpfruser.S b/arch/cris/arch-v10/lib/csumcpfruser.S
new file mode 100644
index 000000000000..5f41ccd62754
--- /dev/null
+++ b/arch/cris/arch-v10/lib/csumcpfruser.S
@@ -0,0 +1,64 @@
+/*
+ * Add-on to transform csum_partial_copy_nocheck in checksumcopy.S into
+ * csum_partial_copy_from_user by adding exception records.
+ *
+ * Copyright (C) 2001 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.
+ moveq -EFAULT,$r9
+ move.d $r9,[[$sp]]
+
+;; Clear the rest of the destination area using memset. Preserve the
+;; checksum for the readable bytes.
+ push $srp
+ push $r13
+ move.d $r11,$r10
+ clear.d $r11
+ jsr memset
+ pop $r10
+ jump [$sp+]
+
+ .previous
+ .section __ex_table,"a"
+ .dword 1b,4b
+ .dword 2b,5b
+ .dword 3b,6b
+ .previous
diff --git a/arch/cris/arch-v10/lib/dmacopy.c b/arch/cris/arch-v10/lib/dmacopy.c
new file mode 100644
index 000000000000..e5fb44f505c5
--- /dev/null
+++ b/arch/cris/arch-v10/lib/dmacopy.c
@@ -0,0 +1,43 @@
+/* $Id: dmacopy.c,v 1.1 2001/12/17 13:59:27 bjornw Exp $
+ *
+ * memcpy for large blocks, using memory-memory DMA channels 6 and 7 in Etrax
+ */
+
+#include <asm/svinto.h>
+#include <asm/io.h>
+
+#define D(x)
+
+void *dma_memcpy(void *pdst,
+ const void *psrc,
+ unsigned int pn)
+{
+ static etrax_dma_descr indma, outdma;
+
+ D(printk("dma_memcpy %d bytes... ", pn));
+
+#if 0
+ *R_GEN_CONFIG = genconfig_shadow =
+ (genconfig_shadow & ~0x3c0000) |
+ IO_STATE(R_GEN_CONFIG, dma6, intdma7) |
+ IO_STATE(R_GEN_CONFIG, dma7, intdma6);
+#endif
+ indma.sw_len = outdma.sw_len = pn;
+ indma.ctrl = d_eol | d_eop;
+ outdma.ctrl = d_eol;
+ indma.buf = psrc;
+ outdma.buf = pdst;
+
+ *R_DMA_CH6_FIRST = &indma;
+ *R_DMA_CH7_FIRST = &outdma;
+ *R_DMA_CH6_CMD = IO_STATE(R_DMA_CH6_CMD, cmd, start);
+ *R_DMA_CH7_CMD = IO_STATE(R_DMA_CH7_CMD, cmd, start);
+
+ while(*R_DMA_CH7_CMD == 1) /* wait for completion */ ;
+
+ D(printk("done\n"));
+
+}
+
+
+
diff --git a/arch/cris/arch-v10/lib/dram_init.S b/arch/cris/arch-v10/lib/dram_init.S
new file mode 100644
index 000000000000..2ef4ad5706ef
--- /dev/null
+++ b/arch/cris/arch-v10/lib/dram_init.S
@@ -0,0 +1,205 @@
+/* $Id: dram_init.S,v 1.4 2003/09/22 09:21:59 starvik Exp $
+ *
+ * DRAM/SDRAM initialization - alter with care
+ * This file is intended to be included from other assembler files
+ *
+ * Note: This file may not modify r9 because r9 is used to carry
+ * information from the decompresser to the kernel
+ *
+ * Copyright (C) 2000, 2001 Axis Communications AB
+ *
+ * Authors: Mikael Starvik (starvik@axis.com)
+ *
+ * $Log: dram_init.S,v $
+ * Revision 1.4 2003/09/22 09:21:59 starvik
+ * Decompresser is linked to 0x407xxxxx and sdram commands are at 0x000xxxxx
+ * so we need to mask off 12 bits.
+ *
+ * Revision 1.3 2003/03/31 09:38:37 starvik
+ * Corrected calculation of end of sdram init commands
+ *
+ * Revision 1.2 2002/11/19 13:33:29 starvik
+ * Changes from Linux 2.4
+ *
+ * Revision 1.13 2002/10/30 07:42:28 starvik
+ * Always read SDRAM command sequence from flash
+ *
+ * Revision 1.12 2002/08/09 11:37:37 orjanf
+ * Added double initialization work-around for Samsung SDRAMs.
+ *
+ * Revision 1.11 2002/06/04 11:43:21 starvik
+ * Check if mrs_data is specified in kernelconfig (necessary for MCM)
+ *
+ * Revision 1.10 2001/10/04 12:00:21 martinnn
+ * Added missing underscores.
+ *
+ * Revision 1.9 2001/10/01 14:47:35 bjornw
+ * Added register prefixes and removed underscores
+ *
+ * Revision 1.8 2001/05/15 07:12:45 hp
+ * Copy warning from head.S about r8 and r9
+ *
+ * Revision 1.7 2001/04/18 12:05:39 bjornw
+ * Fixed comments, and explicitely include config.h to be sure its there
+ *
+ * Revision 1.6 2001/04/10 06:20:16 starvik
+ * Delay should be 200us, not 200ns
+ *
+ * Revision 1.5 2001/04/09 06:01:13 starvik
+ * Added support for 100 MHz SDRAMs
+ *
+ * Revision 1.4 2001/03/26 14:24:01 bjornw
+ * Namechange of some config options
+ *
+ * Revision 1.3 2001/03/23 08:29:41 starvik
+ * Corrected calculation of mrs_data
+ *
+ * Revision 1.2 2001/02/08 15:20:00 starvik
+ * Corrected SDRAM initialization
+ * Should now be included as inline
+ *
+ * Revision 1.1 2001/01/29 13:08:02 starvik
+ * Initial version
+ * This file should be included from all assembler files that needs to
+ * initialize DRAM/SDRAM.
+ *
+ */
+
+/* Just to be certain the config file is included, we include it here
+ * explicitely instead of depending on it being included in the file that
+ * uses this code.
+ */
+
+#include <linux/config.h>
+
+ ;; WARNING! The registers r8 and r9 are used as parameters carrying
+ ;; information from the decompressor (if the kernel was compressed).
+ ;; They should not be used in the code below.
+
+#ifndef CONFIG_SVINTO_SIM
+ move.d CONFIG_ETRAX_DEF_R_WAITSTATES, $r0
+ move.d $r0, [R_WAITSTATES]
+
+ move.d CONFIG_ETRAX_DEF_R_BUS_CONFIG, $r0
+ move.d $r0, [R_BUS_CONFIG]
+
+#ifndef CONFIG_ETRAX_SDRAM
+ move.d CONFIG_ETRAX_DEF_R_DRAM_CONFIG, $r0
+ move.d $r0, [R_DRAM_CONFIG]
+
+ move.d CONFIG_ETRAX_DEF_R_DRAM_TIMING, $r0
+ move.d $r0, [R_DRAM_TIMING]
+#else
+ ;; Samsung SDRAMs seem to require to be initialized twice to work properly.
+ moveq 2, $r6
+_sdram_init:
+
+ ; Refer to ETRAX 100LX Designers Reference for a description of SDRAM initialization
+
+ ; Bank configuration
+ move.d CONFIG_ETRAX_DEF_R_SDRAM_CONFIG, $r0
+ move.d $r0, [R_SDRAM_CONFIG]
+
+ ; Calculate value of mrs_data
+ ; CAS latency = 2 && bus_width = 32 => 0x40
+ ; CAS latency = 3 && bus_width = 32 => 0x60
+ ; CAS latency = 2 && bus_width = 16 => 0x20
+ ; CAS latency = 3 && bus_width = 16 => 0x30
+
+ ; Check if value is already supplied in kernel config
+ move.d CONFIG_ETRAX_DEF_R_SDRAM_TIMING, $r2
+ and.d 0x00ff0000, $r2
+ bne _set_timing
+ lsrq 16, $r2
+
+ move.d 0x40, $r2 ; Assume 32 bits and CAS latency = 2
+ move.d CONFIG_ETRAX_DEF_R_SDRAM_TIMING, $r1
+ move.d $r1, $r3
+ and.d 0x03, $r1 ; Get CAS latency
+ and.d 0x1000, $r3 ; 50 or 100 MHz?
+ beq _speed_50
+ nop
+_speed_100:
+ cmp.d 0x00, $r1 ; CAS latency = 2?
+ beq _bw_check
+ nop
+ or.d 0x20, $r2 ; CAS latency = 3
+ ba _bw_check
+ nop
+_speed_50:
+ cmp.d 0x01, $r1 ; CAS latency = 2?
+ beq _bw_check
+ nop
+ or.d 0x20, $r2 ; CAS latency = 3
+_bw_check:
+ move.d CONFIG_ETRAX_DEF_R_SDRAM_CONFIG, $r1
+ and.d 0x800000, $r1 ; DRAM width is bit 23
+ bne _set_timing
+ nop
+ lsrq 1, $r2 ; 16 bits. Shift down value.
+
+ ; Set timing parameters. Starts master clock
+_set_timing:
+ move.d CONFIG_ETRAX_DEF_R_SDRAM_TIMING, $r1
+ and.d 0x8000f9ff, $r1 ; Make sure mrs data and command is 0
+ or.d 0x80000000, $r1 ; Make sure sdram enable bit is set
+ move.d $r1, $r5
+ or.d 0x0000c000, $r1 ; ref = disable
+ lslq 16, $r2 ; mrs data starts at bit 16
+ or.d $r2, $r1
+ move.d $r1, [R_SDRAM_TIMING]
+
+ ; Wait 200us
+ move.d 10000, $r2
+1: bne 1b
+ subq 1, $r2
+
+ ; Issue initialization command sequence
+ move.d _sdram_commands_start, $r2
+ and.d 0x000fffff, $r2 ; Make sure commands are read from flash
+ move.d _sdram_commands_end, $r3
+ and.d 0x000fffff, $r3
+1: clear.d $r4
+ move.b [$r2+], $r4
+ lslq 9, $r4 ; Command starts at bit 9
+ or.d $r1, $r4
+ move.d $r4, [R_SDRAM_TIMING]
+ nop ; Wait five nop cycles between each command
+ nop
+ nop
+ nop
+ nop
+ cmp.d $r2, $r3
+ bne 1b
+ nop
+ move.d $r5, [R_SDRAM_TIMING]
+ subq 1, $r6
+ bne _sdram_init
+ nop
+ ba _sdram_commands_end
+ nop
+
+_sdram_commands_start:
+ .byte 3 ; Precharge
+ .byte 0 ; nop
+ .byte 2 ; refresh
+ .byte 0 ; nop
+ .byte 2 ; refresh
+ .byte 0 ; nop
+ .byte 2 ; refresh
+ .byte 0 ; nop
+ .byte 2 ; refresh
+ .byte 0 ; nop
+ .byte 2 ; refresh
+ .byte 0 ; nop
+ .byte 2 ; refresh
+ .byte 0 ; nop
+ .byte 2 ; refresh
+ .byte 0 ; nop
+ .byte 2 ; refresh
+ .byte 0 ; nop
+ .byte 1 ; mrs
+ .byte 0 ; nop
+_sdram_commands_end:
+#endif
+#endif
diff --git a/arch/cris/arch-v10/lib/hw_settings.S b/arch/cris/arch-v10/lib/hw_settings.S
new file mode 100644
index 000000000000..56905aaa7b6e
--- /dev/null
+++ b/arch/cris/arch-v10/lib/hw_settings.S
@@ -0,0 +1,62 @@
+/*
+ * $Id: hw_settings.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $
+ *
+ * This table is used by some tools to extract hardware parameters.
+ * The table should be included in the kernel and the decompressor.
+ * Don't forget to update the tools if you change this table.
+ *
+ * Copyright (C) 2001 Axis Communications AB
+ *
+ * Authors: Mikael Starvik (starvik@axis.com)
+ */
+
+#define PA_SET_VALUE ((CONFIG_ETRAX_DEF_R_PORT_PA_DIR << 8) | \
+ (CONFIG_ETRAX_DEF_R_PORT_PA_DATA))
+#define PB_SET_VALUE ((CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG << 16) | \
+ (CONFIG_ETRAX_DEF_R_PORT_PB_DIR << 8) | \
+ (CONFIG_ETRAX_DEF_R_PORT_PB_DATA))
+
+ .ascii "HW_PARAM_MAGIC" ; Magic number
+ .dword 0xc0004000 ; Kernel start address
+
+ ; Debug port
+#ifdef CONFIG_ETRAX_DEBUG_PORT0
+ .dword 0
+#elif defined(CONFIG_ETRAX_DEBUG_PORT1)
+ .dword 1
+#elif defined(CONFIG_ETRAX_DEBUG_PORT2)
+ .dword 2
+#elif defined(CONFIG_ETRAX_DEBUG_PORT3)
+ .dword 3
+#else
+ .dword 4 ; No debug
+#endif
+
+ ; SDRAM or EDO DRAM?
+#ifdef CONFIG_ETRAX_SDRAM
+ .dword 1
+#else
+ .dword 0
+#endif
+
+ ; Register values
+ .dword R_WAITSTATES
+ .dword CONFIG_ETRAX_DEF_R_WAITSTATES
+ .dword R_BUS_CONFIG
+ .dword CONFIG_ETRAX_DEF_R_BUS_CONFIG
+#ifdef CONFIG_ETRAX_SDRAM
+ .dword R_SDRAM_CONFIG
+ .dword CONFIG_ETRAX_DEF_R_SDRAM_CONFIG
+ .dword R_SDRAM_TIMING
+ .dword CONFIG_ETRAX_DEF_R_SDRAM_TIMING
+#else
+ .dword R_DRAM_CONFIG
+ .dword CONFIG_ETRAX_DEF_R_DRAM_CONFIG
+ .dword R_DRAM_TIMING
+ .dword CONFIG_ETRAX_DEF_R_DRAM_TIMING
+#endif
+ .dword R_PORT_PA_SET
+ .dword PA_SET_VALUE
+ .dword R_PORT_PB_SET
+ .dword PB_SET_VALUE
+ .dword 0 ; No more register values
diff --git a/arch/cris/arch-v10/lib/memset.c b/arch/cris/arch-v10/lib/memset.c
new file mode 100644
index 000000000000..82bb66839171
--- /dev/null
+++ b/arch/cris/arch-v10/lib/memset.c
@@ -0,0 +1,252 @@
+/*#************************************************************************#*/
+/*#-------------------------------------------------------------------------*/
+/*# */
+/*# FUNCTION NAME: memset() */
+/*# */
+/*# PARAMETERS: void* dst; Destination address. */
+/*# int c; Value of byte to write. */
+/*# int len; Number of bytes to write. */
+/*# */
+/*# RETURNS: dst. */
+/*# */
+/*# DESCRIPTION: Sets the memory dst of length len bytes to c, as standard. */
+/*# Framework taken from memcpy. This routine is */
+/*# very sensitive to compiler changes in register allocation. */
+/*# Should really be rewritten to avoid this problem. */
+/*# */
+/*#-------------------------------------------------------------------------*/
+/*# */
+/*# HISTORY */
+/*# */
+/*# DATE NAME CHANGES */
+/*# ---- ---- ------- */
+/*# 990713 HP Tired of watching this function (or */
+/*# really, the nonoptimized generic */
+/*# implementation) take up 90% of simulator */
+/*# output. Measurements needed. */
+/*# */
+/*#-------------------------------------------------------------------------*/
+
+#include <linux/types.h>
+
+/* No, there's no macro saying 12*4, since it is "hard" to get it into
+ the asm in a good way. Thus better to expose the problem everywhere.
+ */
+
+/* Assuming 1 cycle per dword written or read (ok, not really true), and
+ one 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 ZERO_BLOCK_SIZE (1*12*4)
+
+void *memset(void *pdst,
+ int c,
+ size_t plen)
+{
+ /* Ok. Now we want the parameters put in special registers.
+ Make sure the compiler is able to make something useful of 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. Only those memsetting about 3..4
+ bytes or less get penalized compared to the generic implementation
+ - and that's not really sane use. */
+
+ /* Ugh. This is fragile at best. Check with newer GCC releases, if
+ they compile cascaded "x |= x << 8" sanely! */
+ __asm__("movu.b %0,$r13\n\t"
+ "lslq 8,$r13\n\t"
+ "move.b %0,$r13\n\t"
+ "move.d $r13,%0\n\t"
+ "lslq 16,$r13\n\t"
+ "or.d $r13,%0"
+ : "=r" (lc) : "0" (lc) : "r13");
+
+ {
+ register char *dst __asm__ ("r13") = pdst;
+
+ /* This is NONPORTABLE, but since this whole routine is */
+ /* grossly nonportable that doesn't matter. */
+
+ if (((unsigned long) pdst & 3) != 0
+ /* Oops! n=0 must be a legal 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;
+ }
+ }
+
+ /* Now the fun part. For the threshold value of this, check the equation
+ above. */
+ /* Decide which copying method to use. */
+ if (n >= ZERO_BLOCK_SIZE)
+ {
+ /* 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.
+
+ 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
+ "r13=r13, r12=r12, r11=r11" */
+ __asm__ volatile ("
+ ;; Check that the following is true (same register names on
+ ;; both sides of equal sign, as in r8=r8):
+ ;; %0=r13, %1=r12, %4=r11
+ ;;
+ ;; Save the registers we'll clobber in the movem process
+ ;; on the stack. Don't mention them to gcc, it will only be
+ ;; upset.
+ subq 11*4,$sp
+ movem $r10,[$sp]
+
+ move.d $r11,$r0
+ move.d $r11,$r1
+ move.d $r11,$r2
+ move.d $r11,$r3
+ move.d $r11,$r4
+ move.d $r11,$r5
+ move.d $r11,$r6
+ move.d $r11,$r7
+ move.d $r11,$r8
+ move.d $r11,$r9
+ move.d $r11,$r10
+
+ ;; Now we've got this:
+ ;; r13 - dst
+ ;; r12 - n
+
+ ;; Update n for the first loop
+ subq 12*4,$r12
+0:
+ subq 12*4,$r12
+ bge 0b
+ movem $r11,[$r13+]
+
+ addq 12*4,$r12 ;; compensate for last loop underflowing n
+
+ ;; Restore registers from stack
+ movem [$sp+],$r10"
+
+ /* Outputs */ : "=r" (dst), "=r" (n)
+ /* Inputs */ : "0" (dst), "1" (n), "r" (lc));
+
+ }
+
+ /* Either we directly starts copying, 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,n. */
+
+ while ( n >= 16 )
+ {
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ n -= 16;
+ }
+
+ /* A switch() is definitely the fastest although it takes a LOT of code.
+ * Particularly if you inline code this.
+ */
+ switch (n)
+ {
+ case 0:
+ break;
+ case 1:
+ *(char*)dst = (char) lc;
+ break;
+ case 2:
+ *(short*)dst = (short) lc;
+ break;
+ case 3:
+ *((short*)dst)++ = (short) lc;
+ *(char*)dst = (char) lc;
+ break;
+ case 4:
+ *((long*)dst)++ = lc;
+ break;
+ case 5:
+ *((long*)dst)++ = lc;
+ *(char*)dst = (char) lc;
+ break;
+ case 6:
+ *((long*)dst)++ = lc;
+ *(short*)dst = (short) lc;
+ break;
+ case 7:
+ *((long*)dst)++ = lc;
+ *((short*)dst)++ = (short) lc;
+ *(char*)dst = (char) lc;
+ break;
+ case 8:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ break;
+ case 9:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *(char*)dst = (char) lc;
+ break;
+ case 10:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *(short*)dst = (short) lc;
+ break;
+ case 11:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((short*)dst)++ = (short) lc;
+ *(char*)dst = (char) lc;
+ break;
+ case 12:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ break;
+ case 13:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *(char*)dst = (char) lc;
+ break;
+ case 14:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *(short*)dst = (short) lc;
+ break;
+ case 15:
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((long*)dst)++ = lc;
+ *((short*)dst)++ = (short) lc;
+ *(char*)dst = (char) lc;
+ break;
+ }
+ }
+
+ return return_dst; /* destination pointer. */
+} /* memset() */
diff --git a/arch/cris/arch-v10/lib/old_checksum.c b/arch/cris/arch-v10/lib/old_checksum.c
new file mode 100644
index 000000000000..22a6f0aa9cef
--- /dev/null
+++ b/arch/cris/arch-v10/lib/old_checksum.c
@@ -0,0 +1,85 @@
+/* $Id: old_checksum.c,v 1.3 2003/10/27 08:04:32 starvik Exp $
+ *
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * IP/TCP/UDP checksumming routines
+ *
+ * Authors: Jorge Cwik, <jorge@laser.satlink.net>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ * Tom May, <ftom@netcom.com>
+ * Lots of code moved from tcp.c and ip.c; see those files
+ * for more names.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <net/checksum.h>
+#include <net/module.h>
+
+#undef PROFILE_CHECKSUM
+
+#ifdef PROFILE_CHECKSUM
+/* these are just for profiling the checksum code with an oscillioscope.. uh */
+#if 0
+#define BITOFF *((unsigned char *)0xb0000030) = 0xff
+#define BITON *((unsigned char *)0xb0000030) = 0x0
+#endif
+#include <asm/io.h>
+#define CBITON LED_ACTIVE_SET(1)
+#define CBITOFF LED_ACTIVE_SET(0)
+#define BITOFF
+#define BITON
+#else
+#define BITOFF
+#define BITON
+#define CBITOFF
+#define CBITON
+#endif
+
+/*
+ * computes a partial checksum, e.g. for TCP/UDP fragments
+ */
+
+#include <asm/delay.h>
+
+unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
+{
+ /*
+ * Experiments with ethernet and slip connections show that buff
+ * is aligned on either a 2-byte or 4-byte boundary.
+ */
+ const unsigned char *endMarker = buff + len;
+ const unsigned char *marker = endMarker - (len % 16);
+#if 0
+ if((int)buff & 0x3)
+ printk("unaligned buff %p\n", buff);
+ __delay(900); /* extra delay of 90 us to test performance hit */
+#endif
+ BITON;
+ while (buff < marker) {
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ sum += *((unsigned short *)buff)++;
+ }
+ marker = endMarker - (len % 2);
+ while(buff < marker) {
+ sum += *((unsigned short *)buff)++;
+ }
+ if(endMarker - buff > 0) {
+ sum += *buff; /* add extra byte seperately */
+ }
+ BITOFF;
+ return(sum);
+}
+
+EXPORT_SYMBOL(csum_partial);
diff --git a/arch/cris/arch-v10/lib/string.c b/arch/cris/arch-v10/lib/string.c
new file mode 100644
index 000000000000..8ffde4901b57
--- /dev/null
+++ b/arch/cris/arch-v10/lib/string.c
@@ -0,0 +1,225 @@
+/*#************************************************************************#*/
+/*#-------------------------------------------------------------------------*/
+/*# */
+/*# FUNCTION NAME: memcpy() */
+/*# */
+/*# PARAMETERS: void* dst; Destination address. */
+/*# void* src; Source address. */
+/*# int len; Number of bytes to copy. */
+/*# */
+/*# RETURNS: dst. */
+/*# */
+/*# DESCRIPTION: Copies len bytes of memory from src to dst. No guarantees */
+/*# about copying of overlapping memory areas. This routine is */
+/*# very sensitive to compiler changes in register allocation. */
+/*# Should really be rewritten to avoid this problem. */
+/*# */
+/*#-------------------------------------------------------------------------*/
+/*# */
+/*# HISTORY */
+/*# */
+/*# DATE NAME CHANGES */
+/*# ---- ---- ------- */
+/*# 941007 Kenny R Creation */
+/*# 941011 Kenny R Lots of optimizations and inlining. */
+/*# 941129 Ulf A Adapted for use in libc. */
+/*# 950216 HP N==0 forgotten if non-aligned src/dst. */
+/*# Added some optimizations. */
+/*# 001025 HP Make src and dst char *. Align dst to */
+/*# dword, not just word-if-both-src-and-dst- */
+/*# are-misaligned. */
+/*# */
+/*#-------------------------------------------------------------------------*/
+
+#include <linux/types.h>
+
+void *memcpy(void *pdst,
+ const void *psrc,
+ size_t pn)
+{
+ /* Ok. 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 char *dst __asm__ ("r13") = pdst;
+ register 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--;
+ *(char*)dst = *(char*)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 >= 44*2) /* Break even between movem and
+ move16 is at 38.7*2, but modulo 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.
+
+ 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
+ "r13=r13, r11=r11, r12=r12" */
+ __asm__ volatile ("
+ ;; Check that the following is true (same register names on
+ ;; both sides of equal sign, as in r8=r8):
+ ;; %0=r13, %1=r11, %2=r12
+ ;;
+ ;; Save the registers we'll use in the movem process
+ ;; on the stack.
+ subq 11*4,$sp
+ movem $r10,[$sp]
+
+ ;; Now we've got this:
+ ;; r11 - src
+ ;; r13 - dst
+ ;; r12 - n
+
+ ;; Update n for the first loop
+ subq 44,$r12
+0:
+ movem [$r11+],$r10
+ subq 44,$r12
+ bge 0b
+ movem $r10,[$r13+]
+
+ addq 44,$r12 ;; compensate for last loop underflowing n
+
+ ;; Restore registers from stack
+ movem [$sp+],$r10"
+
+ /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n)
+ /* Inputs */ : "0" (dst), "1" (src), "2" (n));
+
+ }
+
+ /* Either we directly starts copying, 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,n. */
+
+ while ( n >= 16 )
+ {
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ n -= 16;
+ }
+
+ /* A switch() is definitely the fastest although it takes a LOT of code.
+ * Particularly if you inline code this.
+ */
+ switch (n)
+ {
+ case 0:
+ break;
+ case 1:
+ *(char*)dst = *(char*)src;
+ break;
+ case 2:
+ *(short*)dst = *(short*)src;
+ break;
+ case 3:
+ *((short*)dst)++ = *((short*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ case 4:
+ *((long*)dst)++ = *((long*)src)++;
+ break;
+ case 5:
+ *((long*)dst)++ = *((long*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ case 6:
+ *((long*)dst)++ = *((long*)src)++;
+ *(short*)dst = *(short*)src;
+ break;
+ case 7:
+ *((long*)dst)++ = *((long*)src)++;
+ *((short*)dst)++ = *((short*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ case 8:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ break;
+ case 9:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ case 10:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *(short*)dst = *(short*)src;
+ break;
+ case 11:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((short*)dst)++ = *((short*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ case 12:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ break;
+ case 13:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ case 14:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *(short*)dst = *(short*)src;
+ break;
+ case 15:
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((long*)dst)++ = *((long*)src)++;
+ *((short*)dst)++ = *((short*)src)++;
+ *(char*)dst = *(char*)src;
+ break;
+ }
+
+ return return_dst; /* destination pointer. */
+} /* memcpy() */
diff --git a/arch/cris/arch-v10/lib/usercopy.c b/arch/cris/arch-v10/lib/usercopy.c
new file mode 100644
index 000000000000..43778d53c254
--- /dev/null
+++ b/arch/cris/arch-v10/lib/usercopy.c
@@ -0,0 +1,523 @@
+/*
+ * User address space access functions.
+ * The non-inlined parts of asm-cris/uaccess.h are here.
+ *
+ * Copyright (C) 2000, 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 2.96 20000427 (experimental)".
+
+ Check regularly...
+
+ Note that the PC saved at a bus-fault is the address *after* the
+ faulting instruction, which means the branch-target for instructions in
+ delay-slots for taken branches. Note also that the postincrement in
+ the instruction is performed regardless of bus-fault; the register is
+ seen updated in fault handlers.
+
+ Oh, and on the code formatting issue, to whomever feels like "fixing
+ it" to Conformity: I'm too "lazy", but why don't you go ahead and "fix"
+ string.c too. I just don't think too many people will hack this file
+ for the code format to be an issue. */
+
+
+/* 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 allright, 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;
+ }
+ }
+
+ /* Decide which copying method to use. */
+ if (n >= 44*2) /* Break even between movem and
+ move16 is at 38.7*2, but modulo 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.
+
+ 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
+ "r13=r13, r11=r11, r12=r12". */
+ __asm__ volatile ("\
+ .ifnc %0%1%2%3,$r13$r11$r12$r10 \n\
+ .err \n\
+ .endif \n\
+
+ ;; Save the registers we'll use in the movem process
+ ;; on the stack.
+ subq 11*4,$sp
+ movem $r10,[$sp]
+
+ ;; Now we've got this:
+ ;; r11 - src
+ ;; r13 - dst
+ ;; r12 - n
+
+ ;; Update n for the first loop
+ subq 44,$r12
+
+; Since the noted PC of a faulting instruction in a delay-slot of a taken
+; branch, is that of the branch target, we actually point at the from-movem
+; for this case. There is no ambiguity here; if there was a fault in that
+; instruction (meaning a kernel oops), the faulted PC would be the address
+; after *that* movem.
+
+0:
+ movem [$r11+],$r10
+ subq 44,$r12
+ bge 0b
+ movem $r10,[$r13+]
+1:
+ addq 44,$r12 ;; compensate for last loop underflowing n
+
+ ;; Restore registers from stack
+ movem [$sp+],$r10
+2:
+ .section .fixup,\"ax\"
+
+; To provide a correct count in r10 of bytes that failed to be copied,
+; we jump back into the loop if the loop-branch was taken. There is no
+; performance penalty for sany use; the program will segfault soon enough.
+
+3:
+ move.d [$sp],$r10
+ addq 44,$r10
+ move.d $r10,[$sp]
+ jump 0b
+4:
+ movem [$sp+],$r10
+ addq 44,$r10
+ addq 44,$r12
+ jump 2b
+
+ .previous
+ .section __ex_table,\"a\"
+ .dword 0b,3b
+ .dword 1b,4b
+ .previous"
+
+ /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n), "=r" (retn)
+ /* Inputs */ : "0" (dst), "1" (src), "2" (n), "3" (retn));
+
+ }
+
+ /* Either we directly start copying, 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. */
+
+ 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;
+}
+
+/* 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 __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 allright, 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;
+ }
+
+ /* Decide which copying method to use. */
+ if (n >= 44*2) /* Break even between movem and
+ move16 is at 38.7*2, but modulo 44.
+ FIXME: We use move4 now. */
+ {
+ /* 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.
+
+ 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
+ "r13=r13, r11=r11, r12=r12" */
+ __asm__ volatile ("
+ .ifnc %0%1%2%3,$r13$r11$r12$r10 \n\
+ .err \n\
+ .endif \n\
+
+ ;; Save the registers we'll use in the movem process
+ ;; on the stack.
+ subq 11*4,$sp
+ movem $r10,[$sp]
+
+ ;; Now we've got this:
+ ;; r11 - src
+ ;; r13 - dst
+ ;; r12 - n
+
+ ;; Update n for the first loop
+ subq 44,$r12
+0:
+ movem [$r11+],$r10
+1:
+ subq 44,$r12
+ bge 0b
+ movem $r10,[$r13+]
+
+ addq 44,$r12 ;; compensate for last loop underflowing n
+
+ ;; Restore registers from stack
+ movem [$sp+],$r10
+4:
+ .section .fixup,\"ax\"
+
+;; Do not jump back into the loop if we fail. For some uses, we get a
+;; page fault somewhere on the line. Without checking for page limits,
+;; we don't know where, but we need to copy accurately and keep an
+;; accurate count; not just clear the whole line. To do that, we fall
+;; down in the code below, proceeding with smaller amounts. It should
+;; be kept in mind that we have to cater to code like what at one time
+;; was in fs/super.c:
+;; i = size - copy_from_user((void *)page, data, size);
+;; which would cause repeated faults while clearing the remainder of
+;; the SIZE bytes at PAGE after the first fault.
+;; A caveat here is that we must not fall through from a failing page
+;; to a valid page.
+
+3:
+ movem [$sp+],$r10
+ addq 44,$r12 ;; Get back count before faulting point.
+ subq 44,$r11 ;; Get back pointer to faulting movem-line.
+ jump 4b ;; Fall through, pretending the fault didn't happen.
+
+ .previous
+ .section __ex_table,\"a\"
+ .dword 1b,3b
+ .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;
+}
+
+/* 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 allright, 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 >= (1*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\
+
+ ;; Save the registers we'll clobber in the movem process
+ ;; on the stack. Don't mention them to gcc, it will only be
+ ;; upset.
+ subq 11*4,$sp
+ movem $r10,[$sp]
+
+ clear.d $r0
+ clear.d $r1
+ clear.d $r2
+ clear.d $r3
+ clear.d $r4
+ clear.d $r5
+ clear.d $r6
+ clear.d $r7
+ clear.d $r8
+ clear.d $r9
+ clear.d $r10
+ clear.d $r11
+
+ ;; Now we've got this:
+ ;; r13 - dst
+ ;; r12 - n
+
+ ;; Update n for the first loop
+ subq 12*4,$r12
+0:
+ subq 12*4,$r12
+ bge 0b
+ movem $r11,[$r13+]
+1:
+ addq 12*4,$r12 ;; compensate for last loop underflowing n
+
+ ;; Restore registers from stack
+ movem [$sp+],$r10
+2:
+ .section .fixup,\"ax\"
+3:
+ move.d [$sp],$r10
+ addq 12*4,$r10
+ move.d $r10,[$sp]
+ clear.d $r10
+ jump 0b
+
+4:
+ movem [$sp+],$r10
+ addq 12*4,$r10
+ addq 12*4,$r12
+ jump 2b
+
+ .previous
+ .section __ex_table,\"a\"
+ .dword 0b,3b
+ .dword 1b,4b
+ .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;
+}