1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2015-2016 Ruslan Bukin <[email protected]>
5 * All rights reserved.
6 * Copyright (c) 2022 Mitchell Horne <[email protected]>
7 * Copyright (c) 2023 The FreeBSD Foundation
8 *
9 * Portions of this software were developed by SRI International and the
10 * University of Cambridge Computer Laboratory under DARPA/AFRL contract
11 * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme.
12 *
13 * Portions of this software were developed by the University of Cambridge
14 * Computer Laboratory as part of the CTSRD Project, with support from the
15 * UK Higher Education Innovation Fund (HEIF).
16 *
17 * Portions of this software were developed by Mitchell Horne
18 * <[email protected]> under sponsorship from the FreeBSD Foundation.
19 *
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions
22 * are met:
23 * 1. Redistributions of source code must retain the above copyright
24 * notice, this list of conditions and the following disclaimer.
25 * 2. Redistributions in binary form must reproduce the above copyright
26 * notice, this list of conditions and the following disclaimer in the
27 * documentation and/or other materials provided with the distribution.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 */
41
42 #include "opt_platform.h"
43
44 #include <sys/cdefs.h>
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/ctype.h>
48 #include <sys/kernel.h>
49 #include <sys/pcpu.h>
50 #include <sys/sysctl.h>
51
52 #include <machine/cpu.h>
53 #include <machine/cpufunc.h>
54 #include <machine/elf.h>
55 #include <machine/md_var.h>
56
57 #ifdef FDT
58 #include <dev/fdt/fdt_common.h>
59 #include <dev/ofw/openfirm.h>
60 #include <dev/ofw/ofw_bus_subr.h>
61 #endif
62
63 const char machine[] = "riscv";
64
65 SYSCTL_CONST_STRING(_hw, HW_MACHINE, machine, CTLFLAG_RD | CTLFLAG_CAPRD,
66 machine, "Machine class");
67
68 /* Hardware implementation info. These values may be empty. */
69 register_t mvendorid; /* The CPU's JEDEC vendor ID */
70 register_t marchid; /* The architecture ID */
71 register_t mimpid; /* The implementation ID */
72
73 u_int mmu_caps;
74
75 /* Supervisor-mode extension support. */
76 bool __read_frequently has_sstc;
77 bool __read_frequently has_sscofpmf;
78
79 struct cpu_desc {
80 const char *cpu_mvendor_name;
81 const char *cpu_march_name;
82 u_int isa_extensions; /* Single-letter extensions. */
83 u_int mmu_caps;
84 u_int smode_extensions;
85 #define SV_SSTC (1 << 0)
86 #define SV_SVNAPOT (1 << 1)
87 #define SV_SVPBMT (1 << 2)
88 #define SV_SVINVAL (1 << 3)
89 #define SV_SSCOFPMF (1 << 4)
90 };
91
92 struct cpu_desc cpu_desc[MAXCPU];
93
94 /*
95 * Micro-architecture tables.
96 */
97 struct marchid_entry {
98 register_t march_id;
99 const char *march_name;
100 };
101
102 #define MARCHID_END { -1ul, NULL }
103
104 /* Open-source RISC-V architecture IDs; globally allocated. */
105 static const struct marchid_entry global_marchids[] = {
106 { MARCHID_UCB_ROCKET, "UC Berkeley Rocket" },
107 { MARCHID_UCB_BOOM, "UC Berkeley Boom" },
108 { MARCHID_UCB_SPIKE, "UC Berkeley Spike" },
109 { MARCHID_UCAM_RVBS, "University of Cambridge RVBS" },
110 MARCHID_END
111 };
112
113 static const struct marchid_entry sifive_marchids[] = {
114 { MARCHID_SIFIVE_U7, "6/7/P200/X200-Series Processor" },
115 MARCHID_END
116 };
117
118 /*
119 * Known CPU vendor/manufacturer table.
120 */
121 static const struct {
122 register_t mvendor_id;
123 const char *mvendor_name;
124 const struct marchid_entry *marchid_table;
125 } mvendor_ids[] = {
126 { MVENDORID_UNIMPL, "Unspecified", NULL },
127 { MVENDORID_SIFIVE, "SiFive", sifive_marchids },
128 { MVENDORID_THEAD, "T-Head", NULL },
129 };
130
131 /*
132 * The ISA string describes the complete set of instructions supported by a
133 * RISC-V CPU. The string begins with a small prefix (e.g. rv64) indicating the
134 * base ISA. It is followed first by single-letter ISA extensions, and then
135 * multi-letter ISA extensions.
136 *
137 * Underscores are used mainly to separate consecutive multi-letter extensions,
138 * but may optionally appear between any two extensions. An extension may be
139 * followed by a version number, in the form of 'Mpm', where M is the
140 * extension's major version number, and 'm' is the minor version number.
141 *
142 * The format is described in detail by the "ISA Extension Naming Conventions"
143 * chapter of the unprivileged spec.
144 */
145 #define ISA_PREFIX ("rv" __XSTRING(__riscv_xlen))
146 #define ISA_PREFIX_LEN (sizeof(ISA_PREFIX) - 1)
147
148 static __inline int
parse_ext_s(struct cpu_desc * desc,char * isa,int idx,int len)149 parse_ext_s(struct cpu_desc *desc, char *isa, int idx, int len)
150 {
151 #define CHECK_S_EXT(str, flag) \
152 do { \
153 if (strncmp(&isa[idx], (str), \
154 MIN(strlen(str), len - idx)) == 0) { \
155 desc->smode_extensions |= flag; \
156 return (idx + strlen(str)); \
157 } \
158 } while (0)
159
160 /* Check for known/supported extensions. */
161 CHECK_S_EXT("sstc", SV_SSTC);
162 CHECK_S_EXT("svnapot", SV_SVNAPOT);
163 CHECK_S_EXT("svpbmt", SV_SVPBMT);
164 CHECK_S_EXT("svinval", SV_SVINVAL);
165 CHECK_S_EXT("sscofpmf", SV_SSCOFPMF);
166
167 #undef CHECK_S_EXT
168
169 /*
170 * Proceed to the next multi-letter extension or the end of the
171 * string.
172 */
173 while (isa[idx] != '_' && idx < len) {
174 idx++;
175 }
176
177 return (idx);
178 }
179
180 static __inline int
parse_ext_x(struct cpu_desc * desc __unused,char * isa,int idx,int len)181 parse_ext_x(struct cpu_desc *desc __unused, char *isa, int idx, int len)
182 {
183 /*
184 * Proceed to the next multi-letter extension or the end of the
185 * string.
186 */
187 while (isa[idx] != '_' && idx < len) {
188 idx++;
189 }
190
191 return (idx);
192 }
193
194 static __inline int
parse_ext_z(struct cpu_desc * desc __unused,char * isa,int idx,int len)195 parse_ext_z(struct cpu_desc *desc __unused, char *isa, int idx, int len)
196 {
197 /*
198 * Proceed to the next multi-letter extension or the end of the
199 * string.
200 *
201 * TODO: parse some of these.
202 */
203 while (isa[idx] != '_' && idx < len) {
204 idx++;
205 }
206
207 return (idx);
208 }
209
210 static __inline int
parse_ext_version(char * isa,int idx,u_int * majorp __unused,u_int * minorp __unused)211 parse_ext_version(char *isa, int idx, u_int *majorp __unused,
212 u_int *minorp __unused)
213 {
214 /* Major version. */
215 while (isdigit(isa[idx]))
216 idx++;
217
218 if (isa[idx] != 'p')
219 return (idx);
220 else
221 idx++;
222
223 /* Minor version. */
224 while (isdigit(isa[idx]))
225 idx++;
226
227 return (idx);
228 }
229
230 /*
231 * Parse the ISA string, building up the set of HWCAP bits as they are found.
232 */
233 static int
parse_riscv_isa(struct cpu_desc * desc,char * isa,int len)234 parse_riscv_isa(struct cpu_desc *desc, char *isa, int len)
235 {
236 int i;
237
238 /* Check the string prefix. */
239 if (strncmp(isa, ISA_PREFIX, ISA_PREFIX_LEN) != 0) {
240 printf("%s: Unrecognized ISA string: %s\n", __func__, isa);
241 return (-1);
242 }
243
244 i = ISA_PREFIX_LEN;
245 while (i < len) {
246 switch(isa[i]) {
247 case 'a':
248 case 'c':
249 case 'd':
250 case 'f':
251 case 'i':
252 case 'm':
253 desc->isa_extensions |= HWCAP_ISA_BIT(isa[i]);
254 i++;
255 break;
256 case 'g':
257 desc->isa_extensions |= HWCAP_ISA_G;
258 i++;
259 break;
260 case 's':
261 /*
262 * XXX: older versions of this string erroneously
263 * indicated supervisor and user mode support as
264 * single-letter extensions. Detect and skip both 's'
265 * and 'u'.
266 */
267 if (isa[i - 1] != '_' && isa[i + 1] == 'u') {
268 i += 2;
269 continue;
270 }
271
272 /*
273 * Supervisor-level extension namespace.
274 */
275 i = parse_ext_s(desc, isa, i, len);
276 break;
277 case 'x':
278 /*
279 * Custom extension namespace. For now, we ignore
280 * these.
281 */
282 i = parse_ext_x(desc, isa, i, len);
283 break;
284 case 'z':
285 /*
286 * Multi-letter standard extension namespace.
287 */
288 i = parse_ext_z(desc, isa, i, len);
289 break;
290 case '_':
291 i++;
292 continue;
293 default:
294 /* Unrecognized/unsupported. */
295 i++;
296 break;
297 }
298
299 i = parse_ext_version(isa, i, NULL, NULL);
300 }
301
302 return (0);
303 }
304
305 #ifdef FDT
306 static void
parse_mmu_fdt(struct cpu_desc * desc,phandle_t node)307 parse_mmu_fdt(struct cpu_desc *desc, phandle_t node)
308 {
309 char mmu[16];
310
311 desc->mmu_caps |= MMU_SV39;
312 if (OF_getprop(node, "mmu-type", mmu, sizeof(mmu)) > 0) {
313 if (strcmp(mmu, "riscv,sv48") == 0)
314 desc->mmu_caps |= MMU_SV48;
315 else if (strcmp(mmu, "riscv,sv57") == 0)
316 desc->mmu_caps |= MMU_SV48 | MMU_SV57;
317 }
318 }
319
320 static void
identify_cpu_features_fdt(u_int cpu,struct cpu_desc * desc)321 identify_cpu_features_fdt(u_int cpu, struct cpu_desc *desc)
322 {
323 char isa[1024];
324 phandle_t node;
325 ssize_t len;
326 pcell_t reg;
327 u_int hart;
328
329 node = OF_finddevice("/cpus");
330 if (node == -1) {
331 printf("%s: could not find /cpus node in FDT\n", __func__);
332 return;
333 }
334
335 hart = pcpu_find(cpu)->pc_hart;
336
337 /*
338 * Locate our current CPU's node in the device-tree, and parse its
339 * contents to detect supported CPU/ISA features and extensions.
340 */
341 for (node = OF_child(node); node > 0; node = OF_peer(node)) {
342 /* Skip any non-CPU nodes, such as cpu-map. */
343 if (!ofw_bus_node_is_compatible(node, "riscv"))
344 continue;
345
346 /* Find this CPU */
347 if (OF_getencprop(node, "reg", ®, sizeof(reg)) <= 0 ||
348 reg != hart)
349 continue;
350
351 len = OF_getprop(node, "riscv,isa", isa, sizeof(isa));
352 KASSERT(len <= sizeof(isa), ("ISA string truncated"));
353 if (len == -1) {
354 printf("%s: could not find 'riscv,isa' property "
355 "for CPU %d, hart %u\n", __func__, cpu, hart);
356 return;
357 }
358
359 /*
360 * The string is specified to be lowercase, but let's be
361 * certain.
362 */
363 for (int i = 0; i < len; i++)
364 isa[i] = tolower(isa[i]);
365 if (parse_riscv_isa(desc, isa, len) != 0)
366 return;
367
368 /* Check MMU features. */
369 parse_mmu_fdt(desc, node);
370
371 /* We are done. */
372 break;
373 }
374 if (node <= 0) {
375 printf("%s: could not find FDT node for CPU %u, hart %u\n",
376 __func__, cpu, hart);
377 }
378 }
379 #endif
380
381 static void
identify_cpu_features(u_int cpu,struct cpu_desc * desc)382 identify_cpu_features(u_int cpu, struct cpu_desc *desc)
383 {
384 #ifdef FDT
385 identify_cpu_features_fdt(cpu, desc);
386 #endif
387 }
388
389 /*
390 * Update kernel/user global state based on the feature parsing results, stored
391 * in desc.
392 *
393 * We keep only the subset of values common to all CPUs.
394 */
395 static void
update_global_capabilities(u_int cpu,struct cpu_desc * desc)396 update_global_capabilities(u_int cpu, struct cpu_desc *desc)
397 {
398 #define UPDATE_CAP(t, v) \
399 do { \
400 if (cpu == 0) { \
401 (t) = (v); \
402 } else { \
403 (t) &= (v); \
404 } \
405 } while (0)
406
407 /* Update the capabilities exposed to userspace via AT_HWCAP. */
408 UPDATE_CAP(elf_hwcap, (u_long)desc->isa_extensions);
409
410 /*
411 * MMU capabilities, e.g. Sv48.
412 */
413 UPDATE_CAP(mmu_caps, desc->mmu_caps);
414
415 /* Supervisor-mode extension support. */
416 UPDATE_CAP(has_sstc, (desc->smode_extensions & SV_SSTC) != 0);
417 UPDATE_CAP(has_sscofpmf, (desc->smode_extensions & SV_SSCOFPMF) != 0);
418
419 #undef UPDATE_CAP
420 }
421
422 static void
identify_cpu_ids(struct cpu_desc * desc)423 identify_cpu_ids(struct cpu_desc *desc)
424 {
425 const struct marchid_entry *table = NULL;
426 int i;
427
428 desc->cpu_mvendor_name = "Unknown";
429 desc->cpu_march_name = "Unknown";
430
431 /*
432 * Search for a recognized vendor, and possibly obtain the secondary
433 * table for marchid lookup.
434 */
435 for (i = 0; i < nitems(mvendor_ids); i++) {
436 if (mvendorid == mvendor_ids[i].mvendor_id) {
437 desc->cpu_mvendor_name = mvendor_ids[i].mvendor_name;
438 table = mvendor_ids[i].marchid_table;
439 break;
440 }
441 }
442
443 if (marchid == MARCHID_UNIMPL) {
444 desc->cpu_march_name = "Unspecified";
445 return;
446 }
447
448 if (MARCHID_IS_OPENSOURCE(marchid)) {
449 table = global_marchids;
450 } else if (table == NULL)
451 return;
452
453 for (i = 0; table[i].march_name != NULL; i++) {
454 if (marchid == table[i].march_id) {
455 desc->cpu_march_name = table[i].march_name;
456 break;
457 }
458 }
459 }
460
461 void
identify_cpu(u_int cpu)462 identify_cpu(u_int cpu)
463 {
464 struct cpu_desc *desc = &cpu_desc[cpu];
465
466 identify_cpu_ids(desc);
467 identify_cpu_features(cpu, desc);
468
469 update_global_capabilities(cpu, desc);
470 }
471
472 void
printcpuinfo(u_int cpu)473 printcpuinfo(u_int cpu)
474 {
475 struct cpu_desc *desc;
476 u_int hart;
477
478 desc = &cpu_desc[cpu];
479 hart = pcpu_find(cpu)->pc_hart;
480
481 /* XXX: check this here so we are guaranteed to have console output. */
482 KASSERT(desc->isa_extensions != 0,
483 ("Empty extension set for CPU %u, did parsing fail?", cpu));
484
485 /*
486 * Suppress the output of some fields in the common case of identical
487 * CPU features.
488 */
489 #define SHOULD_PRINT(_field) \
490 (cpu == 0 || desc[0]._field != desc[-1]._field)
491
492 /* Always print summary line. */
493 printf("CPU %-3u: Vendor=%s Core=%s (Hart %u)\n", cpu,
494 desc->cpu_mvendor_name, desc->cpu_march_name, hart);
495
496 /* These values are global. */
497 if (cpu == 0)
498 printf(" marchid=%#lx, mimpid=%#lx\n", marchid, mimpid);
499
500 if (SHOULD_PRINT(mmu_caps)) {
501 printf(" MMU: %#b\n", desc->mmu_caps,
502 "\020"
503 "\01Sv39"
504 "\02Sv48"
505 "\03Sv57");
506 }
507
508 if (SHOULD_PRINT(isa_extensions)) {
509 printf(" ISA: %#b\n", desc->isa_extensions,
510 "\020"
511 "\01Atomic"
512 "\03Compressed"
513 "\04Double"
514 "\06Float"
515 "\15Mult/Div");
516 }
517
518 if (SHOULD_PRINT(smode_extensions)) {
519 printf(" S-mode Extensions: %#b\n", desc->smode_extensions,
520 "\020"
521 "\01Sstc"
522 "\02Svnapot"
523 "\03Svpbmt"
524 "\04Svinval"
525 "\05Sscofpmf");
526 }
527
528 #undef SHOULD_PRINT
529 }
530