From e68ad867f77e1a839ad496ffcbd88b9c96769b5b Mon Sep 17 00:00:00 2001 From: Alan Kao Date: Tue, 9 Oct 2018 10:18:30 +0800 Subject: Extract FPU context operations from entry.S We move __fstate_save and __fstate_restore to a new source file, fpu.S. Signed-off-by: Alan Kao Cc: Greentime Hu Cc: Vincent Chen Cc: Zong Li Cc: Nick Hu Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/Makefile | 1 + arch/riscv/kernel/entry.S | 87 ------------------------------------- arch/riscv/kernel/fpu.S | 106 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 87 deletions(-) create mode 100644 arch/riscv/kernel/fpu.S diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index e1274fc03af4..bd433efd915e 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -13,6 +13,7 @@ extra-y += vmlinux.lds obj-y += cpu.o obj-y += cpufeature.o obj-y += entry.o +obj-y += fpu.o obj-y += irq.o obj-y += process.o obj-y += ptrace.o diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index fa2c08e3c05e..59c02e2bf739 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -357,93 +357,6 @@ ENTRY(__switch_to) ret ENDPROC(__switch_to) -ENTRY(__fstate_save) - li a2, TASK_THREAD_F0 - add a0, a0, a2 - li t1, SR_FS - csrs sstatus, t1 - frcsr t0 - fsd f0, TASK_THREAD_F0_F0(a0) - fsd f1, TASK_THREAD_F1_F0(a0) - fsd f2, TASK_THREAD_F2_F0(a0) - fsd f3, TASK_THREAD_F3_F0(a0) - fsd f4, TASK_THREAD_F4_F0(a0) - fsd f5, TASK_THREAD_F5_F0(a0) - fsd f6, TASK_THREAD_F6_F0(a0) - fsd f7, TASK_THREAD_F7_F0(a0) - fsd f8, TASK_THREAD_F8_F0(a0) - fsd f9, TASK_THREAD_F9_F0(a0) - fsd f10, TASK_THREAD_F10_F0(a0) - fsd f11, TASK_THREAD_F11_F0(a0) - fsd f12, TASK_THREAD_F12_F0(a0) - fsd f13, TASK_THREAD_F13_F0(a0) - fsd f14, TASK_THREAD_F14_F0(a0) - fsd f15, TASK_THREAD_F15_F0(a0) - fsd f16, TASK_THREAD_F16_F0(a0) - fsd f17, TASK_THREAD_F17_F0(a0) - fsd f18, TASK_THREAD_F18_F0(a0) - fsd f19, TASK_THREAD_F19_F0(a0) - fsd f20, TASK_THREAD_F20_F0(a0) - fsd f21, TASK_THREAD_F21_F0(a0) - fsd f22, TASK_THREAD_F22_F0(a0) - fsd f23, TASK_THREAD_F23_F0(a0) - fsd f24, TASK_THREAD_F24_F0(a0) - fsd f25, TASK_THREAD_F25_F0(a0) - fsd f26, TASK_THREAD_F26_F0(a0) - fsd f27, TASK_THREAD_F27_F0(a0) - fsd f28, TASK_THREAD_F28_F0(a0) - fsd f29, TASK_THREAD_F29_F0(a0) - fsd f30, TASK_THREAD_F30_F0(a0) - fsd f31, TASK_THREAD_F31_F0(a0) - sw t0, TASK_THREAD_FCSR_F0(a0) - csrc sstatus, t1 - ret -ENDPROC(__fstate_save) - -ENTRY(__fstate_restore) - li a2, TASK_THREAD_F0 - add a0, a0, a2 - li t1, SR_FS - lw t0, TASK_THREAD_FCSR_F0(a0) - csrs sstatus, t1 - fld f0, TASK_THREAD_F0_F0(a0) - fld f1, TASK_THREAD_F1_F0(a0) - fld f2, TASK_THREAD_F2_F0(a0) - fld f3, TASK_THREAD_F3_F0(a0) - fld f4, TASK_THREAD_F4_F0(a0) - fld f5, TASK_THREAD_F5_F0(a0) - fld f6, TASK_THREAD_F6_F0(a0) - fld f7, TASK_THREAD_F7_F0(a0) - fld f8, TASK_THREAD_F8_F0(a0) - fld f9, TASK_THREAD_F9_F0(a0) - fld f10, TASK_THREAD_F10_F0(a0) - fld f11, TASK_THREAD_F11_F0(a0) - fld f12, TASK_THREAD_F12_F0(a0) - fld f13, TASK_THREAD_F13_F0(a0) - fld f14, TASK_THREAD_F14_F0(a0) - fld f15, TASK_THREAD_F15_F0(a0) - fld f16, TASK_THREAD_F16_F0(a0) - fld f17, TASK_THREAD_F17_F0(a0) - fld f18, TASK_THREAD_F18_F0(a0) - fld f19, TASK_THREAD_F19_F0(a0) - fld f20, TASK_THREAD_F20_F0(a0) - fld f21, TASK_THREAD_F21_F0(a0) - fld f22, TASK_THREAD_F22_F0(a0) - fld f23, TASK_THREAD_F23_F0(a0) - fld f24, TASK_THREAD_F24_F0(a0) - fld f25, TASK_THREAD_F25_F0(a0) - fld f26, TASK_THREAD_F26_F0(a0) - fld f27, TASK_THREAD_F27_F0(a0) - fld f28, TASK_THREAD_F28_F0(a0) - fld f29, TASK_THREAD_F29_F0(a0) - fld f30, TASK_THREAD_F30_F0(a0) - fld f31, TASK_THREAD_F31_F0(a0) - fscsr t0 - csrc sstatus, t1 - ret -ENDPROC(__fstate_restore) - - .section ".rodata" /* Exception vector table */ ENTRY(excp_vect_table) diff --git a/arch/riscv/kernel/fpu.S b/arch/riscv/kernel/fpu.S new file mode 100644 index 000000000000..1defb0618aff --- /dev/null +++ b/arch/riscv/kernel/fpu.S @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2012 Regents of the University of California + * Copyright (C) 2017 SiFive + * + * 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. See the + * GNU General Public License for more details. + */ + +#include + +#include +#include +#include + +ENTRY(__fstate_save) + li a2, TASK_THREAD_F0 + add a0, a0, a2 + li t1, SR_FS + csrs sstatus, t1 + frcsr t0 + fsd f0, TASK_THREAD_F0_F0(a0) + fsd f1, TASK_THREAD_F1_F0(a0) + fsd f2, TASK_THREAD_F2_F0(a0) + fsd f3, TASK_THREAD_F3_F0(a0) + fsd f4, TASK_THREAD_F4_F0(a0) + fsd f5, TASK_THREAD_F5_F0(a0) + fsd f6, TASK_THREAD_F6_F0(a0) + fsd f7, TASK_THREAD_F7_F0(a0) + fsd f8, TASK_THREAD_F8_F0(a0) + fsd f9, TASK_THREAD_F9_F0(a0) + fsd f10, TASK_THREAD_F10_F0(a0) + fsd f11, TASK_THREAD_F11_F0(a0) + fsd f12, TASK_THREAD_F12_F0(a0) + fsd f13, TASK_THREAD_F13_F0(a0) + fsd f14, TASK_THREAD_F14_F0(a0) + fsd f15, TASK_THREAD_F15_F0(a0) + fsd f16, TASK_THREAD_F16_F0(a0) + fsd f17, TASK_THREAD_F17_F0(a0) + fsd f18, TASK_THREAD_F18_F0(a0) + fsd f19, TASK_THREAD_F19_F0(a0) + fsd f20, TASK_THREAD_F20_F0(a0) + fsd f21, TASK_THREAD_F21_F0(a0) + fsd f22, TASK_THREAD_F22_F0(a0) + fsd f23, TASK_THREAD_F23_F0(a0) + fsd f24, TASK_THREAD_F24_F0(a0) + fsd f25, TASK_THREAD_F25_F0(a0) + fsd f26, TASK_THREAD_F26_F0(a0) + fsd f27, TASK_THREAD_F27_F0(a0) + fsd f28, TASK_THREAD_F28_F0(a0) + fsd f29, TASK_THREAD_F29_F0(a0) + fsd f30, TASK_THREAD_F30_F0(a0) + fsd f31, TASK_THREAD_F31_F0(a0) + sw t0, TASK_THREAD_FCSR_F0(a0) + csrc sstatus, t1 + ret +ENDPROC(__fstate_save) + +ENTRY(__fstate_restore) + li a2, TASK_THREAD_F0 + add a0, a0, a2 + li t1, SR_FS + lw t0, TASK_THREAD_FCSR_F0(a0) + csrs sstatus, t1 + fld f0, TASK_THREAD_F0_F0(a0) + fld f1, TASK_THREAD_F1_F0(a0) + fld f2, TASK_THREAD_F2_F0(a0) + fld f3, TASK_THREAD_F3_F0(a0) + fld f4, TASK_THREAD_F4_F0(a0) + fld f5, TASK_THREAD_F5_F0(a0) + fld f6, TASK_THREAD_F6_F0(a0) + fld f7, TASK_THREAD_F7_F0(a0) + fld f8, TASK_THREAD_F8_F0(a0) + fld f9, TASK_THREAD_F9_F0(a0) + fld f10, TASK_THREAD_F10_F0(a0) + fld f11, TASK_THREAD_F11_F0(a0) + fld f12, TASK_THREAD_F12_F0(a0) + fld f13, TASK_THREAD_F13_F0(a0) + fld f14, TASK_THREAD_F14_F0(a0) + fld f15, TASK_THREAD_F15_F0(a0) + fld f16, TASK_THREAD_F16_F0(a0) + fld f17, TASK_THREAD_F17_F0(a0) + fld f18, TASK_THREAD_F18_F0(a0) + fld f19, TASK_THREAD_F19_F0(a0) + fld f20, TASK_THREAD_F20_F0(a0) + fld f21, TASK_THREAD_F21_F0(a0) + fld f22, TASK_THREAD_F22_F0(a0) + fld f23, TASK_THREAD_F23_F0(a0) + fld f24, TASK_THREAD_F24_F0(a0) + fld f25, TASK_THREAD_F25_F0(a0) + fld f26, TASK_THREAD_F26_F0(a0) + fld f27, TASK_THREAD_F27_F0(a0) + fld f28, TASK_THREAD_F28_F0(a0) + fld f29, TASK_THREAD_F29_F0(a0) + fld f30, TASK_THREAD_F30_F0(a0) + fld f31, TASK_THREAD_F31_F0(a0) + fscsr t0 + csrc sstatus, t1 + ret +ENDPROC(__fstate_restore) -- cgit v1.2.3-59-g8ed1b From 007f5c35895786fdc797f13313b9493fe5d5e655 Mon Sep 17 00:00:00 2001 From: Alan Kao Date: Tue, 9 Oct 2018 10:18:31 +0800 Subject: Refactor FPU code in signal setup/return procedures FPU-related logic is separated from normal signal handling path in this patch. Kernel can easily be configured to exclude those procedures for no-FPU systems. Signed-off-by: Alan Kao Cc: Greentime Hu Cc: Vincent Chen Cc: Zong Li Cc: Nick Hu Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/signal.c | 68 ++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c index 718d0c984ef0..6a18b9819ead 100644 --- a/arch/riscv/kernel/signal.c +++ b/arch/riscv/kernel/signal.c @@ -37,45 +37,63 @@ struct rt_sigframe { struct ucontext uc; }; -static long restore_d_state(struct pt_regs *regs, - struct __riscv_d_ext_state __user *state) +static long restore_fp_state(struct pt_regs *regs, + union __riscv_fp_state *sc_fpregs) { long err; + struct __riscv_d_ext_state __user *state = &sc_fpregs->d; + size_t i; + err = __copy_from_user(¤t->thread.fstate, state, sizeof(*state)); - if (likely(!err)) - fstate_restore(current, regs); + if (unlikely(err)) + return err; + + fstate_restore(current, regs); + + /* We support no other extension state at this time. */ + for (i = 0; i < ARRAY_SIZE(sc_fpregs->q.reserved); i++) { + u32 value; + + err = __get_user(value, &sc_fpregs->q.reserved[i]); + if (unlikely(err)) + break; + if (value != 0) + return -EINVAL; + } + return err; } -static long save_d_state(struct pt_regs *regs, - struct __riscv_d_ext_state __user *state) +static long save_fp_state(struct pt_regs *regs, + union __riscv_fp_state *sc_fpregs) { + long err; + struct __riscv_d_ext_state __user *state = &sc_fpregs->d; + size_t i; + fstate_save(current, regs); - return __copy_to_user(state, ¤t->thread.fstate, sizeof(*state)); + err = __copy_to_user(state, ¤t->thread.fstate, sizeof(*state)); + if (unlikely(err)) + return err; + + /* We support no other extension state at this time. */ + for (i = 0; i < ARRAY_SIZE(sc_fpregs->q.reserved); i++) { + err = __put_user(0, &sc_fpregs->q.reserved[i]); + if (unlikely(err)) + break; + } + + return err; } static long restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) { long err; - size_t i; /* sc_regs is structured the same as the start of pt_regs */ err = __copy_from_user(regs, &sc->sc_regs, sizeof(sc->sc_regs)); - if (unlikely(err)) - return err; /* Restore the floating-point state. */ - err = restore_d_state(regs, &sc->sc_fpregs.d); - if (unlikely(err)) - return err; - /* We support no other extension state at this time. */ - for (i = 0; i < ARRAY_SIZE(sc->sc_fpregs.q.reserved); i++) { - u32 value; - err = __get_user(value, &sc->sc_fpregs.q.reserved[i]); - if (unlikely(err)) - break; - if (value != 0) - return -EINVAL; - } + err |= restore_fp_state(regs, &sc->sc_fpregs); return err; } @@ -124,14 +142,10 @@ static long setup_sigcontext(struct rt_sigframe __user *frame, { struct sigcontext __user *sc = &frame->uc.uc_mcontext; long err; - size_t i; /* sc_regs is structured the same as the start of pt_regs */ err = __copy_to_user(&sc->sc_regs, regs, sizeof(sc->sc_regs)); /* Save the floating-point state. */ - err |= save_d_state(regs, &sc->sc_fpregs.d); - /* We support no other extension state at this time. */ - for (i = 0; i < ARRAY_SIZE(sc->sc_fpregs.q.reserved); i++) - err |= __put_user(0, &sc->sc_fpregs.q.reserved[i]); + err |= save_fp_state(regs, &sc->sc_fpregs); return err; } -- cgit v1.2.3-59-g8ed1b From e8be5302330281bd9f77834600f63e8cc4560d3d Mon Sep 17 00:00:00 2001 From: Alan Kao Date: Tue, 9 Oct 2018 10:18:32 +0800 Subject: Cleanup ISA string setting This patch cleanup the MARCH string passing to both compiler and assembler. Note that the CFLAGS should not contain "fd" before we have mechnisms like kernel_fpu_begin/end in other architectures. Signed-off-by: Alan Kao Cc: Greentime Hu Cc: Vincent Chen Cc: Zong Li Cc: Nick Hu Signed-off-by: Palmer Dabbelt --- arch/riscv/Makefile | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 61ec42405ec9..01393e1b2921 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -28,7 +28,6 @@ ifeq ($(CONFIG_ARCH_RV64I),y) KBUILD_CFLAGS += $(call cc-ifversion, -ge, 0500, -DCONFIG_ARCH_SUPPORTS_INT128) - KBUILD_MARCH = rv64im KBUILD_LDFLAGS += -melf64lriscv else BITS := 32 @@ -36,22 +35,20 @@ else KBUILD_CFLAGS += -mabi=ilp32 KBUILD_AFLAGS += -mabi=ilp32 - KBUILD_MARCH = rv32im KBUILD_LDFLAGS += -melf32lriscv endif KBUILD_CFLAGS += -Wall -ifeq ($(CONFIG_RISCV_ISA_A),y) - KBUILD_ARCH_A = a -endif -ifeq ($(CONFIG_RISCV_ISA_C),y) - KBUILD_ARCH_C = c -endif - -KBUILD_AFLAGS += -march=$(KBUILD_MARCH)$(KBUILD_ARCH_A)fd$(KBUILD_ARCH_C) +# ISA string setting +riscv-march-$(CONFIG_ARCH_RV32I) := rv32im +riscv-march-$(CONFIG_ARCH_RV64I) := rv64im +riscv-march-$(CONFIG_RISCV_ISA_A) := $(riscv-march-y)a +riscv-march-y := $(riscv-march-y)fd +riscv-march-$(CONFIG_RISCV_ISA_C) := $(riscv-march-y)c +KBUILD_CFLAGS += -march=$(subst fd,,$(riscv-march-y)) +KBUILD_AFLAGS += -march=$(riscv-march-y) -KBUILD_CFLAGS += -march=$(KBUILD_MARCH)$(KBUILD_ARCH_A)$(KBUILD_ARCH_C) KBUILD_CFLAGS += -mno-save-restore KBUILD_CFLAGS += -DCONFIG_PAGE_OFFSET=$(CONFIG_PAGE_OFFSET) -- cgit v1.2.3-59-g8ed1b From 9671f7061433e2c58b9894093eada1898595b85d Mon Sep 17 00:00:00 2001 From: Alan Kao Date: Tue, 9 Oct 2018 10:18:33 +0800 Subject: Allow to disable FPU support FPU codes have been separated from common part in previous patches. This patch add the CONFIG_FPU option and some stubs, so that a no-FPU configuration is allowed. Signed-off-by: Alan Kao Cc: Greentime Hu Cc: Vincent Chen Cc: Zong Li Cc: Nick Hu Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 9 +++++++++ arch/riscv/Makefile | 2 +- arch/riscv/include/asm/switch_to.h | 10 ++++++++++ arch/riscv/kernel/Makefile | 2 +- arch/riscv/kernel/process.c | 4 +++- arch/riscv/kernel/signal.c | 5 +++++ 6 files changed, 29 insertions(+), 3 deletions(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index a344980287a5..a63f9dbb4706 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -208,6 +208,15 @@ config RISCV_BASE_PMU endmenu +config FPU + bool "FPU support" + default y + help + Say N here if you want to disable all floating-point related procedure + in the kernel. + + If you don't know what to do here, say Y. + endmenu menu "Kernel type" diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 01393e1b2921..901770fc9bd2 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -44,7 +44,7 @@ KBUILD_CFLAGS += -Wall riscv-march-$(CONFIG_ARCH_RV32I) := rv32im riscv-march-$(CONFIG_ARCH_RV64I) := rv64im riscv-march-$(CONFIG_RISCV_ISA_A) := $(riscv-march-y)a -riscv-march-y := $(riscv-march-y)fd +riscv-march-$(CONFIG_FPU) := $(riscv-march-y)fd riscv-march-$(CONFIG_RISCV_ISA_C) := $(riscv-march-y)c KBUILD_CFLAGS += -march=$(subst fd,,$(riscv-march-y)) KBUILD_AFLAGS += -march=$(riscv-march-y) diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h index dd6b05bff75b..093050b03543 100644 --- a/arch/riscv/include/asm/switch_to.h +++ b/arch/riscv/include/asm/switch_to.h @@ -18,6 +18,7 @@ #include #include +#ifdef CONFIG_FPU extern void __fstate_save(struct task_struct *save_to); extern void __fstate_restore(struct task_struct *restore_from); @@ -55,6 +56,15 @@ static inline void __switch_to_aux(struct task_struct *prev, fstate_restore(next, task_pt_regs(next)); } +#define DEFAULT_SSTATUS (SR_SPIE | SR_FS_INITIAL) + +#else +#define fstate_save(task, regs) do { } while (0) +#define fstate_restore(task, regs) do { } while (0) +#define __switch_to_aux(__prev, __next) do { } while (0) +#define DEFAULT_SSTATUS (SR_SPIE | SR_FS_OFF) +#endif + extern struct task_struct *__switch_to(struct task_struct *, struct task_struct *); diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index bd433efd915e..f13f7f276639 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -13,7 +13,6 @@ extra-y += vmlinux.lds obj-y += cpu.o obj-y += cpufeature.o obj-y += entry.o -obj-y += fpu.o obj-y += irq.o obj-y += process.o obj-y += ptrace.o @@ -32,6 +31,7 @@ obj-y += vdso/ CFLAGS_setup.o := -mcmodel=medany +obj-$(CONFIG_FPU) += fpu.o obj-$(CONFIG_SMP) += smpboot.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_MODULES) += module.o diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index d7c6ca7c95ae..07d515655aa9 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -76,7 +76,7 @@ void show_regs(struct pt_regs *regs) void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) { - regs->sstatus = SR_SPIE /* User mode, irqs on */ | SR_FS_INITIAL; + regs->sstatus = DEFAULT_SSTATUS; regs->sepc = pc; regs->sp = sp; set_fs(USER_DS); @@ -84,12 +84,14 @@ void start_thread(struct pt_regs *regs, unsigned long pc, void flush_thread(void) { +#ifdef CONFIG_FPU /* * Reset FPU context * frm: round to nearest, ties to even (IEEE default) * fflags: accrued exceptions cleared */ memset(¤t->thread.fstate, 0, sizeof(current->thread.fstate)); +#endif } int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c index 6a18b9819ead..2450b824d799 100644 --- a/arch/riscv/kernel/signal.c +++ b/arch/riscv/kernel/signal.c @@ -37,6 +37,7 @@ struct rt_sigframe { struct ucontext uc; }; +#ifdef CONFIG_FPU static long restore_fp_state(struct pt_regs *regs, union __riscv_fp_state *sc_fpregs) { @@ -85,6 +86,10 @@ static long save_fp_state(struct pt_regs *regs, return err; } +#else +#define save_fp_state(task, regs) (0) +#define restore_fp_state(task, regs) (0) +#endif static long restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) -- cgit v1.2.3-59-g8ed1b From 9411ec60c23d868124d9c1b1d491937aebe07afa Mon Sep 17 00:00:00 2001 From: Alan Kao Date: Tue, 9 Oct 2018 10:18:34 +0800 Subject: Auto-detect whether a FPU exists We expect that a kernel with CONFIG_FPU=y can still support no-FPU machines. To do so, the kernel should first examine the existence of a FPU, then do nothing if a FPU does exist; otherwise, it should disable/bypass all FPU-related functions. In this patch, a new global variable, has_fpu, is created and determined when parsing the hardware capability from device tree during booting. This variable is used in those FPU-related functions. Signed-off-by: Alan Kao Cc: Greentime Hu Cc: Vincent Chen Cc: Zong Li Cc: Nick Hu Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/switch_to.h | 8 ++++---- arch/riscv/kernel/cpufeature.c | 8 ++++++++ arch/riscv/kernel/process.c | 4 +++- arch/riscv/kernel/signal.c | 6 ++++-- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h index 093050b03543..733559083f24 100644 --- a/arch/riscv/include/asm/switch_to.h +++ b/arch/riscv/include/asm/switch_to.h @@ -56,13 +56,12 @@ static inline void __switch_to_aux(struct task_struct *prev, fstate_restore(next, task_pt_regs(next)); } -#define DEFAULT_SSTATUS (SR_SPIE | SR_FS_INITIAL) - +extern bool has_fpu; #else +#define has_fpu false #define fstate_save(task, regs) do { } while (0) #define fstate_restore(task, regs) do { } while (0) #define __switch_to_aux(__prev, __next) do { } while (0) -#define DEFAULT_SSTATUS (SR_SPIE | SR_FS_OFF) #endif extern struct task_struct *__switch_to(struct task_struct *, @@ -72,7 +71,8 @@ extern struct task_struct *__switch_to(struct task_struct *, do { \ struct task_struct *__prev = (prev); \ struct task_struct *__next = (next); \ - __switch_to_aux(__prev, __next); \ + if (has_fpu) \ + __switch_to_aux(__prev, __next); \ ((last) = __switch_to(__prev, __next)); \ } while (0) diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index 17011a870044..46942e635266 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -22,6 +22,9 @@ #include unsigned long elf_hwcap __read_mostly; +#ifdef CONFIG_FPU +bool has_fpu __read_mostly; +#endif void riscv_fill_hwcap(void) { @@ -58,4 +61,9 @@ void riscv_fill_hwcap(void) elf_hwcap |= isa2hwcap[(unsigned char)(isa[i])]; pr_info("elf_hwcap is 0x%lx", elf_hwcap); + +#ifdef CONFIG_FPU + if (elf_hwcap & (COMPAT_HWCAP_ISA_F | COMPAT_HWCAP_ISA_D)) + has_fpu = true; +#endif } diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index 07d515655aa9..bef19993ea92 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -76,7 +76,9 @@ void show_regs(struct pt_regs *regs) void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) { - regs->sstatus = DEFAULT_SSTATUS; + regs->sstatus = SR_SPIE; + if (has_fpu) + regs->sstatus |= SR_FS_INITIAL; regs->sepc = pc; regs->sp = sp; set_fs(USER_DS); diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c index 2450b824d799..f9b5e7e352ef 100644 --- a/arch/riscv/kernel/signal.c +++ b/arch/riscv/kernel/signal.c @@ -98,7 +98,8 @@ static long restore_sigcontext(struct pt_regs *regs, /* sc_regs is structured the same as the start of pt_regs */ err = __copy_from_user(regs, &sc->sc_regs, sizeof(sc->sc_regs)); /* Restore the floating-point state. */ - err |= restore_fp_state(regs, &sc->sc_fpregs); + if (has_fpu) + err |= restore_fp_state(regs, &sc->sc_fpregs); return err; } @@ -150,7 +151,8 @@ static long setup_sigcontext(struct rt_sigframe __user *frame, /* sc_regs is structured the same as the start of pt_regs */ err = __copy_to_user(&sc->sc_regs, regs, sizeof(sc->sc_regs)); /* Save the floating-point state. */ - err |= save_fp_state(regs, &sc->sc_fpregs); + if (has_fpu) + err |= save_fp_state(regs, &sc->sc_fpregs); return err; } -- cgit v1.2.3-59-g8ed1b From 7f47c73b355f300cf162f3a664e43d557d2cb30d Mon Sep 17 00:00:00 2001 From: Zong Li Date: Tue, 2 Oct 2018 16:52:27 +0800 Subject: RISC-V: Build tishift only on 64-bit Only RV64 supports 128 integer size. Signed-off-by: Zong Li Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/lib/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index 445ec84f9a47..5739bd05d289 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -2,6 +2,7 @@ lib-y += delay.o lib-y += memcpy.o lib-y += memset.o lib-y += uaccess.o -lib-y += tishift.o + +lib-(CONFIG_64BIT) += tishift.o lib-$(CONFIG_32BIT) += udivdi3.o -- cgit v1.2.3-59-g8ed1b From 51858aaf9bea3ddf166bf9d252a1fc351260b497 Mon Sep 17 00:00:00 2001 From: Zong Li Date: Tue, 2 Oct 2018 16:52:28 +0800 Subject: RISC-V: Use swiotlb on RV64 only Only RV64 supports swiotlb. On RV32, it don't select the SWIOTLB. Signed-off-by: Zong Li Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/setup.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index b2d26d9d8489..c9461985db7e 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -227,7 +227,10 @@ void __init setup_arch(char **cmdline_p) setup_bootmem(); paging_init(); unflatten_device_tree(); + +#ifdef CONFIG_SWIOTLB swiotlb_init(1); +#endif #ifdef CONFIG_SMP setup_smp(); -- cgit v1.2.3-59-g8ed1b From 6315730e9eab7de5fa9864bb13a352713f48aef1 Mon Sep 17 00:00:00 2001 From: Zong Li Date: Tue, 2 Oct 2018 16:52:29 +0800 Subject: lib: Add umoddi3 and udivmoddi4 of GCC library routines Add umoddi3 and udivmoddi4 support for 32-bit. The RV32 need the umoddi3 to do modulo when the operands are long long type, like other libraries implementation such as ucmpdi2, lshrdi3 and so on. I encounter the undefined reference 'umoddi3' when I use the in house dma driver, although it is in house driver, but I think that umoddi3 is a common function for RV32. The udivmoddi4 and umoddi3 are copies from libgcc in gcc. There are other functions use the udivmoddi4 in libgcc, so I separate the umoddi3 and udivmoddi4 for flexible extension in the future. Signed-off-by: Zong Li Signed-off-by: Palmer Dabbelt --- lib/Kconfig | 3 + lib/Makefile | 1 + lib/udivmoddi4.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/umoddi3.c | 32 ++++++ 4 files changed, 346 insertions(+) create mode 100644 lib/udivmoddi4.c create mode 100644 lib/umoddi3.c diff --git a/lib/Kconfig b/lib/Kconfig index a3928d4438b5..d82f20609939 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -621,3 +621,6 @@ config GENERIC_LIB_CMPDI2 config GENERIC_LIB_UCMPDI2 bool + +config GENERIC_LIB_UMODDI3 + bool diff --git a/lib/Makefile b/lib/Makefile index 423876446810..56a8d9c23ef3 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -270,3 +270,4 @@ obj-$(CONFIG_GENERIC_LIB_LSHRDI3) += lshrdi3.o obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o +obj-$(CONFIG_GENERIC_LIB_UMODDI3) += umoddi3.o udivmoddi4.o diff --git a/lib/udivmoddi4.c b/lib/udivmoddi4.c new file mode 100644 index 000000000000..c08bc8a5f1cf --- /dev/null +++ b/lib/udivmoddi4.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * 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. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc. + */ + +#include + +#define count_leading_zeros(COUNT, X) ((COUNT) = __builtin_clz(X)) + +#define W_TYPE_SIZE 32 + +#define __ll_B ((unsigned long) 1 << (W_TYPE_SIZE / 2)) +#define __ll_lowpart(t) ((unsigned long) (t) & (__ll_B - 1)) +#define __ll_highpart(t) ((unsigned long) (t) >> (W_TYPE_SIZE / 2)) + +/* If we still don't have umul_ppmm, define it using plain C. */ +#if !defined(umul_ppmm) +#define umul_ppmm(w1, w0, u, v) \ + do { \ + unsigned long __x0, __x1, __x2, __x3; \ + unsigned short __ul, __vl, __uh, __vh; \ + \ + __ul = __ll_lowpart(u); \ + __uh = __ll_highpart(u); \ + __vl = __ll_lowpart(v); \ + __vh = __ll_highpart(v); \ + \ + __x0 = (unsigned long) __ul * __vl; \ + __x1 = (unsigned long) __ul * __vh; \ + __x2 = (unsigned long) __uh * __vl; \ + __x3 = (unsigned long) __uh * __vh; \ + \ + __x1 += __ll_highpart(__x0); \ + __x1 += __x2; \ + if (__x1 < __x2) \ + __x3 += __ll_B; \ + \ + (w1) = __x3 + __ll_highpart(__x1); \ + (w0) = __ll_lowpart(__x1) * __ll_B + __ll_lowpart(__x0);\ + } while (0) +#endif + +#if !defined(sub_ddmmss) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + do { \ + unsigned long __x; \ + __x = (al) - (bl); \ + (sh) = (ah) - (bh) - (__x > (al)); \ + (sl) = __x; \ + } while (0) +#endif + +/* Define this unconditionally, so it can be used for debugging. */ +#define __udiv_qrnnd_c(q, r, n1, n0, d) \ + do { \ + unsigned long __d1, __d0, __q1, __q0; \ + unsigned long __r1, __r0, __m; \ + __d1 = __ll_highpart(d); \ + __d0 = __ll_lowpart(d); \ + \ + __r1 = (n1) % __d1; \ + __q1 = (n1) / __d1; \ + __m = (unsigned long) __q1 * __d0; \ + __r1 = __r1 * __ll_B | __ll_highpart(n0); \ + if (__r1 < __m) { \ + __q1--, __r1 += (d); \ + if (__r1 >= (d)) \ + if (__r1 < __m) \ + __q1--, __r1 += (d); \ + } \ + __r1 -= __m; \ + \ + __r0 = __r1 % __d1; \ + __q0 = __r1 / __d1; \ + __m = (unsigned long) __q0 * __d0; \ + __r0 = __r0 * __ll_B | __ll_lowpart(n0); \ + if (__r0 < __m) { \ + __q0--, __r0 += (d); \ + if (__r0 >= (d)) \ + if (__r0 < __m) \ + __q0--, __r0 += (d); \ + } \ + __r0 -= __m; \ + \ + (q) = (unsigned long) __q1 * __ll_B | __q0; \ + (r) = __r0; \ + } while (0) + +/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */ +#if !defined(udiv_qrnnd) +#define UDIV_NEEDS_NORMALIZATION 1 +#define udiv_qrnnd __udiv_qrnnd_c +#endif + +unsigned long long __udivmoddi4(unsigned long long u, unsigned long long v, + unsigned long long *rp) +{ + const DWunion nn = {.ll = u }; + const DWunion dd = {.ll = v }; + DWunion rr, ww; + unsigned long d0, d1, n0, n1, n2; + unsigned long q0 = 0, q1 = 0; + unsigned long b, bm; + + d0 = dd.s.low; + d1 = dd.s.high; + n0 = nn.s.low; + n1 = nn.s.high; + +#if !UDIV_NEEDS_NORMALIZATION + + if (d1 == 0) { + if (d0 > n1) { + /* 0q = nn / 0D */ + + udiv_qrnnd(q0, n0, n1, n0, d0); + q1 = 0; + + /* Remainder in n0. */ + } else { + /* qq = NN / 0d */ + + if (d0 == 0) + /* Divide intentionally by zero. */ + d0 = 1 / d0; + + udiv_qrnnd(q1, n1, 0, n1, d0); + udiv_qrnnd(q0, n0, n1, n0, d0); + + /* Remainder in n0. */ + } + + if (rp != 0) { + rr.s.low = n0; + rr.s.high = 0; + *rp = rr.ll; + } + +#else /* UDIV_NEEDS_NORMALIZATION */ + + if (d1 == 0) { + if (d0 > n1) { + /* 0q = nn / 0D */ + + count_leading_zeros(bm, d0); + + if (bm != 0) { + /* + * Normalize, i.e. make the most significant bit + * of the denominator set. + */ + + d0 = d0 << bm; + n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm)); + n0 = n0 << bm; + } + + udiv_qrnnd(q0, n0, n1, n0, d0); + q1 = 0; + + /* Remainder in n0 >> bm. */ + } else { + /* qq = NN / 0d */ + + if (d0 == 0) + /* Divide intentionally by zero. */ + d0 = 1 / d0; + + count_leading_zeros(bm, d0); + + if (bm == 0) { + /* + * From (n1 >= d0) /\ (the most significant bit + * of d0 is set), conclude (the most significant + * bit of n1 is set) /\ (theleading quotient + * digit q1 = 1). + * + * This special case is necessary, not an + * optimization. (Shifts counts of W_TYPE_SIZE + * are undefined.) + */ + + n1 -= d0; + q1 = 1; + } else { + /* Normalize. */ + + b = W_TYPE_SIZE - bm; + + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd(q1, n1, n2, n1, d0); + } + + /* n1 != d0... */ + + udiv_qrnnd(q0, n0, n1, n0, d0); + + /* Remainder in n0 >> bm. */ + } + + if (rp != 0) { + rr.s.low = n0 >> bm; + rr.s.high = 0; + *rp = rr.ll; + } + +#endif /* UDIV_NEEDS_NORMALIZATION */ + + } else { + if (d1 > n1) { + /* 00 = nn / DD */ + + q0 = 0; + q1 = 0; + + /* Remainder in n1n0. */ + if (rp != 0) { + rr.s.low = n0; + rr.s.high = n1; + *rp = rr.ll; + } + } else { + /* 0q = NN / dd */ + + count_leading_zeros(bm, d1); + if (bm == 0) { + /* + * From (n1 >= d1) /\ (the most significant bit + * of d1 is set), conclude (the most significant + * bit of n1 is set) /\ (the quotient digit q0 = + * 0 or 1). + * + * This special case is necessary, not an + * optimization. + */ + + /* + * The condition on the next line takes + * advantage of that n1 >= d1 (true due to + * program flow). + */ + if (n1 > d1 || n0 >= d0) { + q0 = 1; + sub_ddmmss(n1, n0, n1, n0, d1, d0); + } else { + q0 = 0; + } + + q1 = 0; + + if (rp != 0) { + rr.s.low = n0; + rr.s.high = n1; + *rp = rr.ll; + } + } else { + unsigned long m1, m0; + /* Normalize. */ + + b = W_TYPE_SIZE - bm; + + d1 = (d1 << bm) | (d0 >> b); + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd(q0, n1, n2, n1, d1); + umul_ppmm(m1, m0, q0, d0); + + if (m1 > n1 || (m1 == n1 && m0 > n0)) { + q0--; + sub_ddmmss(m1, m0, m1, m0, d1, d0); + } + + q1 = 0; + + /* Remainder in (n1n0 - m1m0) >> bm. */ + if (rp != 0) { + sub_ddmmss(n1, n0, n1, n0, m1, m0); + rr.s.low = (n1 << b) | (n0 >> bm); + rr.s.high = n1 >> bm; + *rp = rr.ll; + } + } + } + } + + ww.s.low = q0; + ww.s.high = q1; + + return ww.ll; +} diff --git a/lib/umoddi3.c b/lib/umoddi3.c new file mode 100644 index 000000000000..d7bbf0f85197 --- /dev/null +++ b/lib/umoddi3.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * 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. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc. + */ + +#include +#include + +extern unsigned long long __udivmoddi4(unsigned long long u, + unsigned long long v, + unsigned long long *rp); + +unsigned long long __umoddi3(unsigned long long u, unsigned long long v) +{ + unsigned long long w; + (void)__udivmoddi4(u, v, &w); + return w; +} +EXPORT_SYMBOL(__umoddi3); -- cgit v1.2.3-59-g8ed1b From 757331db921428295948fed5e7377a436e66d34e Mon Sep 17 00:00:00 2001 From: Zong Li Date: Tue, 2 Oct 2018 16:52:30 +0800 Subject: RISC-V: Select GENERIC_LIB_UMODDI3 on RV32 On 32-bit, it need to use __umoddi3 by some drivers. Signed-off-by: Zong Li Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index a344980287a5..dc262fa0417b 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -108,6 +108,7 @@ config ARCH_RV32I select GENERIC_LIB_ASHRDI3 select GENERIC_LIB_LSHRDI3 select GENERIC_LIB_UCMPDI2 + select GENERIC_LIB_UMODDI3 config ARCH_RV64I bool "RV64I" -- cgit v1.2.3-59-g8ed1b From 827a438156e4c423b6875a092e272933952a2910 Mon Sep 17 00:00:00 2001 From: Vincent Chen Date: Tue, 2 Oct 2018 16:52:31 +0800 Subject: RISC-V: Avoid corrupting the upper 32-bit of phys_addr_t in ioremap For 32bit, the upper 32-bit of phys_addr_t will be flushed to zero after AND with PAGE_MASK because the data type of PAGE_MASK is unsigned long. To fix this problem, the page alignment is done by subtracting the page offset instead of AND with PAGE_MASK. Signed-off-by: Vincent Chen Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/mm/ioremap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/mm/ioremap.c b/arch/riscv/mm/ioremap.c index 70ef2724cdf6..bd2f2db557cc 100644 --- a/arch/riscv/mm/ioremap.c +++ b/arch/riscv/mm/ioremap.c @@ -42,7 +42,7 @@ static void __iomem *__ioremap_caller(phys_addr_t addr, size_t size, /* Page-align mappings */ offset = addr & (~PAGE_MASK); - addr &= PAGE_MASK; + addr -= offset; size = PAGE_ALIGN(size + offset); area = get_vm_area_caller(size, VM_IOREMAP, caller); -- cgit v1.2.3-59-g8ed1b From 1ed4237ab616a05225e11d07bf42d5474deec905 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Tue, 2 Oct 2018 12:14:54 -0700 Subject: RISC-V: No need to pass scause as arg to do_IRQ() The scause is already part of pt_regs so no need to pass scause as separate arg to do_IRQ(). Reviewed-by: Christoph Hellwig Signed-off-by: Anup Patel Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/entry.S | 1 - arch/riscv/kernel/irq.c | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index fa2c08e3c05e..6eaacfa5b63d 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -168,7 +168,6 @@ ENTRY(handle_exception) /* Handle interrupts */ move a0, sp /* pt_regs */ - move a1, s4 /* scause */ tail do_IRQ 1: /* Exceptions run with interrupts enabled */ diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c index 0cfac48a1272..ca4593317e45 100644 --- a/arch/riscv/kernel/irq.c +++ b/arch/riscv/kernel/irq.c @@ -24,12 +24,12 @@ */ #define INTERRUPT_CAUSE_FLAG (1UL << (__riscv_xlen - 1)) -asmlinkage void __irq_entry do_IRQ(struct pt_regs *regs, unsigned long cause) +asmlinkage void __irq_entry do_IRQ(struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); irq_enter(); - switch (cause & ~INTERRUPT_CAUSE_FLAG) { + switch (regs->scause & ~INTERRUPT_CAUSE_FLAG) { case INTERRUPT_CAUSE_TIMER: riscv_timer_interrupt(); break; -- cgit v1.2.3-59-g8ed1b From 566d6c428eadf9dc06df8b2195dff58d9a97c9e6 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Tue, 2 Oct 2018 12:14:55 -0700 Subject: RISC-V: Don't set cacheinfo.{physical_line_partition,attributes} These are just hard coded in the RISC-V port, which doesn't make any sense. We should probably be setting these from device tree entries when they exist, but for now I think it's saner to just leave them all as their default values. Signed-off-by: Palmer Dabbelt Reviewed-by: Christoph Hellwig Reviewed-by: Jeremy Linton Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/cacheinfo.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/riscv/kernel/cacheinfo.c b/arch/riscv/kernel/cacheinfo.c index 0bc86e5f8f3f..cb35ffd8ec6b 100644 --- a/arch/riscv/kernel/cacheinfo.c +++ b/arch/riscv/kernel/cacheinfo.c @@ -22,13 +22,6 @@ static void ci_leaf_init(struct cacheinfo *this_leaf, { this_leaf->level = level; this_leaf->type = type; - /* not a sector cache */ - this_leaf->physical_line_partition = 1; - /* TODO: Add to DTS */ - this_leaf->attributes = - CACHE_WRITE_BACK - | CACHE_READ_ALLOCATE - | CACHE_WRITE_ALLOCATE; } static int __init_cache_level(unsigned int cpu) -- cgit v1.2.3-59-g8ed1b From 19ccf29bb18f08a4583aa899a8cc8c11e5ea85a6 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Tue, 2 Oct 2018 12:14:56 -0700 Subject: RISC-V: Filter ISA and MMU values in cpuinfo We shouldn't be directly passing device tree values to userspace, both because there could be mistakes in device trees and because the kernel doesn't support arbitrary ISAs. Signed-off-by: Palmer Dabbelt [Atish: checkpatch fix and code comment formatting update] Signed-off-by: Atish Patra Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/cpu.c | 68 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c index ca6c81e54e37..1c0bf6620e65 100644 --- a/arch/riscv/kernel/cpu.c +++ b/arch/riscv/kernel/cpu.c @@ -58,6 +58,63 @@ int riscv_of_processor_hart(struct device_node *node) #ifdef CONFIG_PROC_FS +static void print_isa(struct seq_file *f, const char *orig_isa) +{ + static const char *ext = "mafdc"; + const char *isa = orig_isa; + const char *e; + + /* + * Linux doesn't support rv32e or rv128i, and we only support booting + * kernels on harts with the same ISA that the kernel is compiled for. + */ +#if defined(CONFIG_32BIT) + if (strncmp(isa, "rv32i", 5) != 0) + return; +#elif defined(CONFIG_64BIT) + if (strncmp(isa, "rv64i", 5) != 0) + return; +#endif + + /* Print the base ISA, as we already know it's legal. */ + seq_puts(f, "isa\t: "); + seq_write(f, isa, 5); + isa += 5; + + /* + * Check the rest of the ISA string for valid extensions, printing those + * we find. RISC-V ISA strings define an order, so we only print the + * extension bits when they're in order. + */ + for (e = ext; *e != '\0'; ++e) { + if (isa[0] == e[0]) { + seq_write(f, isa, 1); + isa++; + } + } + + /* + * If we were given an unsupported ISA in the device tree then print + * a bit of info describing what went wrong. + */ + if (isa[0] != '\0') + pr_info("unsupported ISA \"%s\" in device tree", orig_isa); +} + +static void print_mmu(struct seq_file *f, const char *mmu_type) +{ +#if defined(CONFIG_32BIT) + if (strcmp(mmu_type, "riscv,sv32") != 0) + return; +#elif defined(CONFIG_64BIT) + if (strcmp(mmu_type, "riscv,sv39") != 0 && + strcmp(mmu_type, "riscv,sv48") != 0) + return; +#endif + + seq_printf(f, "mmu\t: %s\n", mmu_type+6); +} + static void *c_start(struct seq_file *m, loff_t *pos) { *pos = cpumask_next(*pos - 1, cpu_online_mask); @@ -83,13 +140,10 @@ static int c_show(struct seq_file *m, void *v) const char *compat, *isa, *mmu; seq_printf(m, "hart\t: %lu\n", hart_id); - if (!of_property_read_string(node, "riscv,isa", &isa) - && isa[0] == 'r' - && isa[1] == 'v') - seq_printf(m, "isa\t: %s\n", isa); - if (!of_property_read_string(node, "mmu-type", &mmu) - && !strncmp(mmu, "riscv,", 6)) - seq_printf(m, "mmu\t: %s\n", mmu+6); + if (!of_property_read_string(node, "riscv,isa", &isa)) + print_isa(m, isa); + if (!of_property_read_string(node, "mmu-type", &mmu)) + print_mmu(m, mmu); if (!of_property_read_string(node, "compatible", &compat) && strcmp(compat, "riscv")) seq_printf(m, "uarch\t: %s\n", compat); -- cgit v1.2.3-59-g8ed1b From b18d6f05252d6b3f725c08d8831a46b003df5b6b Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Tue, 2 Oct 2018 12:14:57 -0700 Subject: RISC-V: Comment on the TLB flush in smp_callin() This isn't readily apparent from reading the code. Signed-off-by: Palmer Dabbelt [Atish: code comment formatting update] Signed-off-by: Atish Patra Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/smpboot.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 56abab6a9812..712e9ca85904 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -106,6 +106,10 @@ asmlinkage void __init smp_callin(void) trap_init(); notify_cpu_starting(smp_processor_id()); set_cpu_online(smp_processor_id(), 1); + /* + * Remote TLB flushes are ignored while the CPU is offline, so emit + * a local TLB flush right now just in case. + */ local_flush_tlb_all(); local_irq_enable(); preempt_disable(); -- cgit v1.2.3-59-g8ed1b From 6db170ff4c088caaf7806c00b29a55f6df07d7b6 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Tue, 2 Oct 2018 12:14:58 -0700 Subject: RISC-V: Disable preemption before enabling interrupts Currently, irq is enabled before preemption disabling happens. If the scheduler fired right here and cpu is scheduled then it may blow up. Signed-off-by: Palmer Dabbelt [Atish: Commit text and code comment formatting update] Signed-off-by: Atish Patra Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/smpboot.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 712e9ca85904..670749ecd0c2 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -111,7 +111,11 @@ asmlinkage void __init smp_callin(void) * a local TLB flush right now just in case. */ local_flush_tlb_all(); - local_irq_enable(); + /* + * Disable preemption before enabling interrupts, so we don't try to + * schedule a CPU that hasn't actually started yet. + */ preempt_disable(); + local_irq_enable(); cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); } -- cgit v1.2.3-59-g8ed1b From 9639a44394b9859a5576cb36630105733a552bd6 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Tue, 2 Oct 2018 12:14:59 -0700 Subject: RISC-V: Provide a cleaner raw_smp_processor_id() I'm not sure how I managed to miss this the first time, but this is much better. Signed-off-by: Palmer Dabbelt [Atish: code comment formatting and other fixes] Signed-off-by: Atish Patra Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/smp.h | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h index 36016845461d..85d7619eb927 100644 --- a/arch/riscv/include/asm/smp.h +++ b/arch/riscv/include/asm/smp.h @@ -14,13 +14,9 @@ #ifndef _ASM_RISCV_SMP_H #define _ASM_RISCV_SMP_H -/* This both needs asm-offsets.h and is used when generating it. */ -#ifndef GENERATING_ASM_OFFSETS -#include -#endif - #include #include +#include #ifdef CONFIG_SMP @@ -34,12 +30,10 @@ void arch_send_call_function_ipi_mask(struct cpumask *mask); void arch_send_call_function_single_ipi(int cpu); /* - * This is particularly ugly: it appears we can't actually get the definition - * of task_struct here, but we need access to the CPU this task is running on. - * Instead of using C we're using asm-offsets.h to get the current processor - * ID. + * Obtains the hart ID of the currently executing task. This relies on + * THREAD_INFO_IN_TASK, but we define that unconditionally. */ -#define raw_smp_processor_id() (*((int*)((char*)get_current() + TASK_TI_CPU))) +#define raw_smp_processor_id() (current_thread_info()->cpu) #endif /* CONFIG_SMP */ -- cgit v1.2.3-59-g8ed1b From b2f8cfa7ac34202e5fd9551b6507fcd424634c1b Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Tue, 2 Oct 2018 12:15:00 -0700 Subject: RISC-V: Rename riscv_of_processor_hart to riscv_of_processor_hartid It's a bit confusing exactly what this function does: it actually returns the hartid of an OF processor node, failing with -1 on invalid nodes. I've changed the name to _hartid() in order to make that a bit more clear, as well as adding a comment. Signed-off-by: Palmer Dabbelt [Atish: code comment formatting update] Signed-off-by: Atish Patra Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/processor.h | 2 +- arch/riscv/kernel/cpu.c | 7 +++++-- arch/riscv/kernel/smpboot.c | 2 +- drivers/clocksource/riscv_timer.c | 2 +- drivers/irqchip/irq-sifive-plic.c | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h index 3fe4af8147d2..50de774d827a 100644 --- a/arch/riscv/include/asm/processor.h +++ b/arch/riscv/include/asm/processor.h @@ -88,7 +88,7 @@ static inline void wait_for_interrupt(void) } struct device_node; -extern int riscv_of_processor_hart(struct device_node *node); +int riscv_of_processor_hartid(struct device_node *node); extern void riscv_fill_hwcap(void); diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c index 1c0bf6620e65..4723e235dcaa 100644 --- a/arch/riscv/kernel/cpu.c +++ b/arch/riscv/kernel/cpu.c @@ -15,8 +15,11 @@ #include #include -/* Return -1 if not a valid hart */ -int riscv_of_processor_hart(struct device_node *node) +/* + * Returns the hart ID of the given device tree node, or -1 if the device tree + * node isn't a RISC-V hart. + */ +int riscv_of_processor_hartid(struct device_node *node) { const char *isa, *status; u32 hart; diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 670749ecd0c2..cfb0b02db647 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -53,7 +53,7 @@ void __init setup_smp(void) int hart, im_okay_therefore_i_am = 0; while ((dn = of_find_node_by_type(dn, "cpu"))) { - hart = riscv_of_processor_hart(dn); + hart = riscv_of_processor_hartid(dn); if (hart >= 0) { set_cpu_possible(hart, true); set_cpu_present(hart, true); diff --git a/drivers/clocksource/riscv_timer.c b/drivers/clocksource/riscv_timer.c index 4e8b347e43e2..ad7453fc3129 100644 --- a/drivers/clocksource/riscv_timer.c +++ b/drivers/clocksource/riscv_timer.c @@ -84,7 +84,7 @@ void riscv_timer_interrupt(void) static int __init riscv_timer_init_dt(struct device_node *n) { - int cpu_id = riscv_of_processor_hart(n), error; + int cpu_id = riscv_of_processor_hartid(n), error; struct clocksource *cs; if (cpu_id != smp_processor_id()) diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c index 532e9d68c704..c55eaa31cde2 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -176,7 +176,7 @@ static int plic_find_hart_id(struct device_node *node) { for (; node; node = node->parent) { if (of_device_is_compatible(node, "riscv")) - return riscv_of_processor_hart(node); + return riscv_of_processor_hartid(node); } return -1; -- cgit v1.2.3-59-g8ed1b From 177fae4515889e2407810c5167a5227da8b37cce Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Tue, 2 Oct 2018 12:15:01 -0700 Subject: RISC-V: Rename im_okay_therefore_i_am to found_boot_cpu The old name was a bit odd. Signed-off-by: Palmer Dabbelt Signed-off-by: Atish Patra Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/smpboot.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index cfb0b02db647..4a232600cedb 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -50,7 +50,8 @@ void __init smp_prepare_cpus(unsigned int max_cpus) void __init setup_smp(void) { struct device_node *dn = NULL; - int hart, im_okay_therefore_i_am = 0; + int hart; + bool found_boot_cpu = false; while ((dn = of_find_node_by_type(dn, "cpu"))) { hart = riscv_of_processor_hartid(dn); @@ -58,13 +59,13 @@ void __init setup_smp(void) set_cpu_possible(hart, true); set_cpu_present(hart, true); if (hart == smp_processor_id()) { - BUG_ON(im_okay_therefore_i_am); - im_okay_therefore_i_am = 1; + BUG_ON(found_boot_cpu); + found_boot_cpu = true; } } } - BUG_ON(!im_okay_therefore_i_am); + BUG_ON(!found_boot_cpu); } int __cpu_up(unsigned int cpu, struct task_struct *tidle) -- cgit v1.2.3-59-g8ed1b From 46373cb442c56d2f8a4c8b3f777c89d20546c9d5 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Tue, 2 Oct 2018 12:15:02 -0700 Subject: RISC-V: Use mmgrab() commit f1f1007644ff ("mm: add new mmgrab() helper") added a helper that we missed out on. Signed-off-by: Palmer Dabbelt Reviewed-by: Christoph Hellwig Signed-off-by: Atish Patra Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/smpboot.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 4a232600cedb..17e748312afd 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -101,7 +102,7 @@ asmlinkage void __init smp_callin(void) struct mm_struct *mm = &init_mm; /* All kernel threads share the same mm context. */ - atomic_inc(&mm->mm_count); + mmgrab(mm); current->active_mm = mm; trap_init(); -- cgit v1.2.3-59-g8ed1b From a37d56fc401108f39dec9ba83ed923a453937a26 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Tue, 2 Oct 2018 12:15:03 -0700 Subject: RISC-V: Use WRITE_ONCE instead of direct access The secondary harts spin on couple of per cpu variables until both of these are non-zero so it's not necessary to have any ordering here. However, WRITE_ONCE should be used to avoid tearing. Signed-off-by: Atish Patra Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/smpboot.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 17e748312afd..1e478615017c 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -81,8 +81,9 @@ int __cpu_up(unsigned int cpu, struct task_struct *tidle) * the spinning harts that they can continue the boot process. */ smp_mb(); - __cpu_up_stack_pointer[cpu] = task_stack_page(tidle) + THREAD_SIZE; - __cpu_up_task_pointer[cpu] = tidle; + WRITE_ONCE(__cpu_up_stack_pointer[cpu], + task_stack_page(tidle) + THREAD_SIZE); + WRITE_ONCE(__cpu_up_task_pointer[cpu], tidle); while (!cpu_online(cpu)) cpu_relax(); -- cgit v1.2.3-59-g8ed1b From 6825c7a80f1863b975a00042abe140ea24813af2 Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Tue, 2 Oct 2018 12:15:04 -0700 Subject: RISC-V: Add logical CPU indexing for RISC-V Currently, both Linux CPU id and hart id are same. This is not recommended as it will lead to discontinuous CPU indexing in Linux. Moreover, kdump kernel will run from CPU0 which would be absent if we follow existing scheme. Implement a logical mapping between Linux CPU id and hart id to decouple these two. Always mark the boot processor as CPU0 and all other CPUs get the logical CPU id based on their booting order. Signed-off-by: Atish Patra Reviewed-by: Anup Patel Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/smp.h | 24 +++++++++++++++++++++++- arch/riscv/kernel/setup.c | 4 ++++ arch/riscv/kernel/smp.c | 19 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h index 85d7619eb927..47fd61dfc897 100644 --- a/arch/riscv/include/asm/smp.h +++ b/arch/riscv/include/asm/smp.h @@ -18,6 +18,13 @@ #include #include +#define INVALID_HARTID ULONG_MAX +/* + * Mapping between linux logical cpu index and hartid. + */ +extern unsigned long __cpuid_to_hartid_map[NR_CPUS]; +#define cpuid_to_hartid_map(cpu) __cpuid_to_hartid_map[cpu] + #ifdef CONFIG_SMP /* SMP initialization hook for setup_arch */ @@ -29,12 +36,27 @@ void arch_send_call_function_ipi_mask(struct cpumask *mask); /* Hook for the generic smp_call_function_single() routine. */ void arch_send_call_function_single_ipi(int cpu); +int riscv_hartid_to_cpuid(int hartid); +void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out); + /* * Obtains the hart ID of the currently executing task. This relies on * THREAD_INFO_IN_TASK, but we define that unconditionally. */ #define raw_smp_processor_id() (current_thread_info()->cpu) -#endif /* CONFIG_SMP */ +#else + +static inline int riscv_hartid_to_cpuid(int hartid) +{ + return 0; +} +static inline void riscv_cpuid_to_hartid_mask(const struct cpumask *in, + struct cpumask *out) +{ + cpumask_set_cpu(cpuid_to_hartid_map(0), out); +} + +#endif /* CONFIG_SMP */ #endif /* _ASM_RISCV_SMP_H */ diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index b2d26d9d8489..d5d8611066d5 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -82,6 +82,10 @@ EXPORT_SYMBOL(empty_zero_page); /* The lucky hart to first increment this variable will boot the other cores */ atomic_t hart_lottery; +unsigned long __cpuid_to_hartid_map[NR_CPUS] = { + [0 ... NR_CPUS-1] = INVALID_HARTID +}; + #ifdef CONFIG_BLK_DEV_INITRD static void __init setup_initrd(void) { diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c index 906fe21ea21b..0bd48935f886 100644 --- a/arch/riscv/kernel/smp.c +++ b/arch/riscv/kernel/smp.c @@ -38,7 +38,26 @@ enum ipi_message_type { IPI_MAX }; +int riscv_hartid_to_cpuid(int hartid) +{ + int i = -1; + + for (i = 0; i < NR_CPUS; i++) + if (cpuid_to_hartid_map(i) == hartid) + return i; + + pr_err("Couldn't find cpu id for hartid [%d]\n", hartid); + BUG(); + return i; +} +void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out) +{ + int cpu; + + for_each_cpu(cpu, in) + cpumask_set_cpu(cpuid_to_hartid_map(cpu), out); +} /* Unsupported */ int setup_profiling_timer(unsigned int multiplier) { -- cgit v1.2.3-59-g8ed1b From f99fb607fb2bc0d4ce6b9adb764c65e37f40a92b Mon Sep 17 00:00:00 2001 From: Atish Patra Date: Tue, 2 Oct 2018 12:15:05 -0700 Subject: RISC-V: Use Linux logical CPU number instead of hartid Setup the cpu_logical_map during boot. Moreover, every SBI call and PLIC context are based on the physical hartid. Use the logical CPU to hartid mapping to pass correct hartid to respective functions. Signed-off-by: Atish Patra Reviewed-by: Anup Patel Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/tlbflush.h | 16 +++++++++++++--- arch/riscv/kernel/cpu.c | 8 +++++--- arch/riscv/kernel/head.S | 4 +++- arch/riscv/kernel/setup.c | 6 ++++++ arch/riscv/kernel/smp.c | 24 +++++++++++++++--------- arch/riscv/kernel/smpboot.c | 25 ++++++++++++++++--------- drivers/clocksource/riscv_timer.c | 12 ++++++++---- drivers/irqchip/irq-sifive-plic.c | 8 +++++--- 8 files changed, 71 insertions(+), 32 deletions(-) diff --git a/arch/riscv/include/asm/tlbflush.h b/arch/riscv/include/asm/tlbflush.h index 85c2d8bae957..54fee0cadb1e 100644 --- a/arch/riscv/include/asm/tlbflush.h +++ b/arch/riscv/include/asm/tlbflush.h @@ -16,6 +16,7 @@ #define _ASM_RISCV_TLBFLUSH_H #include +#include /* * Flush entire local TLB. 'sfence.vma' implicitly fences with the instruction @@ -49,13 +50,22 @@ static inline void flush_tlb_range(struct vm_area_struct *vma, #include +static inline void remote_sfence_vma(struct cpumask *cmask, unsigned long start, + unsigned long size) +{ + struct cpumask hmask; + + cpumask_clear(&hmask); + riscv_cpuid_to_hartid_mask(cmask, &hmask); + sbi_remote_sfence_vma(hmask.bits, start, size); +} + #define flush_tlb_all() sbi_remote_sfence_vma(NULL, 0, -1) #define flush_tlb_page(vma, addr) flush_tlb_range(vma, addr, 0) #define flush_tlb_range(vma, start, end) \ - sbi_remote_sfence_vma(mm_cpumask((vma)->vm_mm)->bits, \ - start, (end) - (start)) + remote_sfence_vma(mm_cpumask((vma)->vm_mm), start, (end) - (start)) #define flush_tlb_mm(mm) \ - sbi_remote_sfence_vma(mm_cpumask(mm)->bits, 0, -1) + remote_sfence_vma(mm_cpumask(mm), 0, -1) #endif /* CONFIG_SMP */ diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c index 4723e235dcaa..cccc6f61c538 100644 --- a/arch/riscv/kernel/cpu.c +++ b/arch/riscv/kernel/cpu.c @@ -14,6 +14,7 @@ #include #include #include +#include /* * Returns the hart ID of the given device tree node, or -1 if the device tree @@ -138,11 +139,12 @@ static void c_stop(struct seq_file *m, void *v) static int c_show(struct seq_file *m, void *v) { - unsigned long hart_id = (unsigned long)v - 1; - struct device_node *node = of_get_cpu_node(hart_id, NULL); + unsigned long cpu_id = (unsigned long)v - 1; + struct device_node *node = of_get_cpu_node(cpuid_to_hartid_map(cpu_id), + NULL); const char *compat, *isa, *mmu; - seq_printf(m, "hart\t: %lu\n", hart_id); + seq_printf(m, "hart\t: %lu\n", cpu_id); if (!of_property_read_string(node, "riscv,isa", &isa)) print_isa(m, isa); if (!of_property_read_string(node, "mmu-type", &mmu)) diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S index c4d2c63f9a29..711190d473d4 100644 --- a/arch/riscv/kernel/head.S +++ b/arch/riscv/kernel/head.S @@ -47,6 +47,8 @@ ENTRY(_start) /* Save hart ID and DTB physical address */ mv s0, a0 mv s1, a1 + la a2, boot_cpu_hartid + REG_S a0, (a2) /* Initialize page tables and relocate to virtual addresses */ la sp, init_thread_union + THREAD_SIZE @@ -55,7 +57,7 @@ ENTRY(_start) /* Restore C environment */ la tp, init_task - sw s0, TASK_TI_CPU(tp) + sw zero, TASK_TI_CPU(tp) la sp, init_thread_union li a0, ASM_THREAD_SIZE diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index d5d8611066d5..5e9e6f934cc0 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -81,11 +81,17 @@ EXPORT_SYMBOL(empty_zero_page); /* The lucky hart to first increment this variable will boot the other cores */ atomic_t hart_lottery; +unsigned long boot_cpu_hartid; unsigned long __cpuid_to_hartid_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HARTID }; +void __init smp_setup_processor_id(void) +{ + cpuid_to_hartid_map(0) = boot_cpu_hartid; +} + #ifdef CONFIG_BLK_DEV_INITRD static void __init setup_initrd(void) { diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c index 0bd48935f886..4eac0094f47e 100644 --- a/arch/riscv/kernel/smp.c +++ b/arch/riscv/kernel/smp.c @@ -97,14 +97,18 @@ void riscv_software_interrupt(void) static void send_ipi_message(const struct cpumask *to_whom, enum ipi_message_type operation) { - int i; + int cpuid, hartid; + struct cpumask hartid_mask; + cpumask_clear(&hartid_mask); mb(); - for_each_cpu(i, to_whom) - set_bit(operation, &ipi_data[i].bits); - + for_each_cpu(cpuid, to_whom) { + set_bit(operation, &ipi_data[cpuid].bits); + hartid = cpuid_to_hartid_map(cpuid); + cpumask_set_cpu(hartid, &hartid_mask); + } mb(); - sbi_send_ipi(cpumask_bits(to_whom)); + sbi_send_ipi(cpumask_bits(&hartid_mask)); } void arch_send_call_function_ipi_mask(struct cpumask *mask) @@ -146,7 +150,7 @@ void smp_send_reschedule(int cpu) void flush_icache_mm(struct mm_struct *mm, bool local) { unsigned int cpu; - cpumask_t others, *mask; + cpumask_t others, hmask, *mask; preempt_disable(); @@ -164,9 +168,11 @@ void flush_icache_mm(struct mm_struct *mm, bool local) */ cpumask_andnot(&others, mm_cpumask(mm), cpumask_of(cpu)); local |= cpumask_empty(&others); - if (mm != current->active_mm || !local) - sbi_remote_fence_i(others.bits); - else { + if (mm != current->active_mm || !local) { + cpumask_clear(&hmask); + riscv_cpuid_to_hartid_mask(&others, &hmask); + sbi_remote_fence_i(hmask.bits); + } else { /* * It's assumed that at least one strongly ordered operation is * performed on this hart between setting a hart's cpumask bit diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index 1e478615017c..18cda0e8cf94 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -53,17 +53,23 @@ void __init setup_smp(void) struct device_node *dn = NULL; int hart; bool found_boot_cpu = false; + int cpuid = 1; while ((dn = of_find_node_by_type(dn, "cpu"))) { hart = riscv_of_processor_hartid(dn); - if (hart >= 0) { - set_cpu_possible(hart, true); - set_cpu_present(hart, true); - if (hart == smp_processor_id()) { - BUG_ON(found_boot_cpu); - found_boot_cpu = true; - } + if (hart < 0) + continue; + + if (hart == cpuid_to_hartid_map(0)) { + BUG_ON(found_boot_cpu); + found_boot_cpu = 1; + continue; } + + cpuid_to_hartid_map(cpuid) = hart; + set_cpu_possible(cpuid, true); + set_cpu_present(cpuid, true); + cpuid++; } BUG_ON(!found_boot_cpu); @@ -71,6 +77,7 @@ void __init setup_smp(void) int __cpu_up(unsigned int cpu, struct task_struct *tidle) { + int hartid = cpuid_to_hartid_map(cpu); tidle->thread_info.cpu = cpu; /* @@ -81,9 +88,9 @@ int __cpu_up(unsigned int cpu, struct task_struct *tidle) * the spinning harts that they can continue the boot process. */ smp_mb(); - WRITE_ONCE(__cpu_up_stack_pointer[cpu], + WRITE_ONCE(__cpu_up_stack_pointer[hartid], task_stack_page(tidle) + THREAD_SIZE); - WRITE_ONCE(__cpu_up_task_pointer[cpu], tidle); + WRITE_ONCE(__cpu_up_task_pointer[hartid], tidle); while (!cpu_online(cpu)) cpu_relax(); diff --git a/drivers/clocksource/riscv_timer.c b/drivers/clocksource/riscv_timer.c index ad7453fc3129..084e97dc10ed 100644 --- a/drivers/clocksource/riscv_timer.c +++ b/drivers/clocksource/riscv_timer.c @@ -8,6 +8,7 @@ #include #include #include +#include #include /* @@ -84,13 +85,16 @@ void riscv_timer_interrupt(void) static int __init riscv_timer_init_dt(struct device_node *n) { - int cpu_id = riscv_of_processor_hartid(n), error; + int cpuid, hartid, error; struct clocksource *cs; - if (cpu_id != smp_processor_id()) + hartid = riscv_of_processor_hartid(n); + cpuid = riscv_hartid_to_cpuid(hartid); + + if (cpuid != smp_processor_id()) return 0; - cs = per_cpu_ptr(&riscv_clocksource, cpu_id); + cs = per_cpu_ptr(&riscv_clocksource, cpuid); clocksource_register_hz(cs, riscv_timebase); error = cpuhp_setup_state(CPUHP_AP_RISCV_TIMER_STARTING, @@ -98,7 +102,7 @@ static int __init riscv_timer_init_dt(struct device_node *n) riscv_timer_starting_cpu, riscv_timer_dying_cpu); if (error) pr_err("RISCV timer register failed [%d] for cpu = [%d]\n", - error, cpu_id); + error, cpuid); return error; } diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c index c55eaa31cde2..357e9daf94ae 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -15,6 +15,7 @@ #include #include #include +#include /* * This driver implements a version of the RISC-V PLIC with the actual layout @@ -218,7 +219,7 @@ static int __init plic_init(struct device_node *node, struct of_phandle_args parent; struct plic_handler *handler; irq_hw_number_t hwirq; - int cpu; + int cpu, hartid; if (of_irq_parse_one(node, i, &parent)) { pr_err("failed to parse parent for context %d.\n", i); @@ -229,12 +230,13 @@ static int __init plic_init(struct device_node *node, if (parent.args[0] == -1) continue; - cpu = plic_find_hart_id(parent.np); - if (cpu < 0) { + hartid = plic_find_hart_id(parent.np); + if (hartid < 0) { pr_warn("failed to parse hart ID for context %d.\n", i); continue; } + cpu = riscv_hartid_to_cpuid(hartid); handler = per_cpu_ptr(&plic_handlers, cpu); handler->present = true; handler->ctxid = i; -- cgit v1.2.3-59-g8ed1b From 4b26d22fdff1e39647cc5952b01d329e83dedfe1 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Tue, 2 Oct 2018 12:15:06 -0700 Subject: RISC-V: Show CPU ID and Hart ID separately in /proc/cpuinfo Currently, /proc/cpuinfo show logical CPU ID as Hart ID which is in-correct. This patch shows CPU ID and Hart ID separately in /proc/cpuinfo using cpuid_to_hardid_map(). With this patch, contents of /proc/cpuinfo looks as follows: processor : 0 hart : 1 isa : rv64imafdc mmu : sv48 processor : 1 hart : 0 isa : rv64imafdc mmu : sv48 processor : 2 hart : 2 isa : rv64imafdc mmu : sv48 processor : 3 hart : 3 isa : rv64imafdc mmu : sv48 Signed-off-by: Anup Patel Signed-off-by: Atish Patra Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/cpu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c index cccc6f61c538..3a5a2ee31547 100644 --- a/arch/riscv/kernel/cpu.c +++ b/arch/riscv/kernel/cpu.c @@ -81,7 +81,7 @@ static void print_isa(struct seq_file *f, const char *orig_isa) #endif /* Print the base ISA, as we already know it's legal. */ - seq_puts(f, "isa\t: "); + seq_puts(f, "isa\t\t: "); seq_write(f, isa, 5); isa += 5; @@ -96,6 +96,7 @@ static void print_isa(struct seq_file *f, const char *orig_isa) isa++; } } + seq_puts(f, "\n"); /* * If we were given an unsupported ISA in the device tree then print @@ -116,7 +117,7 @@ static void print_mmu(struct seq_file *f, const char *mmu_type) return; #endif - seq_printf(f, "mmu\t: %s\n", mmu_type+6); + seq_printf(f, "mmu\t\t: %s\n", mmu_type+6); } static void *c_start(struct seq_file *m, loff_t *pos) @@ -144,14 +145,15 @@ static int c_show(struct seq_file *m, void *v) NULL); const char *compat, *isa, *mmu; - seq_printf(m, "hart\t: %lu\n", cpu_id); + seq_printf(m, "processor\t: %lu\n", cpu_id); + seq_printf(m, "hart\t\t: %lu\n", cpuid_to_hartid_map(cpu_id)); if (!of_property_read_string(node, "riscv,isa", &isa)) print_isa(m, isa); if (!of_property_read_string(node, "mmu-type", &mmu)) print_mmu(m, mmu); if (!of_property_read_string(node, "compatible", &compat) && strcmp(compat, "riscv")) - seq_printf(m, "uarch\t: %s\n", compat); + seq_printf(m, "uarch\t\t: %s\n", compat); seq_puts(m, "\n"); return 0; -- cgit v1.2.3-59-g8ed1b From 8b20d2db0a6d2761e0fc156eb74f7a55b92b3147 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Tue, 2 Oct 2018 12:15:07 -0700 Subject: RISC-V: Show IPI stats This patch provides arch_show_interrupts() implementation to show IPI stats via /proc/interrupts. Now the contents of /proc/interrupts" will look like below: CPU0 CPU1 CPU2 CPU3 8: 17 7 6 14 SiFive PLIC 8 virtio0 10: 10 10 9 11 SiFive PLIC 10 ttyS0 IPI0: 170 673 251 79 Rescheduling interrupts IPI1: 1 12 27 1 Function call interrupts Signed-off-by: Anup Patel [Atish - Fixed checkpatch errors] Signed-off-by: Atish Patra Reviewed-by: Palmer Dabbelt Changes since v2: - Remove use of IPI_CALL_WAKEUP because it's being removed Changes since v1: - Add stub inline show_ipi_stats() function for !CONFIG_SMP - Make ipi_names[] dynamically sized at compile time - Minor beautification of ipi_names[] using tabs Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/smp.h | 9 +++++++++ arch/riscv/kernel/irq.c | 8 ++++++++ arch/riscv/kernel/smp.c | 39 ++++++++++++++++++++++++++++++++------- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h index 47fd61dfc897..41aa73b476f4 100644 --- a/arch/riscv/include/asm/smp.h +++ b/arch/riscv/include/asm/smp.h @@ -25,8 +25,13 @@ extern unsigned long __cpuid_to_hartid_map[NR_CPUS]; #define cpuid_to_hartid_map(cpu) __cpuid_to_hartid_map[cpu] +struct seq_file; + #ifdef CONFIG_SMP +/* print IPI stats */ +void show_ipi_stats(struct seq_file *p, int prec); + /* SMP initialization hook for setup_arch */ void __init setup_smp(void); @@ -47,6 +52,10 @@ void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out); #else +static inline void show_ipi_stats(struct seq_file *p, int prec) +{ +} + static inline int riscv_hartid_to_cpuid(int hartid) { return 0; diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c index ca4593317e45..48e6b7db83a1 100644 --- a/arch/riscv/kernel/irq.c +++ b/arch/riscv/kernel/irq.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include /* * Possible interrupt causes: @@ -24,6 +26,12 @@ */ #define INTERRUPT_CAUSE_FLAG (1UL << (__riscv_xlen - 1)) +int arch_show_interrupts(struct seq_file *p, int prec) +{ + show_ipi_stats(p, prec); + return 0; +} + asmlinkage void __irq_entry do_IRQ(struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c index 4eac0094f47e..57b1383e5ef7 100644 --- a/arch/riscv/kernel/smp.c +++ b/arch/riscv/kernel/smp.c @@ -22,22 +22,24 @@ #include #include #include +#include #include #include #include -/* A collection of single bit ipi messages. */ -static struct { - unsigned long bits ____cacheline_aligned; -} ipi_data[NR_CPUS] __cacheline_aligned; - enum ipi_message_type { IPI_RESCHEDULE, IPI_CALL_FUNC, IPI_MAX }; +/* A collection of single bit ipi messages. */ +static struct { + unsigned long stats[IPI_MAX] ____cacheline_aligned; + unsigned long bits ____cacheline_aligned; +} ipi_data[NR_CPUS] __cacheline_aligned; + int riscv_hartid_to_cpuid(int hartid) { int i = -1; @@ -67,6 +69,7 @@ int setup_profiling_timer(unsigned int multiplier) void riscv_software_interrupt(void) { unsigned long *pending_ipis = &ipi_data[smp_processor_id()].bits; + unsigned long *stats = ipi_data[smp_processor_id()].stats; /* Clear pending IPI */ csr_clear(sip, SIE_SSIE); @@ -81,11 +84,15 @@ void riscv_software_interrupt(void) if (ops == 0) return; - if (ops & (1 << IPI_RESCHEDULE)) + if (ops & (1 << IPI_RESCHEDULE)) { + stats[IPI_RESCHEDULE]++; scheduler_ipi(); + } - if (ops & (1 << IPI_CALL_FUNC)) + if (ops & (1 << IPI_CALL_FUNC)) { + stats[IPI_CALL_FUNC]++; generic_smp_call_function_interrupt(); + } BUG_ON((ops >> IPI_MAX) != 0); @@ -111,6 +118,24 @@ send_ipi_message(const struct cpumask *to_whom, enum ipi_message_type operation) sbi_send_ipi(cpumask_bits(&hartid_mask)); } +static const char * const ipi_names[] = { + [IPI_RESCHEDULE] = "Rescheduling interrupts", + [IPI_CALL_FUNC] = "Function call interrupts", +}; + +void show_ipi_stats(struct seq_file *p, int prec) +{ + unsigned int cpu, i; + + for (i = 0; i < IPI_MAX; i++) { + seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i, + prec >= 4 ? " " : ""); + for_each_online_cpu(cpu) + seq_printf(p, "%10lu ", ipi_data[cpu].stats[i]); + seq_printf(p, " %s\n", ipi_names[i]); + } +} + void arch_send_call_function_ipi_mask(struct cpumask *mask) { send_ipi_message(mask, IPI_CALL_FUNC); -- cgit v1.2.3-59-g8ed1b From 1760debb51f73ed3e089c8d4e847554901dee4bb Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Tue, 24 Jul 2018 17:17:14 -0700 Subject: RISC-V: Don't set cacheinfo.{physical_line_partition,attributes} These are just hard coded in the RISC-V port, which doesn't make any sense. We should probably be setting these from device tree entries when they exist, but for now I think it's saner to just leave them all as their default values. Reviewed-by: Jeremy Linton Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/cacheinfo.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/riscv/kernel/cacheinfo.c b/arch/riscv/kernel/cacheinfo.c index 0bc86e5f8f3f..cb35ffd8ec6b 100644 --- a/arch/riscv/kernel/cacheinfo.c +++ b/arch/riscv/kernel/cacheinfo.c @@ -22,13 +22,6 @@ static void ci_leaf_init(struct cacheinfo *this_leaf, { this_leaf->level = level; this_leaf->type = type; - /* not a sector cache */ - this_leaf->physical_line_partition = 1; - /* TODO: Add to DTS */ - this_leaf->attributes = - CACHE_WRITE_BACK - | CACHE_READ_ALLOCATE - | CACHE_WRITE_ALLOCATE; } static int __init_cache_level(unsigned int cpu) -- cgit v1.2.3-59-g8ed1b From 86e581e310785782e2025a076dc9a3f5138e5bf3 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Mon, 27 Aug 2018 14:42:53 -0700 Subject: RISC-V: Mask out the F extension on systems without D The RISC-V Linux port doesn't support systems that have the F extension but don't have the D extension -- we actually don't support systems without D either, but Alan's patch set is rectifying that soon. For now I think we can leave this in a semi-broken state and just wait for Alan's patch set to get merged for proper non-FPU support -- the patch set is starting to look good, so doing something in-between doesn't seem like it's worth the work. I don't think it's worth fretting about support for systems with F but not D for now: our glibc ABIs are IMAC and IMAFDC so they probably won't end up being popular. We can always extend this in the future. CC: Alan Kao Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/cpufeature.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index 17011a870044..652d102ffa06 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -57,5 +57,12 @@ void riscv_fill_hwcap(void) for (i = 0; i < strlen(isa); ++i) elf_hwcap |= isa2hwcap[(unsigned char)(isa[i])]; + /* We don't support systems with F but without D, so mask those out + * here. */ + if ((elf_hwcap & COMPAT_HWCAP_ISA_F) && !(elf_hwcap & COMPAT_HWCAP_ISA_D)) { + pr_info("This kernel does not support systems with F but not D"); + elf_hwcap &= ~COMPAT_HWCAP_ISA_F; + } + pr_info("elf_hwcap is 0x%lx", elf_hwcap); } -- cgit v1.2.3-59-g8ed1b From b8c8a9590e4fde82f8c3ee06a521763e6f21e9c8 Mon Sep 17 00:00:00 2001 From: Jim Wilson Date: Wed, 17 Oct 2018 17:59:05 -0700 Subject: RISC-V: Add FP register ptrace support for gdb. Add a variable and a macro to describe FP registers, assuming only D is supported. FP code is conditional on CONFIG_FPU. The FP regs and FCSR are copied separately to avoid copying struct padding. Tested by hand and with the gdb testsuite. Signed-off-by: Jim Wilson Signed-off-by: Palmer Dabbelt --- arch/riscv/include/uapi/asm/elf.h | 3 +++ arch/riscv/kernel/ptrace.c | 52 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/arch/riscv/include/uapi/asm/elf.h b/arch/riscv/include/uapi/asm/elf.h index 1e0dfc36aab9..644a00ce6e2e 100644 --- a/arch/riscv/include/uapi/asm/elf.h +++ b/arch/riscv/include/uapi/asm/elf.h @@ -19,7 +19,10 @@ typedef unsigned long elf_greg_t; typedef struct user_regs_struct elf_gregset_t; #define ELF_NGREG (sizeof(elf_gregset_t) / sizeof(elf_greg_t)) +/* We don't support f without d, or q. */ +typedef __u64 elf_fpreg_t; typedef union __riscv_fp_state elf_fpregset_t; +#define ELF_NFPREG (sizeof(struct __riscv_d_ext_state) / sizeof(elf_fpreg_t)) #if __riscv_xlen == 64 #define ELF_RISCV_R_SYM(r_info) ELF64_R_SYM(r_info) diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 9f82a7e34c64..60f1e02eed36 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -28,6 +28,9 @@ enum riscv_regset { REGSET_X, +#ifdef CONFIG_FPU + REGSET_F, +#endif }; static int riscv_gpr_get(struct task_struct *target, @@ -54,6 +57,45 @@ static int riscv_gpr_set(struct task_struct *target, return ret; } +#ifdef CONFIG_FPU +static int riscv_fpr_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int ret; + struct __riscv_d_ext_state *fstate = &target->thread.fstate; + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, fstate, 0, + offsetof(struct __riscv_d_ext_state, fcsr)); + if (!ret) { + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, fstate, 0, + offsetof(struct __riscv_d_ext_state, fcsr) + + sizeof(fstate->fcsr)); + } + + return ret; +} + +static int riscv_fpr_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret; + struct __riscv_d_ext_state *fstate = &target->thread.fstate; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, fstate, 0, + offsetof(struct __riscv_d_ext_state, fcsr)); + if (!ret) { + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, fstate, 0, + offsetof(struct __riscv_d_ext_state, fcsr) + + sizeof(fstate->fcsr)); + } + + return ret; +} +#endif static const struct user_regset riscv_user_regset[] = { [REGSET_X] = { @@ -64,6 +106,16 @@ static const struct user_regset riscv_user_regset[] = { .get = &riscv_gpr_get, .set = &riscv_gpr_set, }, +#ifdef CONFIG_FPU + [REGSET_F] = { + .core_note_type = NT_PRFPREG, + .n = ELF_NFPREG, + .size = sizeof(elf_fpreg_t), + .align = sizeof(elf_fpreg_t), + .get = &riscv_fpr_get, + .set = &riscv_fpr_set, + }, +#endif }; static const struct user_regset_view riscv_user_native_view = { -- cgit v1.2.3-59-g8ed1b From b90edb33010bcfb9a0d74681be2cdd52300f1e69 Mon Sep 17 00:00:00 2001 From: Jim Wilson Date: Tue, 16 Oct 2018 14:42:59 -0700 Subject: RISC-V: Add futex support. Here is an attempt to add the missing futex support. I started with the MIPS version of futex.h and modified it until I got it working. I tested it on a HiFive Unleashed running Fedora Core 29 using the fc29 4.15 version of the kernel. This was tested against the glibc testsuite, where it fixes 14 nptl related testsuite failures. That unfortunately only tests the cmpxchg support, so I also used the testcase at the end of https://lwn.net/Articles/148830/ which tests the atomic_op functionality, except that it doesn't verify that the operations are atomic, which they obviously are. This testcase runs successfully with the patch and fails without it. I'm not a kernel expert, so there could be details I got wrong here. I wasn't sure about the memory model support, so I used aqrl which seemed safest, and didn't add fences which seemed unnecessary. I'm not sure about the copyright statements, I left in Ralf Baechle's line because I started with his code. Checkpatch reports some style problems, but it is the same style as the MIPS futex.h, and the uses of ENOSYS appear correct even though it complains about them. I don't know if any of that matters. This patch was tested on qemu with the glibc nptl/tst-cond-except testcase, and the wake_op testcase from above. Signed-off-by: Jim Wilson Reviewed-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 1 + arch/riscv/include/asm/Kbuild | 1 - arch/riscv/include/asm/futex.h | 128 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 arch/riscv/include/asm/futex.h diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index a344980287a5..093361e2298c 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -31,6 +31,7 @@ config RISCV select HAVE_MEMBLOCK select HAVE_MEMBLOCK_NODE_MAP select HAVE_DMA_CONTIGUOUS + select HAVE_FUTEX_CMPXCHG if FUTEX select HAVE_GENERIC_DMA_COHERENT select HAVE_PERF_EVENTS select IRQ_DOMAIN diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild index efdbe311e936..6a646d9ea780 100644 --- a/arch/riscv/include/asm/Kbuild +++ b/arch/riscv/include/asm/Kbuild @@ -13,7 +13,6 @@ generic-y += errno.h generic-y += exec.h generic-y += fb.h generic-y += fcntl.h -generic-y += futex.h generic-y += hardirq.h generic-y += hash.h generic-y += hw_irq.h diff --git a/arch/riscv/include/asm/futex.h b/arch/riscv/include/asm/futex.h new file mode 100644 index 000000000000..3b19eba1bc8e --- /dev/null +++ b/arch/riscv/include/asm/futex.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2006 Ralf Baechle (ralf@linux-mips.org) + * Copyright (c) 2018 Jim Wilson (jimw@sifive.com) + */ + +#ifndef _ASM_FUTEX_H +#define _ASM_FUTEX_H + +#ifndef CONFIG_RISCV_ISA_A +/* + * Use the generic interrupt disabling versions if the A extension + * is not supported. + */ +#ifdef CONFIG_SMP +#error "Can't support generic futex calls without A extension on SMP" +#endif +#include + +#else /* CONFIG_RISCV_ISA_A */ + +#include +#include +#include +#include + +#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \ +{ \ + uintptr_t tmp; \ + __enable_user_access(); \ + __asm__ __volatile__ ( \ + "1: " insn " \n" \ + "2: \n" \ + " .section .fixup,\"ax\" \n" \ + " .balign 4 \n" \ + "3: li %[r],%[e] \n" \ + " jump 2b,%[t] \n" \ + " .previous \n" \ + " .section __ex_table,\"a\" \n" \ + " .balign " RISCV_SZPTR " \n" \ + " " RISCV_PTR " 1b, 3b \n" \ + " .previous \n" \ + : [r] "+r" (ret), [ov] "=&r" (oldval), \ + [u] "+m" (*uaddr), [t] "=&r" (tmp) \ + : [op] "Jr" (oparg), [e] "i" (-EFAULT) \ + : "memory"); \ + __disable_user_access(); \ +} + +static inline int +arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr) +{ + int oldval = 0, ret = 0; + + pagefault_disable(); + + switch (op) { + case FUTEX_OP_SET: + __futex_atomic_op("amoswap.w.aqrl %[ov],%z[op],%[u]", + ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_ADD: + __futex_atomic_op("amoadd.w.aqrl %[ov],%z[op],%[u]", + ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_OR: + __futex_atomic_op("amoor.w.aqrl %[ov],%z[op],%[u]", + ret, oldval, uaddr, oparg); + break; + case FUTEX_OP_ANDN: + __futex_atomic_op("amoand.w.aqrl %[ov],%z[op],%[u]", + ret, oldval, uaddr, ~oparg); + break; + case FUTEX_OP_XOR: + __futex_atomic_op("amoxor.w.aqrl %[ov],%z[op],%[u]", + ret, oldval, uaddr, oparg); + break; + default: + ret = -ENOSYS; + } + + pagefault_enable(); + + if (!ret) + *oval = oldval; + + return ret; +} + +static inline int +futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, + u32 oldval, u32 newval) +{ + int ret = 0; + u32 val; + uintptr_t tmp; + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) + return -EFAULT; + + __enable_user_access(); + __asm__ __volatile__ ( + "1: lr.w.aqrl %[v],%[u] \n" + " bne %[v],%z[ov],3f \n" + "2: sc.w.aqrl %[t],%z[nv],%[u] \n" + " bnez %[t],1b \n" + "3: \n" + " .section .fixup,\"ax\" \n" + " .balign 4 \n" + "4: li %[r],%[e] \n" + " jump 3b,%[t] \n" + " .previous \n" + " .section __ex_table,\"a\" \n" + " .balign " RISCV_SZPTR " \n" + " " RISCV_PTR " 1b, 4b \n" + " " RISCV_PTR " 2b, 4b \n" + " .previous \n" + : [r] "+r" (ret), [v] "=&r" (val), [u] "+m" (*uaddr), [t] "=&r" (tmp) + : [ov] "Jr" (oldval), [nv] "Jr" (newval), [e] "i" (-EFAULT) + : "memory"); + __disable_user_access(); + + *uval = val; + return ret; +} + +#endif /* CONFIG_RISCV_ISA_A */ +#endif /* _ASM_FUTEX_H */ -- cgit v1.2.3-59-g8ed1b From f31b8de98853091e86a6391f9cd7948a2397287e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 21 Sep 2018 08:43:21 +0200 Subject: RISC-V: remove the unused return_to_handler export This export is not only not needed, but also breaks symbol versioning due to being an undeclared assembly export. Signed-off-by: Christoph Hellwig Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/mcount.S | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/riscv/kernel/mcount.S b/arch/riscv/kernel/mcount.S index 5721624886a1..8a5593ff9ff3 100644 --- a/arch/riscv/kernel/mcount.S +++ b/arch/riscv/kernel/mcount.S @@ -75,7 +75,6 @@ ENTRY(return_to_handler) RESTORE_RET_ABI_STATE jalr a1 ENDPROC(return_to_handler) -EXPORT_SYMBOL(return_to_handler) #endif #ifndef CONFIG_DYNAMIC_FTRACE -- cgit v1.2.3-59-g8ed1b From ee5928843a93c7d246bbe17c5eed95918ed0ddb1 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 24 Aug 2018 17:33:53 +0900 Subject: riscv: move GCC version check for ARCH_SUPPORTS_INT128 to Kconfig This becomes much neater in Kconfig. Signed-off-by: Masahiro Yamada Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 1 + arch/riscv/Makefile | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 093361e2298c..bb80d3ea82fd 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -113,6 +113,7 @@ config ARCH_RV32I config ARCH_RV64I bool "RV64I" select 64BIT + select ARCH_SUPPORTS_INT128 if GCC_VERSION >= 50000 select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FTRACE_MCOUNT_RECORD diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 61ec42405ec9..33700e4bfc3b 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -25,8 +25,6 @@ ifeq ($(CONFIG_ARCH_RV64I),y) KBUILD_CFLAGS += -mabi=lp64 KBUILD_AFLAGS += -mabi=lp64 - - KBUILD_CFLAGS += $(call cc-ifversion, -ge, 0500, -DCONFIG_ARCH_SUPPORTS_INT128) KBUILD_MARCH = rv64im KBUILD_LDFLAGS += -melf64lriscv -- cgit v1.2.3-59-g8ed1b From aef53f97b505ff94190ce3a06dcd0ded6cf5c0ca Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Thu, 20 Sep 2018 01:48:15 +0300 Subject: RISC-V: Cosmetic menuconfig changes * Move the built-in cmdline configuration on a new menu entry "Boot options", it doesn't make much sense to be part of the debuging menu. * Rename "Kernel Type" menu to "Kernel features" to be more consistent with what other architectures are using, plus "type" is a bit misleading here. Signed-off-by: Nick Kossifidis Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 40 +++++++++++++++++++++++++++++++++++++++- arch/riscv/Kconfig.debug | 35 ----------------------------------- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index bb80d3ea82fd..6c8329beb936 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -212,12 +212,50 @@ endmenu endmenu -menu "Kernel type" +menu "Kernel features" source "kernel/Kconfig.hz" endmenu +menu "Boot options" + +config CMDLINE_BOOL + bool "Built-in kernel command line" + help + For most platforms, it is firmware or second stage bootloader + that by default specifies the kernel command line options. + However, it might be necessary or advantageous to either override + the default kernel command line or add a few extra options to it. + For such cases, this option allows hardcoding command line options + directly into the kernel. + + For that, choose 'Y' here and fill in the extra boot parameters + in CONFIG_CMDLINE. + + The built-in options will be concatenated to the default command + line if CMDLINE_FORCE is set to 'N'. Otherwise, the default + command line will be ignored and replaced by the built-in string. + +config CMDLINE + string "Built-in kernel command string" + depends on CMDLINE_BOOL + default "" + help + Supply command-line options at build time by entering them here. + +config CMDLINE_FORCE + bool "Built-in command line overrides bootloader arguments" + depends on CMDLINE_BOOL + help + Set this option to 'Y' to have the kernel ignore the bootloader + or firmware command line. Instead, the built-in command line + will be used exclusively. + + If you don't know what to do here, say N. + +endmenu + menu "Bus support" config PCI diff --git a/arch/riscv/Kconfig.debug b/arch/riscv/Kconfig.debug index 3224ff6ecf6e..c5a72f17c469 100644 --- a/arch/riscv/Kconfig.debug +++ b/arch/riscv/Kconfig.debug @@ -1,37 +1,2 @@ - -config CMDLINE_BOOL - bool "Built-in kernel command line" - help - For most platforms, it is firmware or second stage bootloader - that by default specifies the kernel command line options. - However, it might be necessary or advantageous to either override - the default kernel command line or add a few extra options to it. - For such cases, this option allows hardcoding command line options - directly into the kernel. - - For that, choose 'Y' here and fill in the extra boot parameters - in CONFIG_CMDLINE. - - The built-in options will be concatenated to the default command - line if CMDLINE_FORCE is set to 'N'. Otherwise, the default - command line will be ignored and replaced by the built-in string. - -config CMDLINE - string "Built-in kernel command string" - depends on CMDLINE_BOOL - default "" - help - Supply command-line options at build time by entering them here. - -config CMDLINE_FORCE - bool "Built-in command line overrides bootloader arguments" - depends on CMDLINE_BOOL - help - Set this option to 'Y' to have the kernel ignore the bootloader - or firmware command line. Instead, the built-in command line - will be used exclusively. - - If you don't know what to do here, say N. - config EARLY_PRINTK def_bool y -- cgit v1.2.3-59-g8ed1b