1 /*- 2 * Copyright (c) 2015-2016 Ruslan Bukin <br@bsdpad.com> 3 * All rights reserved. 4 * 5 * Portions of this software were developed by SRI International and the 6 * University of Cambridge Computer Laboratory under DARPA/AFRL contract 7 * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. 8 * 9 * Portions of this software were developed by the University of Cambridge 10 * Computer Laboratory as part of the CTSRD Project, with support from the 11 * UK Higher Education Innovation Fund (HEIF). 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include "opt_platform.h" 36 37 #include <sys/cdefs.h> 38 __FBSDID("$FreeBSD$"); 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/ctype.h> 43 #include <sys/kernel.h> 44 #include <sys/pcpu.h> 45 #include <sys/sysctl.h> 46 47 #include <machine/cpu.h> 48 #include <machine/cpufunc.h> 49 #include <machine/elf.h> 50 #include <machine/md_var.h> 51 #include <machine/trap.h> 52 53 #ifdef FDT 54 #include <dev/fdt/fdt_common.h> 55 #include <dev/ofw/openfirm.h> 56 #include <dev/ofw/ofw_bus_subr.h> 57 #endif 58 59 char machine[] = "riscv"; 60 61 SYSCTL_STRING(_hw, HW_MACHINE, machine, CTLFLAG_RD | CTLFLAG_CAPRD, machine, 0, 62 "Machine class"); 63 64 /* Hardware implementation info. These values may be empty. */ 65 register_t mvendorid; /* The CPU's JEDEC vendor ID */ 66 register_t marchid; /* The architecture ID */ 67 register_t mimpid; /* The implementation ID */ 68 69 struct cpu_desc { 70 u_int cpu_impl; 71 u_int cpu_part_num; 72 const char *cpu_impl_name; 73 const char *cpu_part_name; 74 }; 75 76 struct cpu_desc cpu_desc[MAXCPU]; 77 78 struct cpu_parts { 79 u_int part_id; 80 const char *part_name; 81 }; 82 #define CPU_PART_NONE { -1, "Unknown Processor" } 83 84 struct cpu_implementers { 85 u_int impl_id; 86 const char *impl_name; 87 }; 88 #define CPU_IMPLEMENTER_NONE { 0, "Unknown Implementer" } 89 90 /* 91 * CPU base 92 */ 93 static const struct cpu_parts cpu_parts_std[] = { 94 { CPU_PART_RV32, "RV32" }, 95 { CPU_PART_RV64, "RV64" }, 96 { CPU_PART_RV128, "RV128" }, 97 CPU_PART_NONE, 98 }; 99 100 /* 101 * Implementers table. 102 */ 103 const struct cpu_implementers cpu_implementers[] = { 104 { CPU_IMPL_UCB_ROCKET, "UC Berkeley Rocket" }, 105 CPU_IMPLEMENTER_NONE, 106 }; 107 108 /* 109 * The ISA string describes the complete set of instructions supported by a 110 * RISC-V CPU. The string begins with a small prefix (e.g. rv64) indicating the 111 * base ISA. It is followed first by single-letter ISA extensions, and then 112 * multi-letter ISA extensions. 113 * 114 * Underscores are used mainly to separate consecutive multi-letter extensions, 115 * but may optionally appear between any two extensions. An extension may be 116 * followed by a version number, in the form of 'Mpm', where M is the 117 * extension's major version number, and 'm' is the minor version number. 118 * 119 * The format is described in detail by the "ISA Extension Naming Conventions" 120 * chapter of the unprivileged spec. 121 */ 122 #define ISA_PREFIX ("rv" __XSTRING(__riscv_xlen)) 123 #define ISA_PREFIX_LEN (sizeof(ISA_PREFIX) - 1) 124 125 static __inline int 126 parse_ext_s(char *isa, int idx, int len) 127 { 128 /* 129 * Proceed to the next multi-letter extension or the end of the 130 * string. 131 * 132 * TODO: parse these once we gain support 133 */ 134 while (isa[idx] != '_' && idx < len) { 135 idx++; 136 } 137 138 return (idx); 139 } 140 141 static __inline int 142 parse_ext_x(char *isa, int idx, int len) 143 { 144 /* 145 * Proceed to the next multi-letter extension or the end of the 146 * string. 147 */ 148 while (isa[idx] != '_' && idx < len) { 149 idx++; 150 } 151 152 return (idx); 153 } 154 155 static __inline int 156 parse_ext_z(char *isa, int idx, int len) 157 { 158 /* 159 * Proceed to the next multi-letter extension or the end of the 160 * string. 161 * 162 * TODO: parse some of these. 163 */ 164 while (isa[idx] != '_' && idx < len) { 165 idx++; 166 } 167 168 return (idx); 169 } 170 171 static __inline int 172 parse_ext_version(char *isa, int idx, u_int *majorp __unused, 173 u_int *minorp __unused) 174 { 175 /* Major version. */ 176 while (isdigit(isa[idx])) 177 idx++; 178 179 if (isa[idx] != 'p') 180 return (idx); 181 else 182 idx++; 183 184 /* Minor version. */ 185 while (isdigit(isa[idx])) 186 idx++; 187 188 return (idx); 189 } 190 191 /* 192 * Parse the ISA string, building up the set of HWCAP bits as they are found. 193 */ 194 static void 195 parse_riscv_isa(char *isa, int len, u_long *hwcapp) 196 { 197 u_long hwcap; 198 int i; 199 200 hwcap = 0; 201 i = ISA_PREFIX_LEN; 202 while (i < len) { 203 switch(isa[i]) { 204 case 'a': 205 case 'c': 206 #ifdef FPE 207 case 'd': 208 case 'f': 209 #endif 210 case 'i': 211 case 'm': 212 hwcap |= HWCAP_ISA_BIT(isa[i]); 213 i++; 214 break; 215 case 'g': 216 hwcap |= HWCAP_ISA_G; 217 i++; 218 break; 219 case 's': 220 /* 221 * XXX: older versions of this string erroneously 222 * indicated supervisor and user mode support as 223 * single-letter extensions. Detect and skip both 's' 224 * and 'u'. 225 */ 226 if (isa[i - 1] != '_' && isa[i + 1] == 'u') { 227 i += 2; 228 continue; 229 } 230 231 /* 232 * Supervisor-level extension namespace. 233 */ 234 i = parse_ext_s(isa, i, len); 235 break; 236 case 'x': 237 /* 238 * Custom extension namespace. For now, we ignore 239 * these. 240 */ 241 i = parse_ext_x(isa, i, len); 242 break; 243 case 'z': 244 /* 245 * Multi-letter standard extension namespace. 246 */ 247 i = parse_ext_z(isa, i, len); 248 break; 249 case '_': 250 i++; 251 continue; 252 default: 253 /* Unrecognized/unsupported. */ 254 i++; 255 break; 256 } 257 258 i = parse_ext_version(isa, i, NULL, NULL); 259 } 260 261 if (hwcapp != NULL) 262 *hwcapp = hwcap; 263 } 264 265 #ifdef FDT 266 static void 267 fill_elf_hwcap(void *dummy __unused) 268 { 269 char isa[1024]; 270 u_long hwcap; 271 phandle_t node; 272 ssize_t len; 273 274 node = OF_finddevice("/cpus"); 275 if (node == -1) { 276 if (bootverbose) 277 printf("fill_elf_hwcap: Can't find cpus node\n"); 278 return; 279 } 280 281 /* 282 * Iterate through the CPUs and examine their ISA string. While we 283 * could assign elf_hwcap to be whatever the boot CPU supports, to 284 * handle the (unusual) case of running a system with hetergeneous 285 * ISAs, keep only the extension bits that are common to all harts. 286 */ 287 for (node = OF_child(node); node > 0; node = OF_peer(node)) { 288 /* Skip any non-CPU nodes, such as cpu-map. */ 289 if (!ofw_bus_node_is_compatible(node, "riscv")) 290 continue; 291 292 len = OF_getprop(node, "riscv,isa", isa, sizeof(isa)); 293 KASSERT(len <= sizeof(isa), ("ISA string truncated")); 294 if (len == -1) { 295 if (bootverbose) 296 printf("fill_elf_hwcap: " 297 "Can't find riscv,isa property\n"); 298 return; 299 } else if (strncmp(isa, ISA_PREFIX, ISA_PREFIX_LEN) != 0) { 300 if (bootverbose) 301 printf("fill_elf_hwcap: " 302 "Unsupported ISA string: %s\n", isa); 303 return; 304 } 305 306 /* 307 * The string is specified to be lowercase, but let's be 308 * certain. 309 */ 310 for (int i = 0; i < len; i++) 311 isa[i] = tolower(isa[i]); 312 parse_riscv_isa(isa, len, &hwcap); 313 314 if (elf_hwcap != 0) 315 elf_hwcap &= hwcap; 316 else 317 elf_hwcap = hwcap; 318 } 319 } 320 321 SYSINIT(identcpu, SI_SUB_CPU, SI_ORDER_ANY, fill_elf_hwcap, NULL); 322 #endif 323 324 void 325 identify_cpu(void) 326 { 327 const struct cpu_parts *cpu_partsp; 328 uint32_t part_id; 329 uint32_t impl_id; 330 uint64_t misa; 331 u_int cpu; 332 size_t i; 333 334 cpu_partsp = NULL; 335 336 /* TODO: can we get misa somewhere ? */ 337 misa = 0; 338 339 cpu = PCPU_GET(cpuid); 340 341 impl_id = CPU_IMPL(mimpid); 342 for (i = 0; i < nitems(cpu_implementers); i++) { 343 if (impl_id == cpu_implementers[i].impl_id || 344 cpu_implementers[i].impl_id == 0) { 345 cpu_desc[cpu].cpu_impl = impl_id; 346 cpu_desc[cpu].cpu_impl_name = cpu_implementers[i].impl_name; 347 cpu_partsp = cpu_parts_std; 348 break; 349 } 350 } 351 352 part_id = CPU_PART(misa); 353 for (i = 0; &cpu_partsp[i] != NULL; i++) { 354 if (part_id == cpu_partsp[i].part_id || 355 cpu_partsp[i].part_id == -1) { 356 cpu_desc[cpu].cpu_part_num = part_id; 357 cpu_desc[cpu].cpu_part_name = cpu_partsp[i].part_name; 358 break; 359 } 360 } 361 362 /* Print details for boot CPU or if we want verbose output */ 363 if (cpu == 0 || bootverbose) { 364 printf("CPU(%d): %s %s\n", cpu, 365 cpu_desc[cpu].cpu_impl_name, 366 cpu_desc[cpu].cpu_part_name); 367 } 368 } 369