1 /* $NetBSD: cpu.c,v 1.3 2023/06/24 07:23:07 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.3 2023/06/24 07:23:07 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 50 #ifdef MULTIPROCESSOR 51 #define NCPUINFO MAXCPUS 52 #else 53 #define NCPUINFO 1 54 #endif /* MULTIPROCESSOR */ 55 56 static void 57 cache_nullop(vaddr_t va, paddr_t pa, psize_t sz) 58 { 59 } 60 61 void (*cpu_sdcache_wbinv_range)(vaddr_t, paddr_t, psize_t) = cache_nullop; 62 void (*cpu_sdcache_inv_range)(vaddr_t, paddr_t, psize_t) = cache_nullop; 63 void (*cpu_sdcache_wb_range)(vaddr_t, paddr_t, psize_t) = cache_nullop; 64 65 u_int riscv_dcache_align = CACHE_LINE_SIZE; 66 u_int riscv_dcache_align_mask = CACHE_LINE_SIZE - 1; 67 68 #define CPU_VENDOR_SIFIVE 0x489 69 70 #define CPU_ARCH_7SERIES 0x8000000000000007 71 72 struct cpu_arch { 73 uint64_t ca_id; 74 const char *ca_name; 75 }; 76 77 struct cpu_arch cpu_arch_sifive[] = { 78 { 79 .ca_id = CPU_ARCH_7SERIES, 80 .ca_name = "7-Series Processor (E7, S7, U7 series)", 81 }, 82 { }, // terminator 83 }; 84 85 struct cpu_vendor { 86 uint32_t cv_id; 87 const char *cv_name; 88 struct cpu_arch *cv_arch; 89 } cpu_vendors[] = { 90 { 91 .cv_id = CPU_VENDOR_SIFIVE, 92 .cv_name = "SiFive", 93 .cv_arch = cpu_arch_sifive, 94 }, 95 }; 96 97 /* 98 * Our exported cpu_info structs; these will be first used by the 99 * secondary cpus as part of cpu_mpstart and the hatching process. 100 */ 101 struct cpu_info cpu_info_store[NCPUINFO] = { 102 [0] = { 103 .ci_cpl = IPL_HIGH, 104 .ci_curlwp = &lwp0, 105 .ci_cpl = IPL_HIGH, 106 .ci_tlb_info = &pmap_tlb0_info, 107 #ifdef MULTIPROCESSOR 108 .ci_flags = CPUF_PRIMARY | CPUF_PRESENT | CPUF_RUNNING, 109 #endif 110 } 111 }; 112 113 114 /* 115 * setup the per-cpu sysctl tree. 116 */ 117 static void 118 cpu_setup_sysctl(device_t dv, struct cpu_info *ci) 119 { 120 const struct sysctlnode *cpunode = NULL; 121 122 sysctl_createv(NULL, 0, NULL, &cpunode, 123 CTLFLAG_PERMANENT, 124 CTLTYPE_NODE, device_xname(dv), NULL, 125 NULL, 0, NULL, 0, 126 CTL_MACHDEP, 127 CTL_CREATE, CTL_EOL); 128 129 if (cpunode == NULL) 130 return; 131 } 132 133 134 static void 135 cpu_identify(device_t self, struct cpu_info *ci) 136 { 137 const register_t mvendorid = sbi_get_mvendorid().value; 138 const register_t marchid = sbi_get_marchid().value; 139 const uint32_t mimpid = sbi_get_mimpid().value; 140 struct cpu_arch *cv_arch = NULL; 141 const char *cv_name = NULL; 142 const char *ca_name = NULL; 143 char vendor[128]; 144 char arch[128]; 145 146 for (size_t i = 0; i < __arraycount(cpu_vendors); i++) { 147 if (mvendorid == cpu_vendors[i].cv_id) { 148 cv_name = cpu_vendors[i].cv_name; 149 cv_arch = cpu_vendors[i].cv_arch; 150 break; 151 } 152 } 153 154 if (cv_arch != NULL) { 155 for (size_t i = 0; cv_arch[i].ca_name != NULL; i++) { 156 if (marchid == cv_arch[i].ca_id) { 157 ca_name = cv_arch[i].ca_name; 158 break; 159 } 160 } 161 } 162 163 if (cv_name == NULL) { 164 snprintf(vendor, sizeof(vendor), "vendor %" PRIxREGISTER, mvendorid); 165 cv_name = vendor; 166 } 167 if (ca_name == NULL) { 168 snprintf(arch, sizeof(arch), "arch %" PRIxREGISTER, marchid); 169 ca_name = arch; 170 } 171 172 aprint_naive("\n"); 173 aprint_normal(": %s %s imp. %" PRIx32 "\n", cv_name, ca_name, mimpid); 174 aprint_verbose_dev(ci->ci_dev, 175 "vendor 0x%" PRIxREGISTER " arch. %" PRIxREGISTER " imp. %" PRIx32 "\n", 176 mvendorid, marchid, mimpid); 177 } 178 179 180 void 181 cpu_attach(device_t dv, cpuid_t id) 182 { 183 const int unit = device_unit(dv); 184 struct cpu_info *ci; 185 186 if (unit == 0) { 187 ci = curcpu(); 188 ci->ci_cpuid = id; 189 } else { 190 #ifdef MULTIPROCESSOR 191 if ((boothowto & RB_MD1) != 0) { 192 aprint_naive("\n"); 193 aprint_normal(": multiprocessor boot disabled\n"); 194 return; 195 } 196 197 KASSERT(unit < MAXCPUS); 198 ci = &cpu_info_store[unit]; 199 200 ci->ci_cpl = IPL_HIGH; 201 ci->ci_cpuid = id; 202 /* ci_cpuid is stored by own cpus when hatching */ 203 204 if (cpu_hatched_p(unit) == 0) { 205 ci->ci_dev = dv; 206 device_set_private(dv, ci); 207 ci->ci_index = -1; 208 209 aprint_naive(": disabled\n"); 210 aprint_normal(": disabled (unresponsive)\n"); 211 return; 212 } 213 #else /* MULTIPROCESSOR */ 214 aprint_naive(": disabled\n"); 215 aprint_normal(": disabled (uniprocessor kernel)\n"); 216 return; 217 #endif /* MULTIPROCESSOR */ 218 } 219 220 ci->ci_dev = dv; 221 device_set_private(dv, ci); 222 223 cpu_identify(dv, ci); 224 225 #ifdef MULTIPROCESSOR 226 kcpuset_create(&ci->ci_shootdowncpus, true); 227 228 ipi_init(ci); 229 230 kcpuset_create(&ci->ci_multicastcpus, true); 231 kcpuset_create(&ci->ci_watchcpus, true); 232 kcpuset_create(&ci->ci_ddbcpus, true); 233 234 if (unit != 0) { 235 mi_cpu_attach(ci); 236 struct pmap_tlb_info *ti = kmem_zalloc(sizeof(*ti), KM_SLEEP); 237 pmap_tlb_info_init(ti); 238 pmap_tlb_info_attach(ti, ci); 239 } 240 #endif /* MULTIPROCESSOR */ 241 cpu_setup_sysctl(dv, ci); 242 243 if (unit != 0) { 244 return; 245 } 246 } 247 248 #ifdef MULTIPROCESSOR 249 /* 250 * Initialise a secondary processor. 251 * 252 * printf isn't available as kmutex(9) relies on curcpu which isn't setup yet. 253 * 254 */ 255 void __noasan 256 cpu_init_secondary_processor(int cpuindex) 257 { 258 cpu_set_hatched(cpuindex); 259 260 /* 261 * return to assembly to wait for cpu_boot_secondary_processors 262 */ 263 } 264 265 266 /* 267 * When we are called, the MMU and caches are on and we are running on the stack 268 * of the idlelwp for this cpu. 269 */ 270 void 271 cpu_hatch(struct cpu_info *ci) 272 { 273 KASSERT(curcpu() == ci); 274 275 ci->ci_cpu_freq = riscv_timer_frequency_get(); 276 277 riscv_timer_init(); 278 279 /* 280 * clear my bit of the mailbox to tell cpu_boot_secondary_processors(). 281 * Consider that if there are cpu0, 1, 2, 3, and cpu2 is unresponsive, 282 * ci_index for each would be cpu0=0, cpu1=1, cpu2=undef, cpu3=2. 283 * therefore we have to use device_unit instead of ci_index for mbox. 284 */ 285 286 cpu_clr_mbox(device_unit(ci->ci_dev)); 287 } 288 #endif /* MULTIPROCESSOR */ 289