2383 lines
55 KiB
C
2383 lines
55 KiB
C
|
|
/**************************************************************************
|
|
* *
|
|
* Copyright (C) 1986-1996, Silicon Graphics, Inc. *
|
|
* *
|
|
* These coded instructions, statements, and computer programs contain *
|
|
* unpublished proprietary information of Silicon Graphics, Inc., and *
|
|
* are protected by Federal copyright law. They may not be disclosed *
|
|
* to third parties or copied or duplicated in any form, in whole or *
|
|
* in part, without the prior written consent of Silicon Graphics, Inc. *
|
|
* *
|
|
**************************************************************************/
|
|
/* Copyright (c) 1984 AT&T */
|
|
/* All Rights Reserved */
|
|
|
|
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
|
|
/* The copyright notice above does not evidence any */
|
|
/* actual or intended publication of such source code. */
|
|
|
|
#ident "$Revision: 3.217 $"
|
|
|
|
#include "sys/param.h"
|
|
#include "sys/systm.h"
|
|
#include "sys/types.h"
|
|
#include "sys/sysmacros.h"
|
|
#include "sys/uadmin.h"
|
|
#include "sys/conf.h"
|
|
#include "ksys/vproc.h"
|
|
#include "sys/proc.h"
|
|
#include "sys/cmn_err.h"
|
|
#include "sys/reg.h"
|
|
#include "sys/pda.h"
|
|
#include "sys/pfdat.h"
|
|
#include "sys/page.h"
|
|
#include "sys/kabi.h"
|
|
#include "sys/klog.h"
|
|
#include "sys/debug.h"
|
|
#include "sys/splock.h"
|
|
#include "sys/stdnames.h"
|
|
#include "sys/timers.h"
|
|
#include "sys/dump.h"
|
|
#include "sys/kopt.h"
|
|
#ifdef CELL_IRIX
|
|
#include "ksys/cell/service.h"
|
|
#include "host/cell/dhost.h"
|
|
#include "invk_dshost_stubs.h"
|
|
#include "I_dshost_stubs.h"
|
|
#endif
|
|
#include "stdarg.h"
|
|
#include "string.h"
|
|
#ifdef SN0
|
|
#include "sys/SN/SN0/ip27log.h"
|
|
#endif
|
|
|
|
extern char *putbuf; /* buffer where cmn_err output can be saved */
|
|
extern int putbufsz; /* size of putbuf buffer */
|
|
unsigned int putbufndx = 0; /* index to next character in putbuf */
|
|
#ifdef EVEREST
|
|
/* For NVRAM error logging on Challenge boxes */
|
|
extern char fru_nvbuff[];
|
|
extern char nvtmpbuf[];
|
|
extern void set_fru_nvram(char* error_state, int len);
|
|
#endif
|
|
extern char *conbuf; /* buffer where console output is accumulated */
|
|
extern int conbufsz; /* size of console buffer */
|
|
int constrlen = 0; /* index to next character in putbuf */
|
|
#if SN0
|
|
static int conbuf_tout; /* conbuf_flush timeout pending */
|
|
#endif
|
|
|
|
extern char *errbuf; /* buffer where error dump output is saved */
|
|
extern int errbufsz; /* size of errbuf buffer */
|
|
unsigned int errbufndx = 0; /* index to next character in errbuf */
|
|
|
|
|
|
|
|
lock_t putbuflck; /* lock to single thread output */
|
|
volatile uint putbuflck_gen = 0; /* putbuf lock generation number. */
|
|
int haspblock = -1; /* cpuid of CPU that has lock */
|
|
int pblock_nest = 0; /* nesting level of putbuflck */
|
|
|
|
int ecc_panic_cpu = -1; /* set to cpuid of cpu in ecc_panic */
|
|
lock_t panic_putbuflck; /* lock to single thread output */
|
|
int panic_haspblock = -1; /* cpuid of CPU that has lock */
|
|
volatile int in_con_message = 0;
|
|
|
|
extern int power_off_flag;
|
|
|
|
void panicspin(void);
|
|
static void errprintf(int, char **, register char *, va_list);
|
|
static void errvprintf(int, char **, register char *, ...);
|
|
static void printL(int, char **, __uint64_t, int);
|
|
static void printn(int, char **, __scunsigned_t, int);
|
|
static void dopromreset(int);
|
|
static void syncreboot(void);
|
|
static void mprboot(void (*)(), char *);
|
|
static void do_mprboot(char *);
|
|
static void _putstr(int, char **, char *);
|
|
void icmn_err(int, char *, va_list);
|
|
void icmn_err_tag(int, int, char *, va_list);
|
|
|
|
void silence_all_audio(void);
|
|
long arcs_write(long, void *, long, long *);
|
|
void cn_write(char *, int);
|
|
void cn_flush(void);
|
|
void prom_reinit(void);
|
|
void prom_autoboot(void);
|
|
void dumptlb(int);
|
|
void checkAllTlbsDumped(void);
|
|
|
|
#ifdef IP30
|
|
void clear_cpu_intr(cpuid_t);
|
|
#endif /* IP30 */
|
|
|
|
#define isdigit(c) (((c) >= '0') && ((c) <= '9'))
|
|
|
|
/* Codes for where output should go. (internal to cmn_err code) */
|
|
#define PRW_BUF 0x01 /* Output to putbuf. */
|
|
#define PRW_CONS 0x02 /* Output to console via sprom. */
|
|
#define PRW_ABUF 0x04 /* Output to arbitrary buffer. */
|
|
#define PRW_ARCS 0x08 /* Output via ARCS prom. */
|
|
#define PRW_KLOG 0x10 /* Output to /dev/klog. */
|
|
#define PRW_RPC 0x20 /* Identifies messages from rpc path */
|
|
#define PRW_FLAG 0x80 /* Identifies character as PRW flag */
|
|
#if defined(SN0) || defined(EVEREST)
|
|
#define PRW_LOGNVRAM 0x100 /* Output to ip27log too. */
|
|
#endif
|
|
#define PRW_ERR 0x200 /* Output to error dump buffer */
|
|
#define PRW_NEWLINE 0x400 /* Add newline if missing from message */
|
|
|
|
int panic_level = 0;
|
|
char *panicstr;
|
|
char panicbuf[DUMP_PANIC_LEN * 3];
|
|
|
|
klogmsgs_t klogmsgs; /* holds associated logging buffer info */
|
|
|
|
/*
|
|
* cmn_err - the common error handling routine for the kernel
|
|
*
|
|
* Used to print diagnostic information directly on console tty.
|
|
* Since it is not interrupt driven, all system activities are pretty
|
|
* much suspended.
|
|
* Should not be used for chit-chat.
|
|
* Kernel printfs are single threaded here to prevent garbled output.
|
|
*/
|
|
void
|
|
cmn_err(register int level, char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
icmn_err_tag(0, level, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
void
|
|
cmn_err_tag(int seqnumber, register int level, char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
icmn_err_tag(seqnumber, level, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
extern void dprintf(char *, ...);
|
|
|
|
#define D1ARG va_arg(ap,__psint_t)
|
|
#define D2ARG D1ARG,D1ARG
|
|
#define D4ARG D2ARG,D2ARG
|
|
#define D8ARG D4ARG,D4ARG
|
|
#define D16ARG D8ARG,D8ARG
|
|
|
|
#define ADD_CPU_ID(_buf) \
|
|
if (dophysid) { \
|
|
if (_buf) \
|
|
errvprintf(PRW_ABUF, _buf, "%s: ", \
|
|
private.p_cpunamestr); \
|
|
else \
|
|
errvprintf(prt_where, NULL, "%s: ", \
|
|
private.p_cpunamestr); \
|
|
} \
|
|
else if (docpuid) { \
|
|
if (_buf) \
|
|
errvprintf(PRW_ABUF, _buf, "CPU %d: ", cpuid()); \
|
|
else \
|
|
errvprintf(prt_where, NULL, "CPU %d: ", cpuid()); \
|
|
}
|
|
|
|
|
|
#ifdef CELL_IRIX
|
|
#define CELL_CONBUFSZ 16384
|
|
char cell_console_buf[CELL_CONBUFSZ];
|
|
int cell_conbuf_start;
|
|
int cell_conbuf_flushed;
|
|
int cell_console_overflow;
|
|
volatile cell_t last_con_cellid;
|
|
sema_t cellconssema; /* cell_console_thread() synchronization */
|
|
|
|
#define SECS_PER_DAY 86400 /* # secs per day */
|
|
#define SECS_PER_HOUR 3600 /* # secs per hour */
|
|
#define SECS_PER_MIN 60 /* # secs per minute */
|
|
|
|
#define IS_GOLDEN_CELL (cellid() == CELL_GOLDEN)
|
|
|
|
/* Define functions placed around console rpc to detect recursive calls */
|
|
int in_con_rpc = -1;
|
|
#define START_CON_RPC() in_con_rpc = cpuid()
|
|
#define END_CON_RPC() in_con_rpc = -1;
|
|
|
|
/* Define routine to identify and timestamp console messages from cells that
|
|
* are in the console buffer.
|
|
*/
|
|
|
|
static void
|
|
start_con_msg(void)
|
|
{
|
|
register int secs, hrs, min;
|
|
|
|
secs = time % SECS_PER_DAY;
|
|
hrs = secs / SECS_PER_HOUR;
|
|
secs %= SECS_PER_HOUR;
|
|
min = secs / SECS_PER_MIN;
|
|
secs %= SECS_PER_MIN;
|
|
errvprintf(PRW_BUF | PRW_KLOG, NULL, "(%d:%d:%d) ", hrs, min, secs);
|
|
}
|
|
|
|
#define ADD_CELL_ID \
|
|
if (!in_con_message || last_con_cellid != cellid()) { \
|
|
start_con_msg(); \
|
|
if (IS_GOLDEN_CELL) { \
|
|
errvprintf(prt_where & ~PRW_BUF, NULL, "(CELL:%d) ", \
|
|
CELL_GOLDEN); \
|
|
} \
|
|
} \
|
|
last_con_cellid = cellid();
|
|
#else /* !CELL_IRIX */
|
|
#define ADD_CELL_ID
|
|
#endif
|
|
|
|
/*
|
|
* Add an action to the SYSLOG for Availmon to pickup and act upon.
|
|
* This routine is called by icmn_err to add some extra input to the
|
|
* syslog which will later be pulled out and acted upon by availmon.
|
|
*/
|
|
void availmon_action(int newlevel, int prt_where)
|
|
{
|
|
newlevel &= CE_AVAILMONALL;
|
|
|
|
if(newlevel){
|
|
errvprintf(prt_where, NULL, "(");
|
|
|
|
if(newlevel & CE_TOOKACTIONS){
|
|
errvprintf(prt_where, NULL, "TOOK-ACTION");
|
|
newlevel &= ~CE_TOOKACTIONS; /* clr*/
|
|
if(newlevel)
|
|
errvprintf(prt_where, NULL, ":"); /* still more bits */
|
|
}
|
|
|
|
if(newlevel & CE_RUNNINGPOOR){
|
|
errvprintf(prt_where, NULL, "SYS-DEGRADED");
|
|
newlevel &= ~CE_RUNNINGPOOR; /* clr*/
|
|
if(newlevel)
|
|
errvprintf(prt_where, NULL, ":");
|
|
}
|
|
|
|
if(newlevel & CE_MAINTENANCE){
|
|
errvprintf(prt_where, NULL, "MAINT-NEEDED");
|
|
newlevel &= ~CE_MAINTENANCE; /* clr*/
|
|
if(newlevel)
|
|
errvprintf(prt_where, NULL, ":");
|
|
}
|
|
|
|
if(newlevel & CE_CONFIGERROR){
|
|
errvprintf(prt_where, NULL, "CONFIG-ISSUE");
|
|
newlevel &= ~CE_CONFIGERROR; /* clr*/
|
|
if(newlevel)
|
|
errvprintf(prt_where, NULL, ":");
|
|
}
|
|
|
|
errvprintf(prt_where,NULL, "):");
|
|
}
|
|
}
|
|
|
|
void
|
|
icmn_err_tag(int seqnum, register int level, char *fmt, va_list ap)
|
|
{
|
|
int docpuid, dophysid, i, s, alevel;
|
|
cpuid_t CpuId = getcpuid();
|
|
int prt_where;
|
|
#if !IP30
|
|
int sync;
|
|
#endif
|
|
/* Extract and mask out the bits for availmon levels */
|
|
alevel = (level & CE_SUBTASKMASK);
|
|
|
|
/* Extract standard cmn_err bits */
|
|
level &= CE_PRIOLEVELMASK;
|
|
|
|
/*
|
|
* Extract and mask off the bit indicating whether
|
|
* to prepend the CPU id to the output string
|
|
*/
|
|
docpuid = (level & CE_CPUID) && (maxcpus > 1);
|
|
dophysid = (level & CE_PHYSID) && (maxcpus > 1);
|
|
#if !IP30
|
|
sync = (level & CE_SYNC);
|
|
#endif
|
|
/* Now, we can extract priority-level bits .... */
|
|
level &= CE_LEVELMASK;
|
|
|
|
|
|
/*
|
|
* Catch early system panics. Avoid cpuid() reference
|
|
* to PDA below.
|
|
*/
|
|
if (pdaindr[CpuId].CpuId != CpuId
|
|
#if MP && !SABLE
|
|
|| !(spinlock_initialized(&putbuflck))
|
|
#endif
|
|
) {
|
|
if (level == CE_PANIC)
|
|
dprintf("PANIC: ");
|
|
dprintf(fmt, D16ARG);
|
|
if (level == CE_PANIC)
|
|
prom_reboot();
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
if (dophysid && !private.p_cpunamestr) {
|
|
docpuid = 1;
|
|
dophysid = 0;
|
|
}
|
|
|
|
/*
|
|
* Make sure intrs are disabled, while handling a panic
|
|
* (locking/unlocking putbuflck below still keeps us at
|
|
* the desired spl).
|
|
*/
|
|
if (level == CE_PANIC || level == CE_PBPANIC) {
|
|
if (!dophysid && (maxcpus > 1))
|
|
docpuid = 1;
|
|
(void) spl7();
|
|
/*
|
|
* enter_panic_mode() will allow us to break the putbuf lock
|
|
* if necessary and will only allow 1 cpu to proceed in the
|
|
* case where multiple cpus panic at roughly the same time.
|
|
*/
|
|
enter_panic_mode();
|
|
}
|
|
|
|
/* if we already have the lock, then this is probably a
|
|
* BAD situation, we certainly don't want to try again!
|
|
* So just ignore it.
|
|
*/
|
|
PUTBUF_LOCK(s);
|
|
|
|
/*
|
|
* Set up to print to putbuf or both console and putbuf,
|
|
* as indicated by the first character of the
|
|
* format.
|
|
*/
|
|
if (*fmt == '!') {
|
|
prt_where = PRW_BUF | PRW_KLOG;
|
|
fmt++;
|
|
} else if (*fmt == '^') {
|
|
prt_where = PRW_CONS | PRW_KLOG;
|
|
fmt++;
|
|
} else if (*fmt == '\\') {
|
|
/* currently used only for the boot time banner in main.c,
|
|
* where we don't want the escape sequence in syslog */
|
|
prt_where = PRW_CONS;
|
|
fmt++;
|
|
} else if (*fmt == '#') {
|
|
prt_where = PRW_ERR;
|
|
fmt++;
|
|
} else
|
|
prt_where = PRW_BUF | PRW_CONS | PRW_KLOG;
|
|
|
|
ADD_CELL_ID; /* This has to be first */
|
|
|
|
/* In order to handle availmon actions, we clear the bits for these
|
|
* then we will pass level to availmon_action() for processing
|
|
* at which point the availmon actions will be added to the output stream.
|
|
*/
|
|
switch (level) {
|
|
|
|
case CE_DEBUG:
|
|
errvprintf(prt_where & ~PRW_CONS, NULL, "<%d>", CE_DEBUG);
|
|
if(seqnum && (prt_where & PRW_KLOG) != 0)
|
|
errvprintf(PRW_KLOG, NULL, "|$(0x%X)", seqnum);
|
|
|
|
availmon_action(alevel, prt_where);
|
|
|
|
#if defined(SN0) || defined(EVEREST)
|
|
if (in_panic_mode())
|
|
prt_where |= PRW_LOGNVRAM;
|
|
#endif
|
|
ADD_CPU_ID(NULL);
|
|
errprintf(prt_where, NULL, fmt, ap);
|
|
break;
|
|
case CE_CONT:
|
|
/* PV #168252, send in pri string before actual msg */
|
|
errvprintf(prt_where & ~PRW_CONS, NULL, "<%d>", CE_CONT);
|
|
|
|
availmon_action(alevel, prt_where);
|
|
|
|
#if defined(SN0) || defined(EVEREST)
|
|
if (in_panic_mode())
|
|
prt_where |= PRW_LOGNVRAM;
|
|
#endif
|
|
ADD_CPU_ID(NULL);
|
|
errprintf(prt_where, NULL, fmt, ap);
|
|
break;
|
|
|
|
case CE_NOTE:
|
|
errvprintf(prt_where & ~PRW_CONS, NULL, "<%d>", CE_NOTE);
|
|
if(seqnum && (prt_where & PRW_KLOG) != 0)
|
|
errvprintf(PRW_KLOG, NULL, "|$(0x%X)",seqnum);
|
|
|
|
#if defined(SN0) || defined(EVEREST)
|
|
if (in_panic_mode())
|
|
prt_where |= PRW_LOGNVRAM;
|
|
#endif
|
|
errvprintf(prt_where, NULL, "NOTICE: ") ;
|
|
|
|
availmon_action(alevel, prt_where);
|
|
|
|
ADD_CPU_ID(NULL);
|
|
errprintf(prt_where | PRW_NEWLINE, NULL, fmt, ap);
|
|
break;
|
|
|
|
case CE_WARN:
|
|
errvprintf(prt_where & ~PRW_CONS, NULL, "<%d>", CE_WARN);
|
|
if(seqnum && (prt_where & PRW_KLOG) != 0)
|
|
errvprintf(PRW_KLOG, NULL, "|$(0x%X)", seqnum);
|
|
|
|
#if defined(SN0) || defined (EVEREST)
|
|
if (in_panic_mode())
|
|
prt_where |= PRW_LOGNVRAM;
|
|
#endif
|
|
errvprintf(prt_where, NULL, "WARNING: ");
|
|
|
|
availmon_action(alevel, prt_where);
|
|
|
|
ADD_CPU_ID(NULL);
|
|
errprintf(prt_where | PRW_NEWLINE, NULL, fmt, ap);
|
|
break;
|
|
|
|
case CE_ALERT:
|
|
errvprintf(prt_where & ~PRW_CONS, NULL, "<%d>", CE_ALERT);
|
|
|
|
if(seqnum && (prt_where & PRW_KLOG) != 0)
|
|
errvprintf(PRW_KLOG, NULL, "|$(0x%X)", seqnum);
|
|
|
|
#if defined(SN0) || defined(EVEREST)
|
|
if (in_panic_mode())
|
|
prt_where |= PRW_LOGNVRAM;
|
|
#endif
|
|
errvprintf(prt_where, NULL, "ALERT: ");
|
|
availmon_action(alevel, prt_where);
|
|
|
|
ADD_CPU_ID(NULL);
|
|
errprintf(prt_where | PRW_NEWLINE, NULL, fmt, ap);
|
|
break;
|
|
|
|
case CE_PANIC:
|
|
/* Fall through to common code */
|
|
|
|
case CE_PBPANIC:
|
|
|
|
switch (panic_level++) {
|
|
case 0:
|
|
{
|
|
int total_number_of_cpus;
|
|
cpuid_t id;
|
|
|
|
clkreld();
|
|
|
|
/*
|
|
* Tell all of the other processors we're
|
|
* panic'ing. They'll just sit and spin,
|
|
* only performing cpuaction() requests.
|
|
*/
|
|
|
|
total_number_of_cpus = MAX_NUMBER_OF_CPUS();
|
|
|
|
for (i = 0; i < total_number_of_cpus; i++) {
|
|
if (((id = pdaindr[i].CpuId) != -1) &&
|
|
(id != cpuid())) {
|
|
SEND_VA_PANICSPIN(id);
|
|
#ifdef IP30
|
|
/*
|
|
* need to clear the ipi
|
|
* here, otherwise, slave
|
|
* processor will see it
|
|
* as soon as it gets to the
|
|
* PROM and may not respond
|
|
* to the master processor
|
|
* later, this causes a reset
|
|
* during reboot-on-panic
|
|
*/
|
|
clear_cpu_intr(id);
|
|
#endif /* IP30 */
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reset the prom port to talk through the
|
|
* proms and also reset the prom text window.
|
|
* Passing in a 1 means this is a panic.
|
|
*/
|
|
dopromreset(1);
|
|
|
|
prt_where = PRW_CONS | PRW_BUF;
|
|
|
|
/* make sure anything sent to cmn_err
|
|
* before the panic, appears before
|
|
* the panic on the console output.
|
|
*/
|
|
conbuf_flush(CONBUF_LOCKED|CONBUF_DRAIN);
|
|
|
|
/*
|
|
* Panicstr is used by the debugger to find
|
|
* the beginning of the panic output string.
|
|
* We save the message there first before
|
|
* putting it in the console buffer.
|
|
*/
|
|
|
|
errvprintf(prt_where & ~PRW_CONS, NULL,
|
|
"\n<%d>", CE_PANIC);
|
|
|
|
|
|
#if defined(SN0) || defined(EVEREST)
|
|
if (in_panic_mode())
|
|
prt_where |= PRW_LOGNVRAM;
|
|
#endif
|
|
panicstr = panicbuf;
|
|
errvprintf(PRW_ABUF, &panicstr, "PANIC: ");
|
|
|
|
availmon_action(alevel, prt_where);
|
|
|
|
panicstr = panicbuf + strlen(panicbuf);
|
|
if (maxcpus > 1) {
|
|
ADD_CPU_ID(&panicstr);
|
|
panicstr = panicbuf + strlen(panicbuf);
|
|
}
|
|
|
|
errprintf(PRW_ABUF | PRW_NEWLINE,
|
|
&panicstr, fmt, ap);
|
|
panicstr = panicbuf;
|
|
|
|
/* Put the panicstr in the console buffer
|
|
* and also in the error buffer
|
|
*/
|
|
|
|
errvprintf(prt_where | PRW_ERR, NULL, panicstr);
|
|
|
|
/* make sure the panic message actually
|
|
* gets sent all the way out.
|
|
*/
|
|
conbuf_flush(CONBUF_LOCKED|CONBUF_DRAIN);
|
|
PUTBUF_UNLOCK(s);
|
|
|
|
dumptlb(cpuid());
|
|
|
|
/* Never let the cpu with the ECC error try to
|
|
* produce a dump.
|
|
*/
|
|
if ((maxcpus>1) && (ecc_panic_cpu == cpuid()))
|
|
panicspin();
|
|
|
|
silence_all_audio();
|
|
DELAY(500000); /* delay .5 second */
|
|
checkAllTlbsDumped();
|
|
syncreboot();
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
case 1 :
|
|
clkreld();
|
|
prt_where = PRW_CONS | PRW_BUF;
|
|
if (maxcpus > 1)
|
|
errvprintf(PRW_CONS | PRW_BUF,
|
|
NULL,
|
|
"\nDOUBLE PANIC: CPU %d: ", cpuid());
|
|
else
|
|
errvprintf(PRW_CONS | PRW_BUF,
|
|
NULL,
|
|
"\nDOUBLE PANIC: ");
|
|
|
|
availmon_action(alevel, prt_where);
|
|
|
|
errprintf(PRW_CONS | PRW_BUF | PRW_NEWLINE,
|
|
NULL, fmt, ap);
|
|
|
|
conbuf_flush(CONBUF_LOCKED|CONBUF_DRAIN);
|
|
|
|
PUTBUF_UNLOCK(s);
|
|
|
|
if (kdebug) debug("ring");
|
|
mdboot(AD_HALT, NULL);
|
|
/*NOTREACHED*/
|
|
for (;;)
|
|
;
|
|
|
|
case 2:
|
|
if (kdebug) debug("ring");
|
|
mdboot(AD_HALT, NULL);
|
|
/* fall through */
|
|
default:
|
|
/* if more than 3, something is wrong with
|
|
symmon or the halt code, so just spin! */
|
|
for (;;)
|
|
;
|
|
}
|
|
/* NOTREACHED */
|
|
default:
|
|
cmn_err(CE_PANIC,
|
|
"unknown level in cmn_err (level=%d, msg=\"%s\")",
|
|
level, fmt, ap);
|
|
}
|
|
|
|
#if !IP30
|
|
/* The master processor must perform the actual dumping to the console.
|
|
* Therefore, send a message to the master processor if necessary;
|
|
* otherwise, do it now.
|
|
*/
|
|
|
|
if (prt_where & PRW_CONS)
|
|
conbuf_flush(CONBUF_LOCKED |
|
|
((sync || in_panic_mode()) ? CONBUF_DRAIN : 0));
|
|
#endif /* !IP30 */
|
|
|
|
/*
|
|
* Dont call klogwakeup() unless we were at spl0. We could be called
|
|
* from error interrupts and already holding locks at >spl0. Doing
|
|
* klogwakeup (except at spl0) could lead to deadlocks.
|
|
*/
|
|
if (isspl0(s))
|
|
klogwakeup();
|
|
else
|
|
klog_need_action++;
|
|
|
|
PUTBUF_UNLOCK(s);
|
|
}
|
|
|
|
void
|
|
icmn_err(register int level, char *fmt, va_list ap)
|
|
{ icmn_err_tag(0, level, fmt, ap);
|
|
}
|
|
|
|
|
|
/* The following does a printf to an arbitrary string. */
|
|
void
|
|
vsprintf(char *buf, char *fmt, va_list ap)
|
|
{
|
|
errprintf(PRW_ABUF, &buf, fmt, ap);
|
|
}
|
|
|
|
void
|
|
sprintf(char *buf, char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vsprintf(buf, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/*
|
|
* Versions of sprintf and vsprintf that don't try to lock anything.
|
|
* These can be used on MP platforms in early startup code before
|
|
* locks have been initialized. (MP ARCS machines need this in
|
|
* order to set up startpc in the environment. See idbg.c)
|
|
*/
|
|
void
|
|
lo_vsprintf(char *buf, char *fmt, va_list ap)
|
|
{
|
|
errprintf(PRW_ABUF, &buf, fmt, ap);
|
|
}
|
|
|
|
void
|
|
lo_sprintf(char *buf, char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
lo_vsprintf(buf, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
void
|
|
printf(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
icmn_err_tag(0, CE_CONT, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
void
|
|
sync_printf(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
icmn_err_tag(0, CE_SYNC | CE_CONT, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/* The following is an interface routine for the drivers. */
|
|
void
|
|
dri_printf(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
icmn_err_tag(0, CE_CONT, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/*
|
|
* prdev prints a warning message.
|
|
* dev is a block special device argument.
|
|
* This routine now accepts a variable number of arguments and
|
|
* coverts it into a string for use by the device specific print routine.
|
|
* NOTE: no error checking that the generated string fits in the buffer is
|
|
* done.
|
|
*/
|
|
void
|
|
prdev(char *fmt, int dev, ...) /* a dev_t dev makes stdarg unhappy */
|
|
{
|
|
va_list ap;
|
|
char prdev_buf[PRDEV_MAXBUFSZ];
|
|
struct bdevsw *my_bdevsw;
|
|
|
|
va_start(ap, dev);
|
|
vsprintf(prdev_buf, fmt, ap);
|
|
va_end(ap);
|
|
|
|
my_bdevsw = get_bdevsw(dev);
|
|
if (bdhold(my_bdevsw)) {
|
|
cmn_err_tag(291,CE_WARN,"%s on bad dev 0x%x\n", prdev_buf, dev);
|
|
return;
|
|
}
|
|
bdprint(my_bdevsw, dev, prdev_buf);
|
|
bdrele(my_bdevsw);
|
|
}
|
|
|
|
/*
|
|
* stdname
|
|
* This routine takes the string and dev arguments and combines
|
|
* them together to produce a standard nameformat for reporting device errors.
|
|
*/
|
|
char *
|
|
stdname(
|
|
char *str,
|
|
char *buf,
|
|
int ctlr,
|
|
int unit,
|
|
int fs)
|
|
{
|
|
char formedstr[STDN_MAXNAMESZ];
|
|
|
|
strncpy(formedstr, str, 3); /* insure that name is only 3 chars */
|
|
formedstr[ 3 ] = '\0';
|
|
strcat(formedstr, STDN_CTLR_FMT);
|
|
if (unit != STDN_NULL_UNIT) {
|
|
strcat(formedstr, STDN_UNIT_FMT);
|
|
if (fs != STDN_NULL_FS) {
|
|
if (fs == STDN_VH_FS)
|
|
strcat(formedstr, STDN_VH_FMT);
|
|
else
|
|
strcat(formedstr, STDN_FS_FMT);
|
|
}
|
|
}
|
|
strcat(formedstr, STDN_END_FMT);
|
|
|
|
sprintf(buf, formedstr, ctlr, unit, fs);
|
|
return(buf);
|
|
}
|
|
|
|
/* Berkeley style panic() routine for compatibility */
|
|
void
|
|
panic(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
icmn_err_tag(0, CE_PANIC, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/* was a macro; as a function we use about 5K less memory... */
|
|
static void
|
|
output(int where, char **buf, uchar_t c)
|
|
{
|
|
#ifdef CELL_IRIX
|
|
static void cell_output(int, uchar_t);
|
|
#endif
|
|
|
|
/* Keep track of current message state */
|
|
|
|
if (c == '\n' || c == '\0') {
|
|
in_con_message = 0;
|
|
}
|
|
else if (!in_con_message) {
|
|
in_con_message = 1;
|
|
}
|
|
|
|
if (where & PRW_ABUF)
|
|
*(*buf)++ = c;
|
|
#ifdef CELL_IRIX
|
|
else if (!IS_GOLDEN_CELL) {
|
|
/* Save in buffer to be flushed later to golden cell */
|
|
|
|
cell_output(where, c);
|
|
}
|
|
#endif
|
|
else {
|
|
if ((where & PRW_KLOG) && c != '\0')
|
|
klogmsgs.klm_buffer[ klogmsgs.klm_writeloc++ & KLM_BUFMASK] = c;
|
|
if (where & PRW_BUF) {
|
|
*(putbuf+(putbufndx & (putbufsz-1))) = c;
|
|
putbufndx++;
|
|
}
|
|
if (where & PRW_ARCS) {
|
|
long a;
|
|
char oc = c;
|
|
if (c == '\n') arcs_write(1,"\r\n",2,&a);
|
|
else arcs_write (1, &oc, 1, &a);
|
|
}
|
|
if (where & PRW_CONS) {
|
|
if (constrlen < conbufsz) {
|
|
*(conbuf+constrlen) = c;
|
|
constrlen ++;
|
|
}
|
|
}
|
|
if (where & PRW_ERR) {
|
|
if (errbufndx < errbufsz) {
|
|
*(errbuf+errbufndx) = c;
|
|
errbufndx++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Scaled down version of C Library printf, called by cmn_err() and sprintf().
|
|
* NOTE: putbuflck should be locked when called and will remain locked.
|
|
*
|
|
* One additional format: %b is supported to decode error registers.
|
|
* Usage is:
|
|
* printf("reg=%b\n", regval, "<base><arg>*");
|
|
* Where <base> is the output base expressed as a control character,
|
|
* e.g. \10 gives octal; \20 gives hex. Each arg is a sequence of
|
|
* characters, the first of which gives the bit number to be inspected
|
|
* (origin 1), and the next characters (up to a control character, i.e.
|
|
* a character <= 32), give the name of the register. Thus
|
|
* printf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
|
|
* would produce output:
|
|
* reg=3<BITTWO,BITONE>
|
|
*
|
|
* For convenience, in 64 bit mode, all the standard %{x,d,u,o} all print
|
|
* 64 bit quantities and thus one must specify something special
|
|
* to print a 32 bit quantity (note that this really only matters
|
|
* when one has more args than arg registers , and one tries to
|
|
* print a 32 bit quantity - one gets garbage in the high bits ...)
|
|
* We should (1/10/95) add %p and %w32 formats to symmon to
|
|
* provide a complete set.
|
|
*/
|
|
static void
|
|
errvprintf(int where, char **buf, char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
errprintf(where, buf, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/* Return true if it is ok to print to the system console through
|
|
* the console driver (i.e. the driver init routine has been called
|
|
* and normal console output is now possible)
|
|
*/
|
|
static int
|
|
console_ready(void)
|
|
{
|
|
int ready;
|
|
|
|
ready = cn_is_inited();
|
|
|
|
#if (IP27 || IP30 || IP32) && !defined(SABLE)
|
|
{
|
|
extern int console_device_attached;
|
|
extern int console_is_tport;
|
|
|
|
/* XXXmdf If we're running textport, we want to get panic
|
|
* messages on the graphical console. Also, we don't care if
|
|
* the serial console has been discovered yet.
|
|
*/
|
|
if (ready && !console_is_tport &&
|
|
#ifdef IP30
|
|
/*
|
|
* want non-panic processor's output to go through poll mode
|
|
* instead of DMA mode since the ioc3 DMA engine was shut down
|
|
* by the panic processor the first time it did a printf
|
|
*/
|
|
(panicstr || !console_device_attached)
|
|
#else
|
|
(in_panic_mode() || !console_device_attached)
|
|
#endif /* IP30 */
|
|
)
|
|
ready = 0;
|
|
}
|
|
#endif
|
|
#if EVEREST
|
|
{
|
|
extern int console_device_attached;
|
|
extern int console_is_tport;
|
|
|
|
if (!console_is_tport &&
|
|
(in_panic_mode() || !console_device_attached))
|
|
ready = 0;
|
|
}
|
|
#endif
|
|
return(ready);
|
|
}
|
|
|
|
static void errprintf_V(int where, char **buf, dev_t arg);
|
|
|
|
static void
|
|
errprintf(int where, char **buf, register char *fmt, va_list ap)
|
|
{
|
|
int i, any;
|
|
__scunsigned_t b;
|
|
register int c;
|
|
register char *s;
|
|
int Lflag, Iflag;
|
|
register int base;
|
|
int skipping_fill;
|
|
char *fillchar; /* pointer to supposed fill character */
|
|
|
|
if ((where & PRW_CONS) && !console_ready())
|
|
where = (where & ~PRW_CONS) | PRW_ARCS;
|
|
|
|
#ifdef SN0
|
|
if (where & PRW_LOGNVRAM)
|
|
ip27log_panic_printf(IP27LOG_FATAL, fmt, ap);
|
|
#endif
|
|
#ifdef EVEREST
|
|
if (where & PRW_LOGNVRAM){
|
|
vsprintf(nvtmpbuf, fmt, ap);
|
|
strcat(fru_nvbuff, nvtmpbuf);
|
|
/* We have everything now, so store the buffer in NVRAM */
|
|
/* Log the FRU information into the NVRAM */
|
|
set_fru_nvram(fru_nvbuff, strlen(fru_nvbuff)+1);
|
|
}
|
|
#endif
|
|
|
|
loop:
|
|
skipping_fill = 0;
|
|
Lflag = Iflag = 0;
|
|
|
|
while ((c = *fmt++) != '%')
|
|
if (c)
|
|
output(where, buf, c);
|
|
else
|
|
goto done;
|
|
|
|
inner_loop:
|
|
if ((c = *fmt++) == 'l' ) {
|
|
if ((c = *fmt++) == 'l') {
|
|
c = *fmt++;
|
|
Lflag++;
|
|
}
|
|
/* silently support %lx */
|
|
} else if (c == 'w') {
|
|
if (*fmt == '3' && *(fmt+1) == '2') {
|
|
Iflag = 1;
|
|
fmt += 2;
|
|
c = *fmt++;
|
|
}
|
|
}
|
|
|
|
switch (c) {
|
|
case 'b':
|
|
b = va_arg(ap, __scunsigned_t); /* register value */
|
|
s = va_arg(ap, char *);
|
|
printn(where, buf, b, *s++);
|
|
any = 0;
|
|
if (b) {
|
|
output(where, buf, '<');
|
|
while (i = *s++) {
|
|
if (b & (1 << (i-1))) {
|
|
if (any)
|
|
output(where, buf, ',');
|
|
any = 1;
|
|
for (; (c = *s) > 32; s++)
|
|
output(where, buf, c);
|
|
} else
|
|
for (; *s > 32; s++)
|
|
;
|
|
}
|
|
output(where, buf, '>');
|
|
}
|
|
break;
|
|
case 'D':
|
|
case 'd':
|
|
base = -10;
|
|
goto number;
|
|
case 'u':
|
|
base = 10;
|
|
goto number;
|
|
case 'p':
|
|
/* generic pointer */
|
|
base = 16;
|
|
goto number;
|
|
case 'X':
|
|
case 'x':
|
|
base = 16;
|
|
goto number;
|
|
case 'o':
|
|
base = 8;
|
|
number:
|
|
if (Lflag)
|
|
printL(where, buf, va_arg(ap, __uint64_t), base);
|
|
else if (Iflag)
|
|
printn(where, buf, va_arg(ap, uint_t), base);
|
|
else
|
|
printn(where, buf, va_arg(ap, __scunsigned_t), base);
|
|
break;
|
|
case 's':
|
|
s = va_arg(ap, char *);
|
|
while (c = *s++) {
|
|
output(where, buf, c);
|
|
}
|
|
break;
|
|
case 'c':
|
|
c = va_arg(ap, int);
|
|
output(where, buf, c);
|
|
break;
|
|
|
|
case 'r':
|
|
case 'R':
|
|
b = va_arg(ap, __scunsigned_t); /* register value */
|
|
s = va_arg(ap, char *); /* addr of descriptor struct */
|
|
if (c == 'R') {
|
|
_putstr(where, buf, "0x");
|
|
printn(where, buf, b, 16);
|
|
}
|
|
if (c == 'r' || b) {
|
|
register struct reg_desc *rd;
|
|
register struct reg_values *rv;
|
|
__psunsigned_t field;
|
|
|
|
output(where, buf, '<');
|
|
any = 0;
|
|
for (rd = (struct reg_desc *)s; rd->rd_mask; rd++) {
|
|
field = b & rd->rd_mask;
|
|
field = (rd->rd_shift > 0)
|
|
? field << rd->rd_shift
|
|
: field >> -rd->rd_shift;
|
|
if (any &&
|
|
(rd->rd_format || rd->rd_values
|
|
|| (rd->rd_name && field)
|
|
)
|
|
)
|
|
output(where, buf, ',');
|
|
if (rd->rd_name) {
|
|
if (rd->rd_format || rd->rd_values
|
|
|| field) {
|
|
_putstr(where, buf, rd->rd_name);
|
|
any = 1;
|
|
}
|
|
if (rd->rd_format || rd->rd_values) {
|
|
output(where, buf, '=');
|
|
any = 1;
|
|
}
|
|
}
|
|
if (rd->rd_format) {
|
|
errprintf(where, buf, rd->rd_format,
|
|
(va_list)&field);
|
|
any = 1;
|
|
if (rd->rd_values)
|
|
output(where, buf, ':');
|
|
}
|
|
if (rd->rd_values) {
|
|
any = 1;
|
|
for (rv = rd->rd_values;
|
|
rv->rv_name;
|
|
rv++) {
|
|
if (field == rv->rv_value) {
|
|
_putstr(where, buf, rv->rv_name);
|
|
break;
|
|
}
|
|
}
|
|
if (rv->rv_name == NULL)
|
|
_putstr(where, buf, "???");
|
|
}
|
|
}
|
|
output(where, buf, '>');
|
|
}
|
|
break;
|
|
|
|
/* Printing of HWG dev_t directly. Done in a routine to
|
|
* save huge local stack space of MAXDEVNAME bytes needed
|
|
* for conversion.
|
|
*/
|
|
case 'v': /* printing HWG vertex_hdl_t */
|
|
errprintf_V(where,buf,vhdl_to_dev(va_arg(ap, vertex_hdl_t)));
|
|
break;
|
|
case 'V': /* printing HWG dev_t */
|
|
errprintf_V(where,buf,va_arg(ap, dev_t));
|
|
break;
|
|
|
|
case '\0':
|
|
goto done;
|
|
|
|
default:
|
|
/*
|
|
* A fancy C printf supports "fill" characters, e.g. %08x
|
|
* which says to display at least 8 digits in hex, using "0"
|
|
* as a leading fill character if the value is otherwise less
|
|
* than 8 nonzero digits. We don't go to the trouble of
|
|
* supporting "fill" formats in the kernel, but we will at
|
|
* least recognize the "fill" digits and ignore them, so %08x
|
|
* gets treated like %x.
|
|
*/
|
|
if (skipping_fill) {
|
|
if (isdigit(c)) {
|
|
/* still skipping "fill" */
|
|
goto inner_loop;
|
|
} else {
|
|
/*
|
|
* Not a digit (and hence not fill), and
|
|
* not a known format type -- that wasn't
|
|
* fill! Backtrack and try again.
|
|
*/
|
|
fmt = fillchar-1;
|
|
goto loop;
|
|
}
|
|
} else {
|
|
if (isdigit(c)) {
|
|
/* may be "fill" formatting */
|
|
fillchar = fmt; /* in case have to backtrack */
|
|
skipping_fill = 1;
|
|
goto inner_loop;
|
|
}
|
|
}
|
|
output(where, buf, c);
|
|
break;
|
|
}
|
|
goto loop;
|
|
|
|
done:
|
|
if ((where & PRW_NEWLINE) && in_con_message)
|
|
output(where, buf, '\n');
|
|
if (where & PRW_ABUF)
|
|
output(where, buf, '\0');
|
|
}
|
|
|
|
static void
|
|
errprintf_V(int where, char **buf, dev_t arg)
|
|
{
|
|
char devnm[MAXDEVNAME];
|
|
char *s;
|
|
int c;
|
|
|
|
/* dev_to_name() never returns NULL. Use dev_t version so old
|
|
* names continue to work.
|
|
*/
|
|
s = dev_to_name(arg,devnm,MAXDEVNAME);
|
|
while (c = *s++)
|
|
output(where,buf,c);
|
|
}
|
|
|
|
/*
|
|
* convert a va_list to a set of args - mostly for passing to prom printf
|
|
* routines.
|
|
*/
|
|
#define MAXVARGS 16
|
|
void
|
|
aptoargs(char *fmt, va_list ap, __psint_t args[])
|
|
{
|
|
int argnum = 0;
|
|
register int c;
|
|
int Iflag, Lflag;
|
|
int skipping_fill;
|
|
char *fillchar; /* pointer to supposed fill character */
|
|
|
|
loop:
|
|
skipping_fill = 0;
|
|
Iflag = Lflag = 0;
|
|
while ((c = *fmt++) != '%') {
|
|
if (c == '\0')
|
|
return;
|
|
}
|
|
|
|
inner_loop:
|
|
if ((c = *fmt++) == 'l' ) {
|
|
if ((c = *fmt++) == 'l') {
|
|
c = *fmt++;
|
|
Lflag++;
|
|
}
|
|
/* silently support %lx */
|
|
} else if (c == 'w') {
|
|
if (*fmt == '3' && *(fmt+1) == '2') {
|
|
Iflag = 1;
|
|
fmt += 2;
|
|
c = *fmt++;
|
|
}
|
|
}
|
|
|
|
switch (c) {
|
|
case 'b':
|
|
args[argnum++] = (__psint_t)va_arg(ap, __scunsigned_t); /* register value */
|
|
args[argnum++] = (__psint_t)va_arg(ap, char *);
|
|
break;
|
|
case 'D':
|
|
case 'd':
|
|
case 'u':
|
|
case 'X':
|
|
case 'x':
|
|
case 'o':
|
|
case 'p':
|
|
if (Lflag) {
|
|
#if (_MIPS_SIM == _MIPS_SIM_ABI32)
|
|
/* keep long long pair in calling sequence */
|
|
if ((argnum & 1) == 0) {
|
|
args[argnum++] = (__psint_t)va_arg(ap, uint_t);
|
|
}
|
|
args[argnum++] = (__psint_t)va_arg(ap, uint_t);
|
|
args[argnum++] = (__psint_t)va_arg(ap, uint_t);
|
|
#else
|
|
args[argnum++] = (__psint_t)va_arg(ap, __uint64_t);
|
|
#endif
|
|
}
|
|
else if (Iflag)
|
|
args[argnum++] = (__psint_t)va_arg(ap, uint_t);
|
|
else
|
|
args[argnum++] = (__psint_t)va_arg(ap, __scunsigned_t);
|
|
break;
|
|
case 's':
|
|
args[argnum++] = (__psint_t)va_arg(ap, char *);
|
|
break;
|
|
case 'c':
|
|
args[argnum++] = (__psint_t)va_arg(ap, int);
|
|
break;
|
|
|
|
case 'r':
|
|
case 'R':
|
|
args[argnum++] = (__psint_t)va_arg(ap, __scunsigned_t); /* register value */
|
|
args[argnum++] = (__psint_t)va_arg(ap, char *); /* addr of descriptor struct */
|
|
break;
|
|
|
|
case '\0':
|
|
return;
|
|
|
|
default:
|
|
/*
|
|
* A fancy C printf supports "fill" characters, e.g. %08x
|
|
* which says to display at least 8 digits in hex, using "0"
|
|
* as a leading fill character if the value is otherwise less
|
|
* than 8 nonzero digits. We don't go to the trouble of
|
|
* supporting "fill" formats in the kernel, but we will at
|
|
* least recognize the "fill" digits and ignore them, so %08x
|
|
* gets treated like %x.
|
|
*/
|
|
if (skipping_fill) {
|
|
if (isdigit(c)) {
|
|
/* still skipping "fill" */
|
|
goto inner_loop;
|
|
} else {
|
|
/*
|
|
* Not a digit (and hence not fill), and
|
|
* not a known format type -- that wasn't
|
|
* fill! Backtrack and try again.
|
|
*/
|
|
fmt = fillchar-1;
|
|
goto loop;
|
|
}
|
|
} else {
|
|
if (isdigit(c)) {
|
|
/* may be "fill" formatting */
|
|
fillchar = fmt; /* in case have to backtrack */
|
|
skipping_fill = 1;
|
|
goto inner_loop;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
goto loop;
|
|
}
|
|
|
|
|
|
static void
|
|
_putstr(int where, char **buf, char *cp)
|
|
{
|
|
char c;
|
|
|
|
while (c = *cp++)
|
|
output(where, buf, c);
|
|
}
|
|
|
|
static void
|
|
printL(int where, char **buf, register __uint64_t n, register int b)
|
|
{
|
|
char prbuf[65]; /* paranoia: large enough for base 2 numbers */
|
|
register char *cp;
|
|
|
|
if (b < 0) {
|
|
b = -b;
|
|
if ((__int64_t)n < 0) {
|
|
output(where, buf, '-');
|
|
n = (__uint64_t)(-(__int64_t)n);
|
|
}
|
|
}
|
|
|
|
cp = prbuf;
|
|
do {
|
|
*cp++ = "0123456789abcdef"[n%b];
|
|
n /= b;
|
|
} while (n);
|
|
for (--cp; cp >= prbuf; --cp)
|
|
output(where, buf, *cp);
|
|
}
|
|
|
|
static void
|
|
printn(int where, char **buf, register __scunsigned_t n, register int b)
|
|
{
|
|
#if (_MIPS_SZLONG == 64)
|
|
printL(where, buf, n, b);
|
|
#else
|
|
char prbuf[33]; /* paranoia: large enough for base 2 numbers */
|
|
register char *cp;
|
|
|
|
if (b < 0) {
|
|
b = -b;
|
|
if ((__scint_t)n < 0) {
|
|
output(where, buf, '-');
|
|
n = (__scunsigned_t)(-(__scint_t)n);
|
|
}
|
|
}
|
|
|
|
cp = prbuf;
|
|
do {
|
|
*cp++ = "0123456789abcdef"[n%b];
|
|
n /= b;
|
|
} while (n);
|
|
for (--cp; cp >= prbuf; --cp)
|
|
output(where, buf, *cp);
|
|
#endif /* _MIPS_SZLONG == 64 */
|
|
}
|
|
|
|
void
|
|
printregs(eframe_t *ep, void (*f)(char *, ...))
|
|
{
|
|
|
|
#define PFMT3 "%s0x%llx 0x%llx 0x%llx\n"
|
|
#define PFMT4 "%s0x%llx 0x%llx 0x%llx 0x%llx\n"
|
|
|
|
#if _MIPS_SIM == _ABI64 || _MIPS_SIM == _ABIN32
|
|
f(PFMT3, "at/v0/v1:\t", ep->ef_at, ep->ef_v0, ep->ef_v1);
|
|
f(PFMT4, "a0-a3:\t\t", ep->ef_a0, ep->ef_a1, ep->ef_a2, ep->ef_a3);
|
|
f(PFMT4, "a4-a7 (t0-t3):\t",
|
|
ep->ef_a4, ep->ef_a5, ep->ef_a6, ep->ef_a7);
|
|
f(PFMT4, "t0-t3 (t4-t7):\t",
|
|
ep->ef_t0, ep->ef_t1, ep->ef_t2, ep->ef_t3);
|
|
f(PFMT4, "s0-s3:\t\t", ep->ef_s0, ep->ef_s1, ep->ef_s2, ep->ef_s3);
|
|
f(PFMT4, "s4-s7:\t\t", ep->ef_s4, ep->ef_s5, ep->ef_s6, ep->ef_s7);
|
|
f(PFMT4, "t8/t9/k0/k1:\t", ep->ef_t8, ep->ef_t9, ep->ef_k0, ep->ef_k1);
|
|
f(PFMT4, "gp/sp/fp/ra:\t", ep->ef_gp, ep->ef_sp, ep->ef_fp, ep->ef_ra);
|
|
f(PFMT3, "mdlo/mdhi/sr:\t", ep->ef_mdlo, ep->ef_mdhi, ep->ef_sr);
|
|
f(PFMT3, "cause/epc/badva:\t", ep->ef_cause, ep->ef_epc, ep->ef_badvaddr);
|
|
|
|
#else
|
|
|
|
f(PFMT3, "at/v0/v1:\t", ep->ef_at, ep->ef_v0, ep->ef_v1);
|
|
f(PFMT4, "a0-a3:\t\t", ep->ef_a0, ep->ef_a1, ep->ef_a2, ep->ef_a3);
|
|
f(PFMT4, "t0-t3:\t\t", ep->ef_t0, ep->ef_t1, ep->ef_t2, ep->ef_t3);
|
|
f(PFMT4, "t4-t7:\t\t", ep->ef_t4, ep->ef_t5, ep->ef_t6, ep->ef_t7);
|
|
f(PFMT4, "s0-s3:\t\t", ep->ef_s0, ep->ef_s1, ep->ef_s2, ep->ef_s3);
|
|
f(PFMT4, "s4-s7:\t\t", ep->ef_s4, ep->ef_s5, ep->ef_s6, ep->ef_s7);
|
|
f(PFMT4, "t8/t9/k0/k1:\t", ep->ef_t8, ep->ef_t9, ep->ef_k0, ep->ef_k1);
|
|
f(PFMT4, "gp/sp/fp/ra:\t", ep->ef_gp, ep->ef_sp, ep->ef_fp, ep->ef_ra);
|
|
f(PFMT3, "mdlo/mdhi/sr:\t", ep->ef_mdlo, ep->ef_mdhi, ep->ef_sr);
|
|
f(PFMT3, "cause/epc/badva:\t", ep->ef_cause, ep->ef_epc, ep->ef_badvaddr);
|
|
#endif
|
|
|
|
#undef PFMT3
|
|
#undef PFMT4
|
|
}
|
|
|
|
|
|
/*
|
|
* Pass the current string to the console ("prom") output routines. Since
|
|
* the conbuf is a circular buffer, it may be necessary to do two writes.
|
|
*
|
|
* The parameter is now flags instead of 'islocked'. If CONBUF_UNLOCKED is
|
|
* set we are being dumping on behalf of another processor and putbuflck
|
|
* must be reacquired. If CONBUF_DRAIN is set, after writing to the console
|
|
* we call an additonal console routine so the output is actually flushed
|
|
* to the output device.
|
|
*/
|
|
#if SN0
|
|
|
|
static void
|
|
conbuf_flush_timeout(register int flags)
|
|
{
|
|
conbuf_tout = 0;
|
|
conbuf_flush(flags);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
conbuf_flush(register int flags)
|
|
{
|
|
register int s;
|
|
#if SN0
|
|
extern int printf_acquire_console(void);
|
|
extern void printf_release_console(void);
|
|
#endif
|
|
|
|
#if defined(CELL_IRIX)
|
|
/* Only the golden_cell flushes to the real console */
|
|
if (cellid() != golden_cell) return;
|
|
#endif
|
|
|
|
#if SN0
|
|
/* on SN0, all output pauses when the system controller daemon
|
|
* takes over the console port to write to the FFSC. We must
|
|
* not corrupt that data by spitting things to the
|
|
* console. Try again in a little while.
|
|
*/
|
|
if (!printf_acquire_console()) {
|
|
/* we only need one timeout at a time. Make sure there
|
|
* isn't already one pending before requesting another
|
|
* one.
|
|
*/
|
|
if (compare_and_swap_int(&conbuf_tout, 0, 1)) {
|
|
if (timeout_nothrd((void(*)())conbuf_flush_timeout,
|
|
(void*)(long)CONBUF_UNLOCKED,
|
|
HZ / 4) == 0)
|
|
/* force the compiler to generate an
|
|
* atomic store
|
|
*/
|
|
*(volatile int*)&conbuf_tout = 0;
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (flags & CONBUF_UNLOCKED)
|
|
PUTBUF_LOCK(s);
|
|
else
|
|
s = spl7();
|
|
|
|
if (constrlen) {
|
|
cn_write(conbuf, constrlen);
|
|
constrlen = 0;
|
|
}
|
|
|
|
/* Make sure data is written all the way to the output display. */
|
|
if (flags & CONBUF_DRAIN)
|
|
cn_flush();
|
|
|
|
if (flags & CONBUF_UNLOCKED)
|
|
PUTBUF_UNLOCK(s);
|
|
else
|
|
splx(s);
|
|
#if SN0
|
|
printf_release_console();
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Wrapper to prevent calling promreset twice
|
|
*/
|
|
static void
|
|
dopromreset(int arg)
|
|
{
|
|
static int prom_is_reset = 0;
|
|
|
|
if (!prom_is_reset) {
|
|
cn_init(arg, 0);
|
|
prom_is_reset = 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Sit and spin after a panic. The only thing we do is:
|
|
*
|
|
* 1. If we're the master processor, we need to flush console output.
|
|
* 2. Check for cpuaction() requests to reboot.
|
|
*
|
|
* This is now a cpuvaction so we don't need to be able to get an action
|
|
* block in order to panic.
|
|
*/
|
|
void
|
|
panicspin(void)
|
|
{
|
|
clkreld(); /* turn off clock */
|
|
|
|
spl7();
|
|
dumptlb(cpuid());
|
|
setjmp(private.p_panicregs_tbl);
|
|
private.p_panicregs_valid = 1;
|
|
|
|
/* If panicing due to ecc error on cpu, we don't want that cpu to
|
|
* initiate the dump. Let master cpu do it instead, unless it is
|
|
* the one with the ecc error. If the master cpu has the error, let
|
|
* the NEIGHBOR cpu do the syncreboot since the panic processor may
|
|
* have the highest number.
|
|
*/
|
|
if (ecc_panic_cpu != -1 && cpuid() != ecc_panic_cpu) {
|
|
if (private.p_flags & PDAF_MASTER) {
|
|
syncreboot();
|
|
} else if ((pdaindr[ecc_panic_cpu].pda->p_flags &
|
|
PDAF_MASTER) &&
|
|
(cpuid() == (ecc_panic_cpu^1))) {
|
|
syncreboot();
|
|
}
|
|
}
|
|
|
|
for (;;) {
|
|
#if EVEREST || SN0 || IP30
|
|
if (kdebug) {
|
|
extern void chkdebug(void);
|
|
chkdebug();
|
|
}
|
|
#endif
|
|
doactions();
|
|
DELAY(500);
|
|
}
|
|
}
|
|
|
|
static void
|
|
syncreboot(void)
|
|
{
|
|
if (kdebug)
|
|
debug("ring");
|
|
|
|
/*
|
|
* Turning off lock bit ``initializes'' it without having
|
|
* to go through metering initialization. We do this just
|
|
* in case... (in case it is already locked, I guess).
|
|
* Setting splockmeter to 0 ensures that we'll skip possible
|
|
* double-trips through metering code.
|
|
*
|
|
* spinlock_init(&putbuflck, "putbuf");
|
|
*/
|
|
|
|
splockmeter = 0;
|
|
#if MP
|
|
putbuflck &= ~SPIN_LOCK;
|
|
haspblock = -1;
|
|
pblock_nest = 0;
|
|
#endif
|
|
dumpsys();
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
/*
|
|
* rebooting routines
|
|
*/
|
|
typedef volatile struct mboot_s {
|
|
int cpunum; /* # of CPU active in system to be rebooted */
|
|
int (*rboot_func)(char *);/* prom entry point */
|
|
lock_t mboot_lock; /* spin lock */
|
|
} mboot_t;
|
|
|
|
static mboot_t mboot;
|
|
|
|
#define REBOOT_WAIT (5 * timer_freq) /* 5 seconds */
|
|
|
|
/*
|
|
* Machine dependent code to reboot
|
|
*/
|
|
void
|
|
mdboot(
|
|
int fcn,
|
|
char *mdep)
|
|
{
|
|
|
|
/*
|
|
* Sync the tod clock with what the kernel is keeping
|
|
*/
|
|
wtodc();
|
|
|
|
switch (fcn) {
|
|
case AD_IBOOT:
|
|
cmn_err_tag(292,CE_WARN,"Sorry, no interactive reboot.");
|
|
case AD_HALT:
|
|
mprboot(prom_reboot, 0);
|
|
break;
|
|
case AD_EXEC:
|
|
cmn_err_tag(293,CE_WARN,"Sorry, no standalone exec.");
|
|
mprboot(prom_reboot, 0);
|
|
break;
|
|
case AD_BOOT:
|
|
mprboot(prom_autoboot, mdep);
|
|
break;
|
|
case AD_POWEROFF:
|
|
power_off_flag = 1;
|
|
/* call prom_reboot so we get the alarmclock auto poweron
|
|
* setup, if necessary. */
|
|
mprboot(prom_reboot, 0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
mprboot(
|
|
void (*func)(),
|
|
char *mdep)
|
|
{
|
|
int s, i, id, total_number_of_cpus;
|
|
extern int (*io_halt[])(void);
|
|
|
|
if (maxcpus > 1)
|
|
cmn_err(CE_CONT,
|
|
"%s started from CPU %d\n",
|
|
power_off_flag ? "Power off" : "Reboot",
|
|
cpuid());
|
|
|
|
spinlock_init((lock_t *)&mboot.mboot_lock, "rbootlck");
|
|
spl7();
|
|
|
|
/*
|
|
* Halt devices. allow devices to stop cleanly
|
|
* AFTER interrupts are shut off.
|
|
*/
|
|
|
|
for(i = 0; io_halt[i]; i++)
|
|
(*io_halt[i])();
|
|
|
|
s = mp_mutex_spinlock((lock_t *)&mboot.mboot_lock);
|
|
mboot.rboot_func = (int (*)())func;
|
|
mboot.cpunum = 1;
|
|
|
|
total_number_of_cpus = MAX_NUMBER_OF_CPUS();
|
|
for(i = 0; i < total_number_of_cpus; i++)
|
|
if (((id = pdaindr[i].CpuId) != -1) &&
|
|
(id != private.p_cpuid)) {
|
|
mboot.cpunum++;
|
|
cpuaction(id, (cpuacfunc_t)do_mprboot, A_NOW);
|
|
}
|
|
mp_mutex_spinunlock((lock_t *)&mboot.mboot_lock, s);
|
|
do_mprboot(mdep);
|
|
}
|
|
|
|
static void
|
|
do_mprboot(char *mdep)
|
|
{
|
|
register int s;
|
|
__uint64_t end_time;
|
|
|
|
ASSERT(mboot.cpunum > 0);
|
|
if (maxcpus > 1 && ! power_off_flag)
|
|
cmn_err(CE_CONT, "CPU %d rebooting\n", cpuid());
|
|
s = mp_mutex_spinlock((lock_t *)&mboot.mboot_lock);
|
|
mboot.cpunum--;
|
|
mp_mutex_spinunlock((lock_t *)&mboot.mboot_lock, s);
|
|
if (cpuid() == masterpda->p_cpuid) {
|
|
/*
|
|
* use the do construct such that conbuf_flush will be called
|
|
* even if the master processor comes here last
|
|
*/
|
|
end_time = absolute_rtc_current_time() + REBOOT_WAIT;
|
|
do {
|
|
if (*(volatile int *)&constrlen)
|
|
conbuf_flush(CONBUF_UNLOCKED|CONBUF_DRAIN);
|
|
#ifdef IP19
|
|
{
|
|
/* No use waiting for the last cpu if we've died due
|
|
* to a cache error on one of the cpus.
|
|
*/
|
|
|
|
extern int ecc_panic_deadcpus(void);
|
|
|
|
if ((mboot.cpunum == 1) && ecc_panic_deadcpus())
|
|
mboot.cpunum = 0;
|
|
}
|
|
#endif /* IP19 */
|
|
/* Put a time limit on the wait: CPUs may be spinning
|
|
* with interrupts off (especially on an NMI)
|
|
*/
|
|
if (absolute_rtc_current_time() > end_time) {
|
|
break;
|
|
}
|
|
|
|
us_delay(100000); /* 0.1 second */
|
|
|
|
} while (mboot.cpunum);
|
|
}
|
|
|
|
(*mboot.rboot_func)(mdep);
|
|
}
|
|
|
|
#define MAX_CONLEN 256
|
|
#ifdef CELL_IRIX
|
|
static void
|
|
get_cell_conmsg(char *msg, int *pbi, int *cc)
|
|
{
|
|
|
|
register int ci = 0;
|
|
register char *cp;
|
|
|
|
do {
|
|
cp = &cell_console_buf[(*pbi)++];
|
|
*pbi %= CELL_CONBUFSZ;
|
|
if (*cp == '\0') {
|
|
continue;
|
|
}
|
|
if (*cp & PRW_FLAG) {
|
|
continue;
|
|
}
|
|
msg[ci++] = *cp;
|
|
(*cc)--;
|
|
if (*cp == '\n') {
|
|
goto done;
|
|
}
|
|
} while (*pbi != cell_conbuf_start && ci < MAX_CONLEN && *cc != 0);
|
|
|
|
done:
|
|
if (*pbi == cell_conbuf_start) {
|
|
*cc = 0;
|
|
}
|
|
msg[ci] = '\0';
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
get_conmsg(char *msg, int *pbi, int *cc)
|
|
{
|
|
|
|
register int ci = 0;
|
|
register char *cp;
|
|
register int putbufmask = putbufsz - 1;
|
|
|
|
if (*cc == 0) {
|
|
goto done;
|
|
}
|
|
|
|
do {
|
|
cp = putbuf + (*pbi)++;
|
|
if (*cp != '\0') {
|
|
msg[ci++] = *cp;
|
|
(*cc)--;
|
|
}
|
|
*pbi %= putbufsz;
|
|
if (*cp == '\n') {
|
|
goto done;
|
|
}
|
|
} while (*pbi != (putbufndx & putbufmask) && *cc != 0 &&
|
|
ci < MAX_CONLEN);
|
|
|
|
done:
|
|
if (*pbi == (putbufndx & putbufmask)) {
|
|
*cc = 0;
|
|
}
|
|
msg[ci] = '\0';
|
|
return;
|
|
}
|
|
|
|
void
|
|
printputbuf(
|
|
int n, /* if < 0 print entire thing; else that many bytes */
|
|
void (*pfunc)(char *, ...))
|
|
{
|
|
|
|
register int lim;
|
|
register int opl;
|
|
register int putbufmask = putbufsz - 1;
|
|
int pbi;
|
|
int cc;
|
|
char msg[MAX_CONLEN];
|
|
|
|
opl = spl7();
|
|
|
|
#ifdef CELL_IRIX
|
|
pbi = cell_conbuf_start;
|
|
cc = n;
|
|
|
|
if (cc > 0) {
|
|
if (cc > CELL_CONBUFSZ) {
|
|
cc = CELL_CONBUFSZ;
|
|
}
|
|
pbi -= cc;
|
|
if (pbi < 0) {
|
|
pbi += CELL_CONBUFSZ;
|
|
}
|
|
}
|
|
|
|
do {
|
|
get_cell_conmsg(msg, &pbi, &cc);
|
|
if (msg[0] != '\0') {
|
|
(*pfunc)(msg);
|
|
}
|
|
} while (pbi != cell_conbuf_start && cc != 0);
|
|
#endif
|
|
|
|
pbi = putbufndx & putbufmask;
|
|
lim = putbufsz;
|
|
cc = n;
|
|
if (cc > 0) {
|
|
if (cc > lim) {
|
|
cc = lim;
|
|
}
|
|
pbi -= cc;
|
|
if (pbi < 0) {
|
|
pbi += lim;
|
|
}
|
|
}
|
|
|
|
do {
|
|
get_conmsg(msg, &pbi, &cc);
|
|
if (msg[0] != '\0') {
|
|
(*pfunc)(msg);
|
|
}
|
|
} while (pbi != (putbufndx & putbufmask) && cc != 0);
|
|
|
|
splx(opl);
|
|
}
|
|
|
|
/*
|
|
* Called by the ASSERT macro in debug.h when an
|
|
* assertion fails.
|
|
* All the pertinent registers have already been saved in assfail
|
|
* in locore which calls this
|
|
*/
|
|
eframe_t _assregs;
|
|
int clrass = 0;
|
|
extern int console_is_tport;
|
|
|
|
void
|
|
_assfail(char *a, char *f, int l)
|
|
{
|
|
int s;
|
|
extern int panic_cpu;
|
|
|
|
/* set so other cpus will stop pronto */
|
|
s = spl7();
|
|
|
|
/*
|
|
* panic_cpu is set in assfail() to the cpu which saved
|
|
* off it's registers so it should be the one to print
|
|
* them and subsequently panic.
|
|
*/
|
|
if (cpuid() == panic_cpu) {
|
|
enter_panic_mode();
|
|
|
|
if (console_is_tport)
|
|
dopromreset(1); /* since we're sort of panicking */
|
|
|
|
printf("assertion failed cpu %d: %s, file: %s, line: %d\n",
|
|
cpuid(), a, f, l);
|
|
printregs(&_assregs, printf);
|
|
}
|
|
|
|
if (clrass == 2) {
|
|
exit_panic_mode();
|
|
splx(s);
|
|
return;
|
|
}
|
|
|
|
if (kdebug) {
|
|
/*
|
|
* Tell all of the other processors we're
|
|
* panicking. They'll just sit and spin,
|
|
* only performing cpuaction() requests.
|
|
*/
|
|
if (cpuid() == panic_cpu) {
|
|
debug_stop_all_cpus((cpumask_t*)-1LL);
|
|
}
|
|
debug("ring");
|
|
}
|
|
|
|
if (clrass) {
|
|
if (clrass != 2)
|
|
clrass = 0;
|
|
exit_panic_mode();
|
|
splx(s);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* enter_panic_mode() will only allow 1 cpu to proceed in the
|
|
* case where multiple cpus assert at roughly the same time.
|
|
*/
|
|
enter_panic_mode();
|
|
|
|
cmn_err(CE_PANIC, "assertion failure!");
|
|
}
|
|
|
|
|
|
/*
|
|
* Some (all?) proms will clear the graphics screen when restarted, not
|
|
* leaving much time to read any messages. Therefore, if the system is
|
|
* panicking, just spin and wait for a hard reset.
|
|
*/
|
|
extern void dprintf(char *, ...);
|
|
|
|
void
|
|
prom_reboot(void)
|
|
{
|
|
extern int reboot_on_panic;
|
|
char *arg;
|
|
|
|
if (panicstr) {
|
|
int old_value = reboot_on_panic;
|
|
/* Some people want to reboot on panic, and others want
|
|
* to spin so they can see the panic message, or avoid
|
|
* panic loops. New proms have the env var 'rebound'
|
|
* to control this. If 'rebound' is set it overrides
|
|
* the stune 'reboot_on_panic' variable.
|
|
*/
|
|
if ((arg = kopt_find("rebound")) && arg[0])
|
|
reboot_on_panic = (arg[0] == 'y');
|
|
|
|
/* If reboot_on_panic was set to 2, it indicates
|
|
* someone wanted to force a hard reset as part of
|
|
* reboot (Fix for some SN0 specific problem).
|
|
* So, if we are rebooting, check for this situation.
|
|
*/
|
|
if (reboot_on_panic && (old_value == 2))
|
|
reboot_on_panic = 2;
|
|
|
|
#ifdef DEBUG
|
|
/* Hope the textport/duart is working */
|
|
if (kdebug) {
|
|
static int panic_sync = 0;
|
|
|
|
/*
|
|
* don't want symmon and IRIX outputting to the serial
|
|
* console at the same time
|
|
*/
|
|
if (cpuid() == masterpda->p_cpuid) {
|
|
dprintf("\nexit debugger to reboot\n\n");
|
|
panic_sync++;
|
|
}
|
|
else {
|
|
while (!panic_sync)
|
|
;
|
|
}
|
|
debug("ring");
|
|
prom_reinit();
|
|
}
|
|
#endif
|
|
|
|
/* If we don't want the system to rebound, wait for a reset
|
|
*/
|
|
if (!reboot_on_panic) {
|
|
extern void cpu_waitforreset(void);
|
|
|
|
if (cpuid() == masterpda->p_cpuid) {
|
|
dprintf("[Press reset to restart the machine.]");
|
|
|
|
/* Wait for a reset or power-off */
|
|
cpu_waitforreset();
|
|
/* NOTREACHED */
|
|
}
|
|
else {
|
|
/*
|
|
* IP30: don't want slave to play with ioc3
|
|
* and power management hardware when master
|
|
* may be outputting to the serial console
|
|
*/
|
|
while (1)
|
|
;
|
|
/* NOTREACHED */
|
|
}
|
|
}
|
|
|
|
if (cpuid() == masterpda->p_cpuid)
|
|
dprintf("Restarting the machine...\n");
|
|
prom_autoboot();
|
|
}
|
|
|
|
/* Not rebounding or panicing, go back to the prom
|
|
*/
|
|
prom_reinit();
|
|
}
|
|
|
|
void
|
|
arcs_printf(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
cpuid_t cpu = getcpuid();
|
|
|
|
va_start(ap, fmt);
|
|
|
|
if (cn_is_inited() && (pdaindr[cpu].CpuId == cpu))
|
|
icmn_err_tag(0, CE_CONT,fmt,ap);
|
|
else
|
|
errprintf(PRW_ARCS, NULL, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/*
|
|
* To prevent deadlocks in certain cases, locking on putbuflck is allowed
|
|
* to succeed if the cpu already has the lock. In the case that a CPU doesn't
|
|
* release the putbuf lock for a long period of time, we'll break it.
|
|
* In order to avoid puttiong yet anotehr lock around the code that breaks
|
|
* the putbuf lock (which might well fail in the same way under the dire
|
|
* circumstances where we'd break the lock in the first place), we've added
|
|
* a delay based on the CPU id. The theory here is that in a panic situation,
|
|
* CPUs may all want to get to the putbuf at the same time while some other
|
|
* CPU is holding the lock, never to release it. By adding an offset based
|
|
* on the CPU id, we minimize the chance that multiple CPUs will break the
|
|
* lock simultaneously.
|
|
*
|
|
* The lock must go to spl7 here because an arbitrarily high level
|
|
* interrupt may do a kernel printf. However, since this may cause
|
|
* the audio driver to miss its deadline, DSD platforms don't allow
|
|
* kernel printfs from interrupt levels above splhi except during
|
|
* a kernel panic.
|
|
*
|
|
* If we are in promlog mode, we are already holding the putbuf lock, so
|
|
* don't worry
|
|
*/
|
|
#if IP20 || IP22 || IP26 || IP28 || IP30 || IP32
|
|
#define splputbuf splhi
|
|
#else
|
|
#define splputbuf spl7
|
|
#endif
|
|
|
|
int pbuf_nested;
|
|
|
|
int
|
|
putbuf_lock()
|
|
{
|
|
int s, i;
|
|
uint gen_number;
|
|
|
|
if (haspblock == cpuid()) {
|
|
s = 0;
|
|
pblock_nest++;
|
|
pbuf_nested++;
|
|
if (!in_promlog_mode() && !in_panic_mode())
|
|
debug("ring");
|
|
} else if (in_panic_mode()) {
|
|
gen_number = putbuflck_gen;
|
|
/*
|
|
* We need to timeout the putbuf lock in this case.
|
|
* Wait PUTBUF_LOCK_USECS + 10 ms * cpuid.
|
|
*/
|
|
for (i = 0; i < (PUTBUF_LOCK_USECS +
|
|
(cpuid() * 10000)); i++) {
|
|
if (s = mutex_spintrylock_spl(&putbuflck, splputbuf))
|
|
break;
|
|
/* Wait a microsecond */
|
|
DELAY(1);
|
|
/*
|
|
* If someone else has obtained and released the
|
|
* putbuf lock while we were holding it, don't break it.
|
|
*/
|
|
if (gen_number != putbuflck_gen)
|
|
i = 0;
|
|
}
|
|
|
|
/* Break the lock. Assume no one will release it now. */
|
|
s = splhi();
|
|
haspblock = cpuid();
|
|
putbuflck_gen++;
|
|
|
|
} else {
|
|
s = mutex_spinlock_spl(&putbuflck, splputbuf);
|
|
haspblock = cpuid();
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
int
|
|
putbuf_trylock()
|
|
{
|
|
int s;
|
|
|
|
s = mutex_spintrylock_spl(&putbuflck, splputbuf);
|
|
return(s);
|
|
}
|
|
|
|
|
|
void
|
|
putbuf_unlock(int s)
|
|
{
|
|
if (pblock_nest) {
|
|
pblock_nest--;
|
|
}
|
|
else {
|
|
haspblock = -1;
|
|
putbuflck_gen++;
|
|
mutex_spinunlock(&putbuflck, s);
|
|
}
|
|
return;
|
|
}
|
|
|
|
#ifdef CELL_IRIX
|
|
#define ADD_CONBUF(_c) cell_console_buf[cell_conbuf_start++] = (char)(_c); \
|
|
cell_conbuf_start %= CELL_CONBUFSZ;
|
|
static void
|
|
cell_output(int where, uchar_t c)
|
|
{
|
|
|
|
int newpos;
|
|
int buflen = 1;
|
|
static int cur_where = 0;
|
|
static int msg_startpos = 0;
|
|
|
|
/*
|
|
* Save buf to circular console buffer so another thread
|
|
* can do the acutal write to the console since it may not
|
|
* be safe to do so now.
|
|
*/
|
|
|
|
if (in_con_rpc == cpuid()) {
|
|
|
|
/* This message was triggered by an rpc of a previous message */
|
|
|
|
where |= PRW_RPC;
|
|
}
|
|
|
|
if (cur_where != where) {
|
|
|
|
/* New message, we prepend an extra control character */
|
|
|
|
buflen = 2;
|
|
cur_where = where;
|
|
msg_startpos = cell_conbuf_start;
|
|
}
|
|
|
|
if (!cell_console_overflow) {
|
|
|
|
/* Figure out the next position in the buffer */
|
|
|
|
newpos = (cell_conbuf_start + buflen) % CELL_CONBUFSZ;
|
|
}
|
|
|
|
if (cell_console_overflow ||
|
|
(newpos >= cell_conbuf_flushed) &&
|
|
((cell_conbuf_start < cell_conbuf_flushed) ||
|
|
(newpos < cell_conbuf_start))) {
|
|
|
|
/*
|
|
* This character would write over older messages that
|
|
* have not been flushed to the console yet so skip
|
|
* it. The overflow flag will be cleared once the
|
|
* buffer has been flushed.
|
|
*/
|
|
|
|
if (!cell_console_overflow) {
|
|
cell_console_overflow = 1;
|
|
|
|
/* The current message overflowed so we delete it */
|
|
|
|
cell_conbuf_start = msg_startpos;
|
|
cell_console_buf[cell_conbuf_start] = '\0';
|
|
|
|
/*
|
|
* Clear the "where" flag so that once the overflow
|
|
* flag is cleared a new message can be started.
|
|
*/
|
|
|
|
cur_where = 0;
|
|
}
|
|
|
|
if (c == '\n') {
|
|
|
|
/* Count the messages lost */
|
|
|
|
cell_console_overflow++;
|
|
}
|
|
}
|
|
else {
|
|
|
|
if (buflen > 1) {
|
|
|
|
/* Put in control character and then message */
|
|
|
|
ADD_CONBUF(where | PRW_FLAG);
|
|
}
|
|
ADD_CONBUF(c);
|
|
if (c == '\n') {
|
|
|
|
/* End of this message */
|
|
|
|
cur_where = 0;
|
|
cvsema(&cellconssema);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define MAX_CONFLUSH 256
|
|
|
|
void
|
|
I_dshost_printf(cell_t client, char *msg, size_t len)
|
|
{
|
|
int i, s;
|
|
int where;
|
|
char c, *cp;
|
|
|
|
/* Process the printf from the remote cell. */
|
|
|
|
PUTBUF_LOCK(s);
|
|
if (!in_con_message || last_con_cellid != client) {
|
|
|
|
/*
|
|
* We need to pre-pend the cell number first.
|
|
*/
|
|
|
|
errvprintf(PRW_KLOG | PRW_CONS, NULL, "(CELL:%d) ", client);
|
|
}
|
|
last_con_cellid = client;
|
|
|
|
/* The printf may contain sections that don't all go to the
|
|
* console. A flag character should precede each one. We also
|
|
* clear the PRW_BUF bit because the printf is already in
|
|
* the console buffer of the remote cell.
|
|
*/
|
|
|
|
i = 0;
|
|
while (i < len && msg[i] != '\0') {
|
|
where = (int)msg[i++];
|
|
ASSERT(where & PRW_FLAG);
|
|
where &= ~(PRW_BUF | PRW_FLAG);
|
|
if (where & PRW_ARCS) {
|
|
|
|
/* Very early console output from remote cell */
|
|
|
|
where &= ~PRW_ARCS;
|
|
where |= PRW_CONS;
|
|
}
|
|
cp = &msg[i];
|
|
while (i < len && !((int)msg[i] & PRW_FLAG) && msg[i] != '\0') {
|
|
i++;
|
|
}
|
|
|
|
/* Found end of current section, print it. */
|
|
|
|
c = msg[i]; /* Save the last (flag) character */
|
|
msg[i] = '\0'; /* Terminate this section */
|
|
errvprintf(where, NULL, cp);
|
|
msg[i] = c; /* Put the flag back */
|
|
}
|
|
PUTBUF_UNLOCK(s);
|
|
}
|
|
|
|
static void
|
|
flush_cell_console_buf(void)
|
|
{
|
|
|
|
int i;
|
|
char *cp, cur_msg[MAX_CONFLUSH];
|
|
int s, buflen;
|
|
service_t svc;
|
|
/* REFERENCED */
|
|
int msgerr;
|
|
|
|
/*
|
|
* For each message in the console buffer flush it to
|
|
* the golden cell so it can deal with it.
|
|
*/
|
|
|
|
while (cell_conbuf_flushed != *(volatile int *)&cell_conbuf_start) {
|
|
|
|
/* Package up a message to flush. We look for '\n' to
|
|
* indicate where it ends.
|
|
*/
|
|
|
|
PUTBUF_LOCK(s);
|
|
buflen = 0;
|
|
while (buflen < (MAX_CONFLUSH - 1) &&
|
|
(cell_conbuf_flushed !=
|
|
*(volatile int *)&cell_conbuf_start)) {
|
|
cp = &cell_console_buf[cell_conbuf_flushed++];
|
|
cell_conbuf_flushed %= CELL_CONBUFSZ;
|
|
if (!buflen && !(*cp & PRW_FLAG)) {
|
|
|
|
/* Not the start of a message */
|
|
|
|
continue;
|
|
}
|
|
cur_msg[buflen++] = *cp;
|
|
if (*cp == '\n') {
|
|
break;
|
|
}
|
|
}
|
|
PUTBUF_UNLOCK(s);
|
|
cur_msg[buflen++] = '\0';
|
|
|
|
if (buflen > 1) {
|
|
|
|
/* We have message to send */
|
|
|
|
SERVICE_MAKE(svc, CELL_GOLDEN, SVC_HOST);
|
|
ASSERT((int)cur_msg[0] & PRW_FLAG);
|
|
if ((int)cur_msg[0] & PRW_RPC) {
|
|
|
|
/* A previous rpc triggered this message.
|
|
* We skip it to prevent a recursive loop.
|
|
*/
|
|
|
|
continue;
|
|
}
|
|
|
|
START_CON_RPC();
|
|
msgerr = invk_dshost_printf(svc, cellid(), cur_msg,
|
|
buflen);
|
|
ASSERT(!msgerr);
|
|
END_CON_RPC();
|
|
|
|
}
|
|
}
|
|
if (cell_console_overflow) {
|
|
|
|
/*
|
|
* The buffer overflowed before we could be woken up. Clear
|
|
* the flag now that we have flushed out the buffer and send
|
|
* out a warning message that some were lost.
|
|
*/
|
|
|
|
i = cell_console_overflow;
|
|
cell_console_overflow = 0;
|
|
printf("WARNING: lost %d console messages\n", i);
|
|
}
|
|
}
|
|
|
|
void
|
|
cell_console_thread(void)
|
|
{
|
|
/*
|
|
* This thread just sleeps waiting for a 'printf' to
|
|
* wake it up when there is a message in the buffer to be
|
|
* flushed to the golden cell.
|
|
*/
|
|
|
|
initnsema(&cellconssema, 0, "cellcons");
|
|
|
|
while (1) {
|
|
flush_cell_console_buf();
|
|
psema(&cellconssema, PSWP);
|
|
}
|
|
}
|
|
#endif /* CELL_IRIX */
|
|
|
|
/*
|
|
* This procedure is meant to be invoked from DBG: prompt as
|
|
* "call _symmon_dump" to take a core dump. Give it some time to do the
|
|
* dump, as it might be doing its work silently.
|
|
*/
|
|
void
|
|
_symmon_dump()
|
|
{
|
|
extern void _hook_exceptions(void);
|
|
|
|
/* reset putbuflck to avoid problems with lock acquisition
|
|
* following an NMI.
|
|
*/
|
|
#if MP
|
|
panic_haspblock = haspblock;
|
|
panic_putbuflck = putbuflck;
|
|
putbuflck &= ~SPIN_LOCK;
|
|
haspblock = -1;
|
|
pblock_nest = 0;
|
|
#endif
|
|
|
|
/* Need to be able to process kernel K2 tlbmisses, so we can't use
|
|
* symmon's tlbmiss handler. Likewise need general exception vector
|
|
* set to kernel for second level tlbmiss processing.
|
|
*/
|
|
kdebug = 0; /* no more symmon ! */
|
|
panic_level = 0; /* arrange to call even from panics */
|
|
private.p_utlbmisshndlr = utlbmiss_swtch[0].u_start;
|
|
private.common_excnorm = get_except_norm();
|
|
|
|
#if 0
|
|
/* may only be needed when entering from POD */
|
|
_hook_exceptions();
|
|
#endif
|
|
cmn_err(CE_PANIC, "symmon_dump routine invoked from symmon/POD");
|
|
|
|
}
|