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