xref: /freebsd/sys/riscv/riscv/identcpu.c (revision 9768746b)
1 /*-
2  * Copyright (c) 2015-2016 Ruslan Bukin <br@bsdpad.com>
3  * All rights reserved.
4  *
5  * Portions of this software were developed by SRI International and the
6  * University of Cambridge Computer Laboratory under DARPA/AFRL contract
7  * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme.
8  *
9  * Portions of this software were developed by the University of Cambridge
10  * Computer Laboratory as part of the CTSRD Project, with support from the
11  * UK Higher Education Innovation Fund (HEIF).
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include "opt_platform.h"
36 
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/ctype.h>
43 #include <sys/kernel.h>
44 #include <sys/pcpu.h>
45 #include <sys/sysctl.h>
46 
47 #include <machine/cpu.h>
48 #include <machine/cpufunc.h>
49 #include <machine/elf.h>
50 #include <machine/md_var.h>
51 #include <machine/trap.h>
52 
53 #ifdef FDT
54 #include <dev/fdt/fdt_common.h>
55 #include <dev/ofw/openfirm.h>
56 #include <dev/ofw/ofw_bus_subr.h>
57 #endif
58 
59 char machine[] = "riscv";
60 
61 SYSCTL_STRING(_hw, HW_MACHINE, machine, CTLFLAG_RD | CTLFLAG_CAPRD, machine, 0,
62     "Machine class");
63 
64 /* Hardware implementation info. These values may be empty. */
65 register_t mvendorid;	/* The CPU's JEDEC vendor ID */
66 register_t marchid;	/* The architecture ID */
67 register_t mimpid;	/* The implementation ID */
68 
69 struct cpu_desc {
70 	u_int		cpu_impl;
71 	u_int		cpu_part_num;
72 	const char	*cpu_impl_name;
73 	const char	*cpu_part_name;
74 };
75 
76 struct cpu_desc cpu_desc[MAXCPU];
77 
78 struct cpu_parts {
79 	u_int		part_id;
80 	const char	*part_name;
81 };
82 #define	CPU_PART_NONE	{ -1, "Unknown Processor" }
83 
84 struct cpu_implementers {
85 	u_int			impl_id;
86 	const char		*impl_name;
87 };
88 #define	CPU_IMPLEMENTER_NONE	{ 0, "Unknown Implementer" }
89 
90 /*
91  * CPU base
92  */
93 static const struct cpu_parts cpu_parts_std[] = {
94 	{ CPU_PART_RV32,	"RV32" },
95 	{ CPU_PART_RV64,	"RV64" },
96 	{ CPU_PART_RV128,	"RV128" },
97 	CPU_PART_NONE,
98 };
99 
100 /*
101  * Implementers table.
102  */
103 const struct cpu_implementers cpu_implementers[] = {
104 	{ CPU_IMPL_UCB_ROCKET,	"UC Berkeley Rocket" },
105 	CPU_IMPLEMENTER_NONE,
106 };
107 
108 /*
109  * The ISA string describes the complete set of instructions supported by a
110  * RISC-V CPU. The string begins with a small prefix (e.g. rv64) indicating the
111  * base ISA. It is followed first by single-letter ISA extensions, and then
112  * multi-letter ISA extensions.
113  *
114  * Underscores are used mainly to separate consecutive multi-letter extensions,
115  * but may optionally appear between any two extensions. An extension may be
116  * followed by a version number, in the form of 'Mpm', where M is the
117  * extension's major version number, and 'm' is the minor version number.
118  *
119  * The format is described in detail by the "ISA Extension Naming Conventions"
120  * chapter of the unprivileged spec.
121  */
122 #define	ISA_PREFIX		("rv" __XSTRING(__riscv_xlen))
123 #define	ISA_PREFIX_LEN		(sizeof(ISA_PREFIX) - 1)
124 
125 static __inline int
126 parse_ext_s(char *isa, int idx, int len)
127 {
128 	/*
129 	 * Proceed to the next multi-letter extension or the end of the
130 	 * string.
131 	 *
132 	 * TODO: parse these once we gain support
133 	 */
134 	while (isa[idx] != '_' && idx < len) {
135 		idx++;
136 	}
137 
138 	return (idx);
139 }
140 
141 static __inline int
142 parse_ext_x(char *isa, int idx, int len)
143 {
144 	/*
145 	 * Proceed to the next multi-letter extension or the end of the
146 	 * string.
147 	 */
148 	while (isa[idx] != '_' && idx < len) {
149 		idx++;
150 	}
151 
152 	return (idx);
153 }
154 
155 static __inline int
156 parse_ext_z(char *isa, int idx, int len)
157 {
158 	/*
159 	 * Proceed to the next multi-letter extension or the end of the
160 	 * string.
161 	 *
162 	 * TODO: parse some of these.
163 	 */
164 	while (isa[idx] != '_' && idx < len) {
165 		idx++;
166 	}
167 
168 	return (idx);
169 }
170 
171 static __inline int
172 parse_ext_version(char *isa, int idx, u_int *majorp __unused,
173     u_int *minorp __unused)
174 {
175 	/* Major version. */
176 	while (isdigit(isa[idx]))
177 		idx++;
178 
179 	if (isa[idx] != 'p')
180 		return (idx);
181 	else
182 		idx++;
183 
184 	/* Minor version. */
185 	while (isdigit(isa[idx]))
186 		idx++;
187 
188 	return (idx);
189 }
190 
191 /*
192  * Parse the ISA string, building up the set of HWCAP bits as they are found.
193  */
194 static void
195 parse_riscv_isa(char *isa, int len, u_long *hwcapp)
196 {
197 	u_long hwcap;
198 	int i;
199 
200 	hwcap = 0;
201 	i = ISA_PREFIX_LEN;
202 	while (i < len) {
203 		switch(isa[i]) {
204 		case 'a':
205 		case 'c':
206 #ifdef FPE
207 		case 'd':
208 		case 'f':
209 #endif
210 		case 'i':
211 		case 'm':
212 			hwcap |= HWCAP_ISA_BIT(isa[i]);
213 			i++;
214 			break;
215 		case 'g':
216 			hwcap |= HWCAP_ISA_G;
217 			i++;
218 			break;
219 		case 's':
220 			/*
221 			 * XXX: older versions of this string erroneously
222 			 * indicated supervisor and user mode support as
223 			 * single-letter extensions. Detect and skip both 's'
224 			 * and 'u'.
225 			 */
226 			if (isa[i - 1] != '_' && isa[i + 1] == 'u') {
227 				i += 2;
228 				continue;
229 			}
230 
231 			/*
232 			 * Supervisor-level extension namespace.
233 			 */
234 			i = parse_ext_s(isa, i, len);
235 			break;
236 		case 'x':
237 			/*
238 			 * Custom extension namespace. For now, we ignore
239 			 * these.
240 			 */
241 			i = parse_ext_x(isa, i, len);
242 			break;
243 		case 'z':
244 			/*
245 			 * Multi-letter standard extension namespace.
246 			 */
247 			i = parse_ext_z(isa, i, len);
248 			break;
249 		case '_':
250 			i++;
251 			continue;
252 		default:
253 			/* Unrecognized/unsupported. */
254 			i++;
255 			break;
256 		}
257 
258 		i = parse_ext_version(isa, i, NULL, NULL);
259 	}
260 
261 	if (hwcapp != NULL)
262 		*hwcapp = hwcap;
263 }
264 
265 #ifdef FDT
266 static void
267 fill_elf_hwcap(void *dummy __unused)
268 {
269 	char isa[1024];
270 	u_long hwcap;
271 	phandle_t node;
272 	ssize_t len;
273 
274 	node = OF_finddevice("/cpus");
275 	if (node == -1) {
276 		if (bootverbose)
277 			printf("fill_elf_hwcap: Can't find cpus node\n");
278 		return;
279 	}
280 
281 	/*
282 	 * Iterate through the CPUs and examine their ISA string. While we
283 	 * could assign elf_hwcap to be whatever the boot CPU supports, to
284 	 * handle the (unusual) case of running a system with hetergeneous
285 	 * ISAs, keep only the extension bits that are common to all harts.
286 	 */
287 	for (node = OF_child(node); node > 0; node = OF_peer(node)) {
288 		/* Skip any non-CPU nodes, such as cpu-map. */
289 		if (!ofw_bus_node_is_compatible(node, "riscv"))
290 			continue;
291 
292 		len = OF_getprop(node, "riscv,isa", isa, sizeof(isa));
293 		KASSERT(len <= sizeof(isa), ("ISA string truncated"));
294 		if (len == -1) {
295 			if (bootverbose)
296 				printf("fill_elf_hwcap: "
297 				    "Can't find riscv,isa property\n");
298 			return;
299 		} else if (strncmp(isa, ISA_PREFIX, ISA_PREFIX_LEN) != 0) {
300 			if (bootverbose)
301 				printf("fill_elf_hwcap: "
302 				    "Unsupported ISA string: %s\n", isa);
303 			return;
304 		}
305 
306 		/*
307 		 * The string is specified to be lowercase, but let's be
308 		 * certain.
309 		 */
310 		for (int i = 0; i < len; i++)
311 			isa[i] = tolower(isa[i]);
312 		parse_riscv_isa(isa, len, &hwcap);
313 
314 		if (elf_hwcap != 0)
315 			elf_hwcap &= hwcap;
316 		else
317 			elf_hwcap = hwcap;
318 	}
319 }
320 
321 SYSINIT(identcpu, SI_SUB_CPU, SI_ORDER_ANY, fill_elf_hwcap, NULL);
322 #endif
323 
324 void
325 identify_cpu(void)
326 {
327 	const struct cpu_parts *cpu_partsp;
328 	uint32_t part_id;
329 	uint32_t impl_id;
330 	uint64_t misa;
331 	u_int cpu;
332 	size_t i;
333 
334 	cpu_partsp = NULL;
335 
336 	/* TODO: can we get misa somewhere ? */
337 	misa = 0;
338 
339 	cpu = PCPU_GET(cpuid);
340 
341 	impl_id	= CPU_IMPL(mimpid);
342 	for (i = 0; i < nitems(cpu_implementers); i++) {
343 		if (impl_id == cpu_implementers[i].impl_id ||
344 		    cpu_implementers[i].impl_id == 0) {
345 			cpu_desc[cpu].cpu_impl = impl_id;
346 			cpu_desc[cpu].cpu_impl_name = cpu_implementers[i].impl_name;
347 			cpu_partsp = cpu_parts_std;
348 			break;
349 		}
350 	}
351 
352 	part_id = CPU_PART(misa);
353 	for (i = 0; &cpu_partsp[i] != NULL; i++) {
354 		if (part_id == cpu_partsp[i].part_id ||
355 		    cpu_partsp[i].part_id == -1) {
356 			cpu_desc[cpu].cpu_part_num = part_id;
357 			cpu_desc[cpu].cpu_part_name = cpu_partsp[i].part_name;
358 			break;
359 		}
360 	}
361 
362 	/* Print details for boot CPU or if we want verbose output */
363 	if (cpu == 0 || bootverbose) {
364 		printf("CPU(%d): %s %s\n", cpu,
365 		    cpu_desc[cpu].cpu_impl_name,
366 		    cpu_desc[cpu].cpu_part_name);
367 	}
368 }
369