1 /* $NetBSD: cpu_fdt.c,v 1.3 2023/09/03 08:48:19 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include "opt_multiprocessor.h" 30 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: cpu_fdt.c,v 1.3 2023/09/03 08:48:19 skrll Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/cpu.h> 36 37 #include <dev/fdt/fdtvar.h> 38 39 #include <riscv/cpufunc.h> 40 #include <riscv/cpuvar.h> 41 #include <riscv/machdep.h> 42 #include <riscv/sbi.h> 43 44 #include <riscv/fdt/riscv_fdtvar.h> 45 46 47 static bool 48 riscv_fdt_cpu_okay(const int child) 49 { 50 const char *s; 51 52 s = fdtbus_get_string(child, "device_type"); 53 if (!s || strcmp(s, "cpu") != 0) 54 return false; 55 56 s = fdtbus_get_string(child, "status"); 57 if (s) { 58 if (strcmp(s, "okay") == 0) 59 return true; 60 if (strcmp(s, "disabled") == 0) 61 return false; 62 return false; 63 } else { 64 return true; 65 } 66 } 67 68 void 69 riscv_fdt_cpu_bootstrap(void) 70 { 71 const int cpus = OF_finddevice("/cpus"); 72 if (cpus == -1) { 73 aprint_error("%s: no /cpus node found\n", __func__); 74 return; 75 } 76 77 /* Count harts and add hart index numbers to the cpu_hartindex array */ 78 u_int cpuindex = 1; 79 for (int child = OF_child(cpus); child; child = OF_peer(child)) { 80 if (!riscv_fdt_cpu_okay(child)) 81 continue; 82 83 uint64_t reg; 84 if (fdtbus_get_reg64(child, 0, ®, NULL) != 0) 85 continue; 86 87 const cpuid_t hartid = reg; 88 if (hartid > MAXCPUS) { 89 aprint_error("hart id too big %lu (%u)", hartid, 90 MAXCPUS); 91 continue; 92 } 93 94 struct sbiret sbiret = sbi_hart_get_status(hartid); 95 switch (sbiret.error) { 96 case SBI_ERR_INVALID_PARAM: 97 aprint_error("Unknown hart id %lx", hartid); 98 continue; 99 case SBI_SUCCESS: 100 break; 101 default: 102 aprint_error("Unexpected error (%ld) from get_status", 103 sbiret.error); 104 } 105 106 /* Assume the BP is the only one started. */ 107 if (sbiret.value == SBI_HART_STARTED) { 108 if (cpu_bphartid != ~0UL) { 109 panic("more than 1 hart started"); 110 } 111 cpu_bphartid = hartid; 112 cpu_hartindex[hartid] = 0; 113 continue; 114 } 115 116 KASSERT(cpuindex < MAXCPUS); 117 cpu_hartindex[hartid] = cpuindex++; 118 } 119 } 120 121 int 122 riscv_fdt_cpu_mpstart(void) 123 { 124 int ret = 0; 125 #ifdef MULTIPROCESSOR 126 const int cpus = OF_finddevice("/cpus"); 127 if (cpus == -1) { 128 aprint_error("%s: no /cpus node found\n", __func__); 129 return 0; 130 } 131 132 /* BootAPs */ 133 u_int cpuindex = 1; 134 for (int child = OF_child(cpus); child; child = OF_peer(child)) { 135 if (!riscv_fdt_cpu_okay(child)) 136 continue; 137 138 uint64_t reg; 139 if (fdtbus_get_reg64(child, 0, ®, NULL) != 0) 140 continue; 141 142 const cpuid_t hartid = reg; 143 if (hartid == cpu_bphartid) 144 continue; /* BP already started */ 145 146 const paddr_t entry = KERN_VTOPHYS(cpu_mpstart); 147 struct sbiret sbiret = sbi_hart_start(hartid, entry, cpuindex); 148 switch (sbiret.error) { 149 case SBI_SUCCESS: 150 break; 151 case SBI_ERR_INVALID_ADDRESS: 152 break; 153 case SBI_ERR_INVALID_PARAM: 154 break; 155 case SBI_ERR_ALREADY_AVAILABLE: 156 break; 157 case SBI_ERR_FAILED: 158 break; 159 default: 160 aprint_error("%s: failed to enable CPU %#lx\n", 161 __func__, hartid); 162 } 163 164 size_t i; 165 /* Wait for AP to start */ 166 for (i = 0x10000000; i > 0; i--) { 167 if (cpu_hatched_p(cpuindex)) 168 break; 169 } 170 171 if (i == 0) { 172 ret++; 173 aprint_error("hart%ld: WARNING: AP %u failed to start\n", 174 hartid, cpuindex); 175 } 176 177 cpuindex++; 178 } 179 #else 180 aprint_normal("%s: kernel compiled without MULTIPROCESSOR\n", __func__); 181 #endif /* MULTIPROCESSOR */ 182 return ret; 183 } 184 185 static int 186 cpu_fdt_match(device_t parent, cfdata_t cf, void *aux) 187 { 188 struct fdt_attach_args * const faa = aux; 189 const int phandle = faa->faa_phandle; 190 const char *device_type; 191 192 device_type = fdtbus_get_string(phandle, "device_type"); 193 return device_type != NULL && strcmp(device_type, "cpu") == 0; 194 } 195 196 static void 197 cpu_fdt_attach(device_t parent, device_t self, void *aux) 198 { 199 struct fdt_attach_args * const faa = aux; 200 const int phandle = faa->faa_phandle; 201 bus_addr_t hartid; 202 203 204 if (fdtbus_get_reg(phandle, 0, &hartid, NULL) != 0) 205 hartid = 0; 206 207 /* Attach the CPU */ 208 cpu_attach(self, hartid); 209 210 fdt_add_bus(self, phandle, faa); 211 } 212 213 CFATTACH_DECL_NEW(cpu_fdt, 0, cpu_fdt_match, cpu_fdt_attach, NULL, NULL); 214