xref: /linux/arch/riscv/kernel/pi/fdt_early.c (revision cb7e3669c683669d93139184adff68a7d9000536)
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/ctype.h>
6 #include <asm/csr.h>
7 
8 #include "pi.h"
9 
get_kaslr_seed(uintptr_t dtb_pa)10 u64 get_kaslr_seed(uintptr_t dtb_pa)
11 {
12 	int node, len;
13 	fdt64_t *prop;
14 	u64 ret;
15 
16 	node = fdt_path_offset((void *)dtb_pa, "/chosen");
17 	if (node < 0)
18 		return 0;
19 
20 	prop = fdt_getprop_w((void *)dtb_pa, node, "kaslr-seed", &len);
21 	if (!prop || len != sizeof(u64))
22 		return 0;
23 
24 	ret = fdt64_to_cpu(*prop);
25 	*prop = 0;
26 	return ret;
27 }
28 
29 /**
30  *  fdt_device_is_available - check if a device is available for use
31  *
32  * @fdt: pointer to the device tree blob
33  * @node: offset of the node whose property to find
34  *
35  *  Returns true if the status property is absent or set to "okay" or "ok",
36  *  false otherwise
37  */
fdt_device_is_available(const void * fdt,int node)38 static bool fdt_device_is_available(const void *fdt, int node)
39 {
40 	const char *status;
41 	int statlen;
42 
43 	status = fdt_getprop(fdt, node, "status", &statlen);
44 	if (!status)
45 		return true;
46 
47 	if (statlen > 0) {
48 		if (!strcmp(status, "okay") || !strcmp(status, "ok"))
49 			return true;
50 	}
51 
52 	return false;
53 }
54 
55 /* Copy of fdt_nodename_eq_ */
fdt_node_name_eq(const void * fdt,int offset,const char * s)56 static int fdt_node_name_eq(const void *fdt, int offset,
57 			    const char *s)
58 {
59 	int olen;
60 	int len = strlen(s);
61 	const char *p = fdt_get_name(fdt, offset, &olen);
62 
63 	if (!p || olen < len)
64 		/* short match */
65 		return 0;
66 
67 	if (memcmp(p, s, len) != 0)
68 		return 0;
69 
70 	if (p[len] == '\0')
71 		return 1;
72 	else if (!memchr(s, '@', len) && (p[len] == '@'))
73 		return 1;
74 	else
75 		return 0;
76 }
77 
78 /**
79  *  isa_string_contains - check if isa string contains an extension
80  *
81  * @isa_str: isa string to search
82  * @ext_name: the extension to search for
83  *
84  *  Returns true if the extension is in the given isa string,
85  *  false otherwise
86  */
isa_string_contains(const char * isa_str,const char * ext_name)87 static bool isa_string_contains(const char *isa_str, const char *ext_name)
88 {
89 	size_t i, single_end, len = strlen(ext_name);
90 	char ext_end;
91 
92 	/* Error must contain rv32/64 */
93 	if (strlen(isa_str) < 4)
94 		return false;
95 
96 	if (len == 1) {
97 		single_end = strcspn(isa_str, "sSxXzZ");
98 		/* Search for single chars between rv32/64 and multi-letter extensions */
99 		for (i = 4; i < single_end; i++) {
100 			if (tolower(isa_str[i]) == ext_name[0])
101 				return true;
102 		}
103 		return false;
104 	}
105 
106 	/* Skip to start of multi-letter extensions */
107 	isa_str = strpbrk(isa_str, "sSxXzZ");
108 	while (isa_str) {
109 		if (strncasecmp(isa_str, ext_name, len) == 0) {
110 			ext_end = isa_str[len];
111 			/* Check if matches the whole extension. */
112 			if (ext_end == '\0' || ext_end == '_')
113 				return true;
114 		}
115 		/* Multi-letter extensions must be split from other multi-letter
116 		 * extensions with an "_", the end of a multi-letter extension will
117 		 * either be the null character or the "_" at the start of the next
118 		 * multi-letter extension.
119 		 */
120 		isa_str = strchr(isa_str, '_');
121 		if (isa_str)
122 			isa_str++;
123 	}
124 
125 	return false;
126 }
127 
128 /**
129  *  early_cpu_isa_ext_available - check if cpu node has an extension
130  *
131  * @fdt: pointer to the device tree blob
132  * @node: offset of the cpu node
133  * @ext_name: the extension to search for
134  *
135  *  Returns true if the cpu node has the extension,
136  *  false otherwise
137  */
early_cpu_isa_ext_available(const void * fdt,int node,const char * ext_name)138 static bool early_cpu_isa_ext_available(const void *fdt, int node, const char *ext_name)
139 {
140 	const void *prop;
141 	int len;
142 
143 	prop = fdt_getprop(fdt, node, "riscv,isa-extensions", &len);
144 	if (prop && fdt_stringlist_contains(prop, len, ext_name))
145 		return true;
146 
147 	prop = fdt_getprop(fdt, node, "riscv,isa", &len);
148 	if (prop && isa_string_contains(prop, ext_name))
149 		return true;
150 
151 	return false;
152 }
153 
154 /**
155  *  fdt_early_match_extension_isa - check if all cpu nodes have an extension
156  *
157  * @fdt: pointer to the device tree blob
158  * @ext_name: the extension to search for
159  *
160  *  Returns true if the all available the cpu nodes have the extension,
161  *  false otherwise
162  */
fdt_early_match_extension_isa(const void * fdt,const char * ext_name)163 bool fdt_early_match_extension_isa(const void *fdt, const char *ext_name)
164 {
165 	int node, parent;
166 	bool ret = false;
167 
168 	parent = fdt_path_offset(fdt, "/cpus");
169 	if (parent < 0)
170 		return false;
171 
172 	fdt_for_each_subnode(node, fdt, parent) {
173 		if (!fdt_node_name_eq(fdt, node, "cpu"))
174 			continue;
175 
176 		if (!fdt_device_is_available(fdt, node))
177 			continue;
178 
179 		if (!early_cpu_isa_ext_available(fdt, node, ext_name))
180 			return false;
181 
182 		ret = true;
183 	}
184 
185 	return ret;
186 }
187 
188 /**
189  *  set_satp_mode_from_fdt - determine SATP mode based on the MMU type in fdt
190  *
191  * @dtb_pa: physical address of the device tree blob
192  *
193  *  Returns the SATP mode corresponding to the MMU type of the first enabled CPU,
194  *  0 otherwise
195  */
set_satp_mode_from_fdt(uintptr_t dtb_pa)196 u64 set_satp_mode_from_fdt(uintptr_t dtb_pa)
197 {
198 	const void *fdt = (const void *)dtb_pa;
199 	const char *mmu_type;
200 	int node, parent;
201 
202 	parent = fdt_path_offset(fdt, "/cpus");
203 	if (parent < 0)
204 		return 0;
205 
206 	fdt_for_each_subnode(node, fdt, parent) {
207 		if (!fdt_node_name_eq(fdt, node, "cpu"))
208 			continue;
209 
210 		if (!fdt_device_is_available(fdt, node))
211 			continue;
212 
213 		mmu_type = fdt_getprop(fdt, node, "mmu-type", NULL);
214 		if (!mmu_type)
215 			break;
216 
217 		if (!strcmp(mmu_type, "riscv,sv39"))
218 			return SATP_MODE_39;
219 		else if (!strcmp(mmu_type, "riscv,sv48"))
220 			return SATP_MODE_48;
221 		break;
222 	}
223 
224 	return 0;
225 }
226