1 /* $OpenBSD: db_machdep.c,v 1.61 2024/02/23 18:19:03 cheloha Exp $ */
2
3 /*
4 * Copyright (c) 1998-2003 Opsycon AB (www.opsycon.se)
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 */
28
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/mutex.h>
32 #include <sys/proc.h>
33 #include <dev/cons.h>
34
35 #include <mips64/cache.h>
36
37 #include <machine/autoconf.h>
38 #include <machine/cpu.h>
39 #include <machine/db_machdep.h>
40 #include <machine/mips_opcode.h>
41 #include <machine/pte.h>
42 #include <machine/frame.h>
43 #include <machine/regnum.h>
44
45 #include <ddb/db_sym.h>
46 #include <ddb/db_extern.h>
47 #include <ddb/db_access.h>
48 #include <ddb/db_command.h>
49 #include <ddb/db_output.h>
50 #include <ddb/db_variables.h>
51 #include <ddb/db_interface.h>
52 #include <ddb/db_run.h>
53
54 #define MIPS_JR_RA 0x03e00008 /* instruction code for jr ra */
55
56 void stacktrace_subr(db_regs_t *, int, int (*)(const char*, ...));
57
58 uint32_t kdbpeek(vaddr_t);
59 uint64_t kdbpeekd(vaddr_t);
60 uint16_t kdbpeekw(vaddr_t);
61 uint8_t kdbpeekb(vaddr_t);
62 void kdbpoke(vaddr_t, uint32_t);
63 void kdbpoked(vaddr_t, uint64_t);
64 void kdbpokew(vaddr_t, uint16_t);
65 void kdbpokeb(vaddr_t, uint8_t);
66 int db_ktrap(int, struct trapframe *);
67
68 void db_print_tlb(uint, uint64_t);
69 void db_trap_trace_cmd(db_expr_t, int, db_expr_t, char *);
70 void db_dump_tlb_cmd(db_expr_t, int, db_expr_t, char *);
71
72 #ifdef MULTIPROCESSOR
73 struct db_mutex ddb_mp_mutex = DB_MUTEX_INITIALIZER;
74 volatile int ddb_state = DDB_STATE_NOT_RUNNING;
75 volatile cpuid_t ddb_active_cpu;
76 int db_switch_cpu;
77 long db_switch_to_cpu;
78 #endif
79
80 db_regs_t ddb_regs;
81
82 #ifdef MULTIPROCESSOR
83 void db_cpuinfo_cmd(db_expr_t, int, db_expr_t, char *);
84 void db_startproc_cmd(db_expr_t, int, db_expr_t, char *);
85 void db_stopproc_cmd(db_expr_t, int, db_expr_t, char *);
86 void db_ddbproc_cmd(db_expr_t, int, db_expr_t, char *);
87 #endif
88
89 struct db_variable db_regs[] = {
90 { "at", (long *)&ddb_regs.ast, FCN_NULL },
91 { "v0", (long *)&ddb_regs.v0, FCN_NULL },
92 { "v1", (long *)&ddb_regs.v1, FCN_NULL },
93 { "a0", (long *)&ddb_regs.a0, FCN_NULL },
94 { "a1", (long *)&ddb_regs.a1, FCN_NULL },
95 { "a2", (long *)&ddb_regs.a2, FCN_NULL },
96 { "a3", (long *)&ddb_regs.a3, FCN_NULL },
97 { "a4", (long *)&ddb_regs.a4, FCN_NULL },
98 { "a5", (long *)&ddb_regs.a5, FCN_NULL },
99 { "a6", (long *)&ddb_regs.a6, FCN_NULL },
100 { "a7", (long *)&ddb_regs.a7, FCN_NULL },
101 { "t0", (long *)&ddb_regs.t0, FCN_NULL },
102 { "t1", (long *)&ddb_regs.t1, FCN_NULL },
103 { "t2", (long *)&ddb_regs.t2, FCN_NULL },
104 { "t3", (long *)&ddb_regs.t3, FCN_NULL },
105 { "s0", (long *)&ddb_regs.s0, FCN_NULL },
106 { "s1", (long *)&ddb_regs.s1, FCN_NULL },
107 { "s2", (long *)&ddb_regs.s2, FCN_NULL },
108 { "s3", (long *)&ddb_regs.s3, FCN_NULL },
109 { "s4", (long *)&ddb_regs.s4, FCN_NULL },
110 { "s5", (long *)&ddb_regs.s5, FCN_NULL },
111 { "s6", (long *)&ddb_regs.s6, FCN_NULL },
112 { "s7", (long *)&ddb_regs.s7, FCN_NULL },
113 { "t8", (long *)&ddb_regs.t8, FCN_NULL },
114 { "t9", (long *)&ddb_regs.t9, FCN_NULL },
115 { "k0", (long *)&ddb_regs.k0, FCN_NULL },
116 { "k1", (long *)&ddb_regs.k1, FCN_NULL },
117 { "gp", (long *)&ddb_regs.gp, FCN_NULL },
118 { "sp", (long *)&ddb_regs.sp, FCN_NULL },
119 { "s8", (long *)&ddb_regs.s8, FCN_NULL },
120 { "ra", (long *)&ddb_regs.ra, FCN_NULL },
121 { "sr", (long *)&ddb_regs.sr, FCN_NULL },
122 { "lo", (long *)&ddb_regs.mullo, FCN_NULL },
123 { "hi", (long *)&ddb_regs.mulhi, FCN_NULL },
124 { "bad", (long *)&ddb_regs.badvaddr,FCN_NULL },
125 { "cs", (long *)&ddb_regs.cause, FCN_NULL },
126 { "pc", (long *)&ddb_regs.pc, FCN_NULL },
127 };
128 struct db_variable *db_eregs = db_regs + nitems(db_regs);
129
130 extern label_t *db_recover;
131
132 int
db_ktrap(int type,struct trapframe * fp)133 db_ktrap(int type, struct trapframe *fp)
134 {
135 switch(type) {
136 case T_BREAK: /* breakpoint */
137 if (db_get_value((fp)->pc, sizeof(int), 0) == BREAK_SOVER) {
138 (fp)->pc += BKPT_SIZE;
139 }
140 break;
141 case -1:
142 break;
143 default:
144 #if 0
145 if (!db_panic)
146 return (0);
147 #endif
148 if (db_recover != 0) {
149 db_error("Caught exception in ddb.\n");
150 /*NOTREACHED*/
151 }
152 printf("stopped on non ddb fault\n");
153 }
154
155 #ifdef MULTIPROCESSOR
156 db_mtx_enter(&ddb_mp_mutex);
157 if (ddb_state == DDB_STATE_EXITING)
158 ddb_state = DDB_STATE_NOT_RUNNING;
159 db_mtx_leave(&ddb_mp_mutex);
160
161 while (db_enter_ddb()) {
162 #endif
163 bcopy((void *)fp, (void *)&ddb_regs, NUMSAVEREGS * sizeof(register_t));
164
165 db_active++;
166 cnpollc(1);
167 db_trap(type, 0);
168 cnpollc(0);
169 db_active--;
170
171 bcopy((void *)&ddb_regs, (void *)fp, NUMSAVEREGS * sizeof(register_t));
172 #ifdef MULTIPROCESSOR
173 if (!db_switch_cpu)
174 ddb_state = DDB_STATE_EXITING;
175 }
176 #endif
177 return 1;
178 }
179
180 #ifdef MULTIPROCESSOR
181 int
db_enter_ddb(void)182 db_enter_ddb(void)
183 {
184 int i;
185 struct cpu_info *ci = curcpu();
186 db_mtx_enter(&ddb_mp_mutex);
187
188 #ifdef DEBUG
189 printf("db_enter_ddb %lu: state %x pause %x\n", ci->ci_cpuid,
190 ddb_state, ci->ci_ddb);
191 #endif
192 /* If we are first in, grab ddb and stop all other CPUs */
193 if (ddb_state == DDB_STATE_NOT_RUNNING) {
194 ddb_active_cpu = cpu_number();
195 ddb_state = DDB_STATE_RUNNING;
196 ci->ci_ddb = CI_DDB_INDDB;
197 for (i = 0; i < ncpus; i++) {
198 if (i != cpu_number() &&
199 get_cpu_info(i)->ci_ddb != CI_DDB_STOPPED) {
200 get_cpu_info(i)->ci_ddb = CI_DDB_SHOULDSTOP;
201 mips64_send_ipi(get_cpu_info(i)->ci_cpuid, MIPS64_IPI_DDB);
202 }
203 }
204 db_mtx_leave(&ddb_mp_mutex);
205 return (1);
206 }
207
208 /* Leaving ddb completely. Start all other CPUs and return 0 */
209 if (ddb_active_cpu == cpu_number() && ddb_state == DDB_STATE_EXITING) {
210 for (i = 0; i < ncpus; i++) {
211 get_cpu_info(i)->ci_ddb = CI_DDB_RUNNING;
212 }
213 db_mtx_leave(&ddb_mp_mutex);
214 return (0);
215 }
216
217 /* We are switching to another CPU. ddb_ddbproc_cmd() has made sure
218 * it is waiting for ddb, we just have to set ddb_active_cpu. */
219 if (ddb_active_cpu == cpu_number() && db_switch_cpu) {
220 ci->ci_ddb = CI_DDB_SHOULDSTOP;
221 db_switch_cpu = 0;
222 ddb_active_cpu = db_switch_to_cpu;
223 get_cpu_info(db_switch_to_cpu)->ci_ddb = CI_DDB_ENTERDDB;
224 }
225
226 /* Wait until we should enter ddb or resume */
227 while (ddb_active_cpu != cpu_number() &&
228 ci->ci_ddb != CI_DDB_RUNNING) {
229 if (ci->ci_ddb == CI_DDB_SHOULDSTOP)
230 ci->ci_ddb = CI_DDB_STOPPED;
231 db_mtx_leave(&ddb_mp_mutex);
232 /* Busy wait without locking, we will confirm with lock later */
233 while (ddb_active_cpu != cpu_number() &&
234 ci->ci_ddb != CI_DDB_RUNNING)
235 ; /* Do nothing */
236 db_mtx_enter(&ddb_mp_mutex);
237 }
238
239 /* Either enter ddb or exit */
240 if (ddb_active_cpu == cpu_number() && ddb_state == DDB_STATE_RUNNING) {
241 ci->ci_ddb = CI_DDB_INDDB;
242 db_mtx_leave(&ddb_mp_mutex);
243 return (1);
244 } else {
245 db_mtx_leave(&ddb_mp_mutex);
246 return (0);
247 }
248 }
249
250 void
db_cpuinfo_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * modif)251 db_cpuinfo_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
252 {
253 int i;
254
255 for (i = 0; i < ncpus; i++) {
256 db_printf("%c%4lu: ", (i == cpu_number()) ? '*' : ' ',
257 get_cpu_info(i)->ci_cpuid);
258 switch(get_cpu_info(i)->ci_ddb) {
259 case CI_DDB_RUNNING:
260 db_printf("running\n");
261 break;
262 case CI_DDB_SHOULDSTOP:
263 db_printf("stopping\n");
264 break;
265 case CI_DDB_STOPPED:
266 db_printf("stopped\n");
267 break;
268 case CI_DDB_ENTERDDB:
269 db_printf("entering ddb\n");
270 break;
271 case CI_DDB_INDDB:
272 db_printf("ddb\n");
273 break;
274 default:
275 db_printf("? (%d)\n",
276 get_cpu_info(i)->ci_ddb);
277 break;
278 }
279 }
280 }
281 #endif
282
283 void
db_read_bytes(vaddr_t addr,size_t size,void * datap)284 db_read_bytes(vaddr_t addr, size_t size, void *datap)
285 {
286 char *data = datap;
287
288 while (size >= sizeof(uint32_t)) {
289 *(uint32_t *)data = kdbpeek(addr);
290 data += sizeof(uint32_t);
291 addr += sizeof(uint32_t);
292 size -= sizeof(uint32_t);
293 }
294
295 if (size >= sizeof(uint16_t)) {
296 *(uint16_t *)data = kdbpeekw(addr);
297 data += sizeof(uint16_t);
298 addr += sizeof(uint16_t);
299 size -= sizeof(uint16_t);
300 }
301
302 if (size)
303 *(uint8_t *)data = kdbpeekb(addr);
304 }
305
306 void
db_write_bytes(vaddr_t addr,size_t size,void * datap)307 db_write_bytes(vaddr_t addr, size_t size, void *datap)
308 {
309 char *data = datap;
310 vaddr_t ptr = addr;
311 size_t len = size;
312
313 while (len >= sizeof(uint32_t)) {
314 kdbpoke(ptr, *(uint32_t *)data);
315 data += sizeof(uint32_t);
316 ptr += sizeof(uint32_t);
317 len -= sizeof(uint32_t);
318 }
319
320 if (len >= sizeof(uint16_t)) {
321 kdbpokew(ptr, *(uint16_t *)data);
322 data += sizeof(uint16_t);
323 ptr += sizeof(uint16_t);
324 len -= sizeof(uint16_t);
325 }
326
327 if (len)
328 kdbpokeb(ptr, *(uint8_t *)data);
329
330 if (addr < VM_MAXUSER_ADDRESS) {
331 struct cpu_info *ci = curcpu();
332
333 Mips_HitSyncDCache(ci, addr, size);
334 Mips_InvalidateICache(ci, addr, size);
335 }
336 }
337
338 void
db_stack_trace_print(db_expr_t addr,int have_addr,db_expr_t count,char * modif,int (* pr)(const char *,...))339 db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count,
340 char *modif, int (*pr)(const char *, ...))
341 {
342 struct trapframe *regs = &ddb_regs;
343
344 if (have_addr) {
345 (*pr)("mips trace requires a trap frame... giving up\n");
346 return;
347 }
348
349 stacktrace_subr(regs, count, pr);
350 }
351
352 /*
353 * To do a single step ddb needs to know the next address
354 * that we will get to. It means that we need to find out
355 * both the address for a branch taken and for not taken, NOT! :-)
356 * MipsEmulateBranch will do the job to find out _exactly_ which
357 * address we will end up at so the 'dual bp' method is not
358 * required.
359 */
360 vaddr_t
next_instr_address(vaddr_t pc,int bd)361 next_instr_address(vaddr_t pc, int bd)
362 {
363 vaddr_t next;
364 uint32_t instr;
365
366 instr = kdbpeek(pc);
367 next = MipsEmulateBranch(&ddb_regs, (vaddr_t)pc, 0, instr);
368 return (next);
369 }
370
371 /*
372 * MIPS machine dependent DDB commands.
373 */
374
375 /*
376 * Do a trap traceback.
377 */
378 void
db_trap_trace_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * m)379 db_trap_trace_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *m)
380 {
381 trapDump("ddb trap trace", db_printf);
382 }
383
384 void
db_print_tlb(uint tlbno,uint64_t tlblo)385 db_print_tlb(uint tlbno, uint64_t tlblo)
386 {
387 /* short description of coherency attributes */
388 static const char *attr[] = {
389 "CCA 0",
390 "CCA 1",
391 "NC ",
392 "C ",
393 "CEX ",
394 "CEXW ",
395 "CCA 6",
396 "NCACC"
397 };
398 paddr_t pa;
399
400 pa = pfn_to_pad(tlblo);
401 if (tlblo & PG_V) {
402 db_printf("%016lx ", pa);
403 #ifdef CPU_MIPS64R2
404 db_printf("%c", tlblo & PG_RI ? 'R' : ' ');
405 db_printf("%c", tlblo & PG_XI ? 'X' : ' ');
406 #endif
407 db_printf("%c", tlblo & PG_M ? 'M' : ' ');
408 db_printf("%c", tlblo & PG_G ? 'G' : ' ');
409 db_printf("%s ", attr[(tlblo >> 3) & 7]);
410 } else {
411 db_printf("invalid ");
412 }
413 }
414
415 /*
416 * Dump TLB contents.
417 * Syntax: machine tlb [/p asid] [/c] [tlb#]
418 * /p: only display tlb entries matching the given asid
419 * /c: check for duplicate entries
420 * tlb#: display <count> entries starting from this index
421 */
422 void
db_dump_tlb_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * m)423 db_dump_tlb_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *m)
424 {
425 int tlbno, last, check, pid;
426 struct tlb_entry tlb, tlbp;
427 struct cpu_info *ci = curcpu();
428
429 pid = -1;
430
431 if (m[0] == 'p') {
432 if (have_addr && addr < PG_ASID_COUNT) {
433 pid = addr;
434 }
435 tlbno = 0;
436 count = ci->ci_hw.tlbsize;
437 } else if (m[0] == 'c') {
438 last = ci->ci_hw.tlbsize;
439 for (tlbno = 0; tlbno < last; tlbno++) {
440 tlb_read(tlbno, &tlb);
441 for (check = tlbno + 1; check < last; check++) {
442 tlb_read(check, &tlbp);
443 if ((tlbp.tlb_hi == tlb.tlb_hi &&
444 (tlb.tlb_lo0 & PG_V || tlb.tlb_lo1 & PG_V)) ||
445 (pfn_to_pad(tlb.tlb_lo0) ==
446 pfn_to_pad(tlbp.tlb_lo0) &&
447 tlb.tlb_lo0 & PG_V) ||
448 (pfn_to_pad(tlb.tlb_lo1) ==
449 pfn_to_pad(tlbp.tlb_lo1) &&
450 tlb.tlb_lo1 & PG_V)) {
451 db_printf("MATCH:\n");
452 db_dump_tlb_cmd(tlbno, 1, 1, "");
453 db_dump_tlb_cmd(check, 1, 1, "");
454 }
455 }
456 }
457 return;
458 } else {
459 if (have_addr && addr < ci->ci_hw.tlbsize) {
460 tlbno = addr;
461 } else {
462 tlbno = 0;
463 count = ci->ci_hw.tlbsize;
464 }
465 }
466 last = tlbno + count;
467 if (last > ci->ci_hw.tlbsize)
468 last = ci->ci_hw.tlbsize;
469
470 if (pid == -1)
471 db_printf("current asid: 0x%02x\n", tlb_get_pid());
472 for (; tlbno < last; tlbno++) {
473 tlb_read(tlbno, &tlb);
474
475 if (pid >= 0 &&
476 (tlb.tlb_hi & PG_ASID_MASK) != (pid << PG_ASID_SHIFT))
477 continue;
478
479 if (tlb.tlb_lo0 & PG_V || tlb.tlb_lo1 & PG_V) {
480 vaddr_t va;
481 uint asid;
482
483 asid = (tlb.tlb_hi & PG_ASID_MASK) >> PG_ASID_SHIFT;
484 va = tlb.tlb_hi & ~((vaddr_t)PG_ASID_MASK);
485 db_printf("%3d v=%016lx", tlbno, va);
486 db_printf("/%02x ", asid);
487
488 db_print_tlb(tlbno, tlb.tlb_lo0);
489 db_print_tlb(tlbno, tlb.tlb_lo1);
490 db_printf(" sz=%llx", tlb.tlb_mask);
491 } else if (pid < 0) {
492 db_printf("%3d v=invalid ", tlbno);
493 }
494 db_printf("\n");
495 }
496 }
497
498
499 const struct db_command db_machine_command_table[] = {
500 { "tlb", db_dump_tlb_cmd, 0, NULL },
501 { "trap", db_trap_trace_cmd, 0, NULL },
502 #ifdef MULTIPROCESSOR
503 { "cpuinfo", db_cpuinfo_cmd, 0, NULL },
504 { "startcpu", db_startproc_cmd, 0, NULL },
505 { "stopcpu", db_stopproc_cmd, 0, NULL },
506 { "ddbcpu", db_ddbproc_cmd, 0, NULL },
507 #endif
508 { NULL, NULL, 0, NULL }
509 };
510
511 void
db_machine_init(void)512 db_machine_init(void)
513 {
514 extern char *ssym;
515 #ifdef MULTIPROCESSOR
516 int i;
517
518 for (i = 0; i < ncpus; i++) {
519 get_cpu_info(i)->ci_ddb = CI_DDB_RUNNING;
520 }
521 #endif
522 if (ssym != NULL) {
523 ddb_init(); /* Init symbols */
524 }
525 }
526
527 #ifdef MULTIPROCESSOR
528 void
db_ddbproc_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * modif)529 db_ddbproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
530 {
531 int cpu_n;
532
533 if (have_addr) {
534 cpu_n = addr;
535 if (cpu_n >= 0 && cpu_n < ncpus &&
536 cpu_n != cpu_number()) {
537 db_stopcpu(cpu_n);
538 db_switch_to_cpu = cpu_n;
539 db_switch_cpu = 1;
540 db_cmd_loop_done = 1;
541 } else {
542 db_printf("Invalid cpu %d\n", (int)addr);
543 }
544 } else {
545 db_printf("CPU not specified\n");
546 }
547 }
548
549 void
db_startproc_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * modif)550 db_startproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
551 {
552 int cpu_n;
553
554 if (have_addr) {
555 cpu_n = addr;
556 if (cpu_n >= 0 && cpu_n < ncpus &&
557 cpu_n != cpu_number())
558 db_startcpu(cpu_n);
559 else
560 db_printf("Invalid cpu %d\n", (int)addr);
561 } else {
562 for (cpu_n = 0; cpu_n < ncpus; cpu_n++) {
563 if (cpu_n != cpu_number()) {
564 db_startcpu(cpu_n);
565 }
566 }
567 }
568 }
569
570 void
db_stopproc_cmd(db_expr_t addr,int have_addr,db_expr_t count,char * modif)571 db_stopproc_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
572 {
573 int cpu_n;
574
575 if (have_addr) {
576 cpu_n = addr;
577 if (cpu_n >= 0 && cpu_n < ncpus &&
578 cpu_n != cpu_number())
579 db_stopcpu(cpu_n);
580 else
581 db_printf("Invalid cpu %d\n", (int)addr);
582 } else {
583 for (cpu_n = 0; cpu_n < ncpus; cpu_n++) {
584 if (cpu_n != cpu_number()) {
585 db_stopcpu(cpu_n);
586 }
587 }
588 }
589 }
590
591 void
db_startcpu(int cpu)592 db_startcpu(int cpu)
593 {
594 if (cpu != cpu_number() && cpu < ncpus) {
595 db_mtx_enter(&ddb_mp_mutex);
596 get_cpu_info(cpu)->ci_ddb = CI_DDB_RUNNING;
597 db_mtx_leave(&ddb_mp_mutex);
598 }
599 }
600
601 void
db_stopcpu(int cpu)602 db_stopcpu(int cpu)
603 {
604 db_mtx_enter(&ddb_mp_mutex);
605 if (cpu != cpu_number() && cpu < ncpus &&
606 get_cpu_info(cpu)->ci_ddb != CI_DDB_STOPPED) {
607 get_cpu_info(cpu)->ci_ddb = CI_DDB_SHOULDSTOP;
608 db_mtx_leave(&ddb_mp_mutex);
609 mips64_send_ipi(cpu, MIPS64_IPI_DDB);
610 } else {
611 db_mtx_leave(&ddb_mp_mutex);
612 }
613 }
614 #endif
615