1 /* $OpenBSD: db_interface.c,v 1.6 2022/04/14 19:47:11 naddy Exp $ */
2 /* $NetBSD: db_interface.c,v 1.12 2001/07/22 11:29:46 wiz Exp $ */
3
4 /*
5 * Mach Operating System
6 * Copyright (c) 1991,1990 Carnegie Mellon University
7 * All Rights Reserved.
8 *
9 * Permission to use, copy, modify and distribute this software and its
10 * documentation is hereby granted, provided that both the copyright
11 * notice and this permission notice appear in all copies of the
12 * software, derivative works or modified versions, and any portions
13 * thereof, and that both notices appear in supporting documentation.
14 *
15 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
16 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
17 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
18 *
19 * Carnegie Mellon requests users of this software to return to
20 *
21 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
22 * School of Computer Science
23 * Carnegie Mellon University
24 * Pittsburgh PA 15213-3890
25 *
26 * any improvements or extensions that they make and grant Carnegie the
27 * rights to redistribute these changes.
28 *
29 * db_interface.c,v 2.4 1991/02/05 17:11:13 mrt (CMU)
30 */
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/mutex.h>
35
36 #include <dev/cons.h>
37 #include <dev/ofw/fdt.h>
38
39 #include <machine/db_machdep.h>
40 #include <ddb/db_command.h>
41 #include <ddb/db_elf.h>
42 #include <ddb/db_extern.h>
43 #include <ddb/db_output.h>
44 #include <ddb/db_run.h>
45 #include <ddb/db_sym.h>
46
47 extern db_regs_t ddb_regs; /* db_trace.c */
48 extern db_symtab_t db_symtab; /* ddb/db_elf.c */
49 extern struct fdt_reg initrd_reg; /* machdep.c */
50
51 #ifdef MULTIPROCESSOR
52
53 struct db_mutex ddb_mp_mutex = DB_MUTEX_INITIALIZER;
54 volatile int ddb_state = DDB_STATE_NOT_RUNNING;
55 volatile cpuid_t ddb_active_cpu;
56 int db_switch_cpu;
57 long db_switch_to_cpu;
58
59 void db_cpuinfo_cmd(db_expr_t, int, db_expr_t, char *);
60 void db_startproc_cmd(db_expr_t, int, db_expr_t, char *);
61 void db_stopproc_cmd(db_expr_t, int, db_expr_t, char *);
62 void db_ddbproc_cmd(db_expr_t, int, db_expr_t, char *);
63
64 void db_stopcpu(int cpu);
65 void db_startcpu(int cpu);
66 int db_enter_ddb(void);
67
68 #endif
69
70 const struct db_command db_machine_command_table[] = {
71 #ifdef MULTIPROCESSOR
72 { "cpuinfo", db_cpuinfo_cmd, 0, NULL },
73 { "startcpu", db_startproc_cmd, 0, NULL },
74 { "stopcpu", db_stopproc_cmd, 0, NULL },
75 { "ddbcpu", db_ddbproc_cmd, 0, NULL },
76 #endif
77 { (char *)NULL }
78 };
79
80 void
db_machine_init(void)81 db_machine_init(void)
82 {
83 db_expr_t val;
84 uint64_t a, b;
85 char *prop_start, *prop_end;
86 void *node;
87 #ifdef MULTIPROCESSOR
88 int i;
89 #endif
90
91 /*
92 * petitboot loads the kernel without symbols.
93 * If an initrd exists, try to load symbols from there.
94 */
95 node = fdt_find_node("/chosen");
96 if (fdt_node_property(node, "linux,initrd-start", &prop_start) != 8 ||
97 fdt_node_property(node, "linux,initrd-end", &prop_end) != 8) {
98 printf("[ no initrd ]\n");
99 return;
100 }
101
102 a = bemtoh64((uint64_t *)prop_start);
103 b = bemtoh64((uint64_t *)prop_end);
104 initrd_reg.addr = trunc_page(a);
105 initrd_reg.size = round_page(b) - initrd_reg.addr;
106 db_elf_sym_init(b - a, (char *)a, (char *)b, "initrd");
107
108 /* The kernel is PIE. Add an offset to most symbols. */
109 if (db_symbol_by_name("db_machine_init", &val) != NULL) {
110 Elf_Sym *symp, *symtab_start, *symtab_end;
111 Elf_Addr offset;
112
113 symtab_start = STAB_TO_SYMSTART(&db_symtab);
114 symtab_end = STAB_TO_SYMEND(&db_symtab);
115
116 offset = (Elf_Addr)db_machine_init - (Elf_Addr)val;
117 for (symp = symtab_start; symp < symtab_end; symp++) {
118 if (symp->st_shndx != SHN_ABS)
119 symp->st_value += offset;
120 }
121 }
122
123 #ifdef MULTIPROCESSOR
124 for (i = 0; i < ncpus; i++) {
125 cpu_info[i].ci_ddb_paused = CI_DDB_RUNNING;
126 }
127 #endif
128 }
129
130 void
db_ktrap(int type,db_regs_t * frame)131 db_ktrap(int type, db_regs_t *frame)
132 {
133 int s;
134
135 #ifdef MULTIPROCESSOR
136 db_mtx_enter(&ddb_mp_mutex);
137 if (ddb_state == DDB_STATE_EXITING)
138 ddb_state = DDB_STATE_NOT_RUNNING;
139 db_mtx_leave(&ddb_mp_mutex);
140
141 while (db_enter_ddb()) {
142 #endif
143 ddb_regs = *frame;
144
145 s = splhigh();
146 db_active++;
147 cnpollc(1);
148 db_trap(type, 0);
149 cnpollc(0);
150 db_active--;
151 splx(s);
152
153 *frame = ddb_regs;
154 #ifdef MULTIPROCESSOR
155 if (!db_switch_cpu)
156 ddb_state = DDB_STATE_EXITING;
157 }
158 #endif
159 }
160
161 #ifdef MULTIPROCESSOR
162
163 int
db_enter_ddb(void)164 db_enter_ddb(void)
165 {
166 struct cpu_info *ci = curcpu();
167 int i;
168
169 db_mtx_enter(&ddb_mp_mutex);
170
171 /* If we are first in, grab ddb and stop all other CPUs */
172 if (ddb_state == DDB_STATE_NOT_RUNNING) {
173 ddb_active_cpu = cpu_number();
174 ddb_state = DDB_STATE_RUNNING;
175 ci->ci_ddb_paused = CI_DDB_INDDB;
176 db_mtx_leave(&ddb_mp_mutex);
177 for (i = 0; i < ncpus; i++) {
178 if (i != cpu_number() &&
179 cpu_info[i].ci_ddb_paused != CI_DDB_STOPPED) {
180 cpu_info[i].ci_ddb_paused = CI_DDB_SHOULDSTOP;
181 intr_send_ipi(&cpu_info[i], IPI_DDB);
182 }
183 }
184 return (1);
185 }
186
187 /* Leaving ddb completely. Start all other CPUs and return 0 */
188 if (ddb_active_cpu == cpu_number() && ddb_state == DDB_STATE_EXITING) {
189 for (i = 0; i < ncpus; i++) {
190 cpu_info[i].ci_ddb_paused = CI_DDB_RUNNING;
191 }
192 db_mtx_leave(&ddb_mp_mutex);
193 return (0);
194 }
195
196 /* We are switching to another CPU. ddb_ddbproc_cmd() has made sure
197 * it is waiting for ddb, we just have to set ddb_active_cpu. */
198 if (ddb_active_cpu == cpu_number() && db_switch_cpu) {
199 ci->ci_ddb_paused = CI_DDB_SHOULDSTOP;
200 db_switch_cpu = 0;
201 ddb_active_cpu = db_switch_to_cpu;
202 cpu_info[db_switch_to_cpu].ci_ddb_paused = CI_DDB_ENTERDDB;
203 }
204
205 /* Wait until we should enter ddb or resume */
206 while (ddb_active_cpu != cpu_number() &&
207 ci->ci_ddb_paused != CI_DDB_RUNNING) {
208 if (ci->ci_ddb_paused == CI_DDB_SHOULDSTOP)
209 ci->ci_ddb_paused = CI_DDB_STOPPED;
210 db_mtx_leave(&ddb_mp_mutex);
211
212 /* Busy wait without locking, we will confirm with lock later */
213 while (ddb_active_cpu != cpu_number() &&
214 ci->ci_ddb_paused != CI_DDB_RUNNING)
215 ; /* Do nothing */
216
217 db_mtx_enter(&ddb_mp_mutex);
218 }
219
220 /* Either enter ddb or exit */
221 if (ddb_active_cpu == cpu_number() && ddb_state == DDB_STATE_RUNNING) {
222 ci->ci_ddb_paused = CI_DDB_INDDB;
223 db_mtx_leave(&ddb_mp_mutex);
224 return (1);
225 } else {
226 db_mtx_leave(&ddb_mp_mutex);
227 return (0);
228 }
229 }
230
231 void
db_cpuinfo_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * modif)232 db_cpuinfo_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
233 {
234 int i;
235
236 for (i = 0; i < ncpus; i++) {
237 db_printf("%c%4d: ", (i == cpu_number()) ? '*' : ' ',
238 cpu_info[i].ci_cpuid);
239 switch(cpu_info[i].ci_ddb_paused) {
240 case CI_DDB_RUNNING:
241 db_printf("running\n");
242 break;
243 case CI_DDB_SHOULDSTOP:
244 db_printf("stopping\n");
245 break;
246 case CI_DDB_STOPPED:
247 db_printf("stopped\n");
248 break;
249 case CI_DDB_ENTERDDB:
250 db_printf("entering ddb\n");
251 break;
252 case CI_DDB_INDDB:
253 db_printf("ddb\n");
254 break;
255 default:
256 db_printf("? (%d)\n",
257 cpu_info[i].ci_ddb_paused);
258 break;
259 }
260 }
261 }
262
263 void
db_ddbproc_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * modif)264 db_ddbproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
265 {
266 int cpu_n;
267
268 if (have_addr) {
269 cpu_n = addr;
270 if (cpu_n >= 0 && cpu_n < ncpus &&
271 cpu_n != cpu_number()) {
272 db_switch_to_cpu = cpu_n;
273 db_switch_cpu = 1;
274 db_cmd_loop_done = 1;
275 } else {
276 db_printf("Invalid cpu %d\n", (int)addr);
277 }
278 } else {
279 db_printf("CPU not specified\n");
280 }
281 }
282
283 void
db_startproc_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * modif)284 db_startproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
285 {
286 int cpu_n;
287
288 if (have_addr) {
289 cpu_n = addr;
290 if (cpu_n >= 0 && cpu_n < ncpus &&
291 cpu_n != cpu_number())
292 db_startcpu(cpu_n);
293 else
294 db_printf("Invalid cpu %d\n", (int)addr);
295 } else {
296 for (cpu_n = 0; cpu_n < ncpus; cpu_n++) {
297 if (cpu_n != cpu_number()) {
298 db_startcpu(cpu_n);
299 }
300 }
301 }
302 }
303
304 void
db_stopproc_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * modif)305 db_stopproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
306 {
307 int cpu_n;
308
309 if (have_addr) {
310 cpu_n = addr;
311 if (cpu_n >= 0 && cpu_n < ncpus &&
312 cpu_n != cpu_number())
313 db_stopcpu(cpu_n);
314 else
315 db_printf("Invalid cpu %d\n", (int)addr);
316 } else {
317 for (cpu_n = 0; cpu_n < ncpus; cpu_n++) {
318 if (cpu_n != cpu_number()) {
319 db_stopcpu(cpu_n);
320 }
321 }
322 }
323 }
324
325 void
db_startcpu(int cpu)326 db_startcpu(int cpu)
327 {
328 if (cpu != cpu_number() && cpu < ncpus) {
329 db_mtx_enter(&ddb_mp_mutex);
330 cpu_info[cpu].ci_ddb_paused = CI_DDB_RUNNING;
331 db_mtx_leave(&ddb_mp_mutex);
332 }
333 }
334
335 void
db_stopcpu(int cpu)336 db_stopcpu(int cpu)
337 {
338 db_mtx_enter(&ddb_mp_mutex);
339 if (cpu != cpu_number() && cpu < ncpus &&
340 cpu_info[cpu].ci_ddb_paused != CI_DDB_STOPPED) {
341 cpu_info[cpu].ci_ddb_paused = CI_DDB_SHOULDSTOP;
342 db_mtx_leave(&ddb_mp_mutex);
343 intr_send_ipi(&cpu_info[cpu], IPI_DDB);
344 } else {
345 db_mtx_leave(&ddb_mp_mutex);
346 }
347 }
348
349 #endif
350