From 1e01a786a904274d308a99b00a0f20e0d288eeff Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:51 -0600 Subject: [PATCH 01/36] riscv/purgatory: return bool from verify_sha256_digest Change the function's return type from int to bool and return the result of memcmp() directly to simplify the code. While at it, cast ->start to 'const u8 *' to better match the expected type. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260509073850.44595-3-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley --- arch/riscv/purgatory/purgatory.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/arch/riscv/purgatory/purgatory.c b/arch/riscv/purgatory/purgatory.c index bbd5cfa4d741..745deeb6e3a5 100644 --- a/arch/riscv/purgatory/purgatory.c +++ b/arch/riscv/purgatory/purgatory.c @@ -17,7 +17,7 @@ u8 purgatory_sha256_digest[SHA256_DIGEST_SIZE] __section(".kexec-purgatory"); struct kexec_sha_region purgatory_sha_regions[KEXEC_SEGMENT_MAX] __section(".kexec-purgatory"); -static int verify_sha256_digest(void) +static bool verify_sha256_digest(void) { struct kexec_sha_region *ptr, *end; struct sha256_ctx sctx; @@ -26,11 +26,10 @@ static int verify_sha256_digest(void) sha256_init(&sctx); end = purgatory_sha_regions + ARRAY_SIZE(purgatory_sha_regions); for (ptr = purgatory_sha_regions; ptr < end; ptr++) - sha256_update(&sctx, (uint8_t *)(ptr->start), ptr->len); + sha256_update(&sctx, (const u8 *)(ptr->start), ptr->len); sha256_final(&sctx, digest); - if (memcmp(digest, purgatory_sha256_digest, sizeof(digest)) != 0) - return 1; - return 0; + + return memcmp(digest, purgatory_sha256_digest, sizeof(digest)) == 0; } /* workaround for a warning with -Wmissing-prototypes */ @@ -38,7 +37,7 @@ void purgatory(void); void purgatory(void) { - if (verify_sha256_digest()) + if (!verify_sha256_digest()) for (;;) /* loop forever */ ; From a24c7f3523ff1112e8dc6c45cea7d9397345a627 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 02/36] riscv/purgatory: add asm/purgatory.h Add arch/riscv/include/asm/purgatory.h and provide the purgatory() prototype via the architecture header, mirroring the x86 layout. Remove the workaround from arch/riscv/purgatory/purgatory.c. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260509073850.44595-4-thorsten.blum@linux.dev [pjw@kernel.org: drop superfluous extern in header file] Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/purgatory.h | 11 +++++++++++ arch/riscv/purgatory/purgatory.c | 5 +---- 2 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 arch/riscv/include/asm/purgatory.h diff --git a/arch/riscv/include/asm/purgatory.h b/arch/riscv/include/asm/purgatory.h new file mode 100644 index 000000000000..5a827e6752b7 --- /dev/null +++ b/arch/riscv/include/asm/purgatory.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_RISCV_PURGATORY_H +#define _ASM_RISCV_PURGATORY_H + +#ifndef __ASSEMBLER__ +#include + +void purgatory(void); +#endif /* __ASSEMBLER__ */ + +#endif /* _ASM_RISCV_PURGATORY_H */ diff --git a/arch/riscv/purgatory/purgatory.c b/arch/riscv/purgatory/purgatory.c index 745deeb6e3a5..3474e3abe5d7 100644 --- a/arch/riscv/purgatory/purgatory.c +++ b/arch/riscv/purgatory/purgatory.c @@ -8,9 +8,9 @@ * */ -#include #include #include +#include #include u8 purgatory_sha256_digest[SHA256_DIGEST_SIZE] __section(".kexec-purgatory"); @@ -32,9 +32,6 @@ static bool verify_sha256_digest(void) return memcmp(digest, purgatory_sha256_digest, sizeof(digest)) == 0; } -/* workaround for a warning with -Wmissing-prototypes */ -void purgatory(void); - void purgatory(void) { if (!verify_sha256_digest()) From 1a9e47c2c38ad2af1916cdd4a0119e87ecb5baae Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 03/36] riscv: ptdump: Replace unbounded sprintf() in dump_prot() Replace the unbounded sprintf("%s", ...) with the faster and safer strscpy(). Replace all other sprintf() calls with the safer snprintf(). Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260127110543.436242-1-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley --- arch/riscv/mm/ptdump.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/riscv/mm/ptdump.c b/arch/riscv/mm/ptdump.c index 34299c2b231f..f4b4a9fcbbd8 100644 --- a/arch/riscv/mm/ptdump.c +++ b/arch/riscv/mm/ptdump.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -213,21 +214,21 @@ static void dump_prot(struct pg_state *st) val = st->current_prot & pte_bits[i].mask; if (val) { if (pte_bits[i].mask == _PAGE_SOFT) - sprintf(s, pte_bits[i].set, val >> 8); + snprintf(s, sizeof(s), pte_bits[i].set, val >> 8); #ifdef CONFIG_64BIT else if (pte_bits[i].mask == _PAGE_MTMASK_SVPBMT) { if (val == _PAGE_NOCACHE_SVPBMT) - sprintf(s, pte_bits[i].set, "NC"); + snprintf(s, sizeof(s), pte_bits[i].set, "NC"); else if (val == _PAGE_IO_SVPBMT) - sprintf(s, pte_bits[i].set, "IO"); + snprintf(s, sizeof(s), pte_bits[i].set, "IO"); else - sprintf(s, pte_bits[i].set, "??"); + snprintf(s, sizeof(s), pte_bits[i].set, "??"); } #endif else - sprintf(s, "%s", pte_bits[i].set); + strscpy(s, pte_bits[i].set); } else { - sprintf(s, "%s", pte_bits[i].clear); + strscpy(s, pte_bits[i].clear); } pt_dump_seq_printf(st->seq, " %s", s); From b954dba17b2ee53dadd34a7aacf55a33a775448f Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 04/36] riscv: pi: replace strlcat with strscpy in get_early_cmdline Use the return value of strscpy() instead of calling strlen(fdt_cmdline) again and return early on string truncation. Drop the explicit size argument since early_cmdline has a fixed length, which strscpy() determines using sizeof() when the argument is omitted. Replace strlcat() with strscpy() to append CONFIG_CMDLINE. Also remove the unnecessary fdt_cmdline NULL initialization. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260504154924.141566-3-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley --- arch/riscv/kernel/pi/cmdline_early.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/riscv/kernel/pi/cmdline_early.c b/arch/riscv/kernel/pi/cmdline_early.c index 389d086a0718..0afbe4077cb8 100644 --- a/arch/riscv/kernel/pi/cmdline_early.c +++ b/arch/riscv/kernel/pi/cmdline_early.c @@ -12,8 +12,8 @@ static char early_cmdline[COMMAND_LINE_SIZE]; static char *get_early_cmdline(uintptr_t dtb_pa) { - const char *fdt_cmdline = NULL; - unsigned int fdt_cmdline_size = 0; + const char *fdt_cmdline; + ssize_t fdt_cmdline_size = 0; int chosen_node; if (!IS_ENABLED(CONFIG_CMDLINE_FORCE)) { @@ -22,18 +22,18 @@ static char *get_early_cmdline(uintptr_t dtb_pa) fdt_cmdline = fdt_getprop((void *)dtb_pa, chosen_node, "bootargs", NULL); if (fdt_cmdline) { - fdt_cmdline_size = strlen(fdt_cmdline); - strscpy(early_cmdline, fdt_cmdline, - COMMAND_LINE_SIZE); + fdt_cmdline_size = strscpy(early_cmdline, fdt_cmdline); + if (fdt_cmdline_size < 0) + return early_cmdline; } } } if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) || IS_ENABLED(CONFIG_CMDLINE_FORCE) || - fdt_cmdline_size == 0 /* CONFIG_CMDLINE_FALLBACK */) { - strlcat(early_cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE); - } + fdt_cmdline_size == 0 /* CONFIG_CMDLINE_FALLBACK */) + strscpy(early_cmdline + fdt_cmdline_size, CONFIG_CMDLINE, + COMMAND_LINE_SIZE - fdt_cmdline_size); return early_cmdline; } From 7d0dec57e52c7b5e14ba4ab6f86e9c17b850f1ec Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 05/36] riscv: use sysfs_emit in cpu_show_ghostwrite Replace sprintf() with sysfs_emit() in cpu_show_ghostwrite(), which is preferred for formatting sysfs output because it provides safer bounds checking. While the current code only emits fixed strings that fit easily within PAGE_SIZE, use sysfs_emit() to follow secure coding best practices. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260510165420.109453-3-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley --- arch/riscv/kernel/bugs.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/riscv/kernel/bugs.c b/arch/riscv/kernel/bugs.c index 3655fe7d678c..e5758e3f1c7e 100644 --- a/arch/riscv/kernel/bugs.c +++ b/arch/riscv/kernel/bugs.c @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include @@ -46,15 +46,15 @@ ssize_t cpu_show_ghostwrite(struct device *dev, struct device_attribute *attr, c if (IS_ENABLED(CONFIG_RISCV_ISA_XTHEADVECTOR)) { switch (ghostwrite_state) { case UNAFFECTED: - return sprintf(buf, "Not affected\n"); + return sysfs_emit(buf, "Not affected\n"); case MITIGATED: - return sprintf(buf, "Mitigation: xtheadvector disabled\n"); + return sysfs_emit(buf, "Mitigation: xtheadvector disabled\n"); case VULNERABLE: fallthrough; default: - return sprintf(buf, "Vulnerable\n"); + return sysfs_emit(buf, "Vulnerable\n"); } - } else { - return sprintf(buf, "Not affected\n"); } + + return sysfs_emit(buf, "Not affected\n"); } From 13fe217f64a7ee71aa58d871c6021a721a5a0c6f Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 06/36] riscv: propagate insert_resource result from add_resource Currently, add_resource() returns 1 on success, even though its callers only check for negative values. Instead, propagate the insert_resource() result from add_resource() to align with standard kernel return-value conventions (0 on success, negative errno on failure). Use %pR to print the full resource range while at it. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260512172034.328405-4-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley --- arch/riscv/kernel/setup.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index c89cc272440b..52d1d2b8f338 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -71,16 +71,13 @@ static struct resource *standard_resources; static int __init add_resource(struct resource *parent, struct resource *res) { - int ret = 0; + int ret; ret = insert_resource(parent, res); - if (ret < 0) { - pr_err("Failed to add a %s resource at %llx\n", - res->name, (unsigned long long) res->start); - return ret; - } + if (ret < 0) + pr_err("Failed to add resource %s %pR\n", res->name, res); - return 1; + return ret; } static int __init add_kernel_resources(void) From c0db0690f7fae7bd8cf1493e2f6b2a672e0c0de8 Mon Sep 17 00:00:00 2001 From: Zong Li Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 07/36] selftests/riscv: fix compiler output flag spacing in all Makefiles Standardize the compiler output flag format across all RISC-V selftests by adding a space between '-o' and '$@'. Although '-o$@' is perfectly valid for GCC/Clang to parse, changing it to '-o $@' with a space aligns with the GNU official documentation conventions, improves readability by visually separating the flag from the target variable, and ensures consistency with other architectures. Currently, RISC-V selftests use '-o$@' (without space) in 13 instances across 6 Makefiles, while all other architectures consistently use '-o $@' (with space). This inconsistency makes RISC-V an outlier in the kernel's selftest infrastructure. Signed-off-by: Zong Li Link: https://patch.msgid.link/20260511032917.3542802-1-zong.li@sifive.com [pjw@kernel.org: cleaned up patch description] Signed-off-by: Paul Walmsley --- tools/testing/selftests/riscv/abi/Makefile | 2 +- tools/testing/selftests/riscv/cfi/Makefile | 2 +- tools/testing/selftests/riscv/hwprobe/Makefile | 6 +++--- tools/testing/selftests/riscv/mm/Makefile | 2 +- tools/testing/selftests/riscv/sigreturn/Makefile | 2 +- tools/testing/selftests/riscv/vector/Makefile | 12 ++++++------ 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/riscv/abi/Makefile b/tools/testing/selftests/riscv/abi/Makefile index ed82ff9c664e..041114675ad5 100644 --- a/tools/testing/selftests/riscv/abi/Makefile +++ b/tools/testing/selftests/riscv/abi/Makefile @@ -7,4 +7,4 @@ TEST_GEN_PROGS := pointer_masking include ../../lib.mk $(OUTPUT)/pointer_masking: pointer_masking.c - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ diff --git a/tools/testing/selftests/riscv/cfi/Makefile b/tools/testing/selftests/riscv/cfi/Makefile index 93b4738c0e2e..418b4b5325a5 100644 --- a/tools/testing/selftests/riscv/cfi/Makefile +++ b/tools/testing/selftests/riscv/cfi/Makefile @@ -16,7 +16,7 @@ ifeq ($(shell $(CC) $(CFLAGS) -nostdlib -xc /dev/null -o /dev/null > /dev/null 2 TEST_GEN_PROGS := cfitests $(OUTPUT)/cfitests: cfitests.c shadowstack.c - $(CC) -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ else $(shell echo "Toolchain doesn't support CFI, skipping CFI kselftest." >&2) diff --git a/tools/testing/selftests/riscv/hwprobe/Makefile b/tools/testing/selftests/riscv/hwprobe/Makefile index cec81610a5f2..71e3f26c541b 100644 --- a/tools/testing/selftests/riscv/hwprobe/Makefile +++ b/tools/testing/selftests/riscv/hwprobe/Makefile @@ -9,10 +9,10 @@ TEST_GEN_PROGS := hwprobe cbo which-cpus include ../../lib.mk $(OUTPUT)/hwprobe: hwprobe.c sys_hwprobe.S - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ $(OUTPUT)/cbo: cbo.c sys_hwprobe.S - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ $(OUTPUT)/which-cpus: which-cpus.c sys_hwprobe.S - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ diff --git a/tools/testing/selftests/riscv/mm/Makefile b/tools/testing/selftests/riscv/mm/Makefile index 4664ed79e20b..24122453e3d0 100644 --- a/tools/testing/selftests/riscv/mm/Makefile +++ b/tools/testing/selftests/riscv/mm/Makefile @@ -12,4 +12,4 @@ TEST_PROGS := run_mmap.sh include ../../lib.mk $(OUTPUT)/mm: mmap_default.c mmap_bottomup.c mmap_tests.h - $(CC) -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ diff --git a/tools/testing/selftests/riscv/sigreturn/Makefile b/tools/testing/selftests/riscv/sigreturn/Makefile index eb8bac9279a8..8c77508641f3 100644 --- a/tools/testing/selftests/riscv/sigreturn/Makefile +++ b/tools/testing/selftests/riscv/sigreturn/Makefile @@ -9,4 +9,4 @@ TEST_GEN_PROGS := sigreturn include ../../lib.mk $(OUTPUT)/sigreturn: sigreturn.c - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ diff --git a/tools/testing/selftests/riscv/vector/Makefile b/tools/testing/selftests/riscv/vector/Makefile index 326dafd739bf..7e0017b3fb8b 100644 --- a/tools/testing/selftests/riscv/vector/Makefile +++ b/tools/testing/selftests/riscv/vector/Makefile @@ -11,29 +11,29 @@ include ../../lib.mk TEST_GEN_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(TEST_GEN_LIBS)) $(OUTPUT)/sys_hwprobe.o: ../hwprobe/sys_hwprobe.S - $(CC) -static -c -o$@ $(CFLAGS) $^ + $(CC) -static -c -o $@ $(CFLAGS) $^ $(OUTPUT)/v_helpers.o: v_helpers.c - $(CC) -static -c -o$@ $(CFLAGS) $^ + $(CC) -static -c -o $@ $(CFLAGS) $^ $(OUTPUT)/vstate_prctl: vstate_prctl.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ $(OUTPUT)/vstate_exec_nolibc: vstate_exec_nolibc.c $(CC) -nostdlib -static -include ../../../../include/nolibc/nolibc.h \ -Wall $(CFLAGS) $(LDFLAGS) $^ -o $@ -lgcc $(OUTPUT)/v_initval: v_initval.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ $(OUTPUT)/v_exec_initval_nolibc: v_exec_initval_nolibc.c $(CC) -nostdlib -static -include ../../../../include/nolibc/nolibc.h \ -Wall $(CFLAGS) $(LDFLAGS) $^ -o $@ -lgcc $(OUTPUT)/vstate_ptrace: vstate_ptrace.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ $(OUTPUT)/validate_v_ptrace: validate_v_ptrace.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o - $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^ + $(CC) -static -o $@ $(CFLAGS) $(LDFLAGS) $^ EXTRA_CLEAN += $(TEST_GEN_OBJ) From 84894ceb3c2ef5c5404359efd4edc6c438aa6d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 6 Jun 2026 20:17:52 -0600 Subject: [PATCH 08/36] riscv: Implement ARCH_HAS_CC_CAN_LINK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generic CC_CAN_LINK detection relies on -m32/-m64 compiler flags. These are not supported by riscv compilers. Use architecture-specific logic using -mabi instead. Prefer the 'd' ABI variant when possible as todays toolchains are most likely to provide a libc for that one. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260316-cc-can-link-riscv-v4-1-64c072b456dd@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index c5754942cf85..a5e4f438e381 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -24,6 +24,7 @@ config RISCV select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2 select ARCH_ENABLE_THP_MIGRATION if TRANSPARENT_HUGEPAGE select ARCH_HAS_BINFMT_FLAT + select ARCH_HAS_CC_CAN_LINK select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VIRTUAL if MMU select ARCH_HAS_DEBUG_VM_PGTABLE @@ -1353,6 +1354,20 @@ config PORTABLE config ARCH_PROC_KCORE_TEXT def_bool y +config ARCH_CC_CAN_LINK + bool + default $(cc_can_link_user,-march=rv64g -mabi=lp64d) if 64BIT && FPU + default $(cc_can_link_user,-march=rv64g -mabi=lp64) if 64BIT + default $(cc_can_link_user,-march=rv32g -mabi=ilp32d) if FPU + default $(cc_can_link_user,-march=rv32g -mabi=ilp32) + +config ARCH_USERFLAGS + string + default "-march=rv64g -mabi=lp64d" if 64BIT && FPU + default "-march=rv64g -mabi=lp64" if 64BIT + default "-march=rv32g -mabi=ilp32d" if FPU + default "-march=rv32g -mabi=ilp32" + menu "Power management options" source "kernel/power/Kconfig" From ecbf894165a2e86b0830eb82be49f861da2a9e0b Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 09/36] riscv: Fix ftrace_graph_ret_addr() to use the correct task pointer The walk_stackframe() function is used to unwind the stack of a given task. When function graph tracing is enabled, ftrace_graph_ret_addr() is called to resolve the original return address if it was modified by the tracer. The current code incorrectly passes 'current' instead of 'task' to ftrace_graph_ret_addr(). This causes incorrect return address resolution when unwinding a stack of a different task (e.g., when the task is blocked in __switch_to). Fix this by passing 'task' instead of 'current' to match the behavior of other architectures (arm64, loongarch, powerpc, s390, x86). Signed-off-by: Rui Qi Link: https://patch.msgid.link/20260408092915.46408-1-qirui.001@bytedance.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c index b41b6255751c..2692d3a06afa 100644 --- a/arch/riscv/kernel/stacktrace.c +++ b/arch/riscv/kernel/stacktrace.c @@ -87,7 +87,7 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs, } else { fp = READ_ONCE_TASK_STACK(task, frame->fp); pc = READ_ONCE_TASK_STACK(task, frame->ra); - pc = ftrace_graph_ret_addr(current, &graph_idx, pc, + pc = ftrace_graph_ret_addr(task, &graph_idx, pc, &frame->ra); if (pc >= (unsigned long)handle_exception && pc < (unsigned long)&ret_from_exception_end) { From 3e17a4b443bbaecc723a2d51faeaa1a0097e43e9 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 10/36] riscv: module: Use generic cmp_int() instead of custom cmp_3way() The module-sections.c file defines a custom cmp_3way() macro to perform 3-way comparisons during relocation sorting. Instead of maintaining our own implementation, use the generic cmp_int() macro provided by the already included . This removes redundant code and relies on standard kernel interfaces. Signed-off-by: Florian Schmaus Link: https://patch.msgid.link/20260512063231.708256-1-florian.schmaus@codasip.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/module-sections.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/riscv/kernel/module-sections.c b/arch/riscv/kernel/module-sections.c index 98eaac6f6606..b3b11b7f7ed9 100644 --- a/arch/riscv/kernel/module-sections.c +++ b/arch/riscv/kernel/module-sections.c @@ -56,17 +56,15 @@ unsigned long module_emit_plt_entry(struct module *mod, unsigned long val) return (unsigned long)&plt[i]; } -#define cmp_3way(a, b) ((a) < (b) ? -1 : (a) > (b)) - static int cmp_rela(const void *a, const void *b) { const Elf_Rela *x = a, *y = b; int i; /* sort by type, symbol index and addend */ - i = cmp_3way(x->r_info, y->r_info); + i = cmp_int(x->r_info, y->r_info); if (i == 0) - i = cmp_3way(x->r_addend, y->r_addend); + i = cmp_int(x->r_addend, y->r_addend); return i; } From 77d980d06f854e77f21b9ffe48da266af9f44a1b Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 11/36] riscv: Implement _THIS_IP_ using inline asm Both GCC [1] and Clang [2] consider the generic version of _THIS_IP_ to be broken: #define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) In particular, the address of a label is only expected to be used with a computed goto. While the generic version more or less works today, it is known to be brittle and may break with current and future optimizations. For example, Clang -O2 always returns 1 when this function is inlined: static inline unsigned long get_ip(void) { return ({ __label__ __here; __here: (unsigned long)&&__here; }); } Fix it by overriding _THIS_IP_ in (which is included by ) using an architecture-specific inline asm version. Additionally, avoiding taking the address of a label prevents compilers from emitting spurious indirect branch targets (e.g. ENDBR or BTI) under control-flow integrity schemes. Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120071 [1] Link: https://github.com/llvm/llvm-project/issues/138272 [2] Signed-off-by: Marco Elver Link: https://patch.msgid.link/20260521000436.3931067-1-elver@google.com Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/linkage.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/include/asm/linkage.h b/arch/riscv/include/asm/linkage.h index 9e88ba23cd2b..7e0210ef4eb4 100644 --- a/arch/riscv/include/asm/linkage.h +++ b/arch/riscv/include/asm/linkage.h @@ -9,4 +9,6 @@ #define __ALIGN .balign 4 #define __ALIGN_STR ".balign 4" +#define _THIS_IP_ ({ unsigned long __ip; asm volatile("auipc %0, 0" : "=r" (__ip)); __ip; }) + #endif /* _ASM_RISCV_LINKAGE_H */ From 3b60f96b852a139ae8256426f2a69e2410f82c9c Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sun, 3 May 2026 05:03:31 +0100 Subject: [PATCH 12/36] riscv: replace select with dependency for visible RELOCATABLE RANDOMIZE_BASE currently selects RELOCATABLE even though RELOCATABLE is visible to users. Some other architectures, like x86, use 'depends on' for RELOCATABLE in their definition of RANDOMIZE_BASE, so let's do the same here. This select-visible Kconfig misusage was detected by Kconfirm, a static analysis tool for Kconfig. Signed-off-by: Julian Braha Link: https://patch.msgid.link/20260503040331.71875-1-julianbraha@gmail.com Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index a5e4f438e381..b1101c336c7c 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -1164,7 +1164,7 @@ config RELOCATABLE config RANDOMIZE_BASE bool "Randomize the address of the kernel image" - select RELOCATABLE + depends on RELOCATABLE depends on MMU && 64BIT help Randomizes the virtual address at which the kernel image is From 9f19a42083f5c1bcb7b0c330fa385d77fbd424fb Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 13/36] riscv: dead code cleanup in kconfig for RISCV_PROBE_VECTOR_UNALIGNED_ACCESS The same Kconfig statement 'depends on RISCV_ISA_V' appears twice for RISCV_PROBE_VECTOR_UNALIGNED_ACCESS. The first instance is in its choice menu, "Vector unaligned Accesses Support", making the second instance in its specific Kconfig definition dead code. I propose removing this second instance. This dead code was found by kconfirm, a static analysis tool for Kconfig. Signed-off-by: Julian Braha Reviewed-by: Jesse Taube Link: https://patch.msgid.link/20260329203249.563434-1-julianbraha@gmail.com Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index b1101c336c7c..bf4bc39c76b8 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -1024,7 +1024,6 @@ choice config RISCV_PROBE_VECTOR_UNALIGNED_ACCESS bool "Probe speed of vector unaligned accesses" select RISCV_VECTOR_MISALIGNED - depends on RISCV_ISA_V help During boot, the kernel will run a series of tests to determine the speed of vector unaligned accesses if they are supported. This probing From 9fd44698b11dbdf4d29fdef0f0da25f320c13854 Mon Sep 17 00:00:00 2001 From: Chen Pei Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 14/36] riscv: ftrace: select HAVE_BUILDTIME_MCOUNT_SORT RISC-V already satisfies all prerequisites for build-time mcount sorting: the sorttable host tool handles EM_RISCV in its machine-type dispatch, and the __mcount_loc section entries are stored as direct virtual addresses in the final vmlinux binary, so no relocation processing is required during the sort step. Select HAVE_BUILDTIME_MCOUNT_SORT so that BUILDTIME_MCOUNT_SORT is automatically enabled when DYNAMIC_FTRACE is configured. This allows sorttable to sort the __mcount_loc section at link time, making the run-time ftrace initialisation path skip the software sort and reducing kernel startup overhead. Verified with CONFIG_FTRACE_SORT_STARTUP_TEST=y, which confirms that the section produced by the build is already in ascending order: [ 0.000000] ftrace section at ffffffff81015a60 sorted properly Signed-off-by: Chen Pei Tested-by: Guo Ren Link: https://patch.msgid.link/20260409114736.907-1-cp0613@linux.alibaba.com Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index bf4bc39c76b8..ce5f4342e2f2 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -152,6 +152,7 @@ config RISCV select HAVE_ARCH_USERFAULTFD_WP if 64BIT && MMU && USERFAULTFD && RISCV_ISA_SVRSW60T59B select HAVE_ARCH_VMAP_STACK if MMU && 64BIT select HAVE_ASM_MODVERSIONS + select HAVE_BUILDTIME_MCOUNT_SORT select HAVE_CONTEXT_TRACKING_USER select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_CONTIGUOUS if MMU From 063e01ded573b62aa90e4a60a73cff9070bb96b9 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Sat, 6 Jun 2026 20:17:53 -0600 Subject: [PATCH 15/36] riscv: kexec_elf: Remove unused pr_fmt definition Remove the pr_fmt macro as no pr_*() calls exist in this file. The prefix string "kexec_image: " is also not appropriate for kexec_elf.c, if pr_fmt is needed in the future, referring to kexec_image.c, a more appropriate prefix like "kexec_file(elf): " can be added at that time. Signed-off-by: Hui Wang Link: https://patch.msgid.link/20260525115159.100177-1-hui.wang@canonical.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/kexec_elf.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/riscv/kernel/kexec_elf.c b/arch/riscv/kernel/kexec_elf.c index 531d348db84d..05fd33104f3d 100644 --- a/arch/riscv/kernel/kexec_elf.c +++ b/arch/riscv/kernel/kexec_elf.c @@ -10,8 +10,6 @@ * for kernel. */ -#define pr_fmt(fmt) "kexec_image: " fmt - #include #include #include From 2b2b207e1162e577cd6208e184d3d3a0fcfa9cca Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Sat, 6 Jun 2026 20:17:54 -0600 Subject: [PATCH 16/36] riscv: cpu_ops: Change return value type of cpu_is_stopped() to bool In the original sbi_cpu_is_stopped(), if rc doesn't equal to the SBI_HSM_STATE_STOPPED, it will return rc to the caller directly. But there is a hidden problem, the rc could be SBI_HSM_STATE_STARTED, if so, this function will report cpu stopped while the cpu isn't really stopped. Furthermore, from the name of cpu_is_stopped(), it gives a sense the return value is a bool type, true means the cpu is stopped, conversely false means the cpu is not stopped. Here change the return value type to bool and change the callers accordingly. This could fix the above two issues. Fixes: f1e58583b9c7c ("RISC-V: Support cpu hotplug") Signed-off-by: Hui Wang Link: https://patch.msgid.link/20260413123515.48423-1-hui.wang@canonical.com [pjw@kernel.org: cleaned up some of the pr_warn() messages] Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/cpu_ops.h | 2 +- arch/riscv/kernel/cpu-hotplug.c | 4 ++-- arch/riscv/kernel/cpu_ops_sbi.c | 11 +++++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/arch/riscv/include/asm/cpu_ops.h b/arch/riscv/include/asm/cpu_ops.h index 176b570ef982..065811fca594 100644 --- a/arch/riscv/include/asm/cpu_ops.h +++ b/arch/riscv/include/asm/cpu_ops.h @@ -24,7 +24,7 @@ struct cpu_operations { struct task_struct *tidle); #ifdef CONFIG_HOTPLUG_CPU void (*cpu_stop)(void); - int (*cpu_is_stopped)(unsigned int cpu); + bool (*cpu_is_stopped)(unsigned int cpu); #endif }; diff --git a/arch/riscv/kernel/cpu-hotplug.c b/arch/riscv/kernel/cpu-hotplug.c index a0ee426f6d93..0bc56d8381b6 100644 --- a/arch/riscv/kernel/cpu-hotplug.c +++ b/arch/riscv/kernel/cpu-hotplug.c @@ -57,8 +57,8 @@ void arch_cpuhp_cleanup_dead_cpu(unsigned int cpu) /* Verify from the firmware if the cpu is really stopped*/ if (cpu_ops->cpu_is_stopped) ret = cpu_ops->cpu_is_stopped(cpu); - if (ret) - pr_warn("CPU%u may not have stopped: %d\n", cpu, ret); + if (!ret) + pr_warn("CPU%u may not have stopped\n", cpu); } /* diff --git a/arch/riscv/kernel/cpu_ops_sbi.c b/arch/riscv/kernel/cpu_ops_sbi.c index 00aff669f5f2..146ceab1011f 100644 --- a/arch/riscv/kernel/cpu_ops_sbi.c +++ b/arch/riscv/kernel/cpu_ops_sbi.c @@ -88,16 +88,19 @@ static void sbi_cpu_stop(void) pr_crit("Unable to stop the cpu %d (%d)\n", smp_processor_id(), ret); } -static int sbi_cpu_is_stopped(unsigned int cpuid) +static bool sbi_cpu_is_stopped(unsigned int cpuid) { int rc; unsigned long hartid = cpuid_to_hartid_map(cpuid); rc = sbi_hsm_hart_get_status(hartid); - if (rc == SBI_HSM_STATE_STOPPED) - return 0; - return rc; + if (rc != SBI_HSM_STATE_STOPPED) { + pr_warn("HART%lu isn't stopped; status %d\n", hartid, rc); + return false; + } + + return true; } #endif From edccaf7be062f3400cdadb45b0e2b4db4cdd8c26 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Sat, 6 Jun 2026 20:17:54 -0600 Subject: [PATCH 17/36] riscv: cpu_ops_sbi: No need to be bothered to check ret.error If the ret.error equals to 0, the sbi_err_map_linux_errno() can also handle it, i.e. if ret.error is SBI_SUCCESS, it will return 0 immediately, so no need to be bothered to check ret.error here. Signed-off-by: Hui Wang Link: https://patch.msgid.link/20260413123515.48423-2-hui.wang@canonical.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/cpu_ops_sbi.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/arch/riscv/kernel/cpu_ops_sbi.c b/arch/riscv/kernel/cpu_ops_sbi.c index 146ceab1011f..ee6e4b5cc39e 100644 --- a/arch/riscv/kernel/cpu_ops_sbi.c +++ b/arch/riscv/kernel/cpu_ops_sbi.c @@ -30,10 +30,8 @@ static int sbi_hsm_hart_start(unsigned long hartid, unsigned long saddr, ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_START, hartid, saddr, priv, 0, 0, 0); - if (ret.error) - return sbi_err_map_linux_errno(ret.error); - else - return 0; + + return sbi_err_map_linux_errno(ret.error); } #ifdef CONFIG_HOTPLUG_CPU @@ -43,10 +41,7 @@ static int sbi_hsm_hart_stop(void) ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STOP, 0, 0, 0, 0, 0, 0); - if (ret.error) - return sbi_err_map_linux_errno(ret.error); - else - return 0; + return sbi_err_map_linux_errno(ret.error); } static int sbi_hsm_hart_get_status(unsigned long hartid) From dea5db8c5775704f24519d130482d9ceaf66c441 Mon Sep 17 00:00:00 2001 From: Anirudh Srinivasan Date: Sat, 6 Jun 2026 20:17:54 -0600 Subject: [PATCH 18/36] riscv: defconfig: Enable Eswin SoCs Enable support for Eswin SoCs in the default configuration, so that defconfig kernel/DTs are bootable on Hifive Premier P550 Board. Signed-off-by: Anirudh Srinivasan Link: https://patch.msgid.link/20260603-eswin_defconfig-v1-1-8471691d3153@oss.tenstorrent.com Signed-off-by: Paul Walmsley --- arch/riscv/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig index c2c37327b987..74ba5acc12a4 100644 --- a/arch/riscv/configs/defconfig +++ b/arch/riscv/configs/defconfig @@ -24,6 +24,7 @@ CONFIG_BLK_DEV_INITRD=y CONFIG_PROFILING=y CONFIG_ARCH_ANDES=y CONFIG_ARCH_ANLOGIC=y +CONFIG_ARCH_ESWIN=y CONFIG_ARCH_MICROCHIP=y CONFIG_ARCH_SIFIVE=y CONFIG_ARCH_SOPHGO=y From f3336b48cf9d3f2d1fc78e3289c0ded2f00876ee Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Sat, 6 Jun 2026 20:17:54 -0600 Subject: [PATCH 19/36] riscv: mm: Define DIRECT_MAP_PHYSMEM_END On RISC-V, the actual mappable range of physical address space is dependent on the current MMU mode i.e. satp_mode (See Documentation/arch/riscv/vm-layout.rst). Define the DIRECT_MAP_PHYSMEM_END macro based on the existing virtual address space layout macros to expose this information to get_free_mem_region(). Otherwise, it returns a region that couldn't be mapped, which breaks ZONE_DEVICE. Cc: stable@vger.kernel.org # v6.13+ Tested-by: Han Gao # SG2044 Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260309-riscv-sparsemem-vmemmap-limits-v1-2-f40efe18e3cd@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/pgtable.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index a1a7c6520a09..5d5756bda82e 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -93,6 +93,16 @@ */ #define vmemmap ((struct page *)VMEMMAP_START - vmemmap_start_pfn) +/* Needed to limit get_free_mem_region() */ +#if defined(CONFIG_FLATMEM) +#define DIRECT_MAP_PHYSMEM_END (phys_ram_base + KERN_VIRT_SIZE - 1) +#elif defined(CONFIG_SPARSEMEM_VMEMMAP) +#define DIRECT_MAP_PHYSMEM_END \ + ((vmemmap_start_pfn + VMEMMAP_SIZE / sizeof(struct page)) * PAGE_SIZE - 1) +#elif defined(CONFIG_SPARSEMEM) +/* DIRECT_MAP_PHYSMEM_END is not limited by VA space assignment in this case */ +#endif + #define PCI_IO_SIZE SZ_16M #define PCI_IO_END VMEMMAP_START #define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE) From b67a1ee0db0094c6cc158b087be6c334ad881a41 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Sat, 6 Jun 2026 20:17:58 -0600 Subject: [PATCH 20/36] riscv: kexec_file: Constrain segment placement to direct map When kexec_file_load places segments with buf_max=ULONG_MAX and top_down=true, they land at the highest available physical addresses. On RISC-V the size of the linear mapping is determined by the active VM mode: SV39 caps the direct map at roughly 128GB, while SV48/SV57 extend the range substantially further. When the installed physical memory exceeds the direct map size of the active mode, top-down placement puts DTB/initrd at physical addresses outside the linearly mapped region. The kexec'd kernel cannot reach them during early boot, triggering a page fault at memcmp in start_kernel. Fix by constraining buf_max to PFN_PHYS(max_low_pfn), which reflects the runtime direct map boundary for the active VM mode (SV39/SV48/ SV57). This keeps all kexec segments within the linearly mapped region while preserving the upstream top_down allocation strategy. Signed-off-by: Han Gao Link: https://patch.msgid.link/20260519170641.123517-1-gaohan@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/kernel/machine_kexec_file.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/machine_kexec_file.c b/arch/riscv/kernel/machine_kexec_file.c index 54e2d9552e93..59d4bbc848a8 100644 --- a/arch/riscv/kernel/machine_kexec_file.c +++ b/arch/riscv/kernel/machine_kexec_file.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -266,7 +267,7 @@ int load_extra_segments(struct kimage *image, unsigned long kernel_start, kbuf.image = image; kbuf.buf_min = kernel_start + kernel_len; - kbuf.buf_max = ULONG_MAX; + kbuf.buf_max = PFN_PHYS(max_low_pfn); #ifdef CONFIG_CRASH_DUMP /* Add elfcorehdr */ From bf4a195f063b0a0805c1417f6aad1dd32ea48f0f Mon Sep 17 00:00:00 2001 From: Zishun Yi Date: Sat, 6 Jun 2026 20:17:58 -0600 Subject: [PATCH 21/36] riscv: cacheinfo: Fix node reference leak in populate_cache_leaves Currently, the while loop drops the reference to prev in each iteration. If the loop terminates early due to a break, the final of_node_put(np) correctly drops the reference to the current node. However, if the loop terminates naturally because np == NULL, calling of_node_put(np) is a no-op. This leaves the last valid node stored in prev without its reference dropped, resulting in a node reference leak. Fix this by changing the final `of_node_put(np)` to `of_node_put(prev)`. Fixes: 94f9bf118f1e ("RISC-V: Fix of_node_* refcount") Cc: stable@vger.kernel.org Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Zishun Yi Link: https://patch.msgid.link/20260509074040.1747800-1-vulab@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/kernel/cacheinfo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/cacheinfo.c b/arch/riscv/kernel/cacheinfo.c index 26b085dbdd07..6c9a1ef2d45a 100644 --- a/arch/riscv/kernel/cacheinfo.c +++ b/arch/riscv/kernel/cacheinfo.c @@ -133,7 +133,7 @@ int populate_cache_leaves(unsigned int cpu) ci_leaf_init(this_leaf++, CACHE_TYPE_DATA, level); levels = level; } - of_node_put(np); + of_node_put(prev); return 0; } From 8ac35bac70e7e581d673b76878f7691cdadc33b8 Mon Sep 17 00:00:00 2001 From: Rui Qi Date: Sat, 6 Jun 2026 20:17:59 -0600 Subject: [PATCH 22/36] riscv: stacktrace: Remove bogus -0x4 offset in non-FP walk_stackframe In the non-frame-pointer version of walk_stackframe, each value read from the stack is treated as a potential return address and has 0x4 subtracted before being used as the program counter. This was intended to convert the return address (the instruction after a call) back to the call site, but it is incorrect: 1. RISC-V has variable-length instructions due to the RVC (compressed instruction) extension. A call instruction can be either 4 bytes (regular) or 2 bytes (compressed, e.g. c.jal). Subtracting a fixed 0x4 assumes all call instructions are 4 bytes, which is wrong for compressed instructions. 2. Stack traces conventionally report return addresses, not call sites. Other architectures (ARM64, x86, ARM) do not subtract instruction size from return addresses in their stack unwinding code. 3. The frame-pointer version of walk_stackframe already dropped the -0x4 offset. Commit b785ec129bd9 ("riscv/ftrace: Add HAVE_FUNCTION_GRAPH_RET_ADDR_PTR support") replaced "pc = frame->ra - 0x4" with ftrace_graph_ret_addr(), and the commit message explicitly noted that "the original calculation, pc = frame->ra - 4, is buggy when the instruction at the return address happened to be a compressed inst." The non-FP version was simply overlooked. Remove the bogus -0x4 offset to match the FP version and the conventions used by other architectures. Fixes: 5d8544e2d007 ("RISC-V: Generic library routines and assembly") Signed-off-by: Rui Qi Link: https://patch.msgid.link/20260603115329.791603-2-qirui.001@bytedance.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/stacktrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c index 2692d3a06afa..c7555447149b 100644 --- a/arch/riscv/kernel/stacktrace.c +++ b/arch/riscv/kernel/stacktrace.c @@ -129,7 +129,7 @@ void notrace walk_stackframe(struct task_struct *task, while (!kstack_end(ksp)) { if (__kernel_text_address(pc) && unlikely(!fn(arg, pc))) break; - pc = READ_ONCE_NOCHECK(*ksp++) - 0x4; + pc = READ_ONCE_NOCHECK(*ksp++); } } From 9ee25d0a70ff4494b4e1d266b962d0a574ef318a Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:45 +0800 Subject: [PATCH 23/36] riscv: mm: Extract helper mark_new_valid_map() In preparation of a future patch using the same mechanism for non-vmalloc addresses, extract the mark_new_valid_map() helper from flush_cache_vmap(). No functional change intended. Cc: stable@vger.kernel.org Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-1-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/cacheflush.h | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 0092513c3376..b1a2ac665792 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -43,20 +43,23 @@ do { \ #ifdef CONFIG_64BIT extern u64 new_vmalloc[NR_CPUS / sizeof(u64) + 1]; extern char _end[]; +static inline void mark_new_valid_map(void) +{ + int i; + + /* + * We don't care if concurrently a cpu resets this value since + * the only place this can happen is in handle_exception() where + * an sfence.vma is emitted. + */ + for (i = 0; i < ARRAY_SIZE(new_vmalloc); ++i) + new_vmalloc[i] = -1ULL; +} #define flush_cache_vmap flush_cache_vmap static inline void flush_cache_vmap(unsigned long start, unsigned long end) { - if (is_vmalloc_or_module_addr((void *)start)) { - int i; - - /* - * We don't care if concurrently a cpu resets this value since - * the only place this can happen is in handle_exception() where - * an sfence.vma is emitted. - */ - for (i = 0; i < ARRAY_SIZE(new_vmalloc); ++i) - new_vmalloc[i] = -1ULL; - } + if (is_vmalloc_or_module_addr((void *)start)) + mark_new_valid_map(); } #define flush_cache_vmap_early(start, end) local_flush_tlb_kernel_range(start, end) #endif From 8d6c8c40e733b3fcaf92fed0a078bba2f6941a3b Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:46 +0800 Subject: [PATCH 24/36] riscv: kfence: Call mark_new_valid_map() for kfence_unprotect() In kfence_protect_page(), which kfence_unprotect() calls, we cannot send IPIs to other CPUs to ask them to flush TLB. This may lead to those CPUs spuriously faulting on a recently allocated kfence object despite it being valid, leading to false positive use-after-free reports. Fix this by calling mark_new_valid_map() so that the page fault handling code path notices the spurious fault and flushes TLB then retries the access. Update the comment in handle_exception to indicate that new_valid_map_cpus_check also handles kfence_unprotect() spurious faults. Note that kfence_protect() has the same stale TLB entries problem, but that leads to false negatives, which is fine with kfence. Cc: stable@vger.kernel.org Reported-by: Yanko Kaneti Fixes: b3431a8bb336 ("riscv: Fix IPIs usage in kfence_protect_page()") Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-2-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/kfence.h | 7 +++++-- arch/riscv/kernel/entry.S | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/arch/riscv/include/asm/kfence.h b/arch/riscv/include/asm/kfence.h index d08bf7fb3aee..29cb3a6ee113 100644 --- a/arch/riscv/include/asm/kfence.h +++ b/arch/riscv/include/asm/kfence.h @@ -6,6 +6,7 @@ #include #include #include +#include #include static inline bool arch_kfence_init_pool(void) @@ -17,10 +18,12 @@ static inline bool kfence_protect_page(unsigned long addr, bool protect) { pte_t *pte = virt_to_kpte(addr); - if (protect) + if (protect) { set_pte(pte, __pte(pte_val(ptep_get(pte)) & ~_PAGE_PRESENT)); - else + } else { set_pte(pte, __pte(pte_val(ptep_get(pte)) | _PAGE_PRESENT)); + mark_new_valid_map(); + } preempt_disable(); local_flush_tlb_kernel_range(addr, addr + PAGE_SIZE); diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index d011fb51c59a..6c14ed7a5613 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -136,8 +136,10 @@ SYM_CODE_START(handle_exception) #ifdef CONFIG_64BIT /* - * The RISC-V kernel does not eagerly emit a sfence.vma after each - * new vmalloc mapping, which may result in exceptions: + * The RISC-V kernel does not flush TLBs on all CPUS after each new + * vmalloc mapping or kfence_unprotect(), which may result in + * exceptions: + * * - if the uarch caches invalid entries, the new mapping would not be * observed by the page table walker and an invalidation is needed. * - if the uarch does not cache invalid entries, a reordered access From a50940adfb8f390337d6ef9afe15b66ec498aceb Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:47 +0800 Subject: [PATCH 25/36] riscv: mm: Rename new_vmalloc into new_valid_map_cpus Since this mechanism is now used for the kfence pool, which comes from the linear mapping and not vmalloc, rename new_vmalloc into new_valid_map_cpus to avoid misleading readers. No functional change intended. Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-3-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/cacheflush.h | 6 ++--- arch/riscv/kernel/entry.S | 38 ++++++++++++++--------------- arch/riscv/mm/init.c | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index b1a2ac665792..8c7a0ef2635a 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -41,7 +41,7 @@ do { \ } while (0) #ifdef CONFIG_64BIT -extern u64 new_vmalloc[NR_CPUS / sizeof(u64) + 1]; +extern u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1]; extern char _end[]; static inline void mark_new_valid_map(void) { @@ -52,8 +52,8 @@ static inline void mark_new_valid_map(void) * the only place this can happen is in handle_exception() where * an sfence.vma is emitted. */ - for (i = 0; i < ARRAY_SIZE(new_vmalloc); ++i) - new_vmalloc[i] = -1ULL; + for (i = 0; i < ARRAY_SIZE(new_valid_map_cpus); ++i) + new_valid_map_cpus[i] = -1ULL; } #define flush_cache_vmap flush_cache_vmap static inline void flush_cache_vmap(unsigned long start, unsigned long end) diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 6c14ed7a5613..ebf6918b4e9b 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -20,44 +20,44 @@ .section .irqentry.text, "ax" -.macro new_vmalloc_check +.macro new_valid_map_cpus_check REG_S a0, TASK_TI_A0(tp) csrr a0, CSR_CAUSE /* Exclude IRQs */ - blt a0, zero, .Lnew_vmalloc_restore_context_a0 + blt a0, zero, .Lnew_valid_map_cpus_restore_context_a0 REG_S a1, TASK_TI_A1(tp) - /* Only check new_vmalloc if we are in page/protection fault */ + /* Only check new_valid_map_cpus if we are in page/protection fault */ li a1, EXC_LOAD_PAGE_FAULT - beq a0, a1, .Lnew_vmalloc_kernel_address + beq a0, a1, .Lnew_valid_map_cpus_kernel_address li a1, EXC_STORE_PAGE_FAULT - beq a0, a1, .Lnew_vmalloc_kernel_address + beq a0, a1, .Lnew_valid_map_cpus_kernel_address li a1, EXC_INST_PAGE_FAULT - bne a0, a1, .Lnew_vmalloc_restore_context_a1 + bne a0, a1, .Lnew_valid_map_cpus_restore_context_a1 -.Lnew_vmalloc_kernel_address: +.Lnew_valid_map_cpus_kernel_address: /* Is it a kernel address? */ csrr a0, CSR_TVAL - bge a0, zero, .Lnew_vmalloc_restore_context_a1 + bge a0, zero, .Lnew_valid_map_cpus_restore_context_a1 /* Check if a new vmalloc mapping appeared that could explain the trap */ REG_S a2, TASK_TI_A2(tp) /* * Computes: - * a0 = &new_vmalloc[BIT_WORD(cpu)] + * a0 = &new_valid_map_cpus[BIT_WORD(cpu)] * a1 = BIT_MASK(cpu) */ lw a2, TASK_TI_CPU(tp) /* - * Compute the new_vmalloc element position: + * Compute the new_valid_map_cpus element position: * (cpu / 64) * 8 = (cpu >> 6) << 3 */ srli a1, a2, 6 slli a1, a1, 3 - la a0, new_vmalloc + la a0, new_valid_map_cpus add a0, a0, a1 /* - * Compute the bit position in the new_vmalloc element: + * Compute the bit position in the new_valid_map_cpus element: * bit_pos = cpu % 64 = cpu - (cpu / 64) * 64 = cpu - (cpu >> 6) << 6 * = cpu - ((cpu >> 6) << 3) << 3 */ @@ -67,12 +67,12 @@ li a2, 1 sll a1, a2, a1 - /* Check the value of new_vmalloc for this cpu */ + /* Check the value of new_valid_map_cpus for this cpu */ REG_L a2, 0(a0) and a2, a2, a1 - beq a2, zero, .Lnew_vmalloc_restore_context + beq a2, zero, .Lnew_valid_map_cpus_restore_context - /* Atomically reset the current cpu bit in new_vmalloc */ + /* Atomically reset the current cpu bit in new_valid_map_cpus */ amoxor.d a0, a1, (a0) /* Only emit a sfence.vma if the uarch caches invalid entries */ @@ -84,11 +84,11 @@ csrw CSR_SCRATCH, x0 sret -.Lnew_vmalloc_restore_context: +.Lnew_valid_map_cpus_restore_context: REG_L a2, TASK_TI_A2(tp) -.Lnew_vmalloc_restore_context_a1: +.Lnew_valid_map_cpus_restore_context_a1: REG_L a1, TASK_TI_A1(tp) -.Lnew_vmalloc_restore_context_a0: +.Lnew_valid_map_cpus_restore_context_a0: REG_L a0, TASK_TI_A0(tp) .endm @@ -146,7 +146,7 @@ SYM_CODE_START(handle_exception) * could "miss" the new mapping and traps: in that case, we only need * to retry the access, no sfence.vma is required. */ - new_vmalloc_check + new_valid_map_cpus_check #endif REG_S sp, TASK_TI_KERNEL_SP(tp) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index fa8d2f6f554b..bede6845f767 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -37,7 +37,7 @@ #include "../kernel/head.h" -u64 new_vmalloc[NR_CPUS / sizeof(u64) + 1]; +u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1]; struct kernel_mapping kernel_map __ro_after_init; EXPORT_SYMBOL(kernel_map); From 26c171fc48539ac88d3697792b1fde9334af836c Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:48 +0800 Subject: [PATCH 26/36] riscv: mm: Use the bitmap API for new_valid_map_cpus The bitmap was defined with incorrect size. Fix it by using the proper bitmap API in C code. The corresponding assembly code is still okay and remains unchanged. Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-4-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/cacheflush.h | 8 +++----- arch/riscv/mm/init.c | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 8c7a0ef2635a..8cfe59483a8f 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -41,19 +41,17 @@ do { \ } while (0) #ifdef CONFIG_64BIT -extern u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1]; +/* This is accessed in assembly code. cpumask_var_t would be too complex. */ +extern DECLARE_BITMAP(new_valid_map_cpus, NR_CPUS); extern char _end[]; static inline void mark_new_valid_map(void) { - int i; - /* * We don't care if concurrently a cpu resets this value since * the only place this can happen is in handle_exception() where * an sfence.vma is emitted. */ - for (i = 0; i < ARRAY_SIZE(new_valid_map_cpus); ++i) - new_valid_map_cpus[i] = -1ULL; + bitmap_fill(new_valid_map_cpus, NR_CPUS); } #define flush_cache_vmap flush_cache_vmap static inline void flush_cache_vmap(unsigned long start, unsigned long end) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index bede6845f767..ea8766c4f3a2 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -37,7 +37,7 @@ #include "../kernel/head.h" -u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1]; +DECLARE_BITMAP(new_valid_map_cpus, NR_CPUS); struct kernel_mapping kernel_map __ro_after_init; EXPORT_SYMBOL(kernel_map); From 1b2c6b56a9fa0dcbef461039937de22b1cbecc7d Mon Sep 17 00:00:00 2001 From: Vivian Wang Date: Tue, 3 Mar 2026 13:29:49 +0800 Subject: [PATCH 27/36] riscv: mm: Unconditionally sfence.vma for spurious fault Svvptc does not guarantee that it's safe to just return here. Since we have already cleared our bit, if, theoretically, the bounded timeframe for the accessed page to become valid still hasn't happened after sret, we could fault again and actually crash. Hopefully, these spurious faults should be rare enough that this is an acceptable slowdown. Cc: stable@vger.kernel.org Fixes: 503638e0babf ("riscv: Stop emitting preventive sfence.vma for new vmalloc mappings") Signed-off-by: Vivian Wang Link: https://patch.msgid.link/20260303-handle-kfence-protect-spurious-fault-v2-5-f80d8354d79d@iscas.ac.cn Signed-off-by: Paul Walmsley --- arch/riscv/kernel/entry.S | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index ebf6918b4e9b..c6988983cdf7 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -75,8 +75,11 @@ /* Atomically reset the current cpu bit in new_valid_map_cpus */ amoxor.d a0, a1, (a0) - /* Only emit a sfence.vma if the uarch caches invalid entries */ - ALTERNATIVE("sfence.vma", "nop", 0, RISCV_ISA_EXT_SVVPTC, 1) + /* + * A sfence.vma is required here. Even if we had Svvptc, there's no + * guarantee that after returning we wouldn't just fault again. + */ + sfence.vma REG_L a0, TASK_TI_A0(tp) REG_L a1, TASK_TI_A1(tp) From 2abd0dba562551d5c27f97ce560684f534c0cf3a Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Sat, 4 Apr 2026 20:55:20 +0200 Subject: [PATCH 28/36] riscv: panic if IRQ handler stacks cannot be allocated init_irq_stacks() and init_irq_scs() may fail when arch_alloc_vmap_stack or scs_alloc return NULL, call panic() in this case. Signed-off-by: Osama Abdelkader Link: https://patch.msgid.link/20260404185522.21767-1-osama.abdelkader@gmail.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/irq.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c index b6af20bc300f..017d42e778be 100644 --- a/arch/riscv/kernel/irq.c +++ b/arch/riscv/kernel/irq.c @@ -75,28 +75,34 @@ DECLARE_PER_CPU(ulong *, irq_shadow_call_stack_ptr); DEFINE_PER_CPU(ulong *, irq_shadow_call_stack_ptr); #endif -static void init_irq_scs(void) +static void __init init_irq_scs(void) { int cpu; + void *s; if (!scs_is_enabled()) return; - for_each_possible_cpu(cpu) - per_cpu(irq_shadow_call_stack_ptr, cpu) = - scs_alloc(cpu_to_node(cpu)); + for_each_possible_cpu(cpu) { + s = scs_alloc(cpu_to_node(cpu)); + if (!s) + panic("Failed to allocate IRQ shadow call stack resources\n"); + per_cpu(irq_shadow_call_stack_ptr, cpu) = s; + } } DEFINE_PER_CPU(ulong *, irq_stack_ptr); #ifdef CONFIG_VMAP_STACK -static void init_irq_stacks(void) +static void __init init_irq_stacks(void) { int cpu; ulong *p; for_each_possible_cpu(cpu) { p = arch_alloc_vmap_stack(IRQ_STACK_SIZE, cpu_to_node(cpu)); + if (!p) + panic("Failed to allocate IRQ stack resources\n"); per_cpu(irq_stack_ptr, cpu) = p; } } @@ -104,7 +110,7 @@ static void init_irq_stacks(void) /* irq stack only needs to be 16 byte aligned - not IRQ_STACK_SIZE aligned. */ DEFINE_PER_CPU_ALIGNED(ulong [IRQ_STACK_SIZE/sizeof(ulong)], irq_stack); -static void init_irq_stacks(void) +static void __init init_irq_stacks(void) { int cpu; @@ -129,8 +135,8 @@ void do_softirq_own_stack(void) #endif /* CONFIG_SOFTIRQ_ON_OWN_STACK */ #else -static void init_irq_scs(void) {} -static void init_irq_stacks(void) {} +static void __init init_irq_scs(void) {} +static void __init init_irq_stacks(void) {} #endif /* CONFIG_IRQ_STACKS */ int arch_show_interrupts(struct seq_file *p, int prec) From e0fd2599f50025d83a0f2ec01c63d204de2c173e Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 3 Jun 2026 00:47:22 +0200 Subject: [PATCH 29/36] riscv: kexec: use min to simplify riscv_kexec_elf_load Use min() to replace the open-coded version and assign the result directly to kbuf.bufsz. Drop the now-unused local size variable. Signed-off-by: Thorsten Blum Reviewed-by: Breno Leitao Link: https://patch.msgid.link/20260602224725.1088385-3-thorsten.blum@linux.dev Signed-off-by: Paul Walmsley --- arch/riscv/kernel/kexec_elf.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/arch/riscv/kernel/kexec_elf.c b/arch/riscv/kernel/kexec_elf.c index 05fd33104f3d..3e9a32acb8f2 100644 --- a/arch/riscv/kernel/kexec_elf.c +++ b/arch/riscv/kernel/kexec_elf.c @@ -17,6 +17,7 @@ #include #include #include +#include #include static int riscv_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr, @@ -25,7 +26,6 @@ static int riscv_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr, { int i; int ret = 0; - size_t size; struct kexec_buf kbuf = {}; const struct elf_phdr *phdr; @@ -36,12 +36,8 @@ static int riscv_kexec_elf_load(struct kimage *image, struct elfhdr *ehdr, if (phdr->p_type != PT_LOAD) continue; - size = phdr->p_filesz; - if (size > phdr->p_memsz) - size = phdr->p_memsz; - kbuf.buffer = (void *) elf_info->buffer + phdr->p_offset; - kbuf.bufsz = size; + kbuf.bufsz = min(phdr->p_filesz, phdr->p_memsz); kbuf.buf_align = phdr->p_align; kbuf.mem = phdr->p_paddr - old_pbase + new_pbase; kbuf.memsz = phdr->p_memsz; From 499578e22ae0cc33d05803f22e5c71ffb7077b2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:50 +0200 Subject: [PATCH 30/36] riscv: vdso: Always declare vdso_start symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the declarations of vdso_start and its related symbols always visible. With that their users don't have to use ifdeffery but can use the better IS_ENABLED() compile-time checks. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-1-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/vdso.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/riscv/include/asm/vdso.h b/arch/riscv/include/asm/vdso.h index 35bf830a5576..f7998d9ad9b2 100644 --- a/arch/riscv/include/asm/vdso.h +++ b/arch/riscv/include/asm/vdso.h @@ -12,12 +12,15 @@ * All systems with an MMU have a VDSO, but systems without an MMU don't * support shared libraries and therefore don't have one. */ -#ifdef CONFIG_MMU #define __VDSO_PAGES 4 #ifndef __ASSEMBLER__ + +#ifdef CONFIG_MMU #include +#endif + #ifdef CONFIG_RISCV_USER_CFI #include #endif @@ -38,15 +41,12 @@ #define COMPAT_VDSO_SYMBOL(base, name) \ (void __user *)((unsigned long)(base) + compat__vdso_##name##_offset) -extern char compat_vdso_start[], compat_vdso_end[]; - #endif /* CONFIG_COMPAT */ extern char vdso_start[], vdso_end[]; extern char vdso_cfi_start[], vdso_cfi_end[]; +extern char compat_vdso_start[], compat_vdso_end[]; #endif /* !__ASSEMBLER__ */ -#endif /* CONFIG_MMU */ - #endif /* _ASM_RISCV_VDSO_H */ From 4fd6505f189d447abbed1f1f6fe6b82649f27a46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:51 +0200 Subject: [PATCH 31/36] riscv: alternative: Use IS_ENABLED() over ifdeffery for apply_vdso_alternatives() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IS_ENABLED() allows better compilation coverage while still optimizing away all the dead code. Also it will make some upcoming changes easier. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-2-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/kernel/alternative.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 7642704c7f18..59991922a5dc 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -173,7 +173,6 @@ static void __init_or_module _apply_alternatives(struct alt_entry *begin, stage); } -#ifdef CONFIG_MMU static void __init apply_vdso_alternatives(void) { const Elf_Ehdr *hdr; @@ -194,9 +193,6 @@ static void __init apply_vdso_alternatives(void) (struct alt_entry *)end, RISCV_ALTERNATIVES_BOOT); } -#else -static void __init apply_vdso_alternatives(void) { } -#endif void __init apply_boot_alternatives(void) { @@ -207,7 +203,8 @@ void __init apply_boot_alternatives(void) (struct alt_entry *)__alt_end, RISCV_ALTERNATIVES_BOOT); - apply_vdso_alternatives(); + if (IS_ENABLED(CONFIG_MMU)) + apply_vdso_alternatives(); } /* From 6386161abb02880ace2cc965e73f7857b351706c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:52 +0200 Subject: [PATCH 32/36] riscv: alternative: Pass vDSO start as parameter to apply_vdso_alternatives() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dedicated vDSO with CFI should also be patched in the same way. To prepare for that move the currently hardcoded vDSO start symbol into a parameter. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-3-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/kernel/alternative.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 59991922a5dc..89c283a5cec7 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -173,14 +173,14 @@ static void __init_or_module _apply_alternatives(struct alt_entry *begin, stage); } -static void __init apply_vdso_alternatives(void) +static void __init apply_vdso_alternatives(void *start) { const Elf_Ehdr *hdr; const Elf_Shdr *shdr; const Elf_Shdr *alt; struct alt_entry *begin, *end; - hdr = (Elf_Ehdr *)vdso_start; + hdr = (Elf_Ehdr *)start; shdr = (void *)hdr + hdr->e_shoff; alt = find_section(hdr, shdr, ".alternative"); if (!alt) @@ -204,7 +204,7 @@ void __init apply_boot_alternatives(void) RISCV_ALTERNATIVES_BOOT); if (IS_ENABLED(CONFIG_MMU)) - apply_vdso_alternatives(); + apply_vdso_alternatives(vdso_start); } /* From d3e0634787a234b40a740b9c398fd320a68db81c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 4 May 2026 08:30:53 +0200 Subject: [PATCH 33/36] riscv: alternative: Also patch the CFI vDSO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dedicated vDSO for CFI-enabled userspace can also contain alternative entries. Patch those, too. Fixes: ccad8c1336b6 ("arch/riscv: add dual vdso creation logic and select vdso based on hw") Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260504-riscv-cfi-vdso-alternative-v1-4-bcdf3d37f62e@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/kernel/alternative.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index 89c283a5cec7..104dc0862c5c 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -205,6 +205,9 @@ void __init apply_boot_alternatives(void) if (IS_ENABLED(CONFIG_MMU)) apply_vdso_alternatives(vdso_start); + + if (IS_ENABLED(CONFIG_RISCV_USER_CFI)) + apply_vdso_alternatives(vdso_cfi_start); } /* From bce35135fecc7a73c60aaa9d2ec699ead1e32661 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 20 May 2026 00:55:46 +0800 Subject: [PATCH 34/36] riscv: also select ARCH_KEEP_MEMBLOCK if kexec is selected On RISC-V, also select ARCH_KEEP_MEMBLOCK if kexec is selected, not only if ACPI is selected. This is because kexec requires the memblock areas to be kept after boot to initialize the secondary kernel. This is needed for both Device Tree and ACPI platforms. Signed-off-by: Han Gao Link: https://patch.msgid.link/20260519165546.123105-1-gaohan@iscas.ac.cn [pjw@kernel.org: change to add the dependency on kexec, rather than making it unconditional; rewrite the patch description accordingly] Signed-off-by: Paul Walmsley --- arch/riscv/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index ce5f4342e2f2..ca7dfbbefca0 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -56,7 +56,7 @@ config RISCV select ARCH_HAS_UBSAN select ARCH_HAS_VDSO_ARCH_DATA if HAVE_GENERIC_VDSO select ARCH_HAVE_NMI_SAFE_CMPXCHG - select ARCH_KEEP_MEMBLOCK if ACPI + select ARCH_KEEP_MEMBLOCK if ACPI || KEXEC select ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE if 64BIT && MMU select ARCH_OPTIONAL_KERNEL_RWX if ARCH_HAS_STRICT_KERNEL_RWX select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT From d585018a9258efed01514bae369ab3d4f21e7b1a Mon Sep 17 00:00:00 2001 From: Nam Cao Date: Thu, 28 May 2026 23:12:30 +0200 Subject: [PATCH 35/36] riscv: misaligned: Fix fast_unaligned_access_speed_key init When booting with unaligned_scalar_speed=fast, fast_unaligned_access_speed_key is initialized incorrectly. The key is currently derived from the fast_misaligned_access cpumask, but that mask is only populated when the unaligned access speed probe runs. Specifying unaligned_scalar_speed=fast skips the probe entirely, leaving the mask uninitialized. The information tracked by fast_misaligned_access is already available in the misaligned_access_speed per-CPU variable. Use that to initialize fast_unaligned_access_speed_key instead and remove the redundant cpumask. Signed-off-by: Nam Cao Link: https://patch.msgid.link/2468816ceb433394099a00d7822f819745276b49.1780002199.git.namcao@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/kernel/unaligned_access_speed.c | 69 +++++++--------------- 1 file changed, 22 insertions(+), 47 deletions(-) diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c index 11c781a4de73..bb57eb5d19df 100644 --- a/arch/riscv/kernel/unaligned_access_speed.c +++ b/arch/riscv/kernel/unaligned_access_speed.c @@ -27,8 +27,6 @@ DEFINE_PER_CPU(long, vector_misaligned_access) = RISCV_HWPROBE_MISALIGNED_VECTOR static long unaligned_scalar_speed_param = RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN; static long unaligned_vector_speed_param = RISCV_HWPROBE_MISALIGNED_VECTOR_UNKNOWN; -static cpumask_t fast_misaligned_access; - static u64 __maybe_unused measure_cycles(void (*func)(void *dst, const void *src, size_t len), void *dst, void *src, size_t len) @@ -131,13 +129,10 @@ static int check_unaligned_access(struct page *page) * Set the value of fast_misaligned_access of a CPU. These operations * are atomic to avoid race conditions. */ - if (ret) { + if (ret) per_cpu(misaligned_access_speed, cpu) = RISCV_HWPROBE_MISALIGNED_SCALAR_FAST; - cpumask_set_cpu(cpu, &fast_misaligned_access); - } else { + else per_cpu(misaligned_access_speed, cpu) = RISCV_HWPROBE_MISALIGNED_SCALAR_SLOW; - cpumask_clear_cpu(cpu, &fast_misaligned_access); - } return 0; } @@ -192,49 +187,24 @@ static void __init check_unaligned_access_speed_all_cpus(void) DEFINE_STATIC_KEY_FALSE(fast_unaligned_access_speed_key); -static void modify_unaligned_access_branches(cpumask_t *mask, int weight) +static void modify_unaligned_access_branches(const cpumask_t *mask) { - if (cpumask_weight(mask) == weight) + bool fast = true; + int cpu; + + for_each_cpu(cpu, mask) { + if (per_cpu(misaligned_access_speed, cpu) != RISCV_HWPROBE_MISALIGNED_SCALAR_FAST) { + fast = false; + break; + } + } + + if (fast) static_branch_enable_cpuslocked(&fast_unaligned_access_speed_key); else static_branch_disable_cpuslocked(&fast_unaligned_access_speed_key); } -static void set_unaligned_access_static_branches_except_cpu(int cpu) -{ - /* - * Same as set_unaligned_access_static_branches, except excludes the - * given CPU from the result. When a CPU is hotplugged into an offline - * state, this function is called before the CPU is set to offline in - * the cpumask, and thus the CPU needs to be explicitly excluded. - */ - - cpumask_t fast_except_me; - - cpumask_and(&fast_except_me, &fast_misaligned_access, cpu_online_mask); - cpumask_clear_cpu(cpu, &fast_except_me); - - modify_unaligned_access_branches(&fast_except_me, num_online_cpus() - 1); -} - -static void set_unaligned_access_static_branches(void) -{ - /* - * This will be called after check_unaligned_access_all_cpus so the - * result of unaligned access speed for all CPUs will be available. - * - * To avoid the number of online cpus changing between reading - * cpu_online_mask and calling num_online_cpus, cpus_read_lock must be - * held before calling this function. - */ - - cpumask_t fast_and_online; - - cpumask_and(&fast_and_online, &fast_misaligned_access, cpu_online_mask); - - modify_unaligned_access_branches(&fast_and_online, num_online_cpus()); -} - static int riscv_online_cpu(unsigned int cpu) { int ret = cpu_online_unaligned_access_init(cpu); @@ -266,14 +236,19 @@ static int riscv_online_cpu(unsigned int cpu) #endif exit: - set_unaligned_access_static_branches(); + modify_unaligned_access_branches(cpu_online_mask); return 0; } static int riscv_offline_cpu(unsigned int cpu) { - set_unaligned_access_static_branches_except_cpu(cpu); + cpumask_t mask; + + cpumask_copy(&mask, cpu_online_mask); + cpumask_clear_cpu(cpu, &mask); + + modify_unaligned_access_branches(&mask); return 0; } @@ -430,7 +405,7 @@ static int __init check_unaligned_access_all_cpus(void) riscv_online_cpu_vec, NULL); cpus_read_lock(); - set_unaligned_access_static_branches(); + modify_unaligned_access_branches(cpu_online_mask); cpus_read_unlock(); return 0; From 319fafd9a3743b617b8547d81c41c99fb67857b1 Mon Sep 17 00:00:00 2001 From: Nam Cao Date: Thu, 28 May 2026 23:12:31 +0200 Subject: [PATCH 36/36] riscv: traps_misaligned: Avoid redundant unaligned access speed probe When a CPU is taken offline and then is brought back online, unaligned access speed probe always runs even though the unaligned access speed is already known, wasting CPU cycles. This is because when a CPU becomes online, the following happen: 1. check_unaligned_access_emulated() is called, which clears misaligned_access_speed if there is no emulation. 2. check_unaligned_access() is called because misaligned_access_speed is cleared, wasting CPU cycles determining something already previous known. Avoid the redundant access speed probe by stop clearing misaligned_access_speed in (1). If access speed is already known, just reuse it. On my Visionfive 2, this reduces CPU bring-up time from 26ms to 0.8ms. Signed-off-by: Nam Cao Link: https://patch.msgid.link/aa5755142537d462a9e3d2074d82ad4eef6774ba.1780002199.git.namcao@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/kernel/traps_misaligned.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kernel/traps_misaligned.c b/arch/riscv/kernel/traps_misaligned.c index 81b7682e6c6d..6e8ae6c66322 100644 --- a/arch/riscv/kernel/traps_misaligned.c +++ b/arch/riscv/kernel/traps_misaligned.c @@ -522,10 +522,10 @@ static bool unaligned_ctl __read_mostly; static void check_unaligned_access_emulated(void *arg __always_unused) { int cpu = smp_processor_id(); - long *mas_ptr = per_cpu_ptr(&misaligned_access_speed, cpu); unsigned long tmp_var, tmp_val; - *mas_ptr = RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN; + if (per_cpu(misaligned_access_speed, cpu) != RISCV_HWPROBE_MISALIGNED_SCALAR_UNKNOWN) + return; __asm__ __volatile__ ( " "REG_L" %[tmp], 1(%[ptr])\n"