---
 arch/riscv/lib/Makefile  |   5 ++
 arch/riscv/lib/ashldi3.c |  42 ++++++++++++++++
 arch/riscv/lib/ashrdi3.c |  44 +++++++++++++++++
 arch/riscv/lib/delay.c   |  42 ++++++++++++++++
 arch/riscv/lib/libgcc.h  |  46 +++++++++++++++++
 arch/riscv/lib/lshrdi3.c |  42 ++++++++++++++++
 arch/riscv/lib/memcpy.S  |  99 +++++++++++++++++++++++++++++++++++++
 arch/riscv/lib/memset.S  | 119 ++++++++++++++++++++++++++++++++++++++++++++
 arch/riscv/lib/uaccess.S | 125 +++++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 564 insertions(+)
 create mode 100644 arch/riscv/lib/Makefile
 create mode 100644 arch/riscv/lib/ashldi3.c
 create mode 100644 arch/riscv/lib/ashrdi3.c
 create mode 100644 arch/riscv/lib/delay.c
 create mode 100644 arch/riscv/lib/libgcc.h
 create mode 100644 arch/riscv/lib/lshrdi3.c
 create mode 100644 arch/riscv/lib/memcpy.S
 create mode 100644 arch/riscv/lib/memset.S
 create mode 100644 arch/riscv/lib/uaccess.S

diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
new file mode 100644
index 000000000000..f644e582f4b8
--- /dev/null
+++ b/arch/riscv/lib/Makefile
@@ -0,0 +1,5 @@
+lib-y  := delay.o memcpy.o memset.o uaccess.o
+
+ifeq ($(CONFIG_64BIT),)
+lib-y += ashldi3.o ashrdi3.o lshrdi3.o
+endif
diff --git a/arch/riscv/lib/ashldi3.c b/arch/riscv/lib/ashldi3.c
new file mode 100644
index 000000000000..9fb71e82ff16
--- /dev/null
+++ b/arch/riscv/lib/ashldi3.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 Darius Rad <dar...@bluespec.com>
+ *
+ *   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, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *   NON INFRINGEMENT.  See the GNU General Public License for
+ *   more details.
+ */
+
+#include <linux/export.h>
+
+#include "libgcc.h"
+
+long long __ashldi3(long long u, word_type b)
+{
+       DWunion uu, w;
+       word_type bm;
+
+       if (b == 0)
+               return u;
+
+       uu.ll = u;
+       bm = 32 - b;
+
+       if (bm <= 0) {
+               w.s.low = 0;
+               w.s.high = (unsigned int) uu.s.low << -bm;
+       } else {
+               const unsigned int carries = (unsigned int) uu.s.low >> bm;
+
+               w.s.low = (unsigned int) uu.s.low << b;
+               w.s.high = ((unsigned int) uu.s.high << b) | carries;
+       }
+
+       return w.ll;
+}
+EXPORT_SYMBOL(__ashldi3);
diff --git a/arch/riscv/lib/ashrdi3.c b/arch/riscv/lib/ashrdi3.c
new file mode 100644
index 000000000000..8a92e7e8de33
--- /dev/null
+++ b/arch/riscv/lib/ashrdi3.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 Darius Rad <dar...@bluespec.com>
+ *
+ *   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, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *   NON INFRINGEMENT.  See the GNU General Public License for
+ *   more details.
+ */
+
+#include <linux/export.h>
+
+#include "libgcc.h"
+
+long long __ashrdi3(long long u, word_type b)
+{
+       DWunion uu, w;
+       word_type bm;
+
+       if (b == 0)
+               return u;
+
+       uu.ll = u;
+       bm = 32 - b;
+
+       if (bm <= 0) {
+               /* w.s.high = 1..1 or 0..0 */
+               w.s.high =
+                   uu.s.high >> 31;
+               w.s.low = uu.s.high >> -bm;
+       } else {
+               const unsigned int carries = (unsigned int) uu.s.high << bm;
+
+               w.s.high = uu.s.high >> b;
+               w.s.low = ((unsigned int) uu.s.low >> b) | carries;
+       }
+
+       return w.ll;
+}
+EXPORT_SYMBOL(__ashrdi3);
diff --git a/arch/riscv/lib/delay.c b/arch/riscv/lib/delay.c
new file mode 100644
index 000000000000..3c7bf85a0b04
--- /dev/null
+++ b/arch/riscv/lib/delay.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 Regents of the University of California
+ *
+ *   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, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *   NON INFRINGEMENT.  See the GNU General Public License for
+ *   more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/param.h>
+#include <linux/timex.h>
+#include <linux/export.h>
+
+void __delay(unsigned long cycles)
+{
+       u64 t0 = get_cycles();
+
+       while ((unsigned long)(get_cycles() - t0) < cycles)
+               cpu_relax();
+}
+
+void udelay(unsigned long usecs)
+{
+       u64 ucycles = (u64)usecs * timebase;
+       do_div(ucycles, 1000000U);
+       __delay((unsigned long)ucycles);
+}
+EXPORT_SYMBOL(udelay);
+
+void ndelay(unsigned long nsecs)
+{
+       u64 ncycles = (u64)nsecs * timebase;
+       do_div(ncycles, 1000000000U);
+       __delay((unsigned long)ncycles);
+}
+EXPORT_SYMBOL(ndelay);
diff --git a/arch/riscv/lib/libgcc.h b/arch/riscv/lib/libgcc.h
new file mode 100644
index 000000000000..2d3fa9d87922
--- /dev/null
+++ b/arch/riscv/lib/libgcc.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 Regents of the University of California
+ *
+ *   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, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *   NON INFRINGEMENT.  See the GNU General Public License for
+ *   more details.
+ */
+
+#ifndef __ASM_LIBGCC_H
+#define __ASM_LIBGCC_H
+
+#include <asm/byteorder.h>
+
+typedef int word_type __attribute__ ((mode (__word__)));
+
+#ifdef __BIG_ENDIAN
+struct DWstruct {
+       int high, low;
+};
+#elif defined(__LITTLE_ENDIAN)
+struct DWstruct {
+       int low, high;
+};
+#else
+#error I feel sick.
+#endif
+
+typedef union {
+       struct DWstruct s;
+       long long ll;
+} DWunion;
+
+extern long long __ashldi3(long long u, word_type b);
+extern long long __ashrdi3(long long u, word_type b);
+extern word_type __cmpdi2(long long a, long long b);
+extern long long __lshrdi3(long long u, word_type b);
+extern long long __muldi3(long long u, long long v);
+extern word_type __ucmpdi2(unsigned long long a, unsigned long long b);
+
+#endif /* __ASM_LIBGCC_H */
diff --git a/arch/riscv/lib/lshrdi3.c b/arch/riscv/lib/lshrdi3.c
new file mode 100644
index 000000000000..ad4e132959f9
--- /dev/null
+++ b/arch/riscv/lib/lshrdi3.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 Darius Rad <dar...@bluespec.com>
+ *
+ *   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, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *   NON INFRINGEMENT.  See the GNU General Public License for
+ *   more details.
+ */
+
+#include <linux/export.h>
+
+#include "libgcc.h"
+
+long long __lshrdi3(long long u, word_type b)
+{
+       DWunion uu, w;
+       word_type bm;
+
+       if (b == 0)
+               return u;
+
+       uu.ll = u;
+       bm = 32 - b;
+
+       if (bm <= 0) {
+               w.s.high = 0;
+               w.s.low = (unsigned int) uu.s.high >> -bm;
+       } else {
+               const unsigned int carries = (unsigned int) uu.s.high << bm;
+
+               w.s.high = (unsigned int) uu.s.high >> b;
+               w.s.low = ((unsigned int) uu.s.low >> b) | carries;
+       }
+
+       return w.ll;
+}
+EXPORT_SYMBOL(__lshrdi3);
diff --git a/arch/riscv/lib/memcpy.S b/arch/riscv/lib/memcpy.S
new file mode 100644
index 000000000000..1d789ff57d7d
--- /dev/null
+++ b/arch/riscv/lib/memcpy.S
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2013 Regents of the University of California
+ *
+ *   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, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *   NON INFRINGEMENT.  See the GNU General Public License for
+ *   more details.
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+
+/* void *memcpy(void *, const void *, size_t) */
+ENTRY(memcpy)
+       move t6, a0  /* Preserve return value */
+
+       /* Defer to byte-oriented copy for small sizes */
+       sltiu a3, a2, 128
+       bnez a3, 4f
+       /* Use word-oriented copy only if low-order bits match */
+       andi a3, t6, SZREG-1
+       andi a4, a1, SZREG-1
+       bne a3, a4, 4f
+
+       beqz a3, 2f  /* Skip if already aligned */
+       /* Round to nearest double word-aligned address
+          greater than or equal to start address */
+       andi a3, a1, ~(SZREG-1)
+       addi a3, a3, SZREG
+       /* Handle initial misalignment */
+       sub a4, a3, a1
+1:
+       lb a5, 0(a1)
+       addi a1, a1, 1
+       sb a5, 0(t6)
+       addi t6, t6, 1
+       bltu a1, a3, 1b
+       sub a2, a2, a4  /* Update count */
+
+2:
+       andi a4, a2, ~((16*SZREG)-1)
+       beqz a4, 4f
+       add a3, a1, a4
+3:
+       REG_L a4,       0(a1)
+       REG_L a5,   SZREG(a1)
+       REG_L a6, 2*SZREG(a1)
+       REG_L a7, 3*SZREG(a1)
+       REG_L t0, 4*SZREG(a1)
+       REG_L t1, 5*SZREG(a1)
+       REG_L t2, 6*SZREG(a1)
+       REG_L t3, 7*SZREG(a1)
+       REG_L t4, 8*SZREG(a1)
+       REG_L t5, 9*SZREG(a1)
+       REG_S a4,       0(t6)
+       REG_S a5,   SZREG(t6)
+       REG_S a6, 2*SZREG(t6)
+       REG_S a7, 3*SZREG(t6)
+       REG_S t0, 4*SZREG(t6)
+       REG_S t1, 5*SZREG(t6)
+       REG_S t2, 6*SZREG(t6)
+       REG_S t3, 7*SZREG(t6)
+       REG_S t4, 8*SZREG(t6)
+       REG_S t5, 9*SZREG(t6)
+       REG_L a4, 10*SZREG(a1)
+       REG_L a5, 11*SZREG(a1)
+       REG_L a6, 12*SZREG(a1)
+       REG_L a7, 13*SZREG(a1)
+       REG_L t0, 14*SZREG(a1)
+       REG_L t1, 15*SZREG(a1)
+       addi a1, a1, 16*SZREG
+       REG_S a4, 10*SZREG(t6)
+       REG_S a5, 11*SZREG(t6)
+       REG_S a6, 12*SZREG(t6)
+       REG_S a7, 13*SZREG(t6)
+       REG_S t0, 14*SZREG(t6)
+       REG_S t1, 15*SZREG(t6)
+       addi t6, t6, 16*SZREG
+       bltu a1, a3, 3b
+       andi a2, a2, (16*SZREG)-1  /* Update count */
+
+4:
+       /* Handle trailing misalignment */
+       beqz a2, 6f
+       add a3, a1, a2
+5:
+       lb a4, 0(a1)
+       addi a1, a1, 1
+       sb a4, 0(t6)
+       addi t6, t6, 1
+       bltu a1, a3, 5b
+6:
+       ret
+END(memcpy)
diff --git a/arch/riscv/lib/memset.S b/arch/riscv/lib/memset.S
new file mode 100644
index 000000000000..9d5156899232
--- /dev/null
+++ b/arch/riscv/lib/memset.S
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2013 Regents of the University of California
+ *
+ *   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, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful, but
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ *   NON INFRINGEMENT.  See the GNU General Public License for
+ *   more details.
+ */
+
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+
+/* void *memset(void *, int, size_t) */
+ENTRY(memset)
+       move t0, a0  /* Preserve return value */
+
+       /* Defer to byte-oriented fill for small sizes */
+       sltiu a3, a2, 16
+       bnez a3, 4f
+
+       /* Round to nearest XLEN-aligned address
+          greater than or equal to start address */
+       addi a3, t0, SZREG-1
+       andi a3, a3, ~(SZREG-1)
+       beq a3, t0, 2f  /* Skip if already aligned */
+       /* Handle initial misalignment */
+       sub a4, a3, t0
+1:
+       sb a1, 0(t0)
+       addi t0, t0, 1
+       bltu t0, a3, 1b
+       sub a2, a2, a4  /* Update count */
+
+2: /* Duff's device with 32 XLEN stores per iteration */
+       /* Broadcast value into all bytes */
+       andi a1, a1, 0xff
+       slli a3, a1, 8
+       or a1, a3, a1
+       slli a3, a1, 16
+       or a1, a3, a1
+#ifdef CONFIG_64BIT
+       slli a3, a1, 32
+       or a1, a3, a1
+#endif
+
+       /* Calculate end address */
+       andi a4, a2, ~(SZREG-1)
+       add a3, t0, a4
+
+       andi a4, a4, 31*SZREG  /* Calculate remainder */
+       beqz a4, 3f            /* Shortcut if no remainder */
+       neg a4, a4
+       addi a4, a4, 32*SZREG  /* Calculate initial offset */
+
+       /* Adjust start address with offset */
+       sub t0, t0, a4
+
+       /* Jump into loop body */
+       /* Assumes 32-bit instruction lengths */
+       la a5, 3f
+#ifdef CONFIG_64BIT
+       srli a4, a4, 1
+#endif
+       add a5, a5, a4
+       jr a5
+3:
+       REG_S a1,        0(t0)
+       REG_S a1,    SZREG(t0)
+       REG_S a1,  2*SZREG(t0)
+       REG_S a1,  3*SZREG(t0)
+       REG_S a1,  4*SZREG(t0)
+       REG_S a1,  5*SZREG(t0)
+       REG_S a1,  6*SZREG(t0)
+       REG_S a1,  7*SZREG(t0)
+       REG_S a1,  8*SZREG(t0)
+       REG_S a1,  9*SZREG(t0)
+       REG_S a1, 10*SZREG(t0)
+       REG_S a1, 11*SZREG(t0)
+       REG_S a1, 12*SZREG(t0)
+       REG_S a1, 13*SZREG(t0)
+       REG_S a1, 14*SZREG(t0)
+       REG_S a1, 15*SZREG(t0)
+       REG_S a1, 16*SZREG(t0)
+       REG_S a1, 17*SZREG(t0)
+       REG_S a1, 18*SZREG(t0)
+       REG_S a1, 19*SZREG(t0)
+       REG_S a1, 20*SZREG(t0)
+       REG_S a1, 21*SZREG(t0)
+       REG_S a1, 22*SZREG(t0)
+       REG_S a1, 23*SZREG(t0)
+       REG_S a1, 24*SZREG(t0)
+       REG_S a1, 25*SZREG(t0)
+       REG_S a1, 26*SZREG(t0)
+       REG_S a1, 27*SZREG(t0)
+       REG_S a1, 28*SZREG(t0)
+       REG_S a1, 29*SZREG(t0)
+       REG_S a1, 30*SZREG(t0)
+       REG_S a1, 31*SZREG(t0)
+       addi t0, t0, 32*SZREG
+       bltu t0, a3, 3b
+       andi a2, a2, SZREG-1  /* Update count */
+
+4:
+       /* Handle trailing misalignment */
+       beqz a2, 6f
+       add a3, t0, a2
+5:
+       sb a1, 0(t0)
+       addi t0, t0, 1
+       bltu t0, a3, 5b
+6:
+       ret
+END(memset)
diff --git a/arch/riscv/lib/uaccess.S b/arch/riscv/lib/uaccess.S
new file mode 100644
index 000000000000..971ae7d86f97
--- /dev/null
+++ b/arch/riscv/lib/uaccess.S
@@ -0,0 +1,125 @@
+#include <linux/linkage.h>
+#include <asm/asm.h>
+#include <asm/csr.h>
+
+       .altmacro
+       .macro fixup op reg addr lbl
+       LOCAL _epc
+_epc:
+       \op \reg, \addr
+       .section __ex_table,"a"
+       .balign SZPTR
+       PTR _epc, \lbl
+       .previous
+       .endm
+
+ENTRY(__copy_user)
+
+#ifdef CONFIG_RV_PUM
+       /* Enable access to user memory */
+       li t6, SR_SUM
+       csrs sstatus, t6
+#endif
+
+       add a3, a1, a2
+       /* Use word-oriented copy only if low-order bits match */
+       andi t0, a0, SZREG-1
+       andi t1, a1, SZREG-1
+       bne t0, t1, 2f
+
+       addi t0, a1, SZREG-1
+       andi t1, a3, ~(SZREG-1)
+       andi t0, t0, ~(SZREG-1)
+       /* a3: terminal address of source region
+        * t0: lowest XLEN-aligned address in source
+        * t1: highest XLEN-aligned address in source
+        */
+       bgeu t0, t1, 2f
+       bltu a1, t0, 4f
+1:
+       fixup REG_L, t2, (a1), 10f
+       fixup REG_S, t2, (a0), 10f
+       addi a1, a1, SZREG
+       addi a0, a0, SZREG
+       bltu a1, t1, 1b
+2:
+       bltu a1, a3, 5f
+
+3:
+#ifdef CONFIG_RV_PUM
+       /* Disable access to user memory */
+       csrc sstatus, t6
+#endif
+       li a0, 0
+       ret
+4: /* Edge case: unalignment */
+       fixup lbu, t2, (a1), 10f
+       fixup sb, t2, (a0), 10f
+       addi a1, a1, 1
+       addi a0, a0, 1
+       bltu a1, t0, 4b
+       j 1b
+5: /* Edge case: remainder */
+       fixup lbu, t2, (a1), 10f
+       fixup sb, t2, (a0), 10f
+       addi a1, a1, 1
+       addi a0, a0, 1
+       bltu a1, a3, 5b
+       j 3b
+ENDPROC(__copy_user)
+
+
+ENTRY(__clear_user)
+
+#ifdef CONFIG_RV_PUM
+       /* Enable access to user memory */
+       li t6, SR_SUM
+       csrs sstatus, t6
+#endif
+
+       add a3, a0, a1
+       addi t0, a0, SZREG-1
+       andi t1, a3, ~(SZREG-1)
+       andi t0, t0, ~(SZREG-1)
+       /* a3: terminal address of target region
+        * t0: lowest doubleword-aligned address in target region
+        * t1: highest doubleword-aligned address in target region
+        */
+       bgeu t0, t1, 2f
+       bltu a0, t0, 4f
+1:
+       fixup REG_S, zero, (a0), 10f
+       addi a0, a0, SZREG
+       bltu a0, t1, 1b
+2:
+       bltu a0, a3, 5f
+
+3:
+#ifdef CONFIG_RV_PUM
+       /* Disable access to user memory */
+       csrc sstatus, t6
+#endif
+       li a0, 0
+       ret
+4: /* Edge case: unalignment */
+       fixup sb, zero, (a0), 10f
+       addi a0, a0, 1
+       bltu a0, t0, 4b
+       j 1b
+5: /* Edge case: remainder */
+       fixup sb, zero, (a0), 10f
+       addi a0, a0, 1
+       bltu a0, a3, 5b
+       j 3b
+ENDPROC(__clear_user)
+
+       .section .fixup,"ax"
+       .balign 4
+10:
+#ifdef CONFIG_RV_PUM
+       /* Disable access to user memory */
+       csrs sstatus, t6
+#endif
+       sub a0, a3, a0
+       ret
+       .previous
-- 
2.13.0

Reply via email to