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