xref: /openbsd-src/sys/arch/mips64/mips64/db_machdep.c (revision fe1fe620f8418f588086cadfe1b03bb5d224b548)
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