Skip to content

Commit 945302d

Browse files
Mr-Bossmanpalmer-dabbelt
authored andcommitted
RISC-V: Use Zkr to seed KASLR base address
Parse the device tree for Zkr in the isa string. If Zkr is present, use it to seed the kernel base address. On an ACPI system, as of this commit, there is no easy way to check if Zkr is present. Blindly running the instruction isn't an option as; we have to be able to trust the firmware. Signed-off-by: Jesse Taube <[email protected]> Reviewed-by: Charlie Jenkins <[email protected]> Reviewed-by: Alexandre Ghiti <[email protected]> Tested-by: Zong Li <[email protected]> Reviewed-by: Conor Dooley <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent b331182 commit 945302d

File tree

5 files changed

+199
-2
lines changed

5 files changed

+199
-2
lines changed

arch/riscv/kernel/pi/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ KBUILD_CFLAGS += -mcmodel=medany
1717

1818
CFLAGS_cmdline_early.o += -D__NO_FORTIFY
1919
CFLAGS_lib-fdt_ro.o += -D__NO_FORTIFY
20+
CFLAGS_fdt_early.o += -D__NO_FORTIFY
2021

2122
$(obj)/%.pi.o: OBJCOPYFLAGS := --prefix-symbols=__pi_ \
2223
--remove-section=.note.gnu.property \
@@ -33,5 +34,5 @@ $(obj)/string.o: $(srctree)/lib/string.c FORCE
3334
$(obj)/ctype.o: $(srctree)/lib/ctype.c FORCE
3435
$(call if_changed_rule,cc_o_c)
3536

36-
obj-y := cmdline_early.pi.o fdt_early.pi.o string.pi.o ctype.pi.o lib-fdt.pi.o lib-fdt_ro.pi.o
37+
obj-y := cmdline_early.pi.o fdt_early.pi.o string.pi.o ctype.pi.o lib-fdt.pi.o lib-fdt_ro.pi.o archrandom_early.pi.o
3738
extra-y := $(patsubst %.pi.o,%.o,$(obj-y))
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
3+
#include <asm/csr.h>
4+
#include <linux/processor.h>
5+
6+
#include "pi.h"
7+
8+
/*
9+
* To avoid rewriting code include asm/archrandom.h and create macros
10+
* for the functions that won't be included.
11+
*/
12+
#undef riscv_has_extension_unlikely
13+
#define riscv_has_extension_likely(...) false
14+
#undef pr_err_once
15+
#define pr_err_once(...)
16+
17+
#include <asm/archrandom.h>
18+
19+
u64 get_kaslr_seed_zkr(const uintptr_t dtb_pa)
20+
{
21+
unsigned long seed = 0;
22+
23+
if (!fdt_early_match_extension_isa((const void *)dtb_pa, "zkr"))
24+
return 0;
25+
26+
if (!csr_seed_long(&seed))
27+
return 0;
28+
29+
return seed;
30+
}

arch/riscv/kernel/pi/fdt_early.c

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <linux/types.h>
33
#include <linux/init.h>
44
#include <linux/libfdt.h>
5+
#include <linux/ctype.h>
56

67
#include "pi.h"
78

@@ -23,3 +24,162 @@ u64 get_kaslr_seed(uintptr_t dtb_pa)
2324
*prop = 0;
2425
return ret;
2526
}
27+
28+
/**
29+
* fdt_device_is_available - check if a device is available for use
30+
*
31+
* @fdt: pointer to the device tree blob
32+
* @node: offset of the node whose property to find
33+
*
34+
* Returns true if the status property is absent or set to "okay" or "ok",
35+
* false otherwise
36+
*/
37+
static bool fdt_device_is_available(const void *fdt, int node)
38+
{
39+
const char *status;
40+
int statlen;
41+
42+
status = fdt_getprop(fdt, node, "status", &statlen);
43+
if (!status)
44+
return true;
45+
46+
if (statlen > 0) {
47+
if (!strcmp(status, "okay") || !strcmp(status, "ok"))
48+
return true;
49+
}
50+
51+
return false;
52+
}
53+
54+
/* Copy of fdt_nodename_eq_ */
55+
static int fdt_node_name_eq(const void *fdt, int offset,
56+
const char *s)
57+
{
58+
int olen;
59+
int len = strlen(s);
60+
const char *p = fdt_get_name(fdt, offset, &olen);
61+
62+
if (!p || olen < len)
63+
/* short match */
64+
return 0;
65+
66+
if (memcmp(p, s, len) != 0)
67+
return 0;
68+
69+
if (p[len] == '\0')
70+
return 1;
71+
else if (!memchr(s, '@', len) && (p[len] == '@'))
72+
return 1;
73+
else
74+
return 0;
75+
}
76+
77+
/**
78+
* isa_string_contains - check if isa string contains an extension
79+
*
80+
* @isa_str: isa string to search
81+
* @ext_name: the extension to search for
82+
*
83+
* Returns true if the extension is in the given isa string,
84+
* false otherwise
85+
*/
86+
static bool isa_string_contains(const char *isa_str, const char *ext_name)
87+
{
88+
size_t i, single_end, len = strlen(ext_name);
89+
char ext_end;
90+
91+
/* Error must contain rv32/64 */
92+
if (strlen(isa_str) < 4)
93+
return false;
94+
95+
if (len == 1) {
96+
single_end = strcspn(isa_str, "sSxXzZ");
97+
/* Search for single chars between rv32/64 and multi-letter extensions */
98+
for (i = 4; i < single_end; i++) {
99+
if (tolower(isa_str[i]) == ext_name[0])
100+
return true;
101+
}
102+
return false;
103+
}
104+
105+
/* Skip to start of multi-letter extensions */
106+
isa_str = strpbrk(isa_str, "sSxXzZ");
107+
while (isa_str) {
108+
if (strncasecmp(isa_str, ext_name, len) == 0) {
109+
ext_end = isa_str[len];
110+
/* Check if matches the whole extension. */
111+
if (ext_end == '\0' || ext_end == '_')
112+
return true;
113+
}
114+
/* Multi-letter extensions must be split from other multi-letter
115+
* extensions with an "_", the end of a multi-letter extension will
116+
* either be the null character or the "_" at the start of the next
117+
* multi-letter extension.
118+
*/
119+
isa_str = strchr(isa_str, '_');
120+
if (isa_str)
121+
isa_str++;
122+
}
123+
124+
return false;
125+
}
126+
127+
/**
128+
* early_cpu_isa_ext_available - check if cpu node has an extension
129+
*
130+
* @fdt: pointer to the device tree blob
131+
* @node: offset of the cpu node
132+
* @ext_name: the extension to search for
133+
*
134+
* Returns true if the cpu node has the extension,
135+
* false otherwise
136+
*/
137+
static bool early_cpu_isa_ext_available(const void *fdt, int node, const char *ext_name)
138+
{
139+
const void *prop;
140+
int len;
141+
142+
prop = fdt_getprop(fdt, node, "riscv,isa-extensions", &len);
143+
if (prop && fdt_stringlist_contains(prop, len, ext_name))
144+
return true;
145+
146+
prop = fdt_getprop(fdt, node, "riscv,isa", &len);
147+
if (prop && isa_string_contains(prop, ext_name))
148+
return true;
149+
150+
return false;
151+
}
152+
153+
/**
154+
* fdt_early_match_extension_isa - check if all cpu nodes have an extension
155+
*
156+
* @fdt: pointer to the device tree blob
157+
* @ext_name: the extension to search for
158+
*
159+
* Returns true if the all available the cpu nodes have the extension,
160+
* false otherwise
161+
*/
162+
bool fdt_early_match_extension_isa(const void *fdt, const char *ext_name)
163+
{
164+
int node, parent;
165+
bool ret = false;
166+
167+
parent = fdt_path_offset(fdt, "/cpus");
168+
if (parent < 0)
169+
return false;
170+
171+
fdt_for_each_subnode(node, fdt, parent) {
172+
if (!fdt_node_name_eq(fdt, node, "cpu"))
173+
continue;
174+
175+
if (!fdt_device_is_available(fdt, node))
176+
continue;
177+
178+
if (!early_cpu_isa_ext_available(fdt, node, ext_name))
179+
return false;
180+
181+
ret = true;
182+
}
183+
184+
return ret;
185+
}

arch/riscv/kernel/pi/pi.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
*/
1212

1313
u64 get_kaslr_seed(uintptr_t dtb_pa);
14+
u64 get_kaslr_seed_zkr(const uintptr_t dtb_pa);
1415
bool set_nokaslr_from_cmdline(uintptr_t dtb_pa);
1516
u64 set_satp_mode_from_cmdline(uintptr_t dtb_pa);
1617

18+
bool fdt_early_match_extension_isa(const void *fdt, const char *ext_name);
19+
1720
#endif /* _RISCV_PI_H_ */

arch/riscv/mm/init.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,7 @@ static void __init pt_ops_set_late(void)
10421042
#ifdef CONFIG_RANDOMIZE_BASE
10431043
extern bool __init __pi_set_nokaslr_from_cmdline(uintptr_t dtb_pa);
10441044
extern u64 __init __pi_get_kaslr_seed(uintptr_t dtb_pa);
1045+
extern u64 __init __pi_get_kaslr_seed_zkr(const uintptr_t dtb_pa);
10451046

10461047
static int __init print_nokaslr(char *p)
10471048
{
@@ -1062,10 +1063,12 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa)
10621063

10631064
#ifdef CONFIG_RANDOMIZE_BASE
10641065
if (!__pi_set_nokaslr_from_cmdline(dtb_pa)) {
1065-
u64 kaslr_seed = __pi_get_kaslr_seed(dtb_pa);
1066+
u64 kaslr_seed = __pi_get_kaslr_seed_zkr(dtb_pa);
10661067
u32 kernel_size = (uintptr_t)(&_end) - (uintptr_t)(&_start);
10671068
u32 nr_pos;
10681069

1070+
if (kaslr_seed == 0)
1071+
kaslr_seed = __pi_get_kaslr_seed(dtb_pa);
10691072
/*
10701073
* Compute the number of positions available: we are limited
10711074
* by the early page table that only has one PUD and we must

0 commit comments

Comments
 (0)