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 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 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 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 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 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 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 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 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 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 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 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 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 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 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