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
riscv_fdt_cpu_okay(const int node)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
riscv_fdt_cpu_bootstrap(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
riscv_fdt_cpu_mpstart(void)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
cpu_fdt_match(device_t parent,cfdata_t cf,void * aux)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
cpu_fdt_attach(device_t parent,device_t self,void * aux)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