aboutsummaryrefslogtreecommitdiff
path: root/arch/m32r/lib
diff options
context:
space:
mode:
Diffstat (limited to 'arch/m32r/lib')
-rw-r--r--arch/m32r/lib/Makefile7
-rw-r--r--arch/m32r/lib/ashxdi3.S297
-rw-r--r--arch/m32r/lib/checksum.S322
-rw-r--r--arch/m32r/lib/csum_partial_copy.c60
-rw-r--r--arch/m32r/lib/delay.c126
-rw-r--r--arch/m32r/lib/getuser.S88
-rw-r--r--arch/m32r/lib/memcpy.S95
-rw-r--r--arch/m32r/lib/memset.S181
-rw-r--r--arch/m32r/lib/putuser.S84
-rw-r--r--arch/m32r/lib/strlen.S120
-rw-r--r--arch/m32r/lib/usercopy.c391
11 files changed, 1771 insertions, 0 deletions
diff --git a/arch/m32r/lib/Makefile b/arch/m32r/lib/Makefile
new file mode 100644
index 000000000000..e632d10c7d78
--- /dev/null
+++ b/arch/m32r/lib/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for M32R-specific library files..
+#
+
+lib-y := checksum.o ashxdi3.o memset.o memcpy.o getuser.o \
+ putuser.o delay.o strlen.o usercopy.o csum_partial_copy.o
+
diff --git a/arch/m32r/lib/ashxdi3.S b/arch/m32r/lib/ashxdi3.S
new file mode 100644
index 000000000000..78effca9d97a
--- /dev/null
+++ b/arch/m32r/lib/ashxdi3.S
@@ -0,0 +1,297 @@
+/*
+ * linux/arch/m32r/lib/ashxdi3.S
+ *
+ * Copyright (C) 2001,2002 Hiroyuki Kondo, and Hirokazu Takata
+ *
+ */
+/* $Id$ */
+
+#include <linux/config.h>
+
+;
+; input (r0,r1) src
+; input r2 shift val
+; r3 scratch
+; output (r0,r1)
+;
+
+#ifdef CONFIG_ISA_DUAL_ISSUE
+
+#ifndef __LITTLE_ENDIAN__
+
+ .text
+ .align 4
+ .globl __ashrdi3
+__ashrdi3:
+ cmpz r2 || ldi r3, #32
+ jc r14 || cmpu r2, r3
+ bc 1f
+ ; case 32 =< shift
+ mv r1, r0 || srai r0, #31
+ addi r2, #-32
+ sra r1, r2
+ jmp r14
+ .fillinsn
+1: ; case shift <32
+ mv r3, r0 || srl r1, r2
+ sra r0, r2 || neg r2, r2
+ sll r3, r2
+ or r1, r3 || jmp r14
+
+ .align 4
+ .globl __ashldi3
+ .globl __lshldi3
+__ashldi3:
+__lshldi3:
+ cmpz r2 || ldi r3, #32
+ jc r14 || cmpu r2, r3
+ bc 1f
+ ; case 32 =< shift
+ mv r0, r1 || addi r2, #-32
+ sll r0, r2 || ldi r1, #0
+ jmp r14
+ .fillinsn
+1: ; case shift <32
+ mv r3, r1 || sll r0, r2
+ sll r1, r2 || neg r2, r2
+ srl r3, r2
+ or r0, r3 || jmp r14
+
+ .align 4
+ .globl __lshrdi3
+__lshrdi3:
+ cmpz r2 || ldi r3, #32
+ jc r14 || cmpu r2, r3
+ bc 1f
+ ; case 32 =< shift
+ mv r1, r0 || addi r2, #-32
+ ldi r0, #0 || srl r1, r2
+ jmp r14
+ .fillinsn
+1: ; case shift <32
+ mv r3, r0 || srl r1, r2
+ srl r0, r2 || neg r2, r2
+ sll r3, r2
+ or r1, r3 || jmp r14
+
+#else /* LITTLE_ENDIAN */
+
+ .text
+ .align 4
+ .globl __ashrdi3
+__ashrdi3:
+ cmpz r2 || ldi r3, #32
+ jc r14 || cmpu r2, r3
+ bc 1f
+ ; case 32 =< shift
+ mv r0, r1 || srai r1, #31
+ addi r2, #-32
+ sra r0, r2
+ jmp r14
+ .fillinsn
+1: ; case shift <32
+ mv r3, r1 || srl r0, r2
+ sra r1, r2 || neg r2, r2
+ sll r3, r2
+ or r0, r3 || jmp r14
+
+ .align 4
+ .globl __ashldi3
+ .globl __lshldi3
+__ashldi3:
+__lshldi3:
+ cmpz r2 || ldi r3, #32
+ jc r14 || cmpu r2, r3
+ bc 1f
+ ; case 32 =< shift
+ mv r1, r0 || addi r2, #-32
+ sll r1, r2 || ldi r0, #0
+ jmp r14
+ .fillinsn
+1: ; case shift <32
+ mv r3, r0 || sll r1, r2
+ sll r0, r2 || neg r2, r2
+ srl r3, r2
+ or r1, r3 || jmp r14
+
+ .align 4
+ .globl __lshrdi3
+__lshrdi3:
+ cmpz r2 || ldi r3, #32
+ jc r14 || cmpu r2, r3
+ bc 1f
+ ; case 32 =< shift
+ mv r0, r1 || addi r2, #-32
+ ldi r1, #0 || srl r0, r2
+ jmp r14
+ .fillinsn
+1: ; case shift <32
+ mv r3, r1 || srl r0, r2
+ srl r1, r2 || neg r2, r2
+ sll r3, r2
+ or r0, r3 || jmp r14
+
+#endif
+
+#else /* not CONFIG_ISA_DUAL_ISSUE */
+
+#ifndef __LITTLE_ENDIAN__
+
+ .text
+ .align 4
+ .globl __ashrdi3
+__ashrdi3:
+ beqz r2, 2f
+ cmpui r2, #32
+ bc 1f
+ ; case 32 =< shift
+ mv r1, r0
+ srai r0, #31
+ addi r2, #-32
+ sra r1, r2
+ jmp r14
+ .fillinsn
+1: ; case shift <32
+ mv r3, r0
+ srl r1, r2
+ sra r0, r2
+ neg r2, r2
+ sll r3, r2
+ or r1, r3
+ .fillinsn
+2:
+ jmp r14
+
+ .align 4
+ .globl __ashldi3
+ .globl __lshldi3
+__ashldi3:
+__lshldi3:
+ beqz r2, 2f
+ cmpui r2, #32
+ bc 1f
+ ; case 32 =< shift
+ mv r0, r1
+ addi r2, #-32
+ sll r0, r2
+ ldi r1, #0
+ jmp r14
+ .fillinsn
+1: ; case shift <32
+ mv r3, r1
+ sll r0, r2
+ sll r1, r2
+ neg r2, r2
+ srl r3, r2
+ or r0, r3
+ .fillinsn
+2:
+ jmp r14
+
+ .align 4
+ .globl __lshrdi3
+__lshrdi3:
+ beqz r2, 2f
+ cmpui r2, #32
+ bc 1f
+ ; case 32 =< shift
+ mv r1, r0
+ ldi r0, #0
+ addi r2, #-32
+ srl r1, r2
+ jmp r14
+ .fillinsn
+1: ; case shift <32
+ mv r3, r0
+ srl r1, r2
+ srl r0, r2
+ neg r2, r2
+ sll r3, r2
+ or r1, r3
+ .fillinsn
+2:
+ jmp r14
+
+#else
+
+ .text
+ .align 4
+ .globl __ashrdi3
+__ashrdi3:
+ beqz r2, 2f
+ cmpui r2, #32
+ bc 1f
+ ; case 32 =< shift
+ mv r0, r1
+ srai r1, #31
+ addi r2, #-32
+ sra r0, r2
+ jmp r14
+ .fillinsn
+1: ; case shift <32
+ mv r3, r1
+ srl r0, r2
+ sra r1, r2
+ neg r2, r2
+ sll r3, r2
+ or r0, r3
+ .fillinsn
+2:
+ jmp r14
+
+ .align 4
+ .globl __ashldi3
+ .globl __lshldi3
+__ashldi3:
+__lshldi3:
+ beqz r2, 2f
+ cmpui r2, #32
+ bc 1f
+ ; case 32 =< shift
+ mv r1, r0
+ addi r2, #-32
+ sll r1, r2
+ ldi r0, #0
+ jmp r14
+ .fillinsn
+1: ; case shift <32
+ mv r3, r0
+ sll r1, r2
+ sll r0, r2
+ neg r2, r2
+ srl r3, r2
+ or r1, r3
+ .fillinsn
+2:
+ jmp r14
+
+ .align 4
+ .globl __lshrdi3
+__lshrdi3:
+ beqz r2, 2f
+ cmpui r2, #32
+ bc 1f
+ ; case 32 =< shift
+ mv r0, r1
+ ldi r1, #0
+ addi r2, #-32
+ srl r0, r2
+ jmp r14
+ .fillinsn
+1: ; case shift <32
+ mv r3, r1
+ srl r0, r2
+ srl r1, r2
+ neg r2, r2
+ sll r3, r2
+ or r0, r3
+ .fillinsn
+2:
+ jmp r14
+
+#endif
+
+#endif /* not CONFIG_ISA_DUAL_ISSUE */
+
+ .end
+
diff --git a/arch/m32r/lib/checksum.S b/arch/m32r/lib/checksum.S
new file mode 100644
index 000000000000..f6fc1bdb87e4
--- /dev/null
+++ b/arch/m32r/lib/checksum.S
@@ -0,0 +1,322 @@
+/*
+ * 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>
+ * Pentium Pro/II routines:
+ * Alexander Kjeldaas <astor@guardian.no>
+ * Finn Arne Gangstad <finnag@guardian.no>
+ * Lots of code moved from tcp.c and ip.c; see those files
+ * for more names.
+ *
+ * Changes: Ingo Molnar, converted csum_partial_copy() to 2.1 exception
+ * handling.
+ * Andi Kleen, add zeroing on error
+ * converted to pure assembler
+ * Hirokazu Takata,Hiroyuki Kondo rewrite for the m32r architecture.
+ *
+ * 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.
+ */
+/* $Id$ */
+
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/errno.h>
+
+/*
+ * computes a partial checksum, e.g. for TCP/UDP fragments
+ */
+
+/*
+unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum)
+ */
+
+
+#ifdef CONFIG_ISA_DUAL_ISSUE
+
+ /*
+ * Experiments with Ethernet and SLIP connections show that buff
+ * is aligned on either a 2-byte or 4-byte boundary. We get at
+ * least a twofold speedup on 486 and Pentium if it is 4-byte aligned.
+ * Fortunately, it is easy to convert 2-byte alignment to 4-byte
+ * alignment for the unrolled loop.
+ */
+
+ .text
+ENTRY(csum_partial)
+ ; Function args
+ ; r0: unsigned char *buff
+ ; r1: int len
+ ; r2: unsigned int sum
+
+ push r2 || ldi r2, #0
+ and3 r7, r0, #1 ; Check alignment.
+ beqz r7, 1f ; Jump if alignment is ok.
+ ; 1-byte mis aligned
+ ldub r4, @r0 || addi r0, #1
+ ; clear c-bit || Alignment uses up bytes.
+ cmp r0, r0 || addi r1, #-1
+ ldi r3, #0 || addx r2, r4
+ addx r2, r3
+ .fillinsn
+1:
+ and3 r4, r0, #2 ; Check alignment.
+ beqz r4, 2f ; Jump if alignment is ok.
+ ; clear c-bit || Alignment uses up two bytes.
+ cmp r0, r0 || addi r1, #-2
+ bgtz r1, 1f ; Jump if we had at least two bytes.
+ bra 4f || addi r1, #2
+ .fillinsn ; len(r1) was < 2. Deal with it.
+1:
+ ; 2-byte aligned
+ lduh r4, @r0 || ldi r3, #0
+ addx r2, r4 || addi r0, #2
+ addx r2, r3
+ .fillinsn
+2:
+ ; 4-byte aligned
+ cmp r0, r0 ; clear c-bit
+ srl3 r6, r1, #5
+ beqz r6, 2f
+ .fillinsn
+
+1: ld r3, @r0+
+ ld r4, @r0+ ; +4
+ ld r5, @r0+ ; +8
+ ld r3, @r0+ || addx r2, r3 ; +12
+ ld r4, @r0+ || addx r2, r4 ; +16
+ ld r5, @r0+ || addx r2, r5 ; +20
+ ld r3, @r0+ || addx r2, r3 ; +24
+ ld r4, @r0+ || addx r2, r4 ; +28
+ addx r2, r5 || addi r6, #-1
+ addx r2, r3
+ addx r2, r4
+ bnez r6, 1b
+
+ addx r2, r6 ; r6=0
+ cmp r0, r0 ; This clears c-bit
+ .fillinsn
+2: and3 r6, r1, #0x1c ; withdraw len
+ beqz r6, 4f
+ srli r6, #2
+ .fillinsn
+
+3: ld r4, @r0+ || addi r6, #-1
+ addx r2, r4
+ bnez r6, 3b
+
+ addx r2, r6 ; r6=0
+ cmp r0, r0 ; This clears c-bit
+ .fillinsn
+4: and3 r1, r1, #3
+ beqz r1, 7f ; if len == 0 goto end
+ and3 r6, r1, #2
+ beqz r6, 5f ; if len < 2 goto 5f(1byte)
+ lduh r4, @r0 || addi r0, #2
+ addi r1, #-2 || slli r4, #16
+ addx r2, r4
+ beqz r1, 6f
+ .fillinsn
+5: ldub r4, @r0 || ldi r1, #0
+#ifndef __LITTLE_ENDIAN__
+ slli r4, #8
+#endif
+ addx r2, r4
+ .fillinsn
+6: addx r2, r1
+ .fillinsn
+7:
+ and3 r0, r2, #0xffff
+ srli r2, #16
+ add r0, r2
+ srl3 r2, r0, #16
+ beqz r2, 1f
+ addi r0, #1
+ and3 r0, r0, #0xffff
+ .fillinsn
+1:
+ beqz r7, 1f ; swap the upper byte for the lower
+ and3 r2, r0, #0xff
+ srl3 r0, r0, #8
+ slli r2, #8
+ or r0, r2
+ .fillinsn
+1:
+ pop r2 || cmp r0, r0
+ addx r0, r2 || ldi r2, #0
+ addx r0, r2
+ jmp r14
+
+#else /* not CONFIG_ISA_DUAL_ISSUE */
+
+ /*
+ * Experiments with Ethernet and SLIP connections show that buff
+ * is aligned on either a 2-byte or 4-byte boundary. We get at
+ * least a twofold speedup on 486 and Pentium if it is 4-byte aligned.
+ * Fortunately, it is easy to convert 2-byte alignment to 4-byte
+ * alignment for the unrolled loop.
+ */
+
+ .text
+ENTRY(csum_partial)
+ ; Function args
+ ; r0: unsigned char *buff
+ ; r1: int len
+ ; r2: unsigned int sum
+
+ push r2
+ ldi r2, #0
+ and3 r7, r0, #1 ; Check alignment.
+ beqz r7, 1f ; Jump if alignment is ok.
+ ; 1-byte mis aligned
+ ldub r4, @r0
+ addi r0, #1
+ addi r1, #-1 ; Alignment uses up bytes.
+ cmp r0, r0 ; clear c-bit
+ ldi r3, #0
+ addx r2, r4
+ addx r2, r3
+ .fillinsn
+1:
+ and3 r4, r0, #2 ; Check alignment.
+ beqz r4, 2f ; Jump if alignment is ok.
+ addi r1, #-2 ; Alignment uses up two bytes.
+ cmp r0, r0 ; clear c-bit
+ bgtz r1, 1f ; Jump if we had at least two bytes.
+ addi r1, #2 ; len(r1) was < 2. Deal with it.
+ bra 4f
+ .fillinsn
+1:
+ ; 2-byte aligned
+ lduh r4, @r0
+ addi r0, #2
+ ldi r3, #0
+ addx r2, r4
+ addx r2, r3
+ .fillinsn
+2:
+ ; 4-byte aligned
+ cmp r0, r0 ; clear c-bit
+ srl3 r6, r1, #5
+ beqz r6, 2f
+ .fillinsn
+
+1: ld r3, @r0+
+ ld r4, @r0+ ; +4
+ ld r5, @r0+ ; +8
+ addx r2, r3
+ addx r2, r4
+ addx r2, r5
+ ld r3, @r0+ ; +12
+ ld r4, @r0+ ; +16
+ ld r5, @r0+ ; +20
+ addx r2, r3
+ addx r2, r4
+ addx r2, r5
+ ld r3, @r0+ ; +24
+ ld r4, @r0+ ; +28
+ addi r6, #-1
+ addx r2, r3
+ addx r2, r4
+ bnez r6, 1b
+ addx r2, r6 ; r6=0
+ cmp r0, r0 ; This clears c-bit
+ .fillinsn
+
+2: and3 r6, r1, #0x1c ; withdraw len
+ beqz r6, 4f
+ srli r6, #2
+ .fillinsn
+
+3: ld r4, @r0+
+ addi r6, #-1
+ addx r2, r4
+ bnez r6, 3b
+ addx r2, r6 ; r6=0
+ cmp r0, r0 ; This clears c-bit
+ .fillinsn
+
+4: and3 r1, r1, #3
+ beqz r1, 7f ; if len == 0 goto end
+ and3 r6, r1, #2
+ beqz r6, 5f ; if len < 2 goto 5f(1byte)
+
+ lduh r4, @r0
+ addi r0, #2
+ addi r1, #-2
+ slli r4, #16
+ addx r2, r4
+ beqz r1, 6f
+ .fillinsn
+5: ldub r4, @r0
+#ifndef __LITTLE_ENDIAN__
+ slli r4, #8
+#endif
+ addx r2, r4
+ .fillinsn
+6: ldi r5, #0
+ addx r2, r5
+ .fillinsn
+7:
+ and3 r0, r2, #0xffff
+ srli r2, #16
+ add r0, r2
+ srl3 r2, r0, #16
+ beqz r2, 1f
+ addi r0, #1
+ and3 r0, r0, #0xffff
+ .fillinsn
+1:
+ beqz r7, 1f
+ mv r2, r0
+ srl3 r0, r2, #8
+ and3 r2, r2, #0xff
+ slli r2, #8
+ or r0, r2
+ .fillinsn
+1:
+ pop r2
+ cmp r0, r0
+ addx r0, r2
+ ldi r2, #0
+ addx r0, r2
+ jmp r14
+
+#endif /* not CONFIG_ISA_DUAL_ISSUE */
+
+/*
+unsigned int csum_partial_copy_generic (const char *src, char *dst,
+ int len, int sum, int *src_err_ptr, int *dst_err_ptr)
+ */
+
+/*
+ * Copy from ds while checksumming, otherwise like csum_partial
+ *
+ * The macros SRC and DST specify the type of access for the instruction.
+ * thus we can call a custom exception handler for all access types.
+ *
+ * FIXME: could someone double-check whether I haven't mixed up some SRC and
+ * DST definitions? It's damn hard to trigger all cases. I hope I got
+ * them all but there's no guarantee.
+ */
+
+ENTRY(csum_partial_copy_generic)
+ nop
+ nop
+ nop
+ nop
+ jmp r14
+ nop
+ nop
+ nop
+
diff --git a/arch/m32r/lib/csum_partial_copy.c b/arch/m32r/lib/csum_partial_copy.c
new file mode 100644
index 000000000000..c871b4606b07
--- /dev/null
+++ b/arch/m32r/lib/csum_partial_copy.c
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ *
+ * M32R specific IP/TCP/UDP checksumming routines
+ * (Some code taken from MIPS architecture)
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1994, 1995 Waldorf Electronics GmbH
+ * Copyright (C) 1998, 1999 Ralf Baechle
+ * Copyright (C) 2001-2005 Hiroyuki Kondo, Hirokazu Takata
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <net/checksum.h>
+#include <asm/byteorder.h>
+#include <asm/string.h>
+#include <asm/uaccess.h>
+
+/*
+ * Copy while checksumming, otherwise like csum_partial
+ */
+unsigned int
+csum_partial_copy_nocheck (const unsigned char *src, unsigned char *dst,
+ int len, unsigned int sum)
+{
+ sum = csum_partial(src, len, sum);
+ memcpy(dst, src, len);
+
+ return sum;
+}
+EXPORT_SYMBOL(csum_partial_copy_nocheck);
+
+/*
+ * Copy from userspace and compute checksum. If we catch an exception
+ * then zero the rest of the buffer.
+ */
+unsigned int
+csum_partial_copy_from_user (const unsigned char __user *src,
+ unsigned char *dst,
+ int len, unsigned int sum, int *err_ptr)
+{
+ int missing;
+
+ missing = copy_from_user(dst, src, len);
+ if (missing) {
+ memset(dst + len - missing, 0, missing);
+ *err_ptr = -EFAULT;
+ }
+
+ return csum_partial(dst, len-missing, sum);
+}
+EXPORT_SYMBOL(csum_partial_copy_from_user);
diff --git a/arch/m32r/lib/delay.c b/arch/m32r/lib/delay.c
new file mode 100644
index 000000000000..fb29632c7eaa
--- /dev/null
+++ b/arch/m32r/lib/delay.c
@@ -0,0 +1,126 @@
+/*
+ * linux/arch/m32r/lib/delay.c
+ *
+ * Copyright (c) 2002 Hitoshi Yamamoto, Hirokazu Takata
+ * Copyright (c) 2004 Hirokazu Takata
+ */
+
+/* $Id$ */
+
+#include <linux/config.h>
+#include <linux/param.h>
+#ifdef CONFIG_SMP
+#include <linux/sched.h>
+#include <asm/current.h>
+#include <asm/smp.h>
+#endif /* CONFIG_SMP */
+#include <asm/processor.h>
+
+void __delay(unsigned long loops)
+{
+#ifdef CONFIG_ISA_DUAL_ISSUE
+ __asm__ __volatile__ (
+ "beqz %0, 2f \n\t"
+ "addi %0, #-1 \n\t"
+
+ " .fillinsn \n\t"
+ "1: \n\t"
+ "cmpz %0 || addi %0, #-1 \n\t"
+ "bc 2f || cmpz %0 \n\t"
+ "bc 2f || addi %0, #-1 \n\t"
+ "cmpz %0 || addi %0, #-1 \n\t"
+ "bc 2f || cmpz %0 \n\t"
+ "bnc 1b || addi %0, #-1 \n\t"
+ " .fillinsn \n\t"
+ "2: \n\t"
+ : "+r" (loops)
+ : "r" (0)
+ : "cbit"
+ );
+#else
+ __asm__ __volatile__ (
+ "beqz %0, 2f \n\t"
+ " .fillinsn \n\t"
+ "1: \n\t"
+ "addi %0, #-1 \n\t"
+ "blez %0, 2f \n\t"
+ "addi %0, #-1 \n\t"
+ "blez %0, 2f \n\t"
+ "addi %0, #-1 \n\t"
+ "blez %0, 2f \n\t"
+ "addi %0, #-1 \n\t"
+ "bgtz %0, 1b \n\t"
+ " .fillinsn \n\t"
+ "2: \n\t"
+ : "+r" (loops)
+ : "r" (0)
+ );
+#endif
+}
+
+void __const_udelay(unsigned long xloops)
+{
+#if defined(CONFIG_ISA_M32R2) && defined(CONFIG_ISA_DSP_LEVEL2)
+ /*
+ * loops [1] = (xloops >> 32) [sec] * loops_per_jiffy [1/jiffy]
+ * * HZ [jiffy/sec]
+ * = (xloops >> 32) [sec] * (loops_per_jiffy * HZ) [1/sec]
+ * = (((xloops * loops_per_jiffy) >> 32) * HZ) [1]
+ *
+ * NOTE:
+ * - '[]' depicts variable's dimension in the above equation.
+ * - "rac" instruction rounds the accumulator in word size.
+ */
+ __asm__ __volatile__ (
+ "srli %0, #1 \n\t"
+ "mulwhi %0, %1 ; a0 \n\t"
+ "mulwu1 %0, %1 ; a1 \n\t"
+ "sadd ; a0 += (a1 >> 16) \n\t"
+ "rac a0, a0, #1 \n\t"
+ "mvfacmi %0, a0 \n\t"
+ : "+r" (xloops)
+ : "r" (current_cpu_data.loops_per_jiffy)
+ : "a0", "a1"
+ );
+#elif defined(CONFIG_ISA_M32R2) || defined(CONFIG_ISA_M32R)
+ /*
+ * u64 ull;
+ * ull = (u64)xloops * (u64)current_cpu_data.loops_per_jiffy;
+ * xloops = (ull >> 32);
+ */
+ __asm__ __volatile__ (
+ "and3 r4, %0, #0xffff \n\t"
+ "and3 r5, %1, #0xffff \n\t"
+ "mul r4, r5 \n\t"
+ "srl3 r6, %0, #16 \n\t"
+ "srli r4, #16 \n\t"
+ "mul r5, r6 \n\t"
+ "add r4, r5 \n\t"
+ "and3 r5, %0, #0xffff \n\t"
+ "srl3 r6, %1, #16 \n\t"
+ "mul r5, r6 \n\t"
+ "add r4, r5 \n\t"
+ "srl3 r5, %0, #16 \n\t"
+ "srli r4, #16 \n\t"
+ "mul r5, r6 \n\t"
+ "add r4, r5 \n\t"
+ "mv %0, r4 \n\t"
+ : "+r" (xloops)
+ : "r" (current_cpu_data.loops_per_jiffy)
+ : "r4", "r5", "r6"
+ );
+#else
+#error unknown isa configuration
+#endif
+ __delay(xloops * HZ);
+}
+
+void __udelay(unsigned long usecs)
+{
+ __const_udelay(usecs * 0x000010c7); /* 2**32 / 1000000 (rounded up) */
+}
+
+void __ndelay(unsigned long nsecs)
+{
+ __const_udelay(nsecs * 0x00005); /* 2**32 / 1000000000 (rounded up) */
+}
diff --git a/arch/m32r/lib/getuser.S b/arch/m32r/lib/getuser.S
new file mode 100644
index 000000000000..58a0db055c5c
--- /dev/null
+++ b/arch/m32r/lib/getuser.S
@@ -0,0 +1,88 @@
+/*
+ * __get_user functions.
+ *
+ * (C) Copyright 2001 Hirokazu Takata
+ *
+ * These functions have a non-standard call interface
+ * to make them more efficient, especially as they
+ * return an error value in addition to the "real"
+ * return value.
+ */
+
+#include <linux/config.h>
+
+/*
+ * __get_user_X
+ *
+ * Inputs: r0 contains the address
+ *
+ * Outputs: r0 is error code (0 or -EFAULT)
+ * r1 contains zero-extended value
+ *
+ * These functions should not modify any other registers,
+ * as they get called from within inline assembly.
+ */
+
+#ifdef CONFIG_ISA_DUAL_ISSUE
+
+ .text
+ .balign 4
+ .globl __get_user_1
+__get_user_1:
+1: ldub r1, @r0 || ldi r0, #0
+ jmp r14
+
+ .balign 4
+ .globl __get_user_2
+__get_user_2:
+2: lduh r1, @r0 || ldi r0, #0
+ jmp r14
+
+ .balign 4
+ .globl __get_user_4
+__get_user_4:
+3: ld r1, @r0 || ldi r0, #0
+ jmp r14
+
+bad_get_user:
+ ldi r1, #0 || ldi r0, #-14
+ jmp r14
+
+#else /* not CONFIG_ISA_DUAL_ISSUE */
+
+ .text
+ .balign 4
+ .globl __get_user_1
+__get_user_1:
+1: ldub r1, @r0
+ ldi r0, #0
+ jmp r14
+
+ .balign 4
+ .globl __get_user_2
+__get_user_2:
+2: lduh r1, @r0
+ ldi r0, #0
+ jmp r14
+
+ .balign 4
+ .globl __get_user_4
+__get_user_4:
+3: ld r1, @r0
+ ldi r0, #0
+ jmp r14
+
+bad_get_user:
+ ldi r1, #0
+ ldi r0, #-14
+ jmp r14
+
+#endif /* not CONFIG_ISA_DUAL_ISSUE */
+
+.section __ex_table,"a"
+ .long 1b,bad_get_user
+ .long 2b,bad_get_user
+ .long 3b,bad_get_user
+.previous
+
+ .end
diff --git a/arch/m32r/lib/memcpy.S b/arch/m32r/lib/memcpy.S
new file mode 100644
index 000000000000..800898a2d2e3
--- /dev/null
+++ b/arch/m32r/lib/memcpy.S
@@ -0,0 +1,95 @@
+/*
+ * linux/arch/m32r/lib/memcpy.S
+ *
+ * Copyright (C) 2001 Hiroyuki Kondo, and Hirokazu Takata
+ * Copyright (C) 2004 Hirokazu Takata
+ *
+ * void *memcopy(void *dst, const void *src, int n);
+ *
+ * dst: r0
+ * src: r1
+ * n : r2
+ */
+/* $Id$ */
+
+
+ .text
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+#ifdef CONFIG_ISA_DUAL_ISSUE
+
+ .text
+ENTRY(memcpy)
+memcopy:
+ mv r4, r0 || mv r7, r0
+ or r7, r1 || cmpz r2
+ jc r14 || cmpeq r0, r1 ; return if r2=0
+ jc r14 ; return if r0=r1
+
+ and3 r7, r7, #3
+ bnez r7, byte_copy
+ srl3 r3, r2, #2
+ and3 r2, r2, #3
+ beqz r3, byte_copy
+ addi r4, #-4
+word_copy:
+ ld r7, @r1+ || addi r3, #-1
+ st r7, @+r4 || cmpz r2
+ bnez r3, word_copy
+ addi r4, #4 || jc r14 ; return if r2=0
+#if defined(CONFIG_ISA_M32R2)
+byte_copy:
+ ldb r7, @r1 || addi r1, #1
+ addi r2, #-1 || stb r7, @r4+
+ bnez r2, byte_copy
+#elif defined(CONFIG_ISA_M32R)
+byte_copy:
+ ldb r7, @r1 || addi r1, #1
+ addi r2, #-1 || stb r7, @r4
+ addi r4, #1
+ bnez r2, byte_copy
+#else
+#error unknown isa configuration
+#endif
+end_memcopy:
+ jmp r14
+
+#else /* not CONFIG_ISA_DUAL_ISSUE */
+
+ .text
+ENTRY(memcpy)
+memcopy:
+ mv r4, r0
+ mv r7, r0
+ or r7, r1
+ beq r0, r1, end_memcopy
+ beqz r2, end_memcopy
+
+ and3 r7, r7, #3
+ bnez r7, byte_copy
+ srl3 r3, r2, #2
+ and3 r2, r2, #3
+ beqz r3, byte_copy
+ addi r4, #-4
+word_copy:
+ ld r7, @r1+
+ addi r3, #-1
+ st r7, @+r4
+ bnez r3, word_copy
+ beqz r2, end_memcopy
+ addi r4, #4
+byte_copy:
+ ldb r7, @r1
+ addi r1, #1
+ addi r2, #-1
+ stb r7, @r4
+ addi r4, #1
+ bnez r2, byte_copy
+end_memcopy:
+ jmp r14
+
+#endif /* not CONFIG_ISA_DUAL_ISSUE */
+
+ .end
diff --git a/arch/m32r/lib/memset.S b/arch/m32r/lib/memset.S
new file mode 100644
index 000000000000..6e26df120acb
--- /dev/null
+++ b/arch/m32r/lib/memset.S
@@ -0,0 +1,181 @@
+/*
+ * linux/arch/m32r/lib/memset.S
+ *
+ * Copyright (C) 2001,2002 Hiroyuki Kondo, and Hirokazu Takata
+ * Copyright (C) 2004 Hirokazu Takata
+ *
+ * void *memset(void *dst, int val, int len);
+ *
+ * dst: r0
+ * val: r1
+ * len: r2
+ * ret: r0
+ *
+ */
+/* $Id$ */
+
+#include <linux/config.h>
+
+ .text
+ .global memset
+
+#ifdef CONFIG_ISA_DUAL_ISSUE
+
+ .align 4
+memset:
+ mv r4, r0 || cmpz r2
+ jc r14
+ cmpui r2, #16
+ bnc qword_align_check
+ cmpui r2, #4
+ bc byte_set
+word_align_check: /* len >= 4 */
+ and3 r3, r4, #3
+ beqz r3, word_set
+ addi r3, #-4
+ neg r3, r3 /* r3 = -(r3 - 4) */
+align_word:
+ stb r1, @r4 || addi r4, #1
+ addi r2, #-1 || addi r3, #-1
+ bnez r3, align_word
+ cmpui r2, #4
+ bc byte_set
+word_set:
+ and3 r1, r1, #0x00ff /* r1: abababab <-- ??????ab */
+ sll3 r3, r1, #8
+ or r1, r3 || addi r4, #-4
+ sll3 r3, r1, #16
+ or r1, r3 || addi r2, #-4
+word_set_loop:
+ st r1, @+r4 || addi r2, #-4
+ bgtz r2, word_set_loop
+ bnez r2, byte_set_wrap
+ st r1, @+r4
+ jmp r14
+
+qword_align_check: /* len >= 16 */
+ and3 r3, r4, #15
+ bnez r3, word_align_check
+qword_set:
+ and3 r1, r1, #0x00ff /* r1: abababab <-- ??????ab */
+ sll3 r3, r1, #8
+ or r1, r3 || addi r4, #-4
+ sll3 r3, r1, #16
+ or r1, r3 || ldi r5, #16
+qword_set_loop:
+ ld r3, @(4,r4) /* cache line allocate */
+ st r1, @+r4 || addi r2, #-16
+ st r1, @+r4 || cmpu r2, r5
+ st r1, @+r4
+ st r1, @+r4
+ bnc qword_set_loop || cmpz r2
+ jc r14
+set_remainder:
+ cmpui r2, #4
+ bc byte_set_wrap1
+ addi r2, #-4
+ bra word_set_loop
+
+byte_set_wrap:
+ addi r2, #4
+ cmpz r2
+ jc r14
+byte_set_wrap1:
+ addi r4, #4
+#if defined(CONFIG_ISA_M32R2)
+byte_set:
+ addi r2, #-1 || stb r1, @r4+
+ bnez r2, byte_set
+#elif defined(CONFIG_ISA_M32R)
+byte_set:
+ addi r2, #-1 || stb r1, @r4
+ addi r4, #1
+ bnez r2, byte_set
+#else
+#error unknown isa configuration
+#endif
+end_memset:
+ jmp r14
+
+#else /* not CONFIG_ISA_DUAL_ISSUE */
+
+ .align 4
+memset:
+ mv r4, r0
+ beqz r2, end_memset
+ cmpui r2, #16
+ bnc qword_align_check
+ cmpui r2, #4
+ bc byte_set
+word_align_check: /* len >= 4 */
+ and3 r3, r4, #3
+ beqz r3, word_set
+ addi r3, #-4
+ neg r3, r3 /* r3 = -(r3 - 4) */
+align_word:
+ stb r1, @r4
+ addi r4, #1
+ addi r2, #-1
+ addi r3, #-1
+ bnez r3, align_word
+ cmpui r2, #4
+ bc byte_set
+word_set:
+ and3 r1, r1, #0x00ff /* r1: abababab <-- ??????ab */
+ sll3 r3, r1, #8
+ or r1, r3
+ sll3 r3, r1, #16
+ or r1, r3
+ addi r2, #-4
+ addi r4, #-4
+word_set_loop:
+ st r1, @+r4
+ addi r2, #-4
+ bgtz r2, word_set_loop
+ bnez r2, byte_set_wrap
+ st r1, @+r4
+ jmp r14
+
+qword_align_check: /* len >= 16 */
+ and3 r3, r4, #15
+ bnez r3, word_align_check
+qword_set:
+ and3 r1, r1, #0x00ff /* r1: abababab <-- ??????ab */
+ sll3 r3, r1, #8
+ or r1, r3
+ sll3 r3, r1, #16
+ or r1, r3
+ addi r4, #-4
+qword_set_loop:
+ ld r3, @(4,r4) /* cache line allocate */
+ addi r2, #-16
+ st r1, @+r4
+ st r1, @+r4
+ cmpui r2, #16
+ st r1, @+r4
+ st r1, @+r4
+ bnc qword_set_loop
+ bnez r2, set_remainder
+ jmp r14
+set_remainder:
+ cmpui r2, #4
+ bc byte_set_wrap1
+ addi r2, #-4
+ bra word_set_loop
+
+byte_set_wrap:
+ addi r2, #4
+ beqz r2, end_memset
+byte_set_wrap1:
+ addi r4, #4
+byte_set:
+ addi r2, #-1
+ stb r1, @r4
+ addi r4, #1
+ bnez r2, byte_set
+end_memset:
+ jmp r14
+
+#endif /* not CONFIG_ISA_DUAL_ISSUE */
+
+ .end
diff --git a/arch/m32r/lib/putuser.S b/arch/m32r/lib/putuser.S
new file mode 100644
index 000000000000..218154cc3890
--- /dev/null
+++ b/arch/m32r/lib/putuser.S
@@ -0,0 +1,84 @@
+/*
+ * __put_user functions.
+ *
+ * (C) Copyright 1998 Linus Torvalds
+ * (C) Copyright 2001 Hirokazu Takata
+ *
+ * These functions have a non-standard call interface
+ * to make them more efficient.
+ */
+
+#include <linux/config.h>
+
+/*
+ * __put_user_X
+ *
+ * Inputs: r0 contains the address
+ * r1 contains the value
+ *
+ * Outputs: r0 is error code (0 or -EFAULT)
+ * r1 is corrupted (will contain "current_task").
+ *
+ * These functions should not modify any other registers,
+ * as they get called from within inline assembly.
+ */
+
+#ifdef CONFIG_ISA_DUAL_ISSUE
+
+ .text
+ .balign 4
+ .globl __put_user_1
+__put_user_1:
+1: stb r1, @r0 || ldi r0, #0
+ jmp r14
+
+ .balign 4
+ .globl __put_user_2
+__put_user_2:
+2: sth r1, @r0 || ldi r0, #0
+ jmp r14
+
+ .balign 4
+ .globl __put_user_4
+__put_user_4:
+3: st r1, @r0 || ldi r0, #0
+ jmp r14
+
+bad_put_user:
+ ldi r0, #-14 || jmp r14
+
+#else /* not CONFIG_ISA_DUAL_ISSUE */
+
+ .text
+ .balign 4
+ .globl __put_user_1
+__put_user_1:
+1: stb r1, @r0
+ ldi r0, #0
+ jmp r14
+
+ .balign 4
+ .globl __put_user_2
+__put_user_2:
+2: sth r1, @r0
+ ldi r0, #0
+ jmp r14
+
+ .balign 4
+ .globl __put_user_4
+__put_user_4:
+3: st r1, @r0
+ ldi r0, #0
+ jmp r14
+
+bad_put_user:
+ ldi r0, #-14
+ jmp r14
+
+#endif /* not CONFIG_ISA_DUAL_ISSUE */
+
+.section __ex_table,"a"
+ .long 1b,bad_put_user
+ .long 2b,bad_put_user
+ .long 3b,bad_put_user
+.previous
diff --git a/arch/m32r/lib/strlen.S b/arch/m32r/lib/strlen.S
new file mode 100644
index 000000000000..8d23cfbd600c
--- /dev/null
+++ b/arch/m32r/lib/strlen.S
@@ -0,0 +1,120 @@
+/*
+ * linux/arch/m32r/strlen.S -- strlen code.
+ *
+ * Copyright (C) 2001 Hirokazu Takata
+ *
+ * size_t strlen(const char *s);
+ *
+ */
+/* $Id$ */
+
+
+#include <linux/config.h>
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+#ifdef CONFIG_ISA_DUAL_ISSUE
+
+ .text
+ENTRY(strlen)
+ mv r6, r0 || ldi r2, #0
+ and3 r0, r0, #3
+ bnez r0, strlen_byte
+;
+strlen_word:
+ ld r0, @r6+
+;
+ seth r5, #high(0x01010101)
+ or3 r5, r5, #low(0x01010101)
+ sll3 r7, r5, #7
+strlen_word_loop:
+ ld r1, @r6+ || not r4, r0
+ sub r0, r5 || and r4, r7
+ and r4, r0
+ bnez r4, strlen_last_bytes
+ ld r0, @r6+ || not r4, r1
+ sub r1, r5 || and r4, r7
+ and r4, r1 || addi r2, #4
+ bnez r4, strlen_last_bytes
+ addi r2, #4 || bra.s strlen_word_loop
+
+ ; NOTE: If a null char. exists, return 0.
+ ; if ((x - 0x01010101) & ~x & 0x80808080)
+ ; return 0;
+;
+strlen_byte:
+ ldb r1, @r6 || addi r6, #1
+ beqz r1, strlen_exit
+ addi r2, #1 || bra.s strlen_byte
+;
+strlen_last_bytes:
+ ldi r0, #4 || addi r6, #-8
+;
+strlen_byte_loop:
+ ldb r1, @r6 || addi r6, #1
+ addi r0, #-1 || cmpz r1
+ bc.s strlen_exit || cmpz r0
+ addi r2, #1 || bnc.s strlen_byte_loop
+;
+strlen_exit:
+ mv r0, r2 || jmp r14
+
+#else /* not CONFIG_ISA_DUAL_ISSUE */
+
+ .text
+ENTRY(strlen)
+ mv r6, r0
+ ldi r2, #0
+ and3 r0, r0, #3
+ bnez r0, strlen_byte
+;
+strlen_word:
+ ld r0, @r6+
+;
+ seth r5, #high(0x01010101)
+ or3 r5, r5, #low(0x01010101)
+ sll3 r7, r5, #7
+strlen_word_loop:
+ ld r1, @r6+
+ not r4, r0 ; NOTE: If a null char. exists, return 0.
+ sub r0, r5 ; if ((x - 0x01010101) & ~x & 0x80808080)
+ and r4, r7 ; return 0;
+ and r4, r0
+ bnez r4, strlen_last_bytes
+ addi r2, #4
+;
+ ld r0, @r6+
+ not r4, r1 ; NOTE: If a null char. exists, return 0.
+ sub r1, r5 ; if ((x - 0x01010101) & ~x & 0x80808080)
+ and r4, r7 ; return 0;
+ and r4, r1
+ bnez r4, strlen_last_bytes
+ addi r2, #4
+ bra strlen_word_loop
+;
+strlen_byte:
+ ldb r1, @r6
+ addi r6, #1
+ beqz r1, strlen_exit
+ addi r2, #1
+ bra strlen_byte
+;
+strlen_last_bytes:
+ ldi r0, #4
+ addi r6, #-8
+;
+strlen_byte_loop:
+ ldb r1, @r6
+ addi r6, #1
+ addi r0, #-1
+ beqz r1, strlen_exit
+ addi r2, #1
+ bnez r0, strlen_byte_loop
+;
+strlen_exit:
+ mv r0, r2
+ jmp r14
+
+#endif /* not CONFIG_ISA_DUAL_ISSUE */
+
+ .end
diff --git a/arch/m32r/lib/usercopy.c b/arch/m32r/lib/usercopy.c
new file mode 100644
index 000000000000..6c6855f1aa05
--- /dev/null
+++ b/arch/m32r/lib/usercopy.c
@@ -0,0 +1,391 @@
+/*
+ * User address space access functions.
+ * The non inlined parts of asm-m32r/uaccess.h are here.
+ *
+ * Copyright 1997 Andi Kleen <ak@muc.de>
+ * Copyright 1997 Linus Torvalds
+ * Copyright 2001, 2002, 2004 Hirokazu Takata
+ */
+#include <linux/config.h>
+#include <linux/prefetch.h>
+#include <linux/string.h>
+#include <linux/thread_info.h>
+#include <asm/uaccess.h>
+
+unsigned long
+__generic_copy_to_user(void *to, const void *from, unsigned long n)
+{
+ prefetch(from);
+ if (access_ok(VERIFY_WRITE, to, n))
+ __copy_user(to,from,n);
+ return n;
+}
+
+unsigned long
+__generic_copy_from_user(void *to, const void *from, unsigned long n)
+{
+ prefetchw(to);
+ if (access_ok(VERIFY_READ, from, n))
+ __copy_user_zeroing(to,from,n);
+ else
+ memset(to, 0, n);
+ return n;
+}
+
+
+/*
+ * Copy a null terminated string from userspace.
+ */
+
+#ifdef CONFIG_ISA_DUAL_ISSUE
+
+#define __do_strncpy_from_user(dst,src,count,res) \
+do { \
+ int __d0, __d1, __d2; \
+ __asm__ __volatile__( \
+ " beqz %1, 2f\n" \
+ " .fillinsn\n" \
+ "0: ldb r14, @%3 || addi %3, #1\n" \
+ " stb r14, @%4 || addi %4, #1\n" \
+ " beqz r14, 1f\n" \
+ " addi %1, #-1\n" \
+ " bnez %1, 0b\n" \
+ " .fillinsn\n" \
+ "1: sub %0, %1\n" \
+ " .fillinsn\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ " .balign 4\n" \
+ "3: seth r14, #high(2b)\n" \
+ " or3 r14, r14, #low(2b)\n" \
+ " jmp r14 || ldi %0, #%5\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .balign 4\n" \
+ " .long 0b,3b\n" \
+ ".previous" \
+ : "=r"(res), "=r"(count), "=&r" (__d0), "=&r" (__d1), \
+ "=&r" (__d2) \
+ : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), \
+ "4"(dst) \
+ : "r14", "cbit", "memory"); \
+} while (0)
+
+#else /* not CONFIG_ISA_DUAL_ISSUE */
+
+#define __do_strncpy_from_user(dst,src,count,res) \
+do { \
+ int __d0, __d1, __d2; \
+ __asm__ __volatile__( \
+ " beqz %1, 2f\n" \
+ " .fillinsn\n" \
+ "0: ldb r14, @%3\n" \
+ " stb r14, @%4\n" \
+ " addi %3, #1\n" \
+ " addi %4, #1\n" \
+ " beqz r14, 1f\n" \
+ " addi %1, #-1\n" \
+ " bnez %1, 0b\n" \
+ " .fillinsn\n" \
+ "1: sub %0, %1\n" \
+ " .fillinsn\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ " .balign 4\n" \
+ "3: ldi %0, #%5\n" \
+ " seth r14, #high(2b)\n" \
+ " or3 r14, r14, #low(2b)\n" \
+ " jmp r14\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .balign 4\n" \
+ " .long 0b,3b\n" \
+ ".previous" \
+ : "=r"(res), "=r"(count), "=&r" (__d0), "=&r" (__d1), \
+ "=&r" (__d2) \
+ : "i"(-EFAULT), "0"(count), "1"(count), "3"(src), \
+ "4"(dst) \
+ : "r14", "cbit", "memory"); \
+} while (0)
+
+#endif /* CONFIG_ISA_DUAL_ISSUE */
+
+long
+__strncpy_from_user(char *dst, const char *src, long count)
+{
+ long res;
+ __do_strncpy_from_user(dst, src, count, res);
+ return res;
+}
+
+long
+strncpy_from_user(char *dst, const char *src, long count)
+{
+ long res = -EFAULT;
+ if (access_ok(VERIFY_READ, src, 1))
+ __do_strncpy_from_user(dst, src, count, res);
+ return res;
+}
+
+
+/*
+ * Zero Userspace
+ */
+
+#ifdef CONFIG_ISA_DUAL_ISSUE
+
+#define __do_clear_user(addr,size) \
+do { \
+ int __dst, __c; \
+ __asm__ __volatile__( \
+ " beqz %1, 9f\n" \
+ " and3 r14, %0, #3\n" \
+ " bnez r14, 2f\n" \
+ " and3 r14, %1, #3\n" \
+ " bnez r14, 2f\n" \
+ " and3 %1, %1, #3\n" \
+ " beqz %2, 2f\n" \
+ " addi %0, #-4\n" \
+ " .fillinsn\n" \
+ "0: ; word clear \n" \
+ " st %6, @+%0 || addi %2, #-1\n" \
+ " bnez %2, 0b\n" \
+ " beqz %1, 9f\n" \
+ " .fillinsn\n" \
+ "2: ; byte clear \n" \
+ " stb %6, @%0 || addi %1, #-1\n" \
+ " addi %0, #1\n" \
+ " bnez %1, 2b\n" \
+ " .fillinsn\n" \
+ "9:\n" \
+ ".section .fixup,\"ax\"\n" \
+ " .balign 4\n" \
+ "4: slli %2, #2\n" \
+ " seth r14, #high(9b)\n" \
+ " or3 r14, r14, #low(9b)\n" \
+ " jmp r14 || add %1, %2\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .balign 4\n" \
+ " .long 0b,4b\n" \
+ " .long 2b,9b\n" \
+ ".previous\n" \
+ : "=&r"(__dst), "=&r"(size), "=&r"(__c) \
+ : "0"(addr), "1"(size), "2"(size / 4), "r"(0) \
+ : "r14", "cbit", "memory"); \
+} while (0)
+
+#else /* not CONFIG_ISA_DUAL_ISSUE */
+
+#define __do_clear_user(addr,size) \
+do { \
+ int __dst, __c; \
+ __asm__ __volatile__( \
+ " beqz %1, 9f\n" \
+ " and3 r14, %0, #3\n" \
+ " bnez r14, 2f\n" \
+ " and3 r14, %1, #3\n" \
+ " bnez r14, 2f\n" \
+ " and3 %1, %1, #3\n" \
+ " beqz %2, 2f\n" \
+ " addi %0, #-4\n" \
+ " .fillinsn\n" \
+ "0: st %6, @+%0 ; word clear \n" \
+ " addi %2, #-1\n" \
+ " bnez %2, 0b\n" \
+ " beqz %1, 9f\n" \
+ " .fillinsn\n" \
+ "2: stb %6, @%0 ; byte clear \n" \
+ " addi %1, #-1\n" \
+ " addi %0, #1\n" \
+ " bnez %1, 2b\n" \
+ " .fillinsn\n" \
+ "9:\n" \
+ ".section .fixup,\"ax\"\n" \
+ " .balign 4\n" \
+ "4: slli %2, #2\n" \
+ " add %1, %2\n" \
+ " seth r14, #high(9b)\n" \
+ " or3 r14, r14, #low(9b)\n" \
+ " jmp r14\n" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .balign 4\n" \
+ " .long 0b,4b\n" \
+ " .long 2b,9b\n" \
+ ".previous\n" \
+ : "=&r"(__dst), "=&r"(size), "=&r"(__c) \
+ : "0"(addr), "1"(size), "2"(size / 4), "r"(0) \
+ : "r14", "cbit", "memory"); \
+} while (0)
+
+#endif /* not CONFIG_ISA_DUAL_ISSUE */
+
+unsigned long
+clear_user(void *to, unsigned long n)
+{
+ if (access_ok(VERIFY_WRITE, to, n))
+ __do_clear_user(to, n);
+ return n;
+}
+
+unsigned long
+__clear_user(void *to, unsigned long n)
+{
+ __do_clear_user(to, n);
+ return n;
+}
+
+/*
+ * Return the size of a string (including the ending 0)
+ *
+ * Return 0 on exception, a value greater than N if too long
+ */
+
+#ifdef CONFIG_ISA_DUAL_ISSUE
+
+long strnlen_user(const char *s, long n)
+{
+ unsigned long mask = -__addr_ok(s);
+ unsigned long res;
+
+ __asm__ __volatile__(
+ " and %0, %5 || mv r1, %1\n"
+ " beqz %0, strnlen_exit\n"
+ " and3 r0, %1, #3\n"
+ " bnez r0, strnlen_byte_loop\n"
+ " cmpui %0, #4\n"
+ " bc strnlen_byte_loop\n"
+ "strnlen_word_loop:\n"
+ "0: ld r0, @%1+\n"
+ " pcmpbz r0\n"
+ " bc strnlen_last_bytes_fixup\n"
+ " addi %0, #-4\n"
+ " beqz %0, strnlen_exit\n"
+ " bgtz %0, strnlen_word_loop\n"
+ "strnlen_last_bytes:\n"
+ " mv %0, %4\n"
+ "strnlen_last_bytes_fixup:\n"
+ " addi %1, #-4\n"
+ "strnlen_byte_loop:\n"
+ "1: ldb r0, @%1 || addi %0, #-1\n"
+ " beqz r0, strnlen_exit\n"
+ " addi %1, #1\n"
+ " bnez %0, strnlen_byte_loop\n"
+ "strnlen_exit:\n"
+ " sub %1, r1\n"
+ " add3 %0, %1, #1\n"
+ " .fillinsn\n"
+ "9:\n"
+ ".section .fixup,\"ax\"\n"
+ " .balign 4\n"
+ "4: addi %1, #-4\n"
+ " .fillinsn\n"
+ "5: seth r1, #high(9b)\n"
+ " or3 r1, r1, #low(9b)\n"
+ " jmp r1 || ldi %0, #0\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .balign 4\n"
+ " .long 0b,4b\n"
+ " .long 1b,5b\n"
+ ".previous"
+ : "=&r" (res), "=r" (s)
+ : "0" (n), "1" (s), "r" (n & 3), "r" (mask), "r"(0x01010101)
+ : "r0", "r1", "cbit");
+
+ /* NOTE: strnlen_user() algorism:
+ * {
+ * char *p;
+ * for (p = s; n-- && *p != '\0'; ++p)
+ * ;
+ * return p - s + 1;
+ * }
+ */
+
+ /* NOTE: If a null char. exists, return 0.
+ * if ((x - 0x01010101) & ~x & 0x80808080)\n"
+ * return 0;\n"
+ */
+
+ return res & mask;
+}
+
+#else /* not CONFIG_ISA_DUAL_ISSUE */
+
+long strnlen_user(const char *s, long n)
+{
+ unsigned long mask = -__addr_ok(s);
+ unsigned long res;
+
+ __asm__ __volatile__(
+ " and %0, %5\n"
+ " mv r1, %1\n"
+ " beqz %0, strnlen_exit\n"
+ " and3 r0, %1, #3\n"
+ " bnez r0, strnlen_byte_loop\n"
+ " cmpui %0, #4\n"
+ " bc strnlen_byte_loop\n"
+ " sll3 r3, %6, #7\n"
+ "strnlen_word_loop:\n"
+ "0: ld r0, @%1+\n"
+ " not r2, r0\n"
+ " sub r0, %6\n"
+ " and r2, r3\n"
+ " and r2, r0\n"
+ " bnez r2, strnlen_last_bytes_fixup\n"
+ " addi %0, #-4\n"
+ " beqz %0, strnlen_exit\n"
+ " bgtz %0, strnlen_word_loop\n"
+ "strnlen_last_bytes:\n"
+ " mv %0, %4\n"
+ "strnlen_last_bytes_fixup:\n"
+ " addi %1, #-4\n"
+ "strnlen_byte_loop:\n"
+ "1: ldb r0, @%1\n"
+ " addi %0, #-1\n"
+ " beqz r0, strnlen_exit\n"
+ " addi %1, #1\n"
+ " bnez %0, strnlen_byte_loop\n"
+ "strnlen_exit:\n"
+ " sub %1, r1\n"
+ " add3 %0, %1, #1\n"
+ " .fillinsn\n"
+ "9:\n"
+ ".section .fixup,\"ax\"\n"
+ " .balign 4\n"
+ "4: addi %1, #-4\n"
+ " .fillinsn\n"
+ "5: ldi %0, #0\n"
+ " seth r1, #high(9b)\n"
+ " or3 r1, r1, #low(9b)\n"
+ " jmp r1\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ " .balign 4\n"
+ " .long 0b,4b\n"
+ " .long 1b,5b\n"
+ ".previous"
+ : "=&r" (res), "=r" (s)
+ : "0" (n), "1" (s), "r" (n & 3), "r" (mask), "r"(0x01010101)
+ : "r0", "r1", "r2", "r3", "cbit");
+
+ /* NOTE: strnlen_user() algorism:
+ * {
+ * char *p;
+ * for (p = s; n-- && *p != '\0'; ++p)
+ * ;
+ * return p - s + 1;
+ * }
+ */
+
+ /* NOTE: If a null char. exists, return 0.
+ * if ((x - 0x01010101) & ~x & 0x80808080)\n"
+ * return 0;\n"
+ */
+
+ return res & mask;
+}
+
+#endif /* CONFIG_ISA_DUAL_ISSUE */
+