xref: /kvm-unit-tests/lib/riscv/isa.c (revision 48d5952451de62a4db23cf73024f702cf1a64fc3)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com>
4  */
5 #include <libcflat.h>
6 #include <bitops.h>
7 #include <devicetree.h>
8 #include <string.h>
9 #include <asm/isa.h>
10 #include <asm/setup.h>
11 
12 typedef void (*isa_func_t)(const char *, int, void *);
13 
14 struct isa_info {
15 	unsigned long hartid;
16 	isa_func_t func;
17 	void *data;
18 };
19 
isa_match(const char * ext,const char * name,int len)20 static bool isa_match(const char *ext, const char *name, int len)
21 {
22 	return len == strlen(ext) && !strncasecmp(name, ext, len);
23 }
24 
25 struct isa_check {
26 	const char *ext;
27 	bool found;
28 };
29 
isa_name(const char * name,int len,void * data)30 static void isa_name(const char *name, int len, void *data)
31 {
32 	struct isa_check *check = (struct isa_check *)data;
33 
34 	if (isa_match(check->ext, name, len))
35 		check->found = true;
36 }
37 
isa_bit(const char * name,int len,void * data)38 static void isa_bit(const char *name, int len, void *data)
39 {
40 	struct thread_info *info = (struct thread_info *)data;
41 
42 	if (isa_match("sstc", name, len))
43 		set_bit(ISA_SSTC, info->isa);
44 }
45 
isa_parse(const char * isa_string,int len,struct isa_info * info)46 static void isa_parse(const char *isa_string, int len, struct isa_info *info)
47 {
48 	assert(isa_string[0] == 'r' && isa_string[1] == 'v');
49 #if __riscv_xlen == 32
50 	assert(isa_string[2] == '3' && isa_string[3] == '2');
51 #else
52 	assert(isa_string[2] == '6' && isa_string[3] == '4');
53 #endif
54 
55 	for (int i = 4; i < len; ++i) {
56 		if (isa_string[i] == '_') {
57 			const char *multi = &isa_string[++i];
58 			int start = i;
59 
60 			while (i < len - 1 && isa_string[i] != '_')
61 				++i;
62 			info->func(multi, i - start, info->data);
63 			if (i < len - 1)
64 				--i;
65 		} else {
66 			info->func(&isa_string[i], 1, info->data);
67 		}
68 	}
69 }
70 
isa_parse_fdt(int cpu_node,u64 hartid,void * data)71 static void isa_parse_fdt(int cpu_node, u64 hartid, void *data)
72 {
73 	struct isa_info *info = (struct isa_info *)data;
74 	const struct fdt_property *prop;
75 	int len;
76 
77 	if (hartid != info->hartid)
78 		return;
79 
80 	prop = fdt_get_property(dt_fdt(), cpu_node, "riscv,isa", &len);
81 	assert(prop);
82 
83 	isa_parse(prop->data, len, info);
84 }
85 
isa_init_acpi(void)86 static void isa_init_acpi(void)
87 {
88 	assert_msg(false, "ACPI not available");
89 }
90 
isa_init(struct thread_info * ti)91 void isa_init(struct thread_info *ti)
92 {
93 	struct isa_info info = {
94 		.hartid = ti->hartid,
95 		.func = isa_bit,
96 		.data = ti,
97 	};
98 	int ret;
99 
100 	if (dt_available()) {
101 		ret = dt_for_each_cpu_node(isa_parse_fdt, &info);
102 		assert(ret == 0);
103 	} else {
104 		isa_init_acpi();
105 	}
106 }
107 
cpu_has_extension_name(int cpu,const char * ext)108 bool cpu_has_extension_name(int cpu, const char *ext)
109 {
110 	struct isa_info info = {
111 		.hartid = cpus[cpu].hartid,
112 		.func = isa_name,
113 		.data = &(struct isa_check){ .ext = ext, },
114 	};
115 	struct isa_check *check = info.data;
116 	int ret;
117 
118 	if (dt_available()) {
119 		ret = dt_for_each_cpu_node(isa_parse_fdt, &info);
120 		assert(ret == 0);
121 	} else {
122 		assert_msg(false, "ACPI not available");
123 	}
124 
125 	return check->found;
126 }
127