1 /* $NetBSD: cpu.c,v 1.12 2026/09/29 18:46:38 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2023 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Nick Hudson 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "opt_multiprocessor.h" 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.12 2026/09/29 18:46:38 skrll Exp $"); 36 37 #include <sys/param.h> 38 39 #include <sys/cpu.h> 40 #include <sys/device.h> 41 #include <sys/kmem.h> 42 #include <sys/reboot.h> 43 #include <sys/sysctl.h> 44 45 #include <riscv/cpu.h> 46 #include <riscv/cpuvar.h> 47 #include <riscv/machdep.h> 48 #include <riscv/sbi.h> 49 #include <riscv/sysreg.h> 50 51 #ifdef MULTIPROCESSOR 52 #define NCPUINFO MAXCPUS 53 #else 54 #define NCPUINFO 1 55 #endif /* MULTIPROCESSOR */ 56 57 struct cpu_arch { 58 uint64_t ca_id; 59 const char *ca_name; 60 }; 61 62 struct cpu_arch cpu_arch_sifive[] = { 63 { 64 .ca_id = CPU_SIFIVE_ARCH_7SERIES, 65 .ca_name = "7-Series Processor (E7, S7, U7 series)", 66 }, 67 { }, // terminator 68 }; 69 70 struct cpu_arch cpu_arch_thead[] = { 71 { 72 .ca_id = 0, 73 .ca_name = "9-Series Processor (C9, E9 series)", 74 }, 75 { }, // terminator 76 }; 77 78 struct cpu_vendor { 79 uint32_t cv_id; 80 const char *cv_name; 81 struct cpu_arch *cv_arch; 82 } cpu_vendors[] = { 83 { 84 .cv_id = CPU_VENDOR_SIFIVE, 85 .cv_name = "SiFive", 86 .cv_arch = cpu_arch_sifive, 87 }, 88 { 89 .cv_id = CPU_VENDOR_THEAD, 90 .cv_name = "T-Head", 91 .cv_arch = cpu_arch_thead, 92 }, 93 }; 94 95 /* 96 * Our exported cpu_info structs; indexed by BP as 0 and APs [1, ncpu - 1] 97 */ 98 struct cpu_info cpu_info_store[NCPUINFO] = { 99 [0] = { 100 .ci_cpl = IPL_HIGH, 101 .ci_curlwp = &lwp0, 102 .ci_tlb_info = &pmap_tlb0_info, 103 #ifdef MULTIPROCESSOR 104 .ci_flags = CPUF_PRIMARY | CPUF_PRESENT | CPUF_RUNNING, 105 #endif 106 } 107 }; 108 109 /* 110 * setup the per-cpu sysctl tree. 111 */ 112 static void 113 cpu_setup_sysctl(device_t dv, struct cpu_info *ci) 114 { 115 const struct sysctlnode *cpunode = NULL; 116 117 sysctl_createv(NULL, 0, NULL, &cpunode, 118 CTLFLAG_PERMANENT, 119 CTLTYPE_NODE, device_xname(dv), NULL, 120 NULL, 0, NULL, 0, 121 CTL_MACHDEP, 122 CTL_CREATE, CTL_EOL); 123 124 if (cpunode == NULL) 125 return; 126 } 127 128 129 static void 130 cpu_identify(device_t self, struct cpu_info *ci) 131 { 132 const register_t mvendorid = sbi_get_mvendorid().value; 133 const register_t marchid = sbi_get_marchid().value; 134 const uint32_t mimpid = sbi_get_mimpid().value; 135 struct cpu_arch *cv_arch = NULL; 136 const char *cv_name = NULL; 137 const char *ca_name = NULL; 138 char vendor[128]; 139 char arch[128]; 140 141 for (size_t i = 0; i < __arraycount(cpu_vendors); i++) { 142 if (mvendorid == cpu_vendors[i].cv_id) { 143 cv_name = cpu_vendors[i].cv_name; 144 cv_arch = cpu_vendors[i].cv_arch; 145 break; 146 } 147 } 148 149 if (cv_arch != NULL) { 150 for (size_t i = 0; cv_arch[i].ca_name != NULL; i++) { 151 if (marchid == cv_arch[i].ca_id) { 152 ca_name = cv_arch[i].ca_name; 153 break; 154 } 155 } 156 } 157 158 if (cv_name == NULL) { 159 snprintf(vendor, sizeof(vendor), "vendor %" PRIxREGISTER, mvendorid); 160 cv_name = vendor; 161 } 162 if (ca_name == NULL) { 163 snprintf(arch, sizeof(arch), "arch %" PRIxREGISTER, marchid); 164 ca_name = arch; 165 } 166 167 aprint_naive("\n"); 168 aprint_normal(": %s %s imp. %" PRIx32 "\n", cv_name, ca_name, mimpid); 169 aprint_verbose_dev(ci->ci_dev, 170 "vendor 0x%" PRIxREGISTER " arch. %" PRIxREGISTER " imp. %" PRIx32 "\n", 171 mvendorid, marchid, mimpid); 172 } 173 174 175 void 176 cpu_attach(device_t dv, cpuid_t hartid) 177 { 178 struct cpu_info *ci; 179 180 /* Check for the BP */ 181 if (hartid == cpu_bphartid) { 182 ci = curcpu(); 183 KASSERTMSG(ci == &cpu_info_store[0], "ci %p", ci); 184 ci->ci_cpuid = hartid; 185 ci->ci_cpu_freq = riscv_timer_frequency_get(); 186 } else { 187 #ifdef MULTIPROCESSOR 188 if ((boothowto & RB_MD1) != 0) { 189 aprint_naive("\n"); 190 aprint_normal(": multiprocessor boot disabled\n"); 191 return; 192 } 193 194 KASSERT(hartid < MAXCPUS); 195 KASSERT(cpu_hartindex[hartid] < MAXCPUS); 196 197 ci = &cpu_info_store[cpu_hartindex[hartid]]; 198 199 ci->ci_cpl = IPL_HIGH; 200 ci->ci_cpuid = hartid; 201 202 if (!cpu_hatched_p(cpu_hartindex[hartid])) { 203 ci->ci_dev = dv; 204 device_set_private(dv, ci); 205 ci->ci_index = -1; 206 207 aprint_naive(": disabled\n"); 208 aprint_normal(": disabled (unresponsive)\n"); 209 return; 210 } 211 #else /* MULTIPROCESSOR */ 212 aprint_naive(": disabled\n"); 213 aprint_normal(": disabled (uniprocessor kernel)\n"); 214 return; 215 #endif /* MULTIPROCESSOR */ 216 } 217 218 ci->ci_dev = dv; 219 device_set_private(dv, ci); 220 221 const char * const xname = device_xname(dv); 222 223 evcnt_attach_dynamic(&ci->ci_ev_fpu_loads, EVCNT_TYPE_MISC, NULL, 224 xname, "fpu loads"); 225 evcnt_attach_dynamic(&ci->ci_ev_fpu_saves, EVCNT_TYPE_MISC, NULL, 226 xname, "fpu saves"); 227 evcnt_attach_dynamic(&ci->ci_ev_fpu_reenables, EVCNT_TYPE_MISC, NULL, 228 xname, "fpu reenables"); 229 230 cpu_identify(dv, ci); 231 232 #ifdef MULTIPROCESSOR 233 ipi_init(ci); 234 235 kcpuset_create(&ci->ci_multicastcpus, true); 236 kcpuset_create(&ci->ci_watchcpus, true); 237 kcpuset_create(&ci->ci_ddbcpus, true); 238 239 if (hartid != cpu_bphartid) { 240 mi_cpu_attach(ci); 241 } 242 #endif /* MULTIPROCESSOR */ 243 cpu_setup_sysctl(dv, ci); 244 } 245 246 #ifdef MULTIPROCESSOR 247 /* 248 * Initialise a secondary processor. 249 * 250 * printf isn't available as kmutex(9) relies on curcpu which isn't setup yet. 251 * 252 */ 253 void __noasan 254 cpu_init_secondary_processor(u_int cpuindex) 255 { 256 cpu_set_hatched(cpuindex); 257 258 /* 259 * return to assembly to wait for cpu_boot_secondary_processors 260 */ 261 } 262 263 264 /* 265 * When we are called, the MMU and caches are on and we are running on the stack 266 * of the idlelwp for this cpu. 267 */ 268 void 269 cpu_hatch(struct cpu_info *ci, unsigned long cpuindex) 270 { 271 KASSERT(curcpu() == ci); 272 273 // Show this CPU as present. 274 atomic_or_ulong(&ci->ci_flags, CPUF_PRESENT); 275 276 ci->ci_cpu_freq = riscv_timer_frequency_get(); 277 278 riscv_timer_init(); 279 280 kcpuset_set(cpus_hatched, cpu_index(ci)); 281 kcpuset_set(cpus_running, cpu_index(ci)); 282 283 /* 284 * clear my bit of the mailbox to tell cpu_boot_secondary_processors(). 285 * Consider that if there are cpu0, 1, 2, 3, and cpu2 is unresponsive, 286 * ci_index for each would be cpu0=0, cpu1=1, cpu2=undef, cpu3=2. 287 * therefore we have to use device_unit instead of ci_index for mbox. 288 */ 289 290 cpu_clr_mbox(cpuindex); 291 } 292 #endif /* MULTIPROCESSOR */ 293