1
0
Files
2022-09-29 17:59:04 +03:00

1015 lines
22 KiB
C

#include <setjmp.h>
#include <sys/types.h>
#include <arcs/types.h>
#include <arcs/signal.h>
#include <sys/systm.h>
#include "fault.h"
#include "sys/sbd.h"
#include "sys/loaddrs.h"
#include "mp.h"
#include "dbgmon.h"
#include <libsc.h>
#include <parser.h>
#if SN
#include <sys/SN/addrs.h>
#include <sys/SN/launch.h>
#include <sys/SN/gda.h>
#include <sys/SN/klconfig.h>
#if SABLE
#include <arcs/spb.h>
#endif
#endif
#if SN || IP30
#include <sys/PCI/PCI_defs.h>
#include <sys/PCI/ioc3.h>
#endif /* SN || IP30 */
#if defined(EVEREST) && defined(MULTIKERNEL)
#include <sys/EVEREST/everest.h>
#include <sys/EVEREST/evintr.h>
#endif
/* private bss data per cpu not in genpda */
static k_machreg_t private_sregs[MAXCPU][NREGS];
static char private_cbuf[MAXCPU][128]; /* buffer for line processing */
jmp_buf private_jbuf[MAXCPU];
jmp_buf private_step_jmp[MAXCPU];
extern int cpu_enabled(cpuid_t);
lock_t user_interface_lock;
int ui_spl;
#if DEBUG
#define DPRINTF(x) printf x
#else
#define DPRINTF(x)
#endif
#ifdef NETDBX
#include "coord.h"
extern lock_t symmon_owner_lock;
extern lock_t cpustate_lock;
void inittracebuf(void);
#endif /* NETDBX */
extern struct cmd_table ctbl[]; /* generic command table */
extern struct cmd_table pd_ctbl[]; /* platform-dependent command table */
#if MULTIPROCESSOR
extern void _do_continue(void *);
extern void start_slave(void);
extern void slave_wait(void);
extern void init_brkpt_lock(void);
cpumask_t waiting_cpus; /* mask of cpus currently in debug */
int loaner_cpuid = NO_CPUID;
volatile int talking_cpuid = NO_CPUID;
volatile enum swtype {noowner, symmon, client} ui_owner = noowner;
volatile int client_cpuid = NO_CPUID;
extern volatile int suspend_cpu;
static void nmi_console_steal(int);
#endif /* MULTIPROCESSOR */
extern void begin_cell(void);
extern void end_cell(void);
#if defined(SN) && defined(SABLE)
extern int fake_prom;
extern void propagate_lowmem(gda_t *, __int64_t);
typedef struct sabsym_mpconf_blk {
unchar phys_id; /* Physical ID of the processor */
unchar virt_id; /* CPU virtual ID */
char *launchaddr; /* routine to start bootstrap */
char *stackaddr; /* Stack pointer */
} sabsym_mpconf_t;
volatile sabsym_mpconf_t sabsymblk[MAXCPUS];
int
sabsym_launch_slave(int virt_id, void (*lnch_fn)(void), void *stack)
{
/* Check to see if virtual ID is current processor */
if (virt_id == cpuid()) {
/* Call launch */
lnch_fn();
} else {
/* Make sure virt_id is valid */
if (virt_id >= MAXCPUS || virt_id < 0) {
printf("launch_slave: error: virt_id (0x%x) is too big.\n",
virt_id);
return -1;
}
/* Set up the MP Block entry */
sabsymblk[virt_id].stackaddr = stack;
/* Do this last! - It will launch immediately. */
sabsymblk[virt_id].launchaddr = (void *)lnch_fn;
}
return 0;
}
int
sabsym_slave_loop(void)
{
uint vpid = cpuid(); /* Virtual processor ID of this CPU */
void (*launch)(void); /* Launch function */
volatile sabsym_mpconf_t *mp; /* Pointer to cpu's MPCONF block */
mp = &(sabsymblk[vpid]);
for (;;) {
us_delay(1000);
if (!mp->launchaddr)
continue;
#if 0
/* Make sure this is a valid MPCONF block */
if (mp->virt_id != vpid) {
mp->launchaddr = 0;
printf("slave_loop: Error: Invalid MPCONF block\n");
continue;
}
#endif /* 0 */
/* Call the launch routine */
launch = (void (*)(void))mp->launchaddr;
mp->launchaddr = 0;
if (IS_KSEG0((__psint_t)launch))
flush_cache();
if (mp->stackaddr == (void *)0)
launch();
else
call_coroutine(launch, 0, (uint *)mp->stackaddr);
}
}
#endif /* SN || SABLE */
/* Initialize bottom half of genpda. Most of pda is initialized by genpda.c.
*/
void
init_symmon_gpda(void)
{
int my_id = cpuid();
register int i;
/* set up pda for all cpu */
for (i = 0; i < MAXCPU; i++) {
register struct generic_pda_s *pda;
/* set up dbgmon stack pointer */
/* XXX fixme - on sn0, we need to divide this into
* a node and a CPU slice to get the stack address.
* It also involves fixing the way we number CPUs in symmon.
*/
#if defined(SN)
{
int nasid, cpu, cnode;
extern nasid_t compact_to_nasid_node[];
gen_pda_tabp[i] = NULL;
cnode = get_cpu_cnode(i);
if (cnode == INVALID_CNODEID)
continue;
nasid = compact_to_nasid_node[cnode];
if (nasid == INVALID_NASID)
continue;
cpu = get_cpu_slice(i);
/*
* if cpu not enabled, we will get -1 as cpu slice.
*/
if (cpu == -1)
continue;
gen_pda_tabp[i] = (gen_pda_t *)GPDA_ADDR(nasid) + cpu;
pda = (struct generic_pda_s *)&GEN_PDA_TAB(i).gdata;
bzero(pda, sizeof(gen_pda_t));
pda->dbg_stack = (int *)(SYMMON_STK_ADDR(nasid,cpu) +
SYMMON_STK_SIZE(nasid) - 8);
#if SABLE
printf("init_symmon_gpda: cpu%d nasid %d cpu %d stack 0x%x\n",
i, nasid, cpu, pda->dbg_stack);
#endif /* SABLE */
}
#else
pda = (struct generic_pda_s *)&gen_pda_tab[i].gdata;
pda->dbg_stack = (int *)SYMMON_STACK_ADDR(i);
#endif
/* set up jmp_bufs */
pda->_restart_buf = private_jbuf[i];
pda->step_jmp = private_step_jmp[i];
pda->step_count = 0;
/* clear nofault */
pda->pda_nofault = 0;
pda->bp_nofault = 0;
/* set up sregs[] */
pda->sregs = private_sregs[i];
/* set up cbuf */
pda->cbuf = private_cbuf[i];
if (i == my_id)
pda->dbg_mode = MODE_DBGMON;
else
pda->dbg_mode = MODE_CLIENT;
}
}
#if MULTIPROCESSOR
#if EVEREST || SN || IP30
#ifndef SABLE
static volatile int slave_ready[MAXCPU];
#else /* SABLE */
volatile int slave_ready[MAXCPU];
#endif /* SABLE */
#endif
void
_cont_slave(int whichcpu)
{
while (suspend_cpu != NO_CPUID) {
DPRINTF(("_cont_slave: wait for susp cpu %d starting cpu %d\n",
suspend_cpu, whichcpu));
}
if (whichcpu == NO_CPUID) {
int i;
if (loaner_cpuid != NO_CPUID) {
/* If there is already a loaner_cpuid() then we're
* probably embedded within another "c all" and
* one of the continued CPUs has hit a breakpoint
* before we finished processing the command.
*/
printf("cpu%d: only continuing self, cpu%d still executing slave_funcs\n", cpuid(), loaner_cpuid);
printf("probably a nested 'c all' command\n");
slave_func(cpuid(), _do_continue, (void *)NULL);
/* NOTREACHED */
_fatal_error("_cont_slave: NOT REACHED\n");
}
for (i=0; i<MAXCPU; i++) {
if ((i != cpuid()) && (i != loaner_cpuid))
slave_func(i, _do_continue, (void *)NULL);
}
/* Continue myself */
slave_func(cpuid(), _do_continue, (void *)1LL);
} else
slave_func(whichcpu, _do_continue, (void *)NULL);
}
#if defined(SABLE) && defined(SN)
int sable_symmon_hwinit=0;
#endif
void
init_mp(void)
{
int i;
#if EVEREST || SN || IP30
uint timeout;
uint maxcpu;
#ifdef SN
gda_t *gdap = GDA;
#endif
#if IP30
extern int cpu_physical(int);
#else /* !IP30 */
extern void cpu_physical(int, int *, int *);
#endif /* !IP30 */
#if SABLE
maxcpu = 4; /* to speed things along */
#else /* !SABLE */
maxcpu = MAXCPU;
#endif /* !SABLE */
#endif /* EVEREST || SN || IP30 */
#if defined(SABLE) && defined(SN)
sable_symmon_hwinit = 1;
if (fake_prom) {
/* first parameter should be GDA_ADDR(master_nasid), but
* for sable we know it's node zero.
* Propagate the GDA.
*/
propagate_lowmem((gda_t*)GDA_ADDR(0), (__int64_t)SPBADDR);
}
#endif /* SABLE && SN */
initlock(&user_interface_lock);
init_brkpt_lock();
#ifdef NETDBX
initlock(&symmon_owner_lock);
initlock(&cpustate_lock);
symmon_owner = 0; /* the boot cpu # */
inittracebuf();
for (i=0; i<maxcpu; i++)
cpustate[i] = OutOfSymmon;
#endif /* NETDBX */
/*
* Symmon divides its stack area into TWO separate stacks!
* It expects to be able to setjmp/longjmp between them.
*/
#define S_SIZE (SYMMON_STACK_SIZE/2)
for (i=0; i<maxcpu; i++) {
#if SN
int nasid, slice;
cnodeid_t cnode;
cnode = get_cpu_cnode(i);
if (cnode == INVALID_CNODEID)
break;
nasid = gdap->g_nasidtable[cnode];
if (nasid == INVALID_NASID)
break;
slice = get_cpu_slice(i);
if (slice == -1)
continue;
if (i != cpuid()) {
#if defined(SABLE)
if (fake_prom) {
sabsym_launch_slave(i, start_slave,
(char *)(GEN_PDA_TAB(i).gdata.dbg_stack)-S_SIZE);
} else
#endif /* SABLE */
if ( cpu_enabled(i)) {
#if DEBUG
printf("Launching CPU %d, nasid %d, slice %d\n",
i, nasid, slice);
#endif
LAUNCH_SLAVE(nasid, slice,
(launch_proc_t)start_slave,
/* param */ 0,
(char *)(GEN_PDA_TAB(i).gdata.dbg_stack)-S_SIZE,
/* gp */ 0);
}
} else {
if (!cpu_enabled(i))
printf("WARNING: Master processor disabled.\n");
}
#else /* !SN */
if (i != cpuid())
launch_slave(i, (void (*)(void *))start_slave, 0, 0, 0,
(char *)(GEN_PDA_TAB(i).gdata.dbg_stack)-S_SIZE);
#endif
}
#undef S_SIZE
#if EVEREST || SN || IP30
/*
* Wait for all CPUs to signal they have started, before
* we continue and do something we shouldn't.
*/
#if SN
timeout = 5000; /* ELSC takes a long time. */
#else
timeout = 1000;
#endif
#if SABLE || EMULATION
timeout = 20000;
#endif
for (i = 0; (i < maxcpu) && (timeout > 0); i++) {
if ((cpuid() != i) && cpu_enabled(i)) {
while (!slave_ready[i] && (timeout > 0)) {
us_delay(1000);
timeout--;
}
}
#if defined(SN) && defined(SABLE)
else if (fake_prom && (cpuid() != i) && slave_ready[i])
printf("cpu%d slave_ready but NOT enabled in config info!\n", i);
#endif /* SABLE */
}
if (timeout == 0) { /* time out folks ... */
#if EVEREST || SN
int slot, slice;
#endif /* EVEREST || SN */
printf("WARNING: symmon did not see CPU(s):");
for (i = 0; i < maxcpu; i++) {
if (cpu_enabled(i)&&(i!=cpuid())&&!slave_ready[i]) {
#if IP30
printf("%d ", cpu_physical(i));
#else
cpu_physical(i, &slot, &slice);
printf("%d[0x%x/0x%x] ", i, slot, slice);
#endif /* IP30 */
}
}
printf("\nWARNING: system may not boot correctly\n");
printf("WARNING: continuing operation ...\n");
}
#endif /* EVEREST || SN || IP30 */
}
static int early_exit_check(void);
#ifdef EVEREST
extern volatile int z8530cons_was_polled;
extern int nmi_z8530cons_steal(int, int);
#define CONSOLE_WAS_POLLED z8530cons_was_polled
#define NMI_CONS_STEAL nmi_z8530cons_steal
#endif /* EVEREST */
#if SN || IP30
extern volatile int ns16550cons_was_polled;
extern int nmi_ns16550cons_steal(int, int);
#define CONSOLE_WAS_POLLED ns16550cons_was_polled
#define NMI_CONS_STEAL nmi_ns16550cons_steal
#endif /* SN */
#if SN || IP30
volatile ioc3reg_t *sscr_addr;
ioc3reg_t save_sscr;
int cons_hw_faulted = 0;
/* save IOC3's state and pause DMA. This should be called any time
* ui_owner transitions from not-symmon to symmon
*/
static void
acquire_console_hw(void)
{
jmp_buf cons_hw_jmpbuf;
/*
* Due to known hub II problems, we can take data bus errors when
* reading/writing IOC3 registers. We don't want to go into an
* infinite loop when trying to print stuff out. Catch the exception
* so we can switch to alternate console.
*/
if (cons_hw_faulted)
return;
if (setjmp(cons_hw_jmpbuf)) {
cons_hw_faulted = 1;
return;
}
private.pda_nofault = cons_hw_jmpbuf;
if (sscr_addr == 0) {
#if SN
extern nasid_t master_nasid;
sscr_addr = &((ioc3_mem_t*)
KL_CONFIG_CH_CONS_INFO(master_nasid)->memory_base)->port_a.sscr;
#else /* IP30 */
sscr_addr = (ioc3reg_t *)(IOC3_PCI_DEVIO_K1PTR + IOC3_SSCR_A_P);
#endif /* SN */
}
save_sscr = PCI_INW(sscr_addr);
if ((save_sscr & SSCR_DMA_EN) && !(save_sscr & SSCR_PAUSE_STATE)) {
PCI_OUTW(sscr_addr, save_sscr | SSCR_DMA_PAUSE);
while((PCI_INW(sscr_addr) & SSCR_PAUSE_STATE) == 0)
;
}
private.pda_nofault = 0;
}
/* restore saved IOC3 state. Should be called any time ui_owner
* transitions from symmon to not-symmon
*/
static void
release_console_hw(void)
{
jmp_buf cons_hw_jmpbuf;
if (cons_hw_faulted)
return;
if (setjmp(cons_hw_jmpbuf)) {
cons_hw_faulted = 1;
return;
}
private.pda_nofault = cons_hw_jmpbuf;
PCI_OUTW(sscr_addr, save_sscr);
while(PCI_INW(sscr_addr) != save_sscr)
;
private.pda_nofault = 0;
}
#define ACQUIRE_CONSOLE_HW() acquire_console_hw()
#define RELEASE_CONSOLE_HW() release_console_hw()
/* Entry point used by client (usually kernel) to lock the
* user interface for a "short time". Note that if the
* client 'crashes' or enters symmon while holding this
* lock, symmon will be unable to obtain control of
* the user interface.
*/
void
cons_lock( int unlock )
{
again:
LOCK_USER_INTERFACE();
if (unlock) { /* unlock */
/* ui_owner SHOULD be client, unless client is
* incorrectly coded.
*/
if (ui_owner == client)
ui_owner = noowner;
client_cpuid = NO_CPUID;
} else { /* lock */
if (ui_owner == noowner) {
ui_owner = client;
client_cpuid = cpuid();
} else {
UNLOCK_USER_INTERFACE();
while (ui_owner != noowner)
;
goto again;
}
}
UNLOCK_USER_INTERFACE();
}
#else
#define ACQUIRE_CONSOLE_HW()
#define RELEASE_CONSOLE_HW()
#endif /* SN || IP30 */
/*
* Functions to acquire and release control of the single
* UART shared by multiple processors.
*/
void
ACQUIRE_USER_INTERFACE(void)
{
int initial_uart_count, delay_count=0, initial_talker, stoleit=0;
int initial_ui_owner=NO_CPUID;
#ifdef NETDBX
if (netrdebug)
return;
#endif /* NETDBX */
initial_uart_count = CONSOLE_WAS_POLLED;
initial_talker = talking_cpuid;
again:
LOCK_USER_INTERFACE();
/* rip UI from client if we see an NMI */
if (private.nmi_int_flag && (ui_owner == client)) {
initial_ui_owner = ui_owner;
ui_owner = noowner;
}
if (talking_cpuid == NO_CPUID) {
if (ui_owner == client) {
UNLOCK_USER_INTERFACE();
goto again;
}
if (ui_owner != symmon)
ACQUIRE_CONSOLE_HW();
else
printf("ERROR: ACQUIRE_UI, no cpuid & ui_owner %d\n",
ui_owner);
ui_owner = symmon;
talking_cpuid = cpuid();
printf("(CPU %d) ", cpuid());
if (initial_ui_owner != NO_CPUID)
printf(" ui_owner at NMI was %d ", initial_ui_owner);
}
if (talking_cpuid != cpuid()) {
CPUMASK_SETB( waiting_cpus, cpuid());
#if defined(EVEREST) && defined(MULTIKERNEL)
/* No reason to have pending DEBUG interrupt if we're
* entering the debugger already.
*/
EV_SET_LOCAL(EV_CIPL0, EVINTR_LEVEL_DEBUG);
#endif /* EVEREST && MULTIKERNEL */
UNLOCK_USER_INTERFACE();
while (talking_cpuid != cpuid()) {
if (cpuid() == 0) {
if (early_exit_check()) {
LOCK_USER_INTERFACE();
CPUMASK_CLRB( waiting_cpus, cpuid());
UNLOCK_USER_INTERFACE();
goto again;
}
}
/* Following check allow a cpu which has received an
* NMI interrupt to attempt to acquire the UI after
* it has been released.
*/
if (private.nmi_int_flag &&
(initial_uart_count != 0) &&
(initial_uart_count == CONSOLE_WAS_POLLED) &&
(initial_talker == talking_cpuid)) {
delay_count++;
/* Don't let all cpus timeout at the same
* time. Empirical data shows that 100000
* delay_counts on an IP19 (150 MHz) takes
* about 1.3 seconds.
*/
if (delay_count > (100000*(cpuid()+1))) {
talking_cpuid = cpuid();
loaner_cpuid = NO_CPUID;
stoleit = 1;
nmi_console_steal( initial_talker );
goto again;
}
} else
delay_count = 0;
}
LOCK_USER_INTERFACE();
CPUMASK_CLRB( waiting_cpus, cpuid() );
UNLOCK_USER_INTERFACE();
begin_cell();
if (private.launch) {
void (*volatile launch)(void *);
launch = private.launch;
private.launch = 0;
/* Call requested routine */
launch(private.launch_arg);
/* In case the launch routine returns */
RELEASE_USER_INTERFACE(NO_CPUID);
goto again;
}
return;
}
UNLOCK_USER_INTERFACE();
begin_cell();
if (private.nmi_int_flag) {
private.nmi_int_flag = 0;
if (stoleit)
printf("(cpu%d) stealing UI from cpu%d after NMI\n",
cpuid(), initial_talker);
}
}
void
RELEASE_USER_INTERFACE(int id)
{
#ifdef NETDBX
if (netrdebug)
return;
#endif /* NETDBX */
end_cell();
LOCK_USER_INTERFACE();
/*
* If someone just loaned the user interface to another
* processor, give it back now.
*/
if ((id == NO_CPUID) && (loaner_cpuid != NO_CPUID)) {
talking_cpuid = loaner_cpuid;
loaner_cpuid = NO_CPUID;
} else
talking_cpuid = id;
if (talking_cpuid == NO_CPUID) {
if (ui_owner == symmon)
RELEASE_CONSOLE_HW();
else
printf("ERROR: RELEASE_UI but ui_owner %d\n",ui_owner);
ui_owner = noowner;
}
UNLOCK_USER_INTERFACE();
}
/* following routine is just like RELEASE_USER_INTERFACE but is used in
* one special case ("c all") where the current "loaner_cpid" would
* ordinarily want to RELEASE, but there are already stopped cpus
* and we instead want to give the interface to one of them.
* NOTE: Not sure we really need this, but makes good debugging code.
*/
void
TRANSFER_USER_INTERFACE(int id)
{
#ifdef NETDBX
if (netrdebug)
return;
#endif /* NETDBX */
end_cell();
LOCK_USER_INTERFACE();
if (loaner_cpuid == NO_CPUID) {
talking_cpuid = id;
} else {
printf("DEBUG...TRANSFER: cpu %d loaner %d TO %d\n", cpuid(), loaner_cpuid, id);
/*
* If someone just loaned the user interface to another
* processor, give it back now.
*/
if (loaner_cpuid != cpuid())
_fatal_error("TRANSFER_UI: loaner != cpuid()\n");
else {
printf("DEBUG...TRANSFER_USER_INTERFACE: from %d to %d\n",
loaner_cpuid, id);
loaner_cpuid = NO_CPUID;
talking_cpuid = id;
}
}
if (talking_cpuid == NO_CPUID) {
if (ui_owner == symmon)
RELEASE_CONSOLE_HW();
else
printf("ERROR: TRANSFER_UI but ui_owner %d\n", ui_owner);
ui_owner = noowner;
}
UNLOCK_USER_INTERFACE();
}
int
slave_func(int whichcpu, void (*func)(void *), void *arg)
{
/* If trying to launch self, just call the routine directly */
if (whichcpu == cpuid()) {
func(arg);
return(0);
}
/* Insure we're launching someone who's currently stopped */
if (!CPUMASK_TSTB( waiting_cpus, whichcpu ))
return(-1);
/*
* Don't allow us to launch others from a launched routine.
* It'd get too confusing to keep track of who's currently got
* access to the console. This keeps it simple.
*/
if (loaner_cpuid != NO_CPUID)
#ifndef EXTRA_DEBUG_OFF
{
printf("DEBUG...Can't call slave_func (0x%x) from a launched routine\n",
func);
printf("DEBUG...my cpuid == %d loaner cpuid == %d launching == %d\n",
cpuid(), loaner_cpuid, whichcpu);
}
#else /* !EXTRA_DEBUG_OFF */
printf("Can't call slave_func from a launched routine\n");
#endif /* !EXTRA_DEBUG_OFF */
else
loaner_cpuid = cpuid();
/* we setup the launch_arg and launch function before dropping the
* UI since otherwise it's possible for the other cpu to obtain
* the UI before we're setup the function call and then it grabs
* the interface without invoking the slave function.
*/
GEN_PDA_TAB(whichcpu).gdata.launch_arg = arg;
GEN_PDA_TAB(whichcpu).gdata.launch = func;
RELEASE_USER_INTERFACE(whichcpu);
ACQUIRE_USER_INTERFACE();
return(0); /* successfully launched */
}
void
do_command_parser(void)
{
private.dbg_mode = MODE_DBGMON;
ACQUIRE_USER_INTERFACE();
if (setjmp(private._restart_buf)) {
ACQUIRE_USER_INTERFACE();
putchar('\n');
}
Signal (SIGINT, main_intr);
symmon_spl();
command_parser(ctbl, PROMPT, 0, 0);
}
#ifdef MULTIPROCESSOR
extern int consgetc(int);
static int
early_exit_check(void)
{
int retval = 0;
LOCK_USER_INTERFACE();
if ((talking_cpuid == NO_CPUID) && (ui_owner != client)) {
if ((char)consgetc(0) == 1 /* CNTRL-A */) {
retval = 1;
}
}
UNLOCK_USER_INTERFACE();
return(retval);
}
static void
nmi_console_steal(int from_cpuid)
{
LOCK_USER_INTERFACE();
if (NMI_CONS_STEAL(0, from_cpuid))
printf("nmi_console_steal: duart lock was held and broken!\n");
UNLOCK_USER_INTERFACE();
return;
}
#endif
#ifndef SN
void
slave_wait(void)
{
private.dbg_mode = MODE_CLIENT;
slave_loop();
/*
* We got here because the user interrupted our sleep.
*/
private.dbg_mode = MODE_DBGMON;
longjmp (private._restart_buf, 1);
}
#endif /* SN */
void
slave_boot(void)
{
#if SN
extern void symmon_nmi();
extern void elscuart_setup(void);
extern void setup_nmi_handler(void (*)());
extern nasid_t compact_to_nasid_node[];
/* Each node has its own copy of the SPB, so we need to
* update the DebugBlock pointer.
*/
init_slave_debugblock_ptr();
/*
* initialize the per cpu nmi structures.
*/
setup_nmi_handler(symmon_nmi);
/*
* Set up the entry-level system controller
*/
elscuart_setup();
#endif
if (setjmp(private._restart_buf)) {
ACQUIRE_USER_INTERFACE(); /* must reacquire */
putchar('\n');
Signal (SIGINT, main_intr);
symmon_spl();
command_parser(ctbl, PROMPT, 0, 0);
}
symmon_spl();
#if EVEREST || SN || IP30
slave_ready[cpuid()] = 1;
#endif
#if defined(SN)
/* On sn0 systems we simply return to the prom after initializing */
return;
#else
slave_wait();
/* NOTREACHED */
#endif
}
static void
print_stopped(void)
{
int cpu;
cpumask_t stopped_cpus = waiting_cpus;
CPUMASK_SETB( stopped_cpus, cpuid());
printf("stopped processors: ");
for (cpu=0; cpu<MAXCPU; cpu++) {
if (CPUMASK_TSTB( stopped_cpus, cpu )) {
printf("%d ", cpu);
}
}
printf("\n");
}
static void
print_running(void)
{
int cpu;
cpumask_t stopped_cpus = waiting_cpus;
CPUMASK_SETB( stopped_cpus, cpuid());
printf("running processors: ");
for (cpu=0; cpu<MAXCPU; cpu++) {
if (cpu_enabled(cpu) && !CPUMASK_TSTB( stopped_cpus, cpu )) {
printf("%d ", cpu);
}
}
printf("\n");
}
/*
* Pass control of the user interface to the specified
* processor, and wait 'till someone tells us what to do.
*/
int
_switch_cpus(int argc, char **argv)
{
numinp_t newid;
int id = cpuid();
if (argc == 1) {
#if EVEREST || SN
printf("Currently connected to cpu %d (slot=0x%x proc=0x%x)\n",
id, cpuid_to_slot[id], cpuid_to_cpu[id]);
if ((loaner_cpuid != NO_CPUID) || (suspend_cpu != NO_CPUID))
printf("loaner cpu %d suspend cpu %d\n", loaner_cpuid,
suspend_cpu);
#elif IP30
printf("Currently connected to cpu %d\n",id);
#endif
print_stopped();
print_running();
return(0);
}
if (argc != 2)
return(1);
argv++;
if (parse_sym(*argv, &newid) && *atob_s(*argv, &newid))
_dbg_error("Illegal cpuid: %s", *argv);
/* doesn't return */
if ((newid < 0) || (newid > MAXCPU)) {
printf("cpuid 0x%lx out of range\n", newid);
return(1);
}
if (newid == id) {
printf("already talking with cpu %d\n", id);
return(0);
}
if (!CPUMASK_TSTB( waiting_cpus, newid )) {
printf("cpu %s is not stopped\n", *argv);
print_stopped();
return(1);
}
RELEASE_USER_INTERFACE(newid);
ACQUIRE_USER_INTERFACE();
return(0);
}
#if defined(SABLE) && defined(SN)
void
sable_symmon_launch_kernel(int cpuid, char *launchaddr, char *stackptr)
{
sabsym_launch_slave( cpuid, (void (*)(void))launchaddr, stackptr );
}
#endif /* SABLE && SN */
#endif /* MULTIPROCESSOR */