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