1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2015-2026 Ruslan Bukin <br@bsdpad.com>
5 * All rights reserved.
6 * Copyright (c) 2022 Mitchell Horne <mhorne@FreeBSD.org>
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 * <mhorne@FreeBSD.org> 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/param.h>
45 #include <sys/systm.h>
46 #include <sys/ctype.h>
47 #include <sys/kernel.h>
48 #include <sys/pcpu.h>
49 #include <sys/sysctl.h>
50
51 #include <machine/cpu.h>
52 #include <machine/cpufunc.h>
53 #include <machine/elf.h>
54 #include <machine/md_var.h>
55 #include <machine/thead.h>
56 #include <machine/cbo.h>
57
58 #ifdef FDT
59 #include <dev/fdt/fdt_common.h>
60 #include <dev/ofw/openfirm.h>
61 #include <dev/ofw/ofw_bus_subr.h>
62 #endif
63
64 const char machine[] = "riscv";
65
66 SYSCTL_CONST_STRING(_hw, HW_MACHINE, machine, CTLFLAG_RD | CTLFLAG_CAPRD,
67 machine, "Machine class");
68
69 /* Hardware implementation info. These values may be empty. */
70 register_t mvendorid; /* The CPU's JEDEC vendor ID */
71 register_t marchid; /* The architecture ID */
72 register_t mimpid; /* The implementation ID */
73
74 u_int mmu_caps;
75
76 /* Supervisor-mode extension support. */
77 bool has_hyp;
78 bool has_vector;
79 bool __read_frequently has_sstc;
80 bool __read_frequently has_sscofpmf;
81 bool has_svpbmt;
82 bool has_svinval;
83
84 /* Z-extensions support. */
85 bool has_zicbom;
86 bool has_zicboz;
87 bool has_zicbop;
88
89 struct cpu_desc {
90 const char *cpu_mvendor_name;
91 const char *cpu_march_name;
92 u_int isa_extensions; /* Single-letter extensions. */
93 u_int mmu_caps;
94 u_int smode_extensions;
95 #define SV_SSTC (1 << 0)
96 #define SV_SVNAPOT (1 << 1)
97 #define SV_SVPBMT (1 << 2)
98 #define SV_SVINVAL (1 << 3)
99 #define SV_SSCOFPMF (1 << 4)
100 u_int z_extensions; /* Multi-letter extensions. */
101 #define Z_ZICBOM (1 << 0)
102 #define Z_ZICBOZ (1 << 1)
103 #define Z_ZICBOP (1 << 2)
104 int cbom_block_size;
105 int cboz_block_size;
106 };
107
108 struct cpu_desc cpu_desc[MAXCPU];
109
110 /*
111 * Micro-architecture tables.
112 */
113 struct marchid_entry {
114 register_t march_id;
115 const char *march_name;
116 };
117
118 #define MARCHID_END { -1ul, NULL }
119
120 /* Open-source RISC-V architecture IDs; globally allocated. */
121 static const struct marchid_entry global_marchids[] = {
122 { MARCHID_UCB_ROCKET, "UC Berkeley Rocket" },
123 { MARCHID_UCB_BOOM, "UC Berkeley Boom" },
124 { MARCHID_UCB_SPIKE, "UC Berkeley Spike" },
125 { MARCHID_UCAM_RVBS, "University of Cambridge RVBS" },
126 MARCHID_END
127 };
128
129 static const struct marchid_entry sifive_marchids[] = {
130 { MARCHID_SIFIVE_U7, "6/7/P200/X200-Series Processor" },
131 { MARCHID_SIFIVE_P5, "P550/P650 Processor" },
132 MARCHID_END
133 };
134
135 static const struct marchid_entry spacemit_marchids[] = {
136 { MARCHID_SPACEMIT_K1, "SpacemiT(R) X60" },
137 MARCHID_END
138 };
139
140 /*
141 * Known CPU vendor/manufacturer table.
142 */
143 static const struct {
144 register_t mvendor_id;
145 const char *mvendor_name;
146 const struct marchid_entry *marchid_table;
147 } mvendor_ids[] = {
148 { MVENDORID_UNIMPL, "Unspecified", NULL },
149 { MVENDORID_SIFIVE, "SiFive", sifive_marchids },
150 { MVENDORID_THEAD, "T-Head", NULL },
151 { MVENDORID_SPACEMIT, "SpacemiT", spacemit_marchids },
152 };
153
154 /*
155 * The ISA string describes the complete set of instructions supported by a
156 * RISC-V CPU. The string begins with a small prefix (e.g. rv64) indicating the
157 * base ISA. It is followed first by single-letter ISA extensions, and then
158 * multi-letter ISA extensions.
159 *
160 * Underscores are used mainly to separate consecutive multi-letter extensions,
161 * but may optionally appear between any two extensions. An extension may be
162 * followed by a version number, in the form of 'Mpm', where M is the
163 * extension's major version number, and 'm' is the minor version number.
164 *
165 * The format is described in detail by the "ISA Extension Naming Conventions"
166 * chapter of the unprivileged spec.
167 */
168 #define ISA_PREFIX ("rv" __XSTRING(__riscv_xlen))
169 #define ISA_PREFIX_LEN (sizeof(ISA_PREFIX) - 1)
170
171 static __inline int
parse_ext_s(struct cpu_desc * desc,char * isa,int idx,int len)172 parse_ext_s(struct cpu_desc *desc, char *isa, int idx, int len)
173 {
174 #define CHECK_S_EXT(str, flag) \
175 do { \
176 if (strncmp(&isa[idx], (str), \
177 MIN(strlen(str), len - idx)) == 0) { \
178 desc->smode_extensions |= flag; \
179 return (idx + strlen(str)); \
180 } \
181 } while (0)
182
183 /* Check for known/supported extensions. */
184 CHECK_S_EXT("sstc", SV_SSTC);
185 CHECK_S_EXT("svnapot", SV_SVNAPOT);
186 CHECK_S_EXT("svpbmt", SV_SVPBMT);
187 CHECK_S_EXT("svinval", SV_SVINVAL);
188 CHECK_S_EXT("sscofpmf", SV_SSCOFPMF);
189
190 #undef CHECK_S_EXT
191
192 /*
193 * Proceed to the next multi-letter extension or the end of the
194 * string.
195 */
196 while (isa[idx] != '_' && idx < len) {
197 idx++;
198 }
199
200 return (idx);
201 }
202
203 static __inline int
parse_ext_x(struct cpu_desc * desc __unused,char * isa,int idx,int len)204 parse_ext_x(struct cpu_desc *desc __unused, char *isa, int idx, int len)
205 {
206 /*
207 * Proceed to the next multi-letter extension or the end of the
208 * string.
209 */
210 while (isa[idx] != '_' && idx < len) {
211 idx++;
212 }
213
214 return (idx);
215 }
216
217 static __inline int
parse_ext_z(struct cpu_desc * desc __unused,char * isa,int idx,int len)218 parse_ext_z(struct cpu_desc *desc __unused, char *isa, int idx, int len)
219 {
220 #define CHECK_Z_EXT(str, flag) \
221 do { \
222 if (strncmp(&isa[idx], (str), \
223 MIN(strlen(str), len - idx)) == 0) { \
224 desc->z_extensions |= flag; \
225 return (idx + strlen(str)); \
226 } \
227 } while (0)
228
229 /* Check for known/supported extensions. */
230 CHECK_Z_EXT("zicbom", Z_ZICBOM);
231 CHECK_Z_EXT("zicboz", Z_ZICBOZ);
232 CHECK_Z_EXT("zicbop", Z_ZICBOP);
233
234 #undef CHECK_Z_EXT
235 /*
236 * Proceed to the next multi-letter extension or the end of the
237 * string.
238 */
239 while (isa[idx] != '_' && idx < len) {
240 idx++;
241 }
242
243 return (idx);
244 }
245
246 static __inline int
parse_ext_version(char * isa,int idx,u_int * majorp __unused,u_int * minorp __unused)247 parse_ext_version(char *isa, int idx, u_int *majorp __unused,
248 u_int *minorp __unused)
249 {
250 /* Major version. */
251 while (isdigit(isa[idx]))
252 idx++;
253
254 if (isa[idx] != 'p')
255 return (idx);
256 else
257 idx++;
258
259 /* Minor version. */
260 while (isdigit(isa[idx]))
261 idx++;
262
263 return (idx);
264 }
265
266 /*
267 * Parse the ISA string, building up the set of HWCAP bits as they are found.
268 */
269 static int
parse_riscv_isa(struct cpu_desc * desc,char * isa,int len)270 parse_riscv_isa(struct cpu_desc *desc, char *isa, int len)
271 {
272 int i;
273
274 /* Check the string prefix. */
275 if (strncmp(isa, ISA_PREFIX, ISA_PREFIX_LEN) != 0) {
276 printf("%s: Unrecognized ISA string: %s\n", __func__, isa);
277 return (-1);
278 }
279
280 i = ISA_PREFIX_LEN;
281 while (i < len) {
282 switch(isa[i]) {
283 case 'a':
284 case 'b':
285 case 'c':
286 case 'd':
287 case 'f':
288 case 'h':
289 case 'i':
290 case 'm':
291 case 'v':
292 desc->isa_extensions |= HWCAP_ISA_BIT(isa[i]);
293 i++;
294 break;
295 case 'g':
296 desc->isa_extensions |= HWCAP_ISA_G;
297 i++;
298 break;
299 case 's':
300 /*
301 * XXX: older versions of this string erroneously
302 * indicated supervisor and user mode support as
303 * single-letter extensions. Detect and skip both 's'
304 * and 'u'.
305 */
306 if (isa[i - 1] != '_' && isa[i + 1] == 'u') {
307 i += 2;
308 continue;
309 }
310
311 /*
312 * Supervisor-level extension namespace.
313 */
314 i = parse_ext_s(desc, isa, i, len);
315 break;
316 case 'x':
317 /*
318 * Custom extension namespace. For now, we ignore
319 * these.
320 */
321 i = parse_ext_x(desc, isa, i, len);
322 break;
323 case 'z':
324 /*
325 * Multi-letter standard extension namespace.
326 */
327 i = parse_ext_z(desc, isa, i, len);
328 break;
329 case '_':
330 i++;
331 continue;
332 default:
333 /* Unrecognized/unsupported. */
334 i++;
335 break;
336 }
337
338 i = parse_ext_version(isa, i, NULL, NULL);
339 }
340
341 return (0);
342 }
343
344 #ifdef FDT
345 static void
parse_mmu_fdt(struct cpu_desc * desc,phandle_t node)346 parse_mmu_fdt(struct cpu_desc *desc, phandle_t node)
347 {
348 char mmu[16];
349
350 desc->mmu_caps |= MMU_SV39;
351 if (OF_getprop(node, "mmu-type", mmu, sizeof(mmu)) > 0) {
352 if (strcmp(mmu, "riscv,sv48") == 0)
353 desc->mmu_caps |= MMU_SV48;
354 else if (strcmp(mmu, "riscv,sv57") == 0)
355 desc->mmu_caps |= MMU_SV48 | MMU_SV57;
356 }
357 }
358
359 static void
parse_cbo_fdt(struct cpu_desc * desc,phandle_t node)360 parse_cbo_fdt(struct cpu_desc *desc, phandle_t node)
361 {
362 int error;
363
364 error = OF_getencprop(node, "riscv,cbom-block-size",
365 &desc->cbom_block_size, sizeof(desc->cbom_block_size));
366 if (error == -1)
367 desc->cbom_block_size = 0;
368
369 error = OF_getencprop(node, "riscv,cboz-block-size",
370 &desc->cboz_block_size, sizeof(desc->cboz_block_size));
371 if (error == -1)
372 desc->cboz_block_size = 0;
373 }
374
375 static void
identify_cpu_features_fdt(u_int cpu,struct cpu_desc * desc)376 identify_cpu_features_fdt(u_int cpu, struct cpu_desc *desc)
377 {
378 char isa[1024];
379 phandle_t node;
380 ssize_t len;
381 pcell_t reg;
382 u_int hart;
383
384 node = OF_finddevice("/cpus");
385 if (node == -1) {
386 printf("%s: could not find /cpus node in FDT\n", __func__);
387 return;
388 }
389
390 hart = pcpu_find(cpu)->pc_hart;
391
392 /*
393 * Locate our current CPU's node in the device-tree, and parse its
394 * contents to detect supported CPU/ISA features and extensions.
395 */
396 for (node = OF_child(node); node > 0; node = OF_peer(node)) {
397 /* Skip any non-CPU nodes, such as cpu-map. */
398 if (!ofw_bus_node_is_compatible(node, "riscv"))
399 continue;
400
401 /* Find this CPU */
402 if (OF_getencprop(node, "reg", ®, sizeof(reg)) <= 0 ||
403 reg != hart)
404 continue;
405
406 len = OF_getprop(node, "riscv,isa", isa, sizeof(isa));
407 KASSERT(len <= sizeof(isa), ("ISA string truncated"));
408 if (len == -1) {
409 printf("%s: could not find 'riscv,isa' property "
410 "for CPU %d, hart %u\n", __func__, cpu, hart);
411 return;
412 }
413
414 if (bootverbose)
415 printf("hart %d isa: %s\n", hart, isa);
416
417 /*
418 * The string is specified to be lowercase, but let's be
419 * certain.
420 */
421 for (int i = 0; i < len; i++)
422 isa[i] = tolower(isa[i]);
423 if (parse_riscv_isa(desc, isa, len) != 0)
424 return;
425
426 /* Check MMU features. */
427 parse_mmu_fdt(desc, node);
428
429 /* Cache-block operations (CBO). */
430 parse_cbo_fdt(desc, node);
431
432 /* We are done. */
433 break;
434 }
435 if (node <= 0) {
436 printf("%s: could not find FDT node for CPU %u, hart %u\n",
437 __func__, cpu, hart);
438 }
439 }
440 #endif
441
442 static void
identify_cpu_features(u_int cpu,struct cpu_desc * desc)443 identify_cpu_features(u_int cpu, struct cpu_desc *desc)
444 {
445 #ifdef FDT
446 identify_cpu_features_fdt(cpu, desc);
447 #endif
448 }
449
450 /*
451 * Update kernel/user global state based on the feature parsing results, stored
452 * in desc.
453 *
454 * We keep only the subset of values common to all CPUs.
455 */
456 static void
update_global_capabilities(u_int cpu,struct cpu_desc * desc)457 update_global_capabilities(u_int cpu, struct cpu_desc *desc)
458 {
459 #define UPDATE_CAP(t, v) \
460 do { \
461 if (cpu == 0) { \
462 (t) = (v); \
463 } else { \
464 (t) &= (v); \
465 } \
466 } while (0)
467
468 /* Update the capabilities exposed to userspace via AT_HWCAP. */
469 UPDATE_CAP(elf_hwcap, (u_long)desc->isa_extensions);
470
471 /*
472 * MMU capabilities, e.g. Sv48.
473 */
474 UPDATE_CAP(mmu_caps, desc->mmu_caps);
475
476 /* Supervisor-mode extension support. */
477 UPDATE_CAP(has_vector, (desc->isa_extensions & HWCAP_ISA_V) != 0);
478 UPDATE_CAP(has_hyp, (desc->isa_extensions & HWCAP_ISA_H) != 0);
479 UPDATE_CAP(has_sstc, (desc->smode_extensions & SV_SSTC) != 0);
480 UPDATE_CAP(has_sscofpmf, (desc->smode_extensions & SV_SSCOFPMF) != 0);
481 UPDATE_CAP(has_svpbmt, (desc->smode_extensions & SV_SVPBMT) != 0);
482 UPDATE_CAP(has_svinval, (desc->smode_extensions & SV_SVINVAL) != 0);
483
484 /* Z extension support. */
485 UPDATE_CAP(has_zicbom, (desc->z_extensions & Z_ZICBOM) != 0);
486 UPDATE_CAP(has_zicboz, (desc->z_extensions & Z_ZICBOZ) != 0);
487 UPDATE_CAP(has_zicbop, (desc->z_extensions & Z_ZICBOP) != 0);
488
489 #undef UPDATE_CAP
490 }
491
492 static void
identify_cpu_ids(struct cpu_desc * desc)493 identify_cpu_ids(struct cpu_desc *desc)
494 {
495 const struct marchid_entry *table = NULL;
496 int i;
497
498 desc->cpu_mvendor_name = "Unknown";
499 desc->cpu_march_name = "Unknown";
500
501 /*
502 * Search for a recognized vendor, and possibly obtain the secondary
503 * table for marchid lookup.
504 */
505 for (i = 0; i < nitems(mvendor_ids); i++) {
506 if (mvendorid == mvendor_ids[i].mvendor_id) {
507 desc->cpu_mvendor_name = mvendor_ids[i].mvendor_name;
508 table = mvendor_ids[i].marchid_table;
509 break;
510 }
511 }
512
513 if (marchid == MARCHID_UNIMPL) {
514 desc->cpu_march_name = "Unspecified";
515 return;
516 }
517
518 if (MARCHID_IS_OPENSOURCE(marchid)) {
519 table = global_marchids;
520 } else if (table == NULL)
521 return;
522
523 for (i = 0; table[i].march_name != NULL; i++) {
524 if (marchid == table[i].march_id) {
525 desc->cpu_march_name = table[i].march_name;
526 break;
527 }
528 }
529 }
530
531 static void
handle_thead_quirks(u_int cpu,struct cpu_desc * desc)532 handle_thead_quirks(u_int cpu, struct cpu_desc *desc)
533 {
534 if (cpu != 0)
535 return;
536
537 /*
538 * For now, it is assumed that T-HEAD CPUs have both marchid and mimpid
539 * values of zero (although we leave this unchecked). It is true in
540 * practice for the early generations of this hardware (C906, C910,
541 * C920). In the future, the identity checks may need to become more
542 * granular, but until then all known T-HEAD quirks are applied
543 * indiscriminantly.
544 *
545 * Note: any changes in this function relating to has_errata_thead_pbmt
546 * may need to be applied to get_pte_fixup_bits (in locore.S) as well.
547 */
548
549 has_errata_thead_pbmt = true;
550 thead_setup_cache();
551 }
552
553 static void
handle_cpu_quirks(u_int cpu,struct cpu_desc * desc)554 handle_cpu_quirks(u_int cpu, struct cpu_desc *desc)
555 {
556 switch (mvendorid) {
557 case MVENDORID_THEAD:
558 handle_thead_quirks(cpu, desc);
559 break;
560 }
561 }
562
563 void
identify_cpu(u_int cpu)564 identify_cpu(u_int cpu)
565 {
566 struct cpu_desc *desc = &cpu_desc[cpu];
567
568 identify_cpu_ids(desc);
569 identify_cpu_features(cpu, desc);
570
571 update_global_capabilities(cpu, desc);
572 handle_cpu_quirks(cpu, desc);
573
574 if (has_zicbom && cpu == 0)
575 cbo_zicbom_setup_cache(desc->cbom_block_size);
576 }
577
578 void
printcpuinfo(u_int cpu)579 printcpuinfo(u_int cpu)
580 {
581 struct cpu_desc *desc;
582 u_int hart;
583
584 desc = &cpu_desc[cpu];
585 hart = pcpu_find(cpu)->pc_hart;
586
587 /* XXX: check this here so we are guaranteed to have console output. */
588 KASSERT(desc->isa_extensions != 0,
589 ("Empty extension set for CPU %u, did parsing fail?", cpu));
590
591 /*
592 * Suppress the output of some fields in the common case of identical
593 * CPU features.
594 */
595 #define SHOULD_PRINT(_field) \
596 (cpu == 0 || desc[0]._field != desc[-1]._field)
597
598 /* Always print summary line. */
599 printf("CPU %-3u: Vendor=%s Core=%s (Hart %u)\n", cpu,
600 desc->cpu_mvendor_name, desc->cpu_march_name, hart);
601
602 /* These values are global. */
603 if (cpu == 0)
604 printf(" marchid=%#lx, mimpid=%#lx\n", marchid, mimpid);
605
606 if (SHOULD_PRINT(mmu_caps)) {
607 printf(" MMU: %#b\n", desc->mmu_caps,
608 "\020"
609 "\01Sv39"
610 "\02Sv48"
611 "\03Sv57");
612 }
613
614 if (SHOULD_PRINT(isa_extensions)) {
615 printf(" ISA: %#b\n", desc->isa_extensions,
616 "\020"
617 "\01Atomic"
618 "\03Compressed"
619 "\04Double"
620 "\06Float"
621 "\10Hypervisor"
622 "\15Mult/Div"
623 "\26Vector");
624 }
625
626 if (SHOULD_PRINT(smode_extensions)) {
627 printf(" S-mode Extensions: %#b\n", desc->smode_extensions,
628 "\020"
629 "\01Sstc"
630 "\02Svnapot"
631 "\03Svpbmt"
632 "\04Svinval"
633 "\05Sscofpmf");
634 }
635
636 #undef SHOULD_PRINT
637 }
638