Add rudimentary multiprocessor support for DDB.

This commit is contained in:
ragge 2001-06-04 21:37:11 +00:00
parent cae5d5a796
commit f81f19e7b3
5 changed files with 167 additions and 65 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: cpu.h,v 1.59 2001/06/04 15:34:15 ragge Exp $ */ /* $NetBSD: cpu.h,v 1.60 2001/06/04 21:37:12 ragge Exp $ */
/* /*
* Copyright (c) 1994 Ludd, University of Lule}, Sweden * Copyright (c) 1994 Ludd, University of Lule}, Sweden
@ -94,6 +94,7 @@ struct cpu_mp_dep {
#define IPI_SEND_CNCHAR 2 /* Write char to console, kernel printf */ #define IPI_SEND_CNCHAR 2 /* Write char to console, kernel printf */
#define IPI_RUNNING 3 /* This CPU just started to run */ #define IPI_RUNNING 3 /* This CPU just started to run */
#define IPI_TBIA 4 /* Flush the TLB */ #define IPI_TBIA 4 /* Flush the TLB */
#define IPI_DDB 5 /* Jump into the DDB loop */
#define IPI_DEST_MASTER -1 /* Destination is mastercpu */ #define IPI_DEST_MASTER -1 /* Destination is mastercpu */
#define IPI_DEST_ALL -2 /* Broadcast */ #define IPI_DEST_ALL -2 /* Broadcast */
@ -133,10 +134,12 @@ struct cpu_info {
vaddr_t ci_istack; /* Interrupt stack location */ vaddr_t ci_istack; /* Interrupt stack location */
int ci_flags; /* See below */ int ci_flags; /* See below */
long ci_ipimsgs; /* Sent IPI bits */ long ci_ipimsgs; /* Sent IPI bits */
struct trapframe *ci_ddb_regs; /* Used by DDB */
#endif #endif
}; };
#define CI_MASTERCPU 1 /* Set if master CPU */ #define CI_MASTERCPU 1 /* Set if master CPU */
#define CI_RUNNING 2 /* Set when a slave CPU is running */ #define CI_RUNNING 2 /* Set when a slave CPU is running */
#define CI_STOPPED 4 /* Stopped (in debugger) */
#if defined(MULTIPROCESSOR) #if defined(MULTIPROCESSOR)
/* /*

View File

@ -1,4 +1,4 @@
/* $NetBSD: db_machdep.h,v 1.9 2001/05/02 15:59:38 matt Exp $ */ /* $NetBSD: db_machdep.h,v 1.10 2001/06/04 21:37:12 ragge Exp $ */
/* /*
* Mach Operating System * Mach Operating System
@ -71,6 +71,8 @@ db_regs_t ddb_regs; /* register state */
#define inst_load(ins) 0 #define inst_load(ins) 0
#define inst_store(ins) 0 #define inst_store(ins) 0
#define DB_MACHINE_COMMANDS
/* Prototypes */ /* Prototypes */
void kdb_trap __P((struct trapframe *)); void kdb_trap __P((struct trapframe *));

View File

@ -1,4 +1,4 @@
/* $NetBSD: lock.h,v 1.7 2001/06/04 15:37:05 ragge Exp $ */ /* $NetBSD: lock.h,v 1.8 2001/06/04 21:37:12 ragge Exp $ */
/* /*
* Copyright (c) 2000 Ludd, University of Lule}, Sweden. * Copyright (c) 2000 Ludd, University of Lule}, Sweden.
@ -70,6 +70,7 @@ __cpu_simple_lock_try(__cpu_simple_lock_t *alp)
return ret; return ret;
} }
#define VAX_LOCK_CHECKS ((1 << IPI_SEND_CNCHAR) | (1 << IPI_DDB))
#define __cpu_simple_lock(alp) \ #define __cpu_simple_lock(alp) \
{ \ { \
struct cpu_info *__ci = curcpu(); \ struct cpu_info *__ci = curcpu(); \
@ -77,7 +78,7 @@ __cpu_simple_lock_try(__cpu_simple_lock_t *alp)
while (__cpu_simple_lock_try(alp) == 0) { \ while (__cpu_simple_lock_try(alp) == 0) { \
int __s; \ int __s; \
\ \
if (__ci->ci_ipimsgs & (1 << IPI_SEND_CNCHAR)) { \ if (__ci->ci_ipimsgs & VAX_LOCK_CHECKS) { \
__s = splipi(); \ __s = splipi(); \
cpu_handle_ipi(); \ cpu_handle_ipi(); \
splx(__s); \ splx(__s); \

View File

@ -1,4 +1,4 @@
/* $NetBSD: db_machdep.c,v 1.28 2001/04/29 22:17:24 matt Exp $ */ /* $NetBSD: db_machdep.c,v 1.29 2001/06/04 21:37:11 ragge Exp $ */
/* /*
* :set tabs=4 * :set tabs=4
@ -38,6 +38,7 @@
* Taken from i386 port and modified for vax. * Taken from i386 port and modified for vax.
*/ */
#include "opt_ddb.h" #include "opt_ddb.h"
#include "opt_multiprocessor.h"
#include <sys/param.h> #include <sys/param.h>
#include <sys/proc.h> #include <sys/proc.h>
@ -49,11 +50,11 @@
#include <dev/cons.h> #include <dev/cons.h>
#include <machine/cpu.h>
#include <machine/db_machdep.h> #include <machine/db_machdep.h>
#include <machine/trap.h> #include <machine/trap.h>
#include <machine/frame.h> #include <machine/frame.h>
#include <machine/pcb.h> #include <machine/pcb.h>
#include <machine/cpu.h>
#include <machine/intr.h> #include <machine/intr.h>
#include <vax/vax/gencons.h> #include <vax/vax/gencons.h>
@ -65,15 +66,57 @@
#include <ddb/db_interface.h> #include <ddb/db_interface.h>
#include <ddb/db_variables.h> #include <ddb/db_variables.h>
extern label_t *db_recover; #include "ioconf.h"
void kdbprinttrap __P((int, int)); extern label_t *db_recover;
void kdbprinttrap(int, int);
int db_active = 0; int db_active = 0;
extern int qdpolling; extern int qdpolling;
static int splsave; /* IPL before entering debugger */ static int splsave; /* IPL before entering debugger */
#ifdef MULTIPROCESSOR
static struct cpu_info *stopcpu;
/*
* Only the master CPU is allowed to enter DDB, but the correct frames
* must still be there. Keep the state-machine here.
*/
static int
pause_cpus(void)
{
volatile struct cpu_info *ci = curcpu();
if (stopcpu == NULL) {
stopcpu = curcpu();
cpu_send_ipi(IPI_DEST_ALL, IPI_DDB);
}
if ((ci->ci_flags & CI_MASTERCPU) == 0) {
ci->ci_flags |= CI_STOPPED;
while (ci->ci_flags & CI_STOPPED)
;
return 1;
} else
return 0;
}
static void
resume_cpus(void)
{
struct cpu_mp_softc *sc;
struct cpu_info *ci;
int i;
stopcpu = NULL;
for (i = 0; i < cpu_cd.cd_ndevs; i++) {
if ((sc = cpu_cd.cd_devs[i]) == NULL)
continue;
ci = &sc->sc_ci;
ci->ci_flags &= ~CI_STOPPED;
}
}
#endif
/* /*
* VAX Call frame on the stack, this from * VAX Call frame on the stack, this from
* "Computer Programming and Architecture, The VAX-11" * "Computer Programming and Architecture, The VAX-11"
@ -81,16 +124,16 @@ static int splsave; /* IPL before entering debugger */
* ISBN 0-932376-07-X * ISBN 0-932376-07-X
*/ */
typedef struct __vax_frame { typedef struct __vax_frame {
u_int vax_cond; /* condition handler */ u_int vax_cond; /* condition handler */
u_int vax_psw:16; /* 16 bit processor status word */ u_int vax_psw:16; /* 16 bit processor status word */
u_int vax_regs:12; /* Register save mask. */ u_int vax_regs:12; /* Register save mask. */
u_int vax_zero:1; /* Always zero */ u_int vax_zero:1; /* Always zero */
u_int vax_calls:1; /* True if CALLS, false if CALLG */ u_int vax_calls:1; /* True if CALLS, false if CALLG */
u_int vax_spa:2; /* Stack pointer alignment */ u_int vax_spa:2; /* Stack pointer alignment */
u_int *vax_ap; /* argument pointer */ u_int *vax_ap; /* argument pointer */
struct __vax_frame *vax_fp; /* frame pointer of previous frame */ struct __vax_frame *vax_fp; /* frame pointer of previous frame */
u_int vax_pc; /* program counter */ u_int vax_pc; /* program counter */
u_int vax_args[1]; /* 0 or more arguments */ u_int vax_args[1]; /* 0 or more arguments */
} VAX_CALLFRAME; } VAX_CALLFRAME;
/* /*
@ -100,23 +143,26 @@ typedef struct __vax_frame {
* contain the registers when panic was called. (easy to debug). * contain the registers when panic was called. (easy to debug).
*/ */
void void
kdb_trap(frame) kdb_trap(struct trapframe *frame)
struct trapframe *frame;
{ {
int s; int s;
#ifdef MULTIPROCESSOR
struct cpu_info *ci = curcpu();
#endif
switch (frame->trap) { switch (frame->trap) {
case T_BPTFLT: /* breakpoint */ case T_BPTFLT: /* breakpoint */
case T_TRCTRAP: /* single_step */ case T_TRCTRAP: /* single_step */
break; break;
/* XXX todo: should be migrated to use VAX_CALLFRAME at some point */ /* XXX todo: should be migrated to use VAX_CALLFRAME at some point */
case T_KDBTRAP: case T_KDBTRAP:
#ifndef MULTIPROCESSOR /* No fancy panic stack conversion here */
if (panicstr) { if (panicstr) {
struct callsframe *pf, *df; struct callsframe *pf, *df;
df = (void *)frame->fp; /* start of debug's calls */ df = (void *)frame->fp; /* start of debug's calls */
pf = (void *)df->ca_fp; /* start of panic's calls */ pf = (void *)df->ca_fp; /* start of panic's calls */
bcopy(&pf->ca_argno, &ddb_regs.r0, sizeof(int) * 12); bcopy(&pf->ca_argno, &ddb_regs.r0, sizeof(int) * 12);
ddb_regs.fp = pf->ca_fp; ddb_regs.fp = pf->ca_fp;
ddb_regs.pc = pf->ca_pc; ddb_regs.pc = pf->ca_pc;
@ -126,6 +172,7 @@ kdb_trap(frame)
ddb_regs.psl |= pf->ca_maskpsw & 0xffe0; ddb_regs.psl |= pf->ca_maskpsw & 0xffe0;
ddb_regs.psl |= (splsave << 16); ddb_regs.psl |= (splsave << 16);
} }
#endif
break; break;
default: default:
@ -139,8 +186,18 @@ kdb_trap(frame)
} }
} }
#ifdef MULTIPROCESSOR
ci->ci_ddb_regs = frame;
if (pause_cpus())
return;
#endif
#ifndef MULTIPROCESSOR
if (!panicstr) if (!panicstr)
bcopy(frame, &ddb_regs, sizeof(struct trapframe)); bcopy(frame, &ddb_regs, sizeof(struct trapframe));
#else
bcopy(stopcpu->ci_ddb_regs, &ddb_regs, sizeof(struct trapframe));
printf("stopped on cpu %d\n", stopcpu->ci_cpuid);
#endif
/* XXX Should switch to interrupt stack here, if needed. */ /* XXX Should switch to interrupt stack here, if needed. */
@ -152,11 +209,16 @@ kdb_trap(frame)
db_active--; db_active--;
splx(s); splx(s);
#ifndef MULTIPROCESSOR
if (!panicstr) if (!panicstr)
bcopy(&ddb_regs, frame, sizeof(struct trapframe)); bcopy(&ddb_regs, frame, sizeof(struct trapframe));
#else
bcopy(&ddb_regs, stopcpu->ci_ddb_regs, sizeof(struct trapframe));
#endif
frame->sp = mfpr(PR_USP); frame->sp = mfpr(PR_USP);
#ifdef MULTIPROCESSOR
return; resume_cpus();
#endif
} }
extern char *traptypes[]; extern char *traptypes[];
@ -182,8 +244,8 @@ kdbprinttrap(type, code)
*/ */
void void
db_read_bytes(addr, size, data) db_read_bytes(addr, size, data)
vaddr_t addr; vaddr_t addr;
register size_t size; register size_t size;
register char *data; register char *data;
{ {
@ -195,8 +257,8 @@ db_read_bytes(addr, size, data)
*/ */
void void
db_write_bytes(addr, size, data) db_write_bytes(addr, size, data)
vaddr_t addr; vaddr_t addr;
register size_t size; register size_t size;
register char *data; register char *data;
{ {
@ -225,13 +287,13 @@ const struct db_variable db_regs[] = {
{"r7", &ddb_regs.r7, FCN_NULL}, {"r7", &ddb_regs.r7, FCN_NULL},
{"r8", &ddb_regs.r8, FCN_NULL}, {"r8", &ddb_regs.r8, FCN_NULL},
{"r9", &ddb_regs.r9, FCN_NULL}, {"r9", &ddb_regs.r9, FCN_NULL},
{"r10", &ddb_regs.r10, FCN_NULL}, {"r10", &ddb_regs.r10, FCN_NULL},
{"r11", &ddb_regs.r11, FCN_NULL}, {"r11", &ddb_regs.r11, FCN_NULL},
{"ap", &ddb_regs.ap, FCN_NULL}, {"ap", &ddb_regs.ap, FCN_NULL},
{"fp", &ddb_regs.fp, FCN_NULL}, {"fp", &ddb_regs.fp, FCN_NULL},
{"sp", &ddb_regs.sp, FCN_NULL}, {"sp", &ddb_regs.sp, FCN_NULL},
{"pc", &ddb_regs.pc, FCN_NULL}, {"pc", &ddb_regs.pc, FCN_NULL},
{"psl", &ddb_regs.psl, FCN_NULL}, {"psl", &ddb_regs.psl, FCN_NULL},
}; };
const struct db_variable * const db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]); const struct db_variable * const db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
@ -254,7 +316,7 @@ db_dump_stack(VAX_CALLFRAME *fp, u_int stackbase,
(*pr)("Stack traceback : \n"); (*pr)("Stack traceback : \n");
if (IN_USERLAND(fp)) { if (IN_USERLAND(fp)) {
(*pr)(" Process is executing in user space.\n"); (*pr)(" Process is executing in user space.\n");
return; return;
} }
@ -330,20 +392,20 @@ db_dump_stack(VAX_CALLFRAME *fp, u_int stackbase,
/* /*
* Implement the trace command which has the form: * Implement the trace command which has the form:
* *
* trace <-- Trace panic (same as before) * trace <-- Trace panic (same as before)
* trace 0x88888 <-- Trace process whose address is 888888 * trace 0x88888 <-- Trace frame whose address is 888888
* trace/t <-- Trace current process (0 if no current proc) * trace/t <-- Trace current process (0 if no current proc)
* trace/t 0tnn <-- Trace process nn (0t for decimal) * trace/t 0tnn <-- Trace process nn (0t for decimal)
*/ */
void void
db_stack_trace_print(addr, have_addr, count, modif, pr) db_stack_trace_print(addr, have_addr, count, modif, pr)
db_expr_t addr; /* Address parameter */ db_expr_t addr; /* Address parameter */
boolean_t have_addr; /* True if addr is valid */ boolean_t have_addr; /* True if addr is valid */
db_expr_t count; /* Optional count */ db_expr_t count; /* Optional count */
char *modif; /* pointer to flag modifier 't' */ char *modif; /* pointer to flag modifier 't' */
void (*pr) __P((const char *, ...)); /* Print function */ void (*pr) __P((const char *, ...)); /* Print function */
{ {
extern vaddr_t proc0paddr; extern vaddr_t proc0paddr;
struct proc *p = curproc; struct proc *p = curproc;
struct user *uarea; struct user *uarea;
int trace_proc; int trace_proc;
@ -366,7 +428,11 @@ db_stack_trace_print(addr, have_addr, count, modif, pr)
} }
(*pr)("panic: %s\n", panicstr); (*pr)("panic: %s\n", panicstr);
/* xxx ? where did we panic and whose stack are we using? */ /* xxx ? where did we panic and whose stack are we using? */
#ifdef MULTIPROCESSOR
db_dump_stack((VAX_CALLFRAME *)(ddb_regs.fp), ddb_regs.ap, pr);
#else
db_dump_stack((VAX_CALLFRAME *)(ddb_regs.sp), ddb_regs.ap, pr); db_dump_stack((VAX_CALLFRAME *)(ddb_regs.sp), ddb_regs.ap, pr);
#endif
return; return;
} }
@ -385,7 +451,7 @@ db_stack_trace_print(addr, have_addr, count, modif, pr)
while (foo != 0) { while (foo != 0) {
int digit = (foo >> 28) & 0xf; int digit = (foo >> 28) & 0xf;
if (digit > 9) { if (digit > 9) {
(*pr)(" No such process.\n"); (*pr)(" No such process.\n");
return; return;
} }
tpid = tpid * 10 + digit; tpid = tpid * 10 + digit;
@ -393,16 +459,13 @@ db_stack_trace_print(addr, have_addr, count, modif, pr)
} }
p = pfind(tpid); p = pfind(tpid);
if (p == NULL) { if (p == NULL) {
(*pr)(" No such process.\n"); (*pr)(" No such process.\n");
return; return;
} }
} }
} else { } else {
p = (struct proc *)(addr); db_dump_stack((VAX_CALLFRAME *)addr, 0, pr);
if (pfind(p->p_pid) != p) { return;
(*pr)(" This address does not point to a valid process.\n");
return;
}
} }
} else { } else {
if (trace_proc) { if (trace_proc) {
@ -426,28 +489,28 @@ db_stack_trace_print(addr, have_addr, count, modif, pr)
curpid = p->p_pid; curpid = p->p_pid;
} }
(*pr)("Process %d\n", curpid); (*pr)("Process %d\n", curpid);
(*pr)(" PCB contents:\n"); (*pr)(" PCB contents:\n");
(*pr)(" KSP = 0x%x\n", (unsigned int)(uarea->u_pcb.KSP)); (*pr)(" KSP = 0x%x\n", (unsigned int)(uarea->u_pcb.KSP));
(*pr)(" ESP = 0x%x\n", (unsigned int)(uarea->u_pcb.ESP)); (*pr)(" ESP = 0x%x\n", (unsigned int)(uarea->u_pcb.ESP));
(*pr)(" SSP = 0x%x\n", (unsigned int)(uarea->u_pcb.SSP)); (*pr)(" SSP = 0x%x\n", (unsigned int)(uarea->u_pcb.SSP));
(*pr)(" USP = 0x%x\n", (unsigned int)(uarea->u_pcb.USP)); (*pr)(" USP = 0x%x\n", (unsigned int)(uarea->u_pcb.USP));
(*pr)(" R[00] = 0x%08x R[06] = 0x%08x\n", (*pr)(" R[00] = 0x%08x R[06] = 0x%08x\n",
(unsigned int)(uarea->u_pcb.R[0]), (unsigned int)(uarea->u_pcb.R[6])); (unsigned int)(uarea->u_pcb.R[0]), (unsigned int)(uarea->u_pcb.R[6]));
(*pr)(" R[01] = 0x%08x R[07] = 0x%08x\n", (*pr)(" R[01] = 0x%08x R[07] = 0x%08x\n",
(unsigned int)(uarea->u_pcb.R[1]), (unsigned int)(uarea->u_pcb.R[7])); (unsigned int)(uarea->u_pcb.R[1]), (unsigned int)(uarea->u_pcb.R[7]));
(*pr)(" R[02] = 0x%08x R[08] = 0x%08x\n", (*pr)(" R[02] = 0x%08x R[08] = 0x%08x\n",
(unsigned int)(uarea->u_pcb.R[2]), (unsigned int)(uarea->u_pcb.R[8])); (unsigned int)(uarea->u_pcb.R[2]), (unsigned int)(uarea->u_pcb.R[8]));
(*pr)(" R[03] = 0x%08x R[09] = 0x%08x\n", (*pr)(" R[03] = 0x%08x R[09] = 0x%08x\n",
(unsigned int)(uarea->u_pcb.R[3]), (unsigned int)(uarea->u_pcb.R[9])); (unsigned int)(uarea->u_pcb.R[3]), (unsigned int)(uarea->u_pcb.R[9]));
(*pr)(" R[04] = 0x%08x R[10] = 0x%08x\n", (*pr)(" R[04] = 0x%08x R[10] = 0x%08x\n",
(unsigned int)(uarea->u_pcb.R[4]), (unsigned int)(uarea->u_pcb.R[10])); (unsigned int)(uarea->u_pcb.R[4]), (unsigned int)(uarea->u_pcb.R[10]));
(*pr)(" R[05] = 0x%08x R[11] = 0x%08x\n", (*pr)(" R[05] = 0x%08x R[11] = 0x%08x\n",
(unsigned int)(uarea->u_pcb.R[5]), (unsigned int)(uarea->u_pcb.R[11])); (unsigned int)(uarea->u_pcb.R[5]), (unsigned int)(uarea->u_pcb.R[11]));
(*pr)(" AP = 0x%x\n", (unsigned int)(uarea->u_pcb.AP)); (*pr)(" AP = 0x%x\n", (unsigned int)(uarea->u_pcb.AP));
(*pr)(" FP = 0x%x\n", (unsigned int)(uarea->u_pcb.FP)); (*pr)(" FP = 0x%x\n", (unsigned int)(uarea->u_pcb.FP));
(*pr)(" PC = 0x%x\n", (unsigned int)(uarea->u_pcb.PC)); (*pr)(" PC = 0x%x\n", (unsigned int)(uarea->u_pcb.PC));
(*pr)(" PSL = 0x%x\n", (unsigned int)(uarea->u_pcb.PSL)); (*pr)(" PSL = 0x%x\n", (unsigned int)(uarea->u_pcb.PSL));
(*pr)(" Trap frame pointer: 0x%x\n", (*pr)(" Trap frame pointer: 0x%x\n",
(unsigned int)(uarea->u_pcb.framep)); (unsigned int)(uarea->u_pcb.framep));
db_dump_stack((VAX_CALLFRAME *)(uarea->u_pcb.FP), db_dump_stack((VAX_CALLFRAME *)(uarea->u_pcb.FP),
(u_int) uarea->u_pcb.KSP, pr); (u_int) uarea->u_pcb.KSP, pr);
@ -522,7 +585,7 @@ db_stack_trace_print(addr, have_addr, count, modif, pr)
paddr = proc0paddr; paddr = proc0paddr;
stackbase = (ddb_regs.psl & PSL_IS ? istack : paddr); stackbase = (ddb_regs.psl & PSL_IS ? istack : paddr);
} }
#endif #endif
} }
@ -553,3 +616,31 @@ kdbrint(tkn)
return 0; return 0;
} }
#ifdef MULTIPROCESSOR
static void
db_mach_cpu(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
{
struct cpu_mp_softc *sc;
struct cpu_info *ci;
if ((addr < 0) || (addr >= cpu_cd.cd_ndevs))
return db_printf("%ld: cpu out of range\n", addr);
if ((sc = cpu_cd.cd_devs[addr]) == NULL)
return db_printf("%ld: cpu not configured\n", addr);
ci = &sc->sc_ci;
if ((ci != curcpu()) && ((ci->ci_flags & CI_STOPPED) == 0))
return db_printf("cpu %ld not stopped???\n", addr);
bcopy(&ddb_regs, stopcpu->ci_ddb_regs, sizeof(struct trapframe));
stopcpu = ci;
bcopy(stopcpu->ci_ddb_regs, &ddb_regs, sizeof(struct trapframe));
db_printf("using cpu %ld", addr);
}
const struct db_command db_machine_command_table[] = {
{ "cpu", db_mach_cpu, 0, 0 },
{ (char *)0, },
};
#endif

View File

@ -1,4 +1,4 @@
/* $NetBSD: multicpu.c,v 1.8 2001/06/04 15:34:16 ragge Exp $ */ /* $NetBSD: multicpu.c,v 1.9 2001/06/04 21:37:11 ragge Exp $ */
/* /*
* Copyright (c) 2000 Ludd, University of Lule}, Sweden. All rights reserved. * Copyright (c) 2000 Ludd, University of Lule}, Sweden. All rights reserved.
@ -221,6 +221,11 @@ cpu_handle_ipi()
case IPI_TBIA: case IPI_TBIA:
mtpr(0, PR_TBIA); mtpr(0, PR_TBIA);
break; break;
case IPI_DDB:
Debugger();
break;
default:
panic("cpu_handle_ipi: bad bit %x\n", bitno);
} }
} }
} }