cpu.c revision 1.6 1 /* $NetBSD: cpu.c,v 1.6 2024/04/07 22:52:53 riastradh 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.6 2024/04/07 22:52:53 riastradh 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; indexed by BP as 0 and APs [1, ncpu - 1]
99 */
100 struct cpu_info cpu_info_store[NCPUINFO] = {
101 [0] = {
102 .ci_cpl = IPL_HIGH,
103 .ci_curlwp = &lwp0,
104 .ci_tlb_info = &pmap_tlb0_info,
105 #ifdef MULTIPROCESSOR
106 .ci_flags = CPUF_PRIMARY | CPUF_PRESENT | CPUF_RUNNING,
107 #endif
108 }
109 };
110
111 /*
112 * setup the per-cpu sysctl tree.
113 */
114 static void
115 cpu_setup_sysctl(device_t dv, struct cpu_info *ci)
116 {
117 const struct sysctlnode *cpunode = NULL;
118
119 sysctl_createv(NULL, 0, NULL, &cpunode,
120 CTLFLAG_PERMANENT,
121 CTLTYPE_NODE, device_xname(dv), NULL,
122 NULL, 0, NULL, 0,
123 CTL_MACHDEP,
124 CTL_CREATE, CTL_EOL);
125
126 if (cpunode == NULL)
127 return;
128 }
129
130
131 static void
132 cpu_identify(device_t self, struct cpu_info *ci)
133 {
134 const register_t mvendorid = sbi_get_mvendorid().value;
135 const register_t marchid = sbi_get_marchid().value;
136 const uint32_t mimpid = sbi_get_mimpid().value;
137 struct cpu_arch *cv_arch = NULL;
138 const char *cv_name = NULL;
139 const char *ca_name = NULL;
140 char vendor[128];
141 char arch[128];
142
143 for (size_t i = 0; i < __arraycount(cpu_vendors); i++) {
144 if (mvendorid == cpu_vendors[i].cv_id) {
145 cv_name = cpu_vendors[i].cv_name;
146 cv_arch = cpu_vendors[i].cv_arch;
147 break;
148 }
149 }
150
151 if (cv_arch != NULL) {
152 for (size_t i = 0; cv_arch[i].ca_name != NULL; i++) {
153 if (marchid == cv_arch[i].ca_id) {
154 ca_name = cv_arch[i].ca_name;
155 break;
156 }
157 }
158 }
159
160 if (cv_name == NULL) {
161 snprintf(vendor, sizeof(vendor), "vendor %" PRIxREGISTER, mvendorid);
162 cv_name = vendor;
163 }
164 if (ca_name == NULL) {
165 snprintf(arch, sizeof(arch), "arch %" PRIxREGISTER, marchid);
166 ca_name = arch;
167 }
168
169 aprint_naive("\n");
170 aprint_normal(": %s %s imp. %" PRIx32 "\n", cv_name, ca_name, mimpid);
171 aprint_verbose_dev(ci->ci_dev,
172 "vendor 0x%" PRIxREGISTER " arch. %" PRIxREGISTER " imp. %" PRIx32 "\n",
173 mvendorid, marchid, mimpid);
174 }
175
176
177 void
178 cpu_attach(device_t dv, cpuid_t hartid)
179 {
180 struct cpu_info *ci;
181
182 /* Check for the BP */
183 if (hartid == cpu_bphartid) {
184 ci = curcpu();
185 KASSERTMSG(ci == &cpu_info_store[0], "ci %p", ci);
186 ci->ci_cpuid = hartid;
187 ci->ci_cpu_freq = riscv_timer_frequency_get();
188 } else {
189 #ifdef MULTIPROCESSOR
190 if ((boothowto & RB_MD1) != 0) {
191 aprint_naive("\n");
192 aprint_normal(": multiprocessor boot disabled\n");
193 return;
194 }
195
196 KASSERT(hartid < MAXCPUS);
197 KASSERT(cpu_hartindex[hartid] < MAXCPUS);
198
199 ci = &cpu_info_store[cpu_hartindex[hartid]];
200
201 ci->ci_cpl = IPL_HIGH;
202 ci->ci_cpuid = hartid;
203
204 if (!cpu_hatched_p(cpu_hartindex[hartid])) {
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 (hartid != cpu_bphartid) {
235 mi_cpu_attach(ci);
236 }
237 #endif /* MULTIPROCESSOR */
238 cpu_setup_sysctl(dv, ci);
239 }
240
241 #ifdef MULTIPROCESSOR
242 /*
243 * Initialise a secondary processor.
244 *
245 * printf isn't available as kmutex(9) relies on curcpu which isn't setup yet.
246 *
247 */
248 void __noasan
249 cpu_init_secondary_processor(u_int cpuindex)
250 {
251 cpu_set_hatched(cpuindex);
252
253 /*
254 * return to assembly to wait for cpu_boot_secondary_processors
255 */
256 }
257
258
259 /*
260 * When we are called, the MMU and caches are on and we are running on the stack
261 * of the idlelwp for this cpu.
262 */
263 void
264 cpu_hatch(struct cpu_info *ci, unsigned long cpuindex)
265 {
266 KASSERT(curcpu() == ci);
267
268 // Show this CPU as present.
269 atomic_or_ulong(&ci->ci_flags, CPUF_PRESENT);
270
271 ci->ci_cpu_freq = riscv_timer_frequency_get();
272
273 riscv_timer_init();
274
275 kcpuset_set(cpus_hatched, cpu_index(ci));
276 kcpuset_set(cpus_running, cpu_index(ci));
277
278 /*
279 * clear my bit of the mailbox to tell cpu_boot_secondary_processors().
280 * Consider that if there are cpu0, 1, 2, 3, and cpu2 is unresponsive,
281 * ci_index for each would be cpu0=0, cpu1=1, cpu2=undef, cpu3=2.
282 * therefore we have to use device_unit instead of ci_index for mbox.
283 */
284
285 cpu_clr_mbox(cpuindex);
286 }
287 #endif /* MULTIPROCESSOR */
288