Skip to content

Commit 26e7aac

Browse files
Alexandre Ghitipalmer-dabbelt
authored andcommitted
riscv: Allow to downgrade paging mode from the command line
Add 2 early command line parameters that allow to downgrade satp mode (using the same naming as x86): - "no5lvl": use a 4-level page table (down from sv57 to sv48) - "no4lvl": use a 3-level page table (down from sv57/sv48 to sv39) Note that going through the device tree to get the kernel command line works with ACPI too since the efi stub creates a device tree anyway with the command line. In KASAN kernels, we can't use the libfdt that early in the boot process since we are not ready to execute instrumented functions. So instead of using the "generic" libfdt, we compile our own versions of those functions that are not instrumented and that are prefixed so that they do not conflict with the generic ones. We also need the non-instrumented versions of the string functions and the prefixed versions of memcpy/memmove. This is largely inspired by commit aacd149 ("arm64: head: avoid relocating the kernel twice for KASLR") from which I removed compilation flags that were not relevant to RISC-V at the moment (LTO, SCS). Also note that we have to link with -z norelro to avoid ld.lld to throw a warning with the new .got sections, like in commit 311bea3 ("arm64: link with -z norelro for LLD or aarch64-elf"). Signed-off-by: Alexandre Ghiti <alexghiti@rivosinc.com> Tested-by: Björn Töpel <bjorn@rivosinc.com> Reviewed-by: Björn Töpel <bjorn@rivosinc.com> Link: https://lore.kernel.org/r/20230424092313.178699-2-alexghiti@rivosinc.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
1 parent d4dda69 commit 26e7aac

10 files changed

Lines changed: 154 additions & 9 deletions

File tree

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3576,7 +3576,10 @@
35763576
emulation library even if a 387 maths coprocessor
35773577
is present.
35783578

3579-
no5lvl [X86-64] Disable 5-level paging mode. Forces
3579+
no4lvl [RISCV] Disable 4-level and 5-level paging modes. Forces
3580+
kernel to use 3-level paging instead.
3581+
3582+
no5lvl [X86-64,RISCV] Disable 5-level paging mode. Forces
35803583
kernel to use 4-level paging instead.
35813584

35823585
nofsgsbase [X86] Disables FSGSBASE instructions.

arch/riscv/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
#
88

99
OBJCOPYFLAGS := -O binary
10+
LDFLAGS_vmlinux := -z norelro
1011
ifeq ($(CONFIG_RELOCATABLE),y)
11-
LDFLAGS_vmlinux += -shared -Bsymbolic -z notext -z norelro --emit-relocs
12+
LDFLAGS_vmlinux += -shared -Bsymbolic -z notext --emit-relocs
1213
KBUILD_CFLAGS += -fPIE
1314
endif
1415
ifeq ($(CONFIG_DYNAMIC_FTRACE),y)

arch/riscv/kernel/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,5 @@ obj-$(CONFIG_EFI) += efi.o
8787
obj-$(CONFIG_COMPAT) += compat_syscall_table.o
8888
obj-$(CONFIG_COMPAT) += compat_signal.o
8989
obj-$(CONFIG_COMPAT) += compat_vdso/
90+
91+
obj-$(CONFIG_64BIT) += pi/

arch/riscv/kernel/pi/Makefile

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
# This file was copied from arm64/kernel/pi/Makefile.
3+
4+
KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) -fpie \
5+
-Os -DDISABLE_BRANCH_PROFILING $(DISABLE_STACKLEAK_PLUGIN) \
6+
$(call cc-option,-mbranch-protection=none) \
7+
-I$(srctree)/scripts/dtc/libfdt -fno-stack-protector \
8+
-D__DISABLE_EXPORTS -ffreestanding \
9+
-fno-asynchronous-unwind-tables -fno-unwind-tables \
10+
$(call cc-option,-fno-addrsig)
11+
12+
KBUILD_CFLAGS += -mcmodel=medany
13+
14+
CFLAGS_cmdline_early.o += -D__NO_FORTIFY
15+
CFLAGS_lib-fdt_ro.o += -D__NO_FORTIFY
16+
17+
GCOV_PROFILE := n
18+
KASAN_SANITIZE := n
19+
KCSAN_SANITIZE := n
20+
UBSAN_SANITIZE := n
21+
KCOV_INSTRUMENT := n
22+
23+
$(obj)/%.pi.o: OBJCOPYFLAGS := --prefix-symbols=__pi_ \
24+
--remove-section=.note.gnu.property \
25+
--prefix-alloc-sections=.init
26+
$(obj)/%.pi.o: $(obj)/%.o FORCE
27+
$(call if_changed,objcopy)
28+
29+
$(obj)/lib-%.o: $(srctree)/lib/%.c FORCE
30+
$(call if_changed_rule,cc_o_c)
31+
32+
$(obj)/string.o: $(srctree)/lib/string.c FORCE
33+
$(call if_changed_rule,cc_o_c)
34+
35+
$(obj)/ctype.o: $(srctree)/lib/ctype.c FORCE
36+
$(call if_changed_rule,cc_o_c)
37+
38+
obj-y := cmdline_early.pi.o string.pi.o ctype.pi.o lib-fdt.pi.o lib-fdt_ro.pi.o
39+
extra-y := $(patsubst %.pi.o,%.o,$(obj-y))
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
#include <linux/types.h>
3+
#include <linux/init.h>
4+
#include <linux/libfdt.h>
5+
#include <linux/string.h>
6+
#include <asm/pgtable.h>
7+
#include <asm/setup.h>
8+
9+
static char early_cmdline[COMMAND_LINE_SIZE];
10+
11+
/*
12+
* Declare the functions that are exported (but prefixed) here so that LLVM
13+
* does not complain it lacks the 'static' keyword (which, if added, makes
14+
* LLVM complain because the function is actually unused in this file).
15+
*/
16+
u64 set_satp_mode_from_cmdline(uintptr_t dtb_pa);
17+
18+
static char *get_early_cmdline(uintptr_t dtb_pa)
19+
{
20+
const char *fdt_cmdline = NULL;
21+
unsigned int fdt_cmdline_size = 0;
22+
int chosen_node;
23+
24+
if (!IS_ENABLED(CONFIG_CMDLINE_FORCE)) {
25+
chosen_node = fdt_path_offset((void *)dtb_pa, "/chosen");
26+
if (chosen_node >= 0) {
27+
fdt_cmdline = fdt_getprop((void *)dtb_pa, chosen_node,
28+
"bootargs", NULL);
29+
if (fdt_cmdline) {
30+
fdt_cmdline_size = strlen(fdt_cmdline);
31+
strscpy(early_cmdline, fdt_cmdline,
32+
COMMAND_LINE_SIZE);
33+
}
34+
}
35+
}
36+
37+
if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) ||
38+
IS_ENABLED(CONFIG_CMDLINE_FORCE) ||
39+
fdt_cmdline_size == 0 /* CONFIG_CMDLINE_FALLBACK */) {
40+
strncat(early_cmdline, CONFIG_CMDLINE,
41+
COMMAND_LINE_SIZE - fdt_cmdline_size);
42+
}
43+
44+
return early_cmdline;
45+
}
46+
47+
static u64 match_noXlvl(char *cmdline)
48+
{
49+
if (strstr(cmdline, "no4lvl"))
50+
return SATP_MODE_48;
51+
else if (strstr(cmdline, "no5lvl"))
52+
return SATP_MODE_57;
53+
54+
return 0;
55+
}
56+
57+
u64 set_satp_mode_from_cmdline(uintptr_t dtb_pa)
58+
{
59+
char *cmdline = get_early_cmdline(dtb_pa);
60+
61+
return match_noXlvl(cmdline);
62+
}

arch/riscv/kernel/vmlinux.lds.S

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ SECTIONS
8383
/* Start of init data section */
8484
__init_data_begin = .;
8585
INIT_DATA_SECTION(16)
86+
87+
/* Those sections result from the compilation of kernel/pi/string.c */
88+
.init.pidata : {
89+
*(.init.srodata.cst8*)
90+
*(.init__bug_table*)
91+
*(.init.sdata*)
92+
}
93+
8694
.init.bss : {
8795
*(.init.bss) /* from the EFI stub */
8896
}
@@ -128,9 +136,10 @@ SECTIONS
128136
__rela_dyn_end = .;
129137
}
130138

139+
.got : { *(.got*) }
140+
131141
#ifdef CONFIG_RELOCATABLE
132142
.data.rel : { *(.data.rel*) }
133-
.got : { *(.got*) }
134143
.plt : { *(.plt) }
135144
.dynamic : { *(.dynamic) }
136145
.dynsym : { *(.dynsym) }

arch/riscv/lib/memcpy.S

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,5 @@ WEAK(memcpy)
106106
6:
107107
ret
108108
END(__memcpy)
109+
SYM_FUNC_ALIAS(__pi_memcpy, __memcpy)
110+
SYM_FUNC_ALIAS(__pi___memcpy, __memcpy)

arch/riscv/lib/memmove.S

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,5 @@ return_from_memmove:
314314

315315
SYM_FUNC_END(memmove)
316316
SYM_FUNC_END(__memmove)
317+
SYM_FUNC_ALIAS(__pi_memmove, __memmove)
318+
SYM_FUNC_ALIAS(__pi___memmove, __memmove)

arch/riscv/lib/strlen.S

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,4 @@ strlen_zbb:
130130
.option pop
131131
#endif
132132
SYM_FUNC_END(strlen)
133+
SYM_FUNC_ALIAS(__pi_strlen, strlen)

arch/riscv/mm/init.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,8 @@ static __init pgprot_t pgprot_from_va(uintptr_t va)
746746
#endif /* CONFIG_STRICT_KERNEL_RWX */
747747

748748
#if defined(CONFIG_64BIT) && !defined(CONFIG_XIP_KERNEL)
749+
u64 __pi_set_satp_mode_from_cmdline(uintptr_t dtb_pa);
750+
749751
static void __init disable_pgtable_l5(void)
750752
{
751753
pgtable_l5_enabled = false;
@@ -760,17 +762,39 @@ static void __init disable_pgtable_l4(void)
760762
satp_mode = SATP_MODE_39;
761763
}
762764

765+
static int __init print_no4lvl(char *p)
766+
{
767+
pr_info("Disabled 4-level and 5-level paging");
768+
return 0;
769+
}
770+
early_param("no4lvl", print_no4lvl);
771+
772+
static int __init print_no5lvl(char *p)
773+
{
774+
pr_info("Disabled 5-level paging");
775+
return 0;
776+
}
777+
early_param("no5lvl", print_no5lvl);
778+
763779
/*
764780
* There is a simple way to determine if 4-level is supported by the
765781
* underlying hardware: establish 1:1 mapping in 4-level page table mode
766782
* then read SATP to see if the configuration was taken into account
767783
* meaning sv48 is supported.
768784
*/
769-
static __init void set_satp_mode(void)
785+
static __init void set_satp_mode(uintptr_t dtb_pa)
770786
{
771787
u64 identity_satp, hw_satp;
772788
uintptr_t set_satp_mode_pmd = ((unsigned long)set_satp_mode) & PMD_MASK;
773-
bool check_l4 = false;
789+
u64 satp_mode_cmdline = __pi_set_satp_mode_from_cmdline(dtb_pa);
790+
791+
if (satp_mode_cmdline == SATP_MODE_57) {
792+
disable_pgtable_l5();
793+
} else if (satp_mode_cmdline == SATP_MODE_48) {
794+
disable_pgtable_l5();
795+
disable_pgtable_l4();
796+
return;
797+
}
774798

775799
create_p4d_mapping(early_p4d,
776800
set_satp_mode_pmd, (uintptr_t)early_pud,
@@ -789,7 +813,8 @@ static __init void set_satp_mode(void)
789813
retry:
790814
create_pgd_mapping(early_pg_dir,
791815
set_satp_mode_pmd,
792-
check_l4 ? (uintptr_t)early_pud : (uintptr_t)early_p4d,
816+
pgtable_l5_enabled ?
817+
(uintptr_t)early_p4d : (uintptr_t)early_pud,
793818
PGDIR_SIZE, PAGE_TABLE);
794819

795820
identity_satp = PFN_DOWN((uintptr_t)&early_pg_dir) | satp_mode;
@@ -800,9 +825,8 @@ static __init void set_satp_mode(void)
800825
local_flush_tlb_all();
801826

802827
if (hw_satp != identity_satp) {
803-
if (!check_l4) {
828+
if (pgtable_l5_enabled) {
804829
disable_pgtable_l5();
805-
check_l4 = true;
806830
memset(early_pg_dir, 0, PAGE_SIZE);
807831
goto retry;
808832
}
@@ -1031,7 +1055,7 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
10311055
#endif
10321056

10331057
#if defined(CONFIG_64BIT) && !defined(CONFIG_XIP_KERNEL)
1034-
set_satp_mode();
1058+
set_satp_mode(dtb_pa);
10351059
#endif
10361060

10371061
/*

0 commit comments

Comments
 (0)