Messages in this thread Patch in this message | ![/](/images/icornerl.gif) | | From | Palmer Dabbelt <> | Subject | [PATCH 5/7] RISC-V: arch/riscv/lib | Date | Mon, 22 May 2017 17:41:05 -0700 |
| |
--- 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 <darius@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 <darius@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 <darius@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
| ![\](/images/icornerr.gif) |