Skip to content

Commit 1c40deb

Browse files
committed
lib: utils/fdt: Add ISA extension validation framework for CPU fixup
Extend the existing MISA-based single-letter ISA fixup into a table-driven validation framework for multi-letter extensions. Extensions declared in DT but missing required properties (e.g. zicbom without riscv,cbom-block-size) are now removed during fixup. New validators: H (MISA check), zicbom (cbom-block-size presence). Adding future validators requires only appending to isa_ext_validators[]. Signed-off-by: Chen Pei <cp0613@linux.alibaba.com>
1 parent 4364526 commit 1c40deb

1 file changed

Lines changed: 151 additions & 0 deletions

File tree

lib/utils/fdt/fdt_fixup.c

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99
*/
1010

1111
#include <libfdt.h>
12+
#include <sbi/riscv_asm.h>
1213
#include <sbi/sbi_console.h>
1314
#include <sbi/sbi_domain.h>
1415
#include <sbi/sbi_math.h>
1516
#include <sbi/sbi_hart.h>
1617
#include <sbi/sbi_scratch.h>
1718
#include <sbi/sbi_string.h>
1819
#include <sbi/sbi_error.h>
20+
#include <sbi/sbi_heap.h>
1921
#include <sbi/sbi_timer.h>
2022
#include <sbi_utils/fdt/fdt_fixup.h>
2123
#include <sbi_utils/fdt/fdt_pmu.h>
@@ -106,6 +108,153 @@ int fdt_add_cpu_idle_states(void *fdt, const struct sbi_cpu_idle_state *state)
106108
return 0;
107109
}
108110

111+
struct isa_ext_validate_entry {
112+
const char *name;
113+
bool (*validate)(void *fdt, int cpu_offset);
114+
};
115+
116+
static bool isa_ext_h_validate(void *fdt, int cpu_offset)
117+
{
118+
(void)fdt;
119+
(void)cpu_offset;
120+
return misa_extension_imp('H');
121+
}
122+
123+
static bool isa_ext_zicbom_validate(void *fdt, int cpu_offset)
124+
{
125+
unsigned long block_size;
126+
return fdt_parse_cbom_block_size(fdt, cpu_offset, &block_size) == 0;
127+
}
128+
129+
static const struct isa_ext_validate_entry isa_ext_validators[] = {
130+
{ "h", isa_ext_h_validate },
131+
{ "zicbom", isa_ext_zicbom_validate },
132+
};
133+
134+
static bool isa_ext_is_valid(const char *name, void *fdt, int cpu_offset)
135+
{
136+
for (int i = 0; i < (int)array_size(isa_ext_validators); i++) {
137+
if (sbi_strcmp(name, isa_ext_validators[i].name) == 0)
138+
return isa_ext_validators[i].validate(fdt, cpu_offset);
139+
}
140+
return true;
141+
}
142+
143+
/*
144+
* Fix up CPU ISA properties based on MISA and extension validation.
145+
* Remove single-letter extensions not present in MISA, and multi-letter
146+
* extensions that fail validation (e.g. missing required DT properties).
147+
*
148+
* Assumes all harts have the same MISA value (homogeneous system).
149+
*/
150+
static void fdt_fixup_cpu_isa(void *fdt, int cpu_offset)
151+
{
152+
const char *isa;
153+
int len, i, j;
154+
size_t buf_size;
155+
char *new_isa;
156+
157+
/* Fix riscv,isa property */
158+
isa = fdt_getprop(fdt, cpu_offset, "riscv,isa", &len);
159+
if (isa && len > 0) {
160+
buf_size = 1UL << log2roundup(len);
161+
new_isa = sbi_zalloc(buf_size);
162+
if (!new_isa)
163+
goto fix_extensions;
164+
165+
j = 0;
166+
167+
/* Copy "rv32" or "rv64" prefix */
168+
for (i = 0; i < len - 1 && i < 4; i++)
169+
new_isa[j++] = isa[i];
170+
171+
for (; i < len - 1 && j < (int)buf_size - 1; i++) {
172+
char c = isa[i];
173+
174+
if (c == '_') {
175+
int end = len - 1;
176+
177+
i++;
178+
while (i < end) {
179+
int tok_start = i;
180+
181+
while (i < end && isa[i] != '_')
182+
i++;
183+
184+
int tok_len = i - tok_start;
185+
char name[64];
186+
187+
if (tok_len > 0 && tok_len < (int)sizeof(name)) {
188+
sbi_memcpy(name, isa + tok_start, tok_len);
189+
name[tok_len] = '\0';
190+
if (isa_ext_is_valid(name, fdt, cpu_offset)) {
191+
new_isa[j++] = '_';
192+
sbi_memcpy(new_isa + j, isa + tok_start, tok_len);
193+
j += tok_len;
194+
} else
195+
sbi_printf("fdt: ISA fixup: removed \"%s\" (validation failed)\n", name);
196+
}
197+
198+
if (i < end && isa[i] == '_')
199+
i++;
200+
}
201+
break;
202+
} else if (c >= 'a' && c <= 'z') {
203+
if (misa_extension_imp(c - 'a' + 'A'))
204+
new_isa[j++] = c;
205+
else
206+
sbi_printf("fdt: ISA fixup: removed '%c' (MISA bit %d not set)\n", c, c - 'a');
207+
}
208+
}
209+
new_isa[j] = '\0';
210+
211+
if (sbi_strcmp(new_isa, isa) != 0)
212+
fdt_setprop_string(fdt, cpu_offset, "riscv,isa", new_isa);
213+
sbi_free(new_isa);
214+
}
215+
216+
fix_extensions:
217+
/* Fix riscv,isa-extensions property (stringlist) */
218+
isa = fdt_getprop(fdt, cpu_offset, "riscv,isa-extensions", &len);
219+
if (isa && len > 0) {
220+
char *new_ext;
221+
int new_len = 0;
222+
223+
buf_size = 1UL << log2roundup(len);
224+
new_ext = sbi_zalloc(buf_size);
225+
if (!new_ext)
226+
return;
227+
228+
i = 0;
229+
while (i < len) {
230+
const char *entry = isa + i;
231+
int entry_len = sbi_strlen(entry);
232+
233+
if (entry_len == 1 && entry[0] >= 'a' && entry[0] <= 'z' && !misa_extension_imp(entry[0] - 'a' + 'A')) {
234+
sbi_printf("fdt: ISA ext fixup: removed \"%s\" (MISA bit %d not set)\n", entry, entry[0] - 'a');
235+
i += entry_len + 1;
236+
continue;
237+
}
238+
239+
if (entry_len > 1 && !isa_ext_is_valid(entry, fdt, cpu_offset)) {
240+
sbi_printf("fdt: ISA ext fixup: removed \"%s\" (validation failed)\n", entry);
241+
i += entry_len + 1;
242+
continue;
243+
}
244+
245+
if (new_len + entry_len + 1 <= (int)buf_size) {
246+
sbi_memcpy(new_ext + new_len, entry, entry_len + 1);
247+
new_len += entry_len + 1;
248+
}
249+
i += entry_len + 1;
250+
}
251+
252+
if (new_len != len)
253+
fdt_setprop(fdt, cpu_offset, "riscv,isa-extensions", new_ext, new_len);
254+
sbi_free(new_ext);
255+
}
256+
}
257+
109258
void fdt_cpu_fixup(void *fdt)
110259
{
111260
struct sbi_scratch *scratch = sbi_scratch_thishart_ptr();
@@ -153,6 +302,8 @@ void fdt_cpu_fixup(void *fdt)
153302
fdt_setprop_string(fdt, cpu_offset, "status",
154303
"disabled");
155304

305+
fdt_fixup_cpu_isa(fdt, cpu_offset);
306+
156307
if (!emulated_zicntr)
157308
continue;
158309

0 commit comments

Comments
 (0)