1 /* $NetBSD: cpu_fdt.c,v 1.4 2024/01/01 13:51:56 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.4 2024/01/01 13:51:56 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 bool 47 riscv_fdt_cpu_okay(const int node) 48 { 49 const char *s; 50 51 s = fdtbus_get_string(node, "device_type"); 52 if (!s || strcmp(s, "cpu") != 0) 53 return false; 54 55 s = fdtbus_get_string(node, "status"); 56 if (s) { 57 if (strcmp(s, "okay") == 0) 58 return true; 59 if (strcmp(s, "disabled") == 0) 60 return false; 61 return false; 62 } else { 63 return true; 64 } 65 } 66 67 void 68 riscv_fdt_cpu_bootstrap(void) 69 { 70 const int cpus = OF_finddevice("/cpus"); 71 if (cpus == -1) { 72 aprint_error("%s: no /cpus node found\n", __func__); 73 return; 74 } 75 76 /* Count harts and add hart index numbers to the cpu_hartindex array */ 77 u_int cpuindex = 1; 78 for (int child = OF_child(cpus); child; child = OF_peer(child)) { 79 if (!riscv_fdt_cpu_okay(child)) 80 continue; 81 82 uint64_t reg; 83 if (fdtbus_get_reg64(child, 0, ®, NULL) != 0) 84 continue; 85 86 const cpuid_t hartid = reg; 87 if (hartid > MAXCPUS) { 88 aprint_error("hart id too big %lu (%u)", hartid, 89 MAXCPUS); 90 continue; 91 } 92 93 struct sbiret sbiret = sbi_hart_get_status(hartid); 94 switch (sbiret.error) { 95 case SBI_ERR_INVALID_PARAM: 96 aprint_error("Unknown hart id %lx", hartid); 97 continue; 98 case SBI_SUCCESS: 99 break; 100 default: 101 aprint_error("Unexpected error (%ld) from get_status", 102 sbiret.error); 103 } 104 105 /* Assume the BP is the only one started. */ 106 if (sbiret.value == SBI_HART_STARTED) { 107 if (cpu_bphartid != ~0UL) { 108 panic("more than 1 hart started"); 109 } 110 cpu_bphartid = hartid; 111 cpu_hartindex[hartid] = 0; 112 continue; 113 } 114 115 KASSERT(cpuindex < MAXCPUS); 116 cpu_hartindex[hartid] = cpuindex++; 117 } 118 } 119 120 int 121 riscv_fdt_cpu_mpstart(void) 122 { 123 int ret = 0; 124 #ifdef MULTIPROCESSOR 125 const int cpus = OF_finddevice("/cpus"); 126 if (cpus == -1) { 127 aprint_error("%s: no /cpus node found\n", __func__); 128 return 0; 129 } 130 131 /* BootAPs */ 132 u_int cpuindex = 1; 133 for (int child = OF_child(cpus); child; child = OF_peer(child)) { 134 if (!riscv_fdt_cpu_okay(child)) 135 continue; 136 137 uint64_t reg; 138 if (fdtbus_get_reg64(child, 0, ®, NULL) != 0) 139 continue; 140 141 const cpuid_t hartid = reg; 142 if (hartid == cpu_bphartid) 143 continue; /* BP already started */ 144 145 const paddr_t entry = KERN_VTOPHYS(cpu_mpstart); 146 struct sbiret sbiret = sbi_hart_start(hartid, entry, cpuindex); 147 switch (sbiret.error) { 148 case SBI_SUCCESS: 149 break; 150 case SBI_ERR_INVALID_ADDRESS: 151 break; 152 case SBI_ERR_INVALID_PARAM: 153 break; 154 case SBI_ERR_ALREADY_AVAILABLE: 155 break; 156 case SBI_ERR_FAILED: 157 break; 158 default: 159 aprint_error("%s: failed to enable CPU %#lx\n", 160 __func__, hartid); 161 } 162 163 size_t i; 164 /* Wait for AP to start */ 165 for (i = 0x10000000; i > 0; i--) { 166 if (cpu_hatched_p(cpuindex)) 167 break; 168 } 169 170 if (i == 0) { 171 ret++; 172 aprint_error("hart%ld: WARNING: AP %u failed to start\n", 173 hartid, cpuindex); 174 } 175 176 cpuindex++; 177 } 178 #else 179 aprint_normal("%s: kernel compiled without MULTIPROCESSOR\n", __func__); 180 #endif /* MULTIPROCESSOR */ 181 return ret; 182 } 183 184 static int 185 cpu_fdt_match(device_t parent, cfdata_t cf, void *aux) 186 { 187 struct fdt_attach_args * const faa = aux; 188 const int phandle = faa->faa_phandle; 189 const char *device_type; 190 191 device_type = fdtbus_get_string(phandle, "device_type"); 192 return device_type != NULL && strcmp(device_type, "cpu") == 0; 193 } 194 195 static void 196 cpu_fdt_attach(device_t parent, device_t self, void *aux) 197 { 198 struct fdt_attach_args * const faa = aux; 199 const int phandle = faa->faa_phandle; 200 bus_addr_t hartid; 201 202 203 if (fdtbus_get_reg(phandle, 0, &hartid, NULL) != 0) 204 hartid = 0; 205 206 /* Attach the CPU */ 207 cpu_attach(self, hartid); 208 209 fdt_add_bus(self, phandle, faa); 210 } 211 212 CFATTACH_DECL_NEW(cpu_fdt, 0, cpu_fdt_match, cpu_fdt_attach, NULL, NULL); 213