xref: /linux/arch/riscv/kernel/pi/fdt_early.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
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 
7 #include "pi.h"
8 
get_kaslr_seed(uintptr_t dtb_pa)9 u64 get_kaslr_seed(uintptr_t dtb_pa)
10 {
11 	int node, len;
12 	fdt64_t *prop;
13 	u64 ret;
14 
15 	node = fdt_path_offset((void *)dtb_pa, "/chosen");
16 	if (node < 0)
17 		return 0;
18 
19 	prop = fdt_getprop_w((void *)dtb_pa, node, "kaslr-seed", &len);
20 	if (!prop || len != sizeof(u64))
21 		return 0;
22 
23 	ret = fdt64_to_cpu(*prop);
24 	*prop = 0;
25 	return ret;
26 }
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  */
fdt_device_is_available(const void * fdt,int node)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_ */
fdt_node_name_eq(const void * fdt,int offset,const char * s)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  */
isa_string_contains(const char * isa_str,const char * ext_name)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  */
early_cpu_isa_ext_available(const void * fdt,int node,const char * ext_name)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  */
fdt_early_match_extension_isa(const void * fdt,const char * ext_name)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 }
186