Commit 96d803da authored by Richard Henderson's avatar Richard Henderson

Merge are.twiddle.net:/home/rth/BK/linus-2.5

into are.twiddle.net:/home/rth/BK/axp-2.5
parents 37e093c1 353cc80d
......@@ -522,6 +522,9 @@ config ALPHA_LARGE_VMALLOC
Say N unless you know you need gobs and gobs of vmalloc space.
config VERBOSE_MCHECK
bool "Verbose Machine Checks"
source "drivers/pci/Kconfig"
config HOTPLUG
......
......@@ -59,6 +59,7 @@ CONFIG_ALPHA_GENERIC=y
# CONFIG_ALPHA_TAKARA is not set
# CONFIG_ALPHA_TITAN is not set
# CONFIG_ALPHA_WILDFIRE is not set
CONFIG_VERBOSE_MCHECK=y
CONFIG_ISA=y
CONFIG_EISA=y
# CONFIG_SBUS is not set
......
......@@ -10,7 +10,7 @@ export-objs := alpha_ksyms.o
obj-y := entry.o traps.o process.o init_task.o osf_sys.o irq.o \
irq_alpha.o signal.o setup.o ptrace.o time.o semaphore.o \
alpha_ksyms.o systbls.o
alpha_ksyms.o systbls.o err_common.o
#
# FIXME!
......
......@@ -755,6 +755,7 @@ cia_pci_clr_err(void)
*(vip)CIA_IOC_CIA_ERR; /* re-read to force write. */
}
#ifdef CONFIG_VERBOSE_MCHECK
static void
cia_decode_pci_error(struct el_CIA_sysdata_mcheck *cia, const char *msg)
{
......@@ -1022,13 +1023,13 @@ cia_decode_parity_error(struct el_CIA_sysdata_mcheck *cia)
printk(KERN_CRIT " Command: %s, Parity bit: %d\n", cmd, par);
printk(KERN_CRIT " Address: %#010lx, Mask: %#lx\n", addr, mask);
}
#endif
static int
cia_decode_mchk(unsigned long la_ptr)
{
struct el_common *com;
struct el_CIA_sysdata_mcheck *cia;
int which;
com = (void *)la_ptr;
cia = (void *)(la_ptr + com->sys_offset);
......@@ -1036,8 +1037,8 @@ cia_decode_mchk(unsigned long la_ptr)
if ((cia->cia_err & CIA_ERR_VALID) == 0)
return 0;
which = cia->cia_err & 0xfff;
switch (ffs(which) - 1) {
#ifdef CONFIG_VERBOSE_MCHECK
switch (ffs(cia->cia_err & 0xfff) - 1) {
case 0: /* CIA_ERR_COR_ERR */
cia_decode_ecc_error(cia, "Corrected ECC error");
break;
......@@ -1109,6 +1110,7 @@ cia_decode_mchk(unsigned long la_ptr)
if (cia->cia_err & CIA_ERR_LOST_IOA_TIMEOUT)
printk(KERN_CRIT "CIA lost machine check: "
"I/O timeout\n");
#endif
return 1;
}
......
......@@ -452,7 +452,8 @@ sys_fork:
bsr $1,do_switch_stack
bis $31,SIGCHLD,$16
mov $31,$17
mov $30,$18
mov $31,$18
mov $30,$19
jsr $26,alpha_clone
bsr $1,undo_switch_stack
ret $31,($26),1
......@@ -463,8 +464,9 @@ sys_fork:
.ent sys_clone
sys_clone:
bsr $1,do_switch_stack
/* arg1 and arg2 come from the user */
mov $30,$18
/* $16, $17, $18, $19 come from the user; $19 is used later
via pt_regs->r19. */
mov $30,$19
jsr $26,alpha_clone
bsr $1,undo_switch_stack
ret $31,($26),1
......
/*
* linux/arch/alpha/kernel/err_common.c
*
* Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation)
*
* Error handling code supporting Alpha systems
*/
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/hwrpb.h>
#include <asm/smp.h>
#include "err_impl.h"
#include "proto.h"
/*
* err_print_prefix -- error handling print routines should prefix
* all prints with this
*/
char *err_print_prefix = KERN_NOTICE;
/*
* Forward references
*/
static void el_print_timestamp(union el_timestamp);
static void el_process_subpackets(struct el_subpacket *, int);
/*
* Generic
*/
void
mchk_dump_mem(void *data, int length, char **annotation)
{
unsigned long *ldata = data;
int i;
for(i = 0; (i * sizeof(*ldata)) < length; i++) {
if (annotation && !annotation[i])
annotation = NULL;
printk("%s %08x: %016lx %s\n",
err_print_prefix,
(unsigned)(i * sizeof(*ldata)), ldata[i],
annotation ? annotation[i] : "");
}
}
void
mchk_dump_logout_frame(struct el_common *mchk_header)
{
printk("%s -- Frame Header --\n"
" Frame Size: %d (0x%x) bytes\n"
" Flags: %s%s\n"
" MCHK Code: 0x%x\n"
" Frame Rev: %d\n"
" Proc Offset: 0x%08x\n"
" Sys Offset: 0x%08x\n"
" -- Processor Region --\n",
err_print_prefix,
mchk_header->size, mchk_header->size,
mchk_header->retry ? "RETRY " : "",
mchk_header->err2 ? "SECOND_ERR " : "",
mchk_header->code,
mchk_header->frame_rev,
mchk_header->proc_offset,
mchk_header->sys_offset);
mchk_dump_mem((void *)
((unsigned long)mchk_header + mchk_header->proc_offset),
mchk_header->sys_offset - mchk_header->proc_offset,
NULL);
printk("%s -- System Region --\n", err_print_prefix);
mchk_dump_mem((void *)
((unsigned long)mchk_header + mchk_header->sys_offset),
mchk_header->size - mchk_header->sys_offset,
NULL);
printk("%s -- End of Frame --\n", err_print_prefix);
}
/*
* EV7 generic
*/
#if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_EV7)
void
ev7_machine_check(u64 vector, u64 la_ptr, struct pt_regs *regs)
{
/*
* Sync the processor
*/
mb();
draina();
/*
* Parse the logout frame without printing first. If the only error(s)
* found are have a disposition of "dismiss", then just dismiss them
* and don't print any message
*/
printk("%sEV7 MACHINE CHECK vector %lx\n", err_print_prefix, vector);
/*
* Release the logout frame
*/
wrmces(0x7);
mb();
}
struct ev7_pal_subpacket {
union {
struct {
u32 mchk_code;
u32 subpacket_count;
u64 whami;
u64 rbox_whami;
u64 rbox_int;
u64 exc_addr;
union el_timestamp timestamp;
u64 halt_code;
u64 reserved;
} logout;
} by_type;
};
static char *el_ev7_processor_subpacket_annotation[] = {
"Subpacket Header", "I_STAT", "DC_STAT",
"C_ADDR", "C_SYNDROME_1", "C_SYNDROME_0",
"C_STAT", "C_STS", "MM_STAT",
"EXC_ADDR", "IER_CM", "ISUM",
"PAL_BASE", "I_CTL", "PROCESS_CONTEXT",
"CBOX_CTL", "CBOX_STP_CTL", "CBOX_ACC_CTL",
"CBOX_LCL_SET", "CBOX_GLB_SET", "BBOX_CTL",
"BBOX_ERR_STS", "BBOX_ERR_IDX", "CBOX_DDP_ERR_STS",
"BBOX_DAT_RMP", NULL
};
static char *el_ev7_zbox_subpacket_annotation[] = {
"Subpacket Header",
"ZBOX(0): DRAM_ERR_STATUS_2 / DRAM_ERR_STATUS_1",
"ZBOX(0): DRAM_ERROR_CTL / DRAM_ERR_STATUS_3",
"ZBOX(0): DIFT_TIMEOUT / DRAM_ERR_ADR",
"ZBOX(0): FRC_ERR_ADR / DRAM_MAPPER_CTL",
"ZBOX(0): reserved / DIFT_ERR_STATUS",
"ZBOX(1): DRAM_ERR_STATUS_2 / DRAM_ERR_STATUS_1",
"ZBOX(1): DRAM_ERROR_CTL / DRAM_ERR_STATUS_3",
"ZBOX(1): DIFT_TIMEOUT / DRAM_ERR_ADR",
"ZBOX(1): FRC_ERR_ADR / DRAM_MAPPER_CTL",
"ZBOX(1): reserved / DIFT_ERR_STATUS",
"CBOX_CTL", "CBOX_STP_CTL",
"ZBOX(0)_ERROR_PA", "ZBOX(1)_ERROR_PA",
"ZBOX(0)_ORED_SYNDROME","ZBOX(1)_ORED_SYNDROME",
NULL
};
static char *el_ev7_rbox_subpacket_annotation[] = {
"Subpacket Header", "RBOX_CFG", "RBOX_N_CFG",
"RBOX_S_CFG", "RBOX_E_CFG", "RBOX_W_CFG",
"RBOX_N_ERR", "RBOX_S_ERR", "RBOX_E_ERR",
"RBOX_W_ERR", "RBOX_IO_CFG", "RBOX_IO_ERR",
"RBOX_L_ERR", "RBOX_WHOAMI", "RBOX_IMASL",
"RBOX_INTQ", "RBOX_INT", NULL
};
static char *el_ev7_io_subpacket_annotation[] = {
"Subpacket Header", "IO_ASIC_REV", "IO_SYS_REV",
"IO7_UPH", "HPI_CTL", "CRD_CTL",
"HEI_CTL", "PO7_ERROR_SUM","PO7_UNCRR_SYM",
"PO7_CRRCT_SYM", "PO7_UGBGE_SYM","PO7_ERR_PKT0",
"PO7_ERR_PKT1", "reserved", "reserved",
"PO0_ERR_SUM", "PO0_TLB_ERR", "PO0_SPL_COMPLT",
"PO0_TRANS_SUM", "PO0_FIRST_ERR","PO0_MULT_ERR",
"DM CSR PH", "DM CSR PH", "DM CSR PH",
"DM CSR PH", "reserved",
"PO1_ERR_SUM", "PO1_TLB_ERR", "PO1_SPL_COMPLT",
"PO1_TRANS_SUM", "PO1_FIRST_ERR","PO1_MULT_ERR",
"DM CSR PH", "DM CSR PH", "DM CSR PH",
"DM CSR PH", "reserved",
"PO2_ERR_SUM", "PO2_TLB_ERR", "PO2_SPL_COMPLT",
"PO2_TRANS_SUM", "PO2_FIRST_ERR","PO2_MULT_ERR",
"DM CSR PH", "DM CSR PH", "DM CSR PH",
"DM CSR PH", "reserved",
"PO3_ERR_SUM", "PO3_TLB_ERR", "PO3_SPL_COMPLT",
"PO3_TRANS_SUM", "PO3_FIRST_ERR","PO3_MULT_ERR",
"DM CSR PH", "DM CSR PH", "DM CSR PH",
"DM CSR PH", "reserved",
NULL
};
static struct el_subpacket_annotation el_ev7_pal_annotations[] = {
SUBPACKET_ANNOTATION(EL_CLASS__PAL,
EL_TYPE__PAL__EV7_PROCESSOR,
1,
"EV7 Processor Subpacket",
el_ev7_processor_subpacket_annotation),
SUBPACKET_ANNOTATION(EL_CLASS__PAL,
EL_TYPE__PAL__EV7_ZBOX,
1,
"EV7 ZBOX Subpacket",
el_ev7_zbox_subpacket_annotation),
SUBPACKET_ANNOTATION(EL_CLASS__PAL,
EL_TYPE__PAL__EV7_RBOX,
1,
"EV7 RBOX Subpacket",
el_ev7_rbox_subpacket_annotation),
SUBPACKET_ANNOTATION(EL_CLASS__PAL,
EL_TYPE__PAL__EV7_IO,
1,
"EV7 IO Subpacket",
el_ev7_io_subpacket_annotation)
};
static struct el_subpacket *
ev7_process_pal_subpacket(struct el_subpacket *header)
{
struct ev7_pal_subpacket *packet;
if (header->class != EL_CLASS__PAL) {
printk("%s ** Unexpected header CLASS %d TYPE %d, aborting\n",
err_print_prefix,
header->class, header->type);
return NULL;
}
packet = (struct ev7_pal_subpacket *)header->by_type.raw.data_start;
switch(header->type) {
case EL_TYPE__PAL__LOGOUT_FRAME:
printk("%s*** MCHK occurred on LPID %ld (RBOX %lx)\n",
err_print_prefix,
packet->by_type.logout.whami,
packet->by_type.logout.rbox_whami);
el_print_timestamp(packet->by_type.logout.timestamp);
printk("%s EXC_ADDR: %016lx\n"
" HALT_CODE: %lx\n",
err_print_prefix,
packet->by_type.logout.exc_addr,
packet->by_type.logout.halt_code);
el_process_subpackets(header,
packet->by_type.logout.subpacket_count);
break;
default:
printk("%s ** PAL TYPE %d SUBPACKET\n",
err_print_prefix,
header->type);
el_annotate_subpacket(header);
break;
}
return (struct el_subpacket *)((unsigned long)header + header->length);
}
struct el_subpacket_handler ev7_pal_subpacket_handler =
SUBPACKET_HANDLER_INIT(EL_CLASS__PAL, ev7_process_pal_subpacket);
void
ev7_register_error_handlers(void)
{
int i;
for(i = 0;
i<sizeof(el_ev7_pal_annotations)/sizeof(el_ev7_pal_annotations[1]);
i++) {
cdl_register_subpacket_annotation(&el_ev7_pal_annotations[i]);
}
cdl_register_subpacket_handler(&ev7_pal_subpacket_handler);
}
#endif /* CONFIG_ALPHA_GENERIC || CONFIG_ALPHA_EV7 */
/*
* EV6 generic
*/
#if defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_EV6)
static int
ev6_parse_ibox(u64 i_stat, int print)
{
int status = MCHK_DISPOSITION_REPORT;
#define EV6__I_STAT__PAR (1UL << 29)
#define EV6__I_STAT__ERRMASK (EV6__I_STAT__PAR)
if (!(i_stat & EV6__I_STAT__ERRMASK))
return MCHK_DISPOSITION_UNKNOWN_ERROR;
if (!print)
return status;
if (i_stat & EV6__I_STAT__PAR)
printk("%s Icache parity error\n", err_print_prefix);
return status;
}
static int
ev6_parse_mbox(u64 mm_stat, u64 d_stat, u64 c_stat, int print)
{
int status = MCHK_DISPOSITION_REPORT;
#define EV6__MM_STAT__DC_TAG_PERR (1UL << 10)
#define EV6__MM_STAT__ERRMASK (EV6__MM_STAT__DC_TAG_PERR)
#define EV6__D_STAT__TPERR_P0 (1UL << 0)
#define EV6__D_STAT__TPERR_P1 (1UL << 1)
#define EV6__D_STAT__ECC_ERR_ST (1UL << 2)
#define EV6__D_STAT__ECC_ERR_LD (1UL << 3)
#define EV6__D_STAT__SEO (1UL << 4)
#define EV6__D_STAT__ERRMASK (EV6__D_STAT__TPERR_P0 | \
EV6__D_STAT__TPERR_P1 | \
EV6__D_STAT__ECC_ERR_ST | \
EV6__D_STAT__ECC_ERR_LD | \
EV6__D_STAT__SEO)
if (!(d_stat & EV6__D_STAT__ERRMASK) &&
!(mm_stat & EV6__MM_STAT__ERRMASK))
return MCHK_DISPOSITION_UNKNOWN_ERROR;
if (!print)
return status;
if (mm_stat & EV6__MM_STAT__DC_TAG_PERR)
printk("%s Dcache tag parity error on probe\n",
err_print_prefix);
if (d_stat & EV6__D_STAT__TPERR_P0)
printk("%s Dcache tag parity error - pipe 0\n",
err_print_prefix);
if (d_stat & EV6__D_STAT__TPERR_P1)
printk("%s Dcache tag parity error - pipe 1\n",
err_print_prefix);
if (d_stat & EV6__D_STAT__ECC_ERR_ST)
printk("%s ECC error occurred on a store\n",
err_print_prefix);
if (d_stat & EV6__D_STAT__ECC_ERR_LD)
printk("%s ECC error occurred on a %s load\n",
err_print_prefix,
c_stat ? "" : "speculative ");
if (d_stat & EV6__D_STAT__SEO)
printk("%s Dcache second error\n", err_print_prefix);
return status;
}
static int
ev6_parse_cbox(u64 c_addr, u64 c1_syn, u64 c2_syn,
u64 c_stat, u64 c_sts, int print)
{
char *sourcename[] = { "UNKNOWN", "UNKNOWN", "UNKNOWN",
"MEMORY", "BCACHE", "DCACHE",
"BCACHE PROBE", "BCACHE PROBE" };
char *streamname[] = { "D", "I" };
char *bitsname[] = { "SINGLE", "DOUBLE" };
int status = MCHK_DISPOSITION_REPORT;
int source = -1, stream = -1, bits = -1;
#define EV6__C_STAT__BC_PERR (0x01)
#define EV6__C_STAT__DC_PERR (0x02)
#define EV6__C_STAT__DSTREAM_MEM_ERR (0x03)
#define EV6__C_STAT__DSTREAM_BC_ERR (0x04)
#define EV6__C_STAT__DSTREAM_DC_ERR (0x05)
#define EV6__C_STAT__PROBE_BC_ERR0 (0x06) /* both 6 and 7 indicate... */
#define EV6__C_STAT__PROBE_BC_ERR1 (0x07) /* ...probe bc error. */
#define EV6__C_STAT__ISTREAM_MEM_ERR (0x0B)
#define EV6__C_STAT__ISTREAM_BC_ERR (0x0C)
#define EV6__C_STAT__DSTREAM_MEM_DBL (0x13)
#define EV6__C_STAT__DSTREAM_BC_DBL (0x14)
#define EV6__C_STAT__ISTREAM_MEM_DBL (0x1B)
#define EV6__C_STAT__ISTREAM_BC_DBL (0x1C)
#define EV6__C_STAT__SOURCE_MEMORY (0x03)
#define EV6__C_STAT__SOURCE_BCACHE (0x04)
#define EV6__C_STAT__SOURCE__S (0)
#define EV6__C_STAT__SOURCE__M (0x07)
#define EV6__C_STAT__ISTREAM__S (3)
#define EV6__C_STAT__ISTREAM__M (0x01)
#define EV6__C_STAT__DOUBLE__S (4)
#define EV6__C_STAT__DOUBLE__M (0x01)
#define EV6__C_STAT__ERRMASK (0x1F)
#define EV6__C_STS__SHARED (1 << 0)
#define EV6__C_STS__DIRTY (1 << 1)
#define EV6__C_STS__VALID (1 << 2)
#define EV6__C_STS__PARITY (1 << 3)
if (!(c_stat & EV6__C_STAT__ERRMASK))
return MCHK_DISPOSITION_UNKNOWN_ERROR;
if (!print)
return status;
source = EXTRACT(c_stat, EV6__C_STAT__SOURCE);
stream = EXTRACT(c_stat, EV6__C_STAT__ISTREAM);
bits = EXTRACT(c_stat, EV6__C_STAT__DOUBLE);
if (c_stat & EV6__C_STAT__BC_PERR) {
printk("%s Bcache tag parity error\n", err_print_prefix);
source = -1;
}
if (c_stat & EV6__C_STAT__DC_PERR) {
printk("%s Dcache tag parity error\n", err_print_prefix);
source = -1;
}
if (c_stat == EV6__C_STAT__PROBE_BC_ERR0 ||
c_stat == EV6__C_STAT__PROBE_BC_ERR1) {
printk("%s Bcache single-bit error on a probe hit\n",
err_print_prefix);
source = -1;
}
if (source != -1)
printk("%s %s-STREAM %s-BIT ECC error from %s\n",
err_print_prefix,
streamname[stream], bitsname[bits], sourcename[source]);
printk("%s Address: 0x%016lx\n"
" Syndrome[upper.lower]: %02lx.%02lx\n",
err_print_prefix,
c_addr,
c2_syn, c1_syn);
if (source == EV6__C_STAT__SOURCE_MEMORY ||
source == EV6__C_STAT__SOURCE_BCACHE)
printk("%s Block status: %s%s%s%s\n",
err_print_prefix,
(c_sts & EV6__C_STS__SHARED) ? "SHARED " : "",
(c_sts & EV6__C_STS__DIRTY) ? "DIRTY " : "",
(c_sts & EV6__C_STS__VALID) ? "VALID " : "",
(c_sts & EV6__C_STS__PARITY) ? "PARITY " : "");
return status;
}
int
ev6_process_logout_frame(struct el_common *mchk_header, int print)
{
struct el_common_EV6_mcheck *ev6mchk =
(struct el_common_EV6_mcheck *)mchk_header;
int status = MCHK_DISPOSITION_UNKNOWN_ERROR;
status |= ev6_parse_ibox(ev6mchk->I_STAT, print);
status |= ev6_parse_mbox(ev6mchk->MM_STAT, ev6mchk->DC_STAT,
ev6mchk->C_STAT, print);
status |= ev6_parse_cbox(ev6mchk->C_ADDR, ev6mchk->DC1_SYNDROME,
ev6mchk->DC0_SYNDROME, ev6mchk->C_STAT,
ev6mchk->C_STS, print);
if (!print)
return status;
if (status != MCHK_DISPOSITION_DISMISS) {
char *saved_err_prefix = err_print_prefix;
/*
* Dump some additional information from the frame
*/
printk("%s EXC_ADDR: 0x%016lx IER_CM: 0x%016lx"
" ISUM: 0x%016lx\n"
" PAL_BASE: 0x%016lx I_CTL: 0x%016lx"
" PCTX: 0x%016lx\n",
err_print_prefix,
ev6mchk->EXC_ADDR, ev6mchk->IER_CM, ev6mchk->ISUM,
ev6mchk->PAL_BASE, ev6mchk->I_CTL, ev6mchk->PCTX);
if (status == MCHK_DISPOSITION_UNKNOWN_ERROR) {
printk("%s UNKNOWN error, frame follows:\n",
err_print_prefix);
} else {
/* had decode -- downgrade print level for frame */
err_print_prefix = KERN_NOTICE;
}
mchk_dump_logout_frame(mchk_header);
err_print_prefix = saved_err_prefix;
}
return status;
}
void
ev6_machine_check(u64 vector, u64 la_ptr, struct pt_regs *regs)
{
struct el_common *mchk_header = (struct el_common *)la_ptr;
/*
* Sync the processor
*/
mb();
draina();
/*
* Parse the logout frame without printing first. If the only error(s)
* found are have a disposition of "dismiss", then just dismiss them
* and don't print any message
*/
if (ev6_process_logout_frame(mchk_header, 0) !=
MCHK_DISPOSITION_DISMISS) {
char *saved_err_prefix = err_print_prefix;
err_print_prefix = KERN_CRIT;
/*
* Either a nondismissable error was detected or no
* recognized error was detected in the logout frame
* -- report the error in either case
*/
printk("%s*CPU %s Error (Vector 0x%x) reported on CPU %d:\n",
err_print_prefix,
(vector == SCB_Q_PROCERR)?"Correctable":"Uncorrectable",
(unsigned int)vector, (int)smp_processor_id());
ev6_process_logout_frame(mchk_header, 1);
dik_show_regs(regs, NULL);
err_print_prefix = saved_err_prefix;
}
/*
* Release the logout frame
*/
wrmces(0x7);
mb();
}
#endif /* CONFIG_ALPHA_GENERIC || CONFIG_ALPHA_EV6 */
/*
* Console Data Log
*/
/* Data */
static struct el_subpacket_handler *subpacket_handler_list = NULL;
static struct el_subpacket_annotation *subpacket_annotation_list = NULL;
static void
el_print_timestamp(union el_timestamp timestamp)
{
if (timestamp.as_int)
printk("%s TIMESTAMP: %d/%d/%02d %d:%02d:%0d\n",
err_print_prefix,
timestamp.b.month, timestamp.b.day,
timestamp.b.year, timestamp.b.hour,
timestamp.b.minute, timestamp.b.second);
}
static struct el_subpacket *
el_process_header_subpacket(struct el_subpacket *header)
{
union el_timestamp timestamp;
char *name = "UNKNOWN EVENT";
int packet_count = 0;
int length = 0;
if (header->class != EL_CLASS__HEADER) {
printk("%s** Unexpected header CLASS %d TYPE %d, aborting\n",
err_print_prefix,
header->class, header->type);
return NULL;
}
switch(header->type) {
case EL_TYPE__HEADER__SYSTEM_ERROR_FRAME:
name = "SYSTEM ERROR";
length = header->by_type.sys_err.frame_length;
packet_count =
header->by_type.sys_err.frame_packet_count;
timestamp.as_int = 0;
break;
case EL_TYPE__HEADER__SYSTEM_EVENT_FRAME:
name = "SYSTEM EVENT";
length = header->by_type.sys_event.frame_length;
packet_count =
header->by_type.sys_event.frame_packet_count;
timestamp = header->by_type.sys_event.timestamp;
break;
case EL_TYPE__HEADER__HALT_FRAME:
name = "ERROR HALT";
length = header->by_type.err_halt.frame_length;
packet_count =
header->by_type.err_halt.frame_packet_count;
timestamp = header->by_type.err_halt.timestamp;
break;
case EL_TYPE__HEADER__LOGOUT_FRAME:
name = "LOGOUT FRAME";
length = header->by_type.logout_header.frame_length;
packet_count = 1;
timestamp.as_int = 0;
break;
default: /* Unknown */
printk("%s** Unknown header - CLASS %d TYPE %d, aborting\n",
err_print_prefix,
header->class, header->type);
return NULL;
}
printk("%s*** %s:\n"
" CLASS %d, TYPE %d\n",
err_print_prefix,
name,
header->class, header->type);
el_print_timestamp(timestamp);
/*
* Process the subpackets
*/
el_process_subpackets(header, packet_count);
/* return the next header */
header = (struct el_subpacket *)
((unsigned long)header + header->length + length);
return header;
}
static void
el_process_subpackets(struct el_subpacket *header, int packet_count)
{
struct el_subpacket *subpacket;
int i;
subpacket = (struct el_subpacket *)
((unsigned long)header + header->length);
for(i = 0; subpacket && i < packet_count; i++) {
printk("%sPROCESSING SUBPACKET %d\n", err_print_prefix, i);
subpacket = el_process_subpacket(subpacket);
}
}
static struct el_subpacket *
el_process_subpacket_reg(struct el_subpacket *header)
{
struct el_subpacket *next = NULL;
struct el_subpacket_handler *h = subpacket_handler_list;
for(; h && h->class != header->class; h = h->next);
if (h) next = h->handler(header);
return next;
}
struct el_subpacket *
el_process_subpacket(struct el_subpacket *header)
{
struct el_subpacket *next = NULL;
switch(header->class) {
case EL_CLASS__TERMINATION:
/* Termination packet, there are no more */
break;
case EL_CLASS__HEADER:
next = el_process_header_subpacket(header);
break;
default:
if (NULL == (next = el_process_subpacket_reg(header))) {
printk("%s** Unexpected header CLASS %d TYPE %d"
" -- aborting.\n",
err_print_prefix,
header->class, header->type);
}
break;
}
return next;
}
void
el_annotate_subpacket(struct el_subpacket *header)
{
struct el_subpacket_annotation *a;
char **annotation = NULL;
for(a = subpacket_annotation_list; a; a = a->next) {
if (a->class == header->class &&
a->type == header->type &&
a->revision == header->revision) {
/*
* We found the annotation
*/
annotation = a->annotation;
printk("%s %s\n", err_print_prefix, a->description);
break;
}
}
mchk_dump_mem(header, header->length, annotation);
}
static void __init
cdl_process_console_data_log(int cpu, struct percpu_struct *pcpu)
{
struct el_subpacket *header = (struct el_subpacket *)
(IDENT_ADDR | pcpu->console_data_log_pa);
int err;
printk("%s******* CONSOLE DATA LOG FOR CPU %d. *******\n"
"*** Error(s) were logged on a previous boot\n",
err_print_prefix, cpu);
for(err = 0; header && (header->class != EL_CLASS__TERMINATION); err++)
header = el_process_subpacket(header);
/* let the console know it's ok to clear the error(s) at restart */
pcpu->console_data_log_pa = 0;
printk("%s*** %d total error(s) logged\n"
"**** END OF CONSOLE DATA LOG FOR CPU %d ****\n",
err_print_prefix, err, cpu);
}
void __init
cdl_check_console_data_log(void)
{
struct percpu_struct *pcpu;
int cpu;
for(cpu = 0; cpu < hwrpb->nr_processors; cpu++) {
pcpu = (struct percpu_struct *)
((unsigned long)hwrpb + hwrpb->processor_offset
+ cpu * hwrpb->processor_size);
if (pcpu->console_data_log_pa)
cdl_process_console_data_log(cpu, pcpu);
}
}
int __init
cdl_register_subpacket_annotation(struct el_subpacket_annotation *new)
{
struct el_subpacket_annotation *a = subpacket_annotation_list;
if (a == NULL) subpacket_annotation_list = new;
else {
for(; a->next != NULL; a = a->next) {
if ((a->class == new->class && a->type == new->type) ||
a == new) {
printk("Attempted to re-register "
"subpacket annotation\n");
return -EINVAL;
}
}
a->next = new;
}
new->next = NULL;
return 0;
}
int __init
cdl_register_subpacket_handler(struct el_subpacket_handler *new)
{
struct el_subpacket_handler *h = subpacket_handler_list;
if (h == NULL) subpacket_handler_list = new;
else {
for(; h->next != NULL; h = h->next) {
if (h->class == new->class || h == new) {
printk("Attempted to re-register "
"subpacket handler\n");
return -EINVAL;
}
}
h->next = new;
}
new->next = NULL;
return 0;
}
/*
* linux/arch/alpha/kernel/err_impl.h
*
* Copyright (C) 2000 Jeff Wiedemeier (Compaq Computer Corporation)
*
* Contains declarations and macros to support Alpha error handling
* implementations.
*/
/*
* SCB Vector definitions
*/
#define SCB_Q_SYSERR 0x620
#define SCB_Q_PROCERR 0x630
#define SCB_Q_SYSMCHK 0x660
#define SCB_Q_PROCMCHK 0x670
#define SCB_Q_SYSEVENT 0x680
/*
* Disposition definitions for logout frame parser
*/
#define MCHK_DISPOSITION_UNKNOWN_ERROR 0x00
#define MCHK_DISPOSITION_REPORT 0x01
#define MCHK_DISPOSITION_DISMISS 0x02
/*
* Error Log definitions
*/
/*
* Types
*/
#define EL_CLASS__TERMINATION (0)
# define EL_TYPE__TERMINATION__TERMINATION (0)
#define EL_CLASS__HEADER (5)
# define EL_TYPE__HEADER__SYSTEM_ERROR_FRAME (1)
# define EL_TYPE__HEADER__SYSTEM_EVENT_FRAME (2)
# define EL_TYPE__HEADER__HALT_FRAME (3)
# define EL_TYPE__HEADER__LOGOUT_FRAME (19)
#define EL_CLASS__GENERAL_NOTIFICATION (9)
#define EL_CLASS__PCI_ERROR_FRAME (11)
#define EL_CLASS__REGATTA_FAMILY (12)
# define EL_TYPE__REGATTA__PROCESSOR_ERROR_FRAME (1)
# define EL_TYPE__REGATTA__SYSTEM_ERROR_FRAME (2)
# define EL_TYPE__REGATTA__ENVIRONMENTAL_FRAME (3)
# define EL_TYPE__REGATTA__TITAN_PCHIP0_EXTENDED (8)
# define EL_TYPE__REGATTA__TITAN_PCHIP1_EXTENDED (9)
# define EL_TYPE__REGATTA__TITAN_MEMORY_EXTENDED (10)
# define EL_TYPE__REGATTA__PROCESSOR_DBL_ERROR_HALT (11)
# define EL_TYPE__REGATTA__SYSTEM_DBL_ERROR_HALT (12)
#define EL_CLASS__PAL (14)
# define EL_TYPE__PAL__LOGOUT_FRAME (1)
# define EL_TYPE__PAL__EV7_PROCESSOR (4)
# define EL_TYPE__PAL__EV7_ZBOX (5)
# define EL_TYPE__PAL__EV7_RBOX (6)
# define EL_TYPE__PAL__EV7_IO (7)
union el_timestamp {
struct {
u8 second;
u8 minute;
u8 hour;
u8 day;
u8 month;
u8 year;
} b;
u64 as_int;
};
struct el_subpacket {
u16 length; /* length of header (in bytes) */
u16 class; /* header class and type... */
u16 type; /* ...determine content */
u16 revision; /* header revision */
union {
struct { /* Class 5, Type 1 - System Error */
u32 frame_length;
u32 frame_packet_count;
} sys_err;
struct { /* Class 5, Type 2 - System Event */
union el_timestamp timestamp;
u32 frame_length;
u32 frame_packet_count;
} sys_event;
struct { /* Class 5, Type 3 - Double Error Halt */
u16 halt_code;
u16 reserved;
union el_timestamp timestamp;
u32 frame_length;
u32 frame_packet_count;
} err_halt;
struct { /* Clasee 5, Type 19 - Logout Frame Header */
u32 frame_length;
u32 frame_flags;
u32 cpu_offset;
u32 system_offset;
} logout_header;
struct { /* Class 12 - Regatta */
u64 cpuid;
u64 data_start[1];
} regatta_frame;
struct { /* Raw */
u64 data_start[1];
} raw;
} by_type;
};
struct el_subpacket_annotation {
struct el_subpacket_annotation *next;
u16 class;
u16 type;
u16 revision;
char *description;
char **annotation;
};
#define SUBPACKET_ANNOTATION(c, t, r, d, a) {NULL, (c), (t), (r), (d), (a)}
struct el_subpacket_handler {
struct el_subpacket_handler *next;
u16 class;
struct el_subpacket *(*handler)(struct el_subpacket *);
};
#define SUBPACKET_HANDLER_INIT(c, h) {NULL, (c), (h)}
/*
* Extract a field from a register given it's name. defines
* for the LSB (__S - shift count) and bitmask (__M) are required
*/
#define EXTRACT(u, f) (((u) >> f##__S) & f##__M)
/*
* err_common.c
*/
extern char *err_print_prefix;
extern void mchk_dump_mem(void *, int, char **);
extern void mchk_dump_logout_frame(struct el_common *);
extern void ev7_register_error_handlers(void);
extern void ev7_machine_check(u64, u64, struct pt_regs *);
extern void ev6_register_error_handlers(void);
extern int ev6_process_logout_frame(struct el_common *, int);
extern void ev6_machine_check(u64, u64, struct pt_regs *);
extern struct el_subpacket *el_process_subpacket(struct el_subpacket *);
extern void el_annotate_subpacket(struct el_subpacket *);
extern void cdl_check_console_data_log(void);
extern int cdl_register_subpacket_annotation(struct el_subpacket_annotation *);
extern int cdl_register_subpacket_handler(struct el_subpacket_handler *);
......@@ -147,10 +147,10 @@ process_mcheck_info(unsigned long vector, unsigned long la_ptr,
mchk_header = (struct el_common *)la_ptr;
printk(KERN_CRIT "%s machine check: vector=0x%lx pc=0x%lx code=0x%lx\n",
printk(KERN_CRIT "%s machine check: vector=0x%lx pc=0x%lx code=0x%x\n",
machine, vector, regs->pc, mchk_header->code);
switch ((unsigned int) mchk_header->code) {
switch (mchk_header->code) {
/* Machine check reasons. Defined according to PALcode sources. */
case 0x80: reason = "tag parity error"; break;
case 0x82: reason = "tag control parity error"; break;
......
......@@ -844,7 +844,7 @@ osf_setsysinfo(unsigned long op, void *buffer, unsigned long nbytes,
{
switch (op) {
case SSI_IEEE_FP_CONTROL: {
unsigned long swcr, fpcr;
unsigned long swcr, fpcr, fex;
/*
* Alpha Architecture Handbook 4.7.7.3:
......@@ -867,9 +867,24 @@ osf_setsysinfo(unsigned long op, void *buffer, unsigned long nbytes,
wrfpcr(fpcr);
/* If any exceptions are now unmasked, send a signal. */
if (((swcr & IEEE_STATUS_MASK)
>> IEEE_STATUS_TO_EXCSUM_SHIFT) & swcr) {
send_sig(SIGFPE, current, 1);
fex = ((swcr & IEEE_STATUS_MASK)
>> IEEE_STATUS_TO_EXCSUM_SHIFT) & swcr;
if (fex) {
siginfo_t info;
int si_code = 0;
if (fex & IEEE_TRAP_ENABLE_DNO) si_code = FPE_FLTUND;
if (fex & IEEE_TRAP_ENABLE_INE) si_code = FPE_FLTRES;
if (fex & IEEE_TRAP_ENABLE_UNF) si_code = FPE_FLTUND;
if (fex & IEEE_TRAP_ENABLE_OVF) si_code = FPE_FLTOVF;
if (fex & IEEE_TRAP_ENABLE_DZE) si_code = FPE_FLTDIV;
if (fex & IEEE_TRAP_ENABLE_INV) si_code = FPE_FLTINV;
info.si_signo = SIGFPE;
info.si_errno = 0;
info.si_code = si_code;
info.si_addr = 0; /* FIXME */
send_sig_info(SIGFPE, &info, current);
}
return 0;
......
......@@ -245,11 +245,10 @@ release_thread(struct task_struct *dead_task)
*/
int
alpha_clone(unsigned long clone_flags, unsigned long usp,
struct switch_stack * swstack)
int *user_tid, struct switch_stack * swstack)
{
struct task_struct *p;
struct pt_regs *u_regs = (struct pt_regs *) (swstack+1);
int *user_tid = (int *)u_regs->r19;
if (!usp)
usp = rdusp();
......@@ -314,6 +313,10 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
childti->pcb.ksp = (unsigned long) childstack;
childti->pcb.flags = 1; /* set FEN, clear everything else */
/* Set a new TLS for the child thread? Peek back into the
syscall arguments that we saved on syscall entry. */
childti->pcb.unique = (clone_flags & CLONE_SETTLS ? regs->r19 : 0);
return 0;
}
......
......@@ -293,8 +293,16 @@ do_sigreturn(struct sigframe *frame, struct pt_regs *regs,
goto give_sigsegv;
/* Send SIGTRAP if we're single-stepping: */
if (ptrace_cancel_bpt (current))
send_sig(SIGTRAP, current, 1);
if (ptrace_cancel_bpt (current)) {
siginfo_t info;
info.si_signo = SIGTRAP;
info.si_errno = 0;
info.si_code = TRAP_BRKPT;
info.si_addr = (void *) regs->pc;
info.si_trapno = 0;
send_sig_info(SIGTRAP, &info, current);
}
return;
give_sigsegv:
......@@ -330,8 +338,16 @@ do_rt_sigreturn(struct rt_sigframe *frame, struct pt_regs *regs,
do_sigaltstack(&st, NULL, rdusp());
/* Send SIGTRAP if we're single-stepping: */
if (ptrace_cancel_bpt (current))
send_sig(SIGTRAP, current, 1);
if (ptrace_cancel_bpt (current)) {
siginfo_t info;
info.si_signo = SIGTRAP;
info.si_errno = 0;
info.si_code = TRAP_BRKPT;
info.si_addr = (void *) regs->pc;
info.si_trapno = 0;
send_sig_info(SIGTRAP, &info, current);
}
return;
give_sigsegv:
......
......@@ -46,6 +46,7 @@
#include <asm/tlbflush.h>
#include "proto.h"
#include "err_impl.h"
#include "irq_impl.h"
#include "pci_impl.h"
#include "machvec_impl.h"
......@@ -100,317 +101,6 @@ nautilus_kill_arch(int mode)
}
}
/* Machine check handler code
*
* Perform analysis of a machine check that was triggered by the EV6
* CPU's fault-detection mechanism.
*/
/* IPR structures for EV6, containing the necessary data for the
* machine check handler to unpick the logout frame
*/
/* I_STAT */
#define EV6__I_STAT__PAR ( 1 << 29 )
/* MM_STAT */
#define EV6__MM_STAT__DC_TAG_PERR ( 1 << 10 )
/* DC_STAT */
#define EV6__DC_STAT__SEO ( 1 << 4 )
#define EV6__DC_STAT__ECC_ERR_LD ( 1 << 3 )
#define EV6__DC_STAT__ECC_ERR_ST ( 1 << 2 )
#define EV6__DC_STAT__TPERR_P1 ( 1 << 1 )
#define EV6__DC_STAT__TPERR_P0 ( 1 )
/* C_STAT */
#define EV6__C_STAT__BC_PERR ( 0x01 )
#define EV6__C_STAT__DC_PERR ( 0x02 )
#define EV6__C_STAT__DSTREAM_MEM_ERR ( 0x03 )
#define EV6__C_STAT__DSTREAM_BC_ERR ( 0x04 )
#define EV6__C_STAT__DSTREAM_DC_ERR ( 0x05 )
#define EV6__C_STAT__PROBE_BC_ERR0 ( 0x06 )
#define EV6__C_STAT__PROBE_BC_ERR1 ( 0x07 )
#define EV6__C_STAT__ISTREAM_MEM_ERR ( 0x0B )
#define EV6__C_STAT__ISTREAM_BC_ERR ( 0x0C )
#define EV6__C_STAT__DSTREAM_MEM_DBL ( 0x13 )
#define EV6__C_STAT__DSTREAM_BC_DBL ( 0x14 )
#define EV6__C_STAT__ISTREAM_MEM_DBL ( 0x1B )
#define EV6__C_STAT__ISTREAM_BC_DBL ( 0x1C )
/* Take the two syndromes from the CBOX error chain and convert them
* into a bit number. */
/* NOTE - since I don't know of any difference between C0 and C1 I
just ignore C1, since in all cases I've seen so far they are
identical. */
static const unsigned char ev6_bit_to_syndrome[72] =
{
0xce, 0xcb, 0xd3, 0xd5, 0xd6, 0xd9, 0xda, 0xdc, /* 0 */
0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x31, 0x34, /* 8 */
0x0e, 0x0b, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c, /* 16 */
0xe3, 0xe5, 0xe6, 0xe9, 0xea, 0xec, 0xf1, 0xf4, /* 24 */
0x4f, 0x4a, 0x52, 0x54, 0x57, 0x58, 0x5b, 0x5d, /* 32 */
0xa2, 0xa4, 0xa7, 0xa8, 0xab, 0xad, 0xb0, 0xb5, /* 40 */
0x8f, 0x8a, 0x92, 0x94, 0x97, 0x98, 0x9b, 0x9d, /* 48 */
0x62, 0x64, 0x67, 0x68, 0x6b, 0x6d, 0x70, 0x75, /* 56 */
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 /* 64 */
};
static int ev6_syn2bit(unsigned long c0, unsigned long c1)
{
int bit;
for (bit = 0; bit < 72; bit++)
if (ev6_bit_to_syndrome[bit] == c0) return bit;
for (bit = 0; bit < 72; bit++)
if (ev6_bit_to_syndrome[bit] == c1) return bit + 64;
return -1; /* not found */
}
/* Single bit ECC errors are categorized here. */
#if 0
static const char *interr = "CPU internal error";
static const char *slotb= "Slot-B error";
static const char *membus= "Memory/EV6-bus error";
#else
static const char *interr = "";
static const char *slotb = "";
static const char *membus = "";
#endif
static void
ev6_crd_interp(char *interp, struct el_common_EV6_mcheck * L)
{
/* Icache data or tag parity error. */
if (L->I_STAT & EV6__I_STAT__PAR) {
sprintf(interp, "%s: I_STAT[PAR]\n "
"Icache data or tag parity error", interr);
return;
}
/* Dcache tag parity error (on issue) (DFAULT). */
if (L->MM_STAT & EV6__MM_STAT__DC_TAG_PERR) {
sprintf(interp, "%s: MM_STAT[DC_TAG_PERR]\n "
"Dcache tag parity error(on issue)", interr);
return;
}
/* Errors relating to D-stream set non-zero DC_STAT.
Mask CRD bits. */
switch (L->DC_STAT & (EV6__DC_STAT__ECC_ERR_ST
| EV6__DC_STAT__ECC_ERR_LD)) {
case EV6__DC_STAT__ECC_ERR_ST:
/* Dcache single-bit ECC error on small store */
sprintf(interp, "%s: DC_STAT[ECC_ERR_ST]\n "
"Dcache single-bit ECC error on small store", interr);
return;
case EV6__DC_STAT__ECC_ERR_LD:
switch (L->C_STAT) {
case 0:
/* Dcache single-bit error on speculative load */
/* Bcache victim read on Dcache/Bcache miss */
sprintf(interp, "%s: DC_STAT[ECC_ERR_LD] C_STAT=0\n "
"Dcache single-bit ECC error on speculative load",
slotb);
return;
case EV6__C_STAT__DSTREAM_DC_ERR:
/* Dcache single bit error on load */
sprintf(interp, "%s: DC_STAT[ECC_ERR_LD] C_STAT[DSTREAM_DC_ERR]\n"
" Dcache single-bit ECC error on speculative load, bit %d",
interr, ev6_syn2bit(L->DC0_SYNDROME, L->DC1_SYNDROME));
return;
case EV6__C_STAT__DSTREAM_BC_ERR:
/* Bcache single-bit error on Dcache fill */
sprintf(interp, "%s: DC_STAT[ECC_ERR_LD] C_STAT[DSTREAM_BC_ERR]\n"
" Bcache single-bit error on Dcache fill, bit %d",
slotb, ev6_syn2bit(L->DC0_SYNDROME, L->DC1_SYNDROME));
return;
case EV6__C_STAT__DSTREAM_MEM_ERR:
/* Memory single-bit error on Dcache fill */
sprintf(interp, "%s (to Dcache): DC_STAT[ECC_ERR_LD] "
"C_STAT[DSTREAM_MEM_ERR]\n "
"Memory single-bit error on Dcache fill, "
"Address 0x%lX, bit %d",
membus, L->C_ADDR, ev6_syn2bit(L->DC0_SYNDROME,
L->DC1_SYNDROME));
return;
}
}
/* I-stream, other misc errors go on C_STAT alone */
switch (L->C_STAT) {
case EV6__C_STAT__ISTREAM_BC_ERR:
/* Bcache single-bit error on Icache fill (also MCHK) */
sprintf(interp, "%s: C_STAT[ISTREAM_BC_ERR]\n "
"Bcache single-bit error on Icache fill, bit %d",
slotb, ev6_syn2bit(L->DC0_SYNDROME, L->DC1_SYNDROME));
return;
case EV6__C_STAT__ISTREAM_MEM_ERR:
/* Memory single-bit error on Icache fill (also MCHK) */
sprintf(interp, "%s : C_STATISTREAM_MEM_ERR]\n "
"Memory single-bit error on Icache fill "
"addr 0x%lX, bit %d",
membus, L->C_ADDR, ev6_syn2bit(L->DC0_SYNDROME,
L->DC1_SYNDROME));
return;
case EV6__C_STAT__PROBE_BC_ERR0:
case EV6__C_STAT__PROBE_BC_ERR1:
/* Bcache single-bit error on a probe hit */
sprintf(interp, "%s: C_STAT[PROBE_BC_ERR]\n "
"Bcache single-bit error on a probe hit, "
"addr 0x%lx, bit %d",
slotb, L->C_ADDR, ev6_syn2bit(L->DC0_SYNDROME,
L->DC1_SYNDROME));
return;
}
}
static void
ev6_mchk_interp(char *interp, struct el_common_EV6_mcheck * L)
{
/* Machine check errors described by DC_STAT */
switch (L->DC_STAT) {
case EV6__DC_STAT__TPERR_P0:
case EV6__DC_STAT__TPERR_P1:
/* Dcache tag parity error (on retry) */
sprintf(interp, "%s: DC_STAT[TPERR_P0|TPERR_P1]\n "
"Dcache tag parity error(on retry)", interr);
return;
case EV6__DC_STAT__SEO:
/* Dcache second error on store */
sprintf(interp, "%s: DC_STAT[SEO]\n "
"Dcache second error during mcheck", interr);
return;
}
/* Machine check errors described by C_STAT */
switch (L->C_STAT) {
case EV6__C_STAT__DC_PERR:
/* Dcache duplicate tag parity error */
sprintf(interp, "%s: C_STAT[DC_PERR]\n "
"Dcache duplicate tag parity error at 0x%lX",
interr, L->C_ADDR);
return;
case EV6__C_STAT__BC_PERR:
/* Bcache tag parity error */
sprintf(interp, "%s: C_STAT[BC_PERR]\n "
"Bcache tag parity error at 0x%lX",
slotb, L->C_ADDR);
return;
case EV6__C_STAT__ISTREAM_BC_ERR:
/* Bcache single-bit error on Icache fill (also CRD) */
sprintf(interp, "%s: C_STAT[ISTREAM_BC_ERR]\n "
"Bcache single-bit error on Icache fill 0x%lX bit %d",
slotb, L->C_ADDR,
ev6_syn2bit(L->DC0_SYNDROME, L->DC1_SYNDROME));
return;
case EV6__C_STAT__ISTREAM_MEM_ERR:
/* Memory single-bit error on Icache fill (also CRD) */
sprintf(interp, "%s: C_STAT[ISTREAM_MEM_ERR]\n "
"Memory single-bit error on Icache fill 0x%lX, bit %d",
membus, L->C_ADDR,
ev6_syn2bit(L->DC0_SYNDROME, L->DC1_SYNDROME));
return;
case EV6__C_STAT__ISTREAM_BC_DBL:
/* Bcache double-bit error on Icache fill */
sprintf(interp, "%s: C_STAT[ISTREAM_BC_DBL]\n "
"Bcache double-bit error on Icache fill at 0x%lX",
slotb, L->C_ADDR);
return;
case EV6__C_STAT__DSTREAM_BC_DBL:
/* Bcache double-bit error on Dcache fill */
sprintf(interp, "%s: C_STAT[DSTREAM_BC_DBL]\n "
"Bcache double-bit error on Dcache fill at 0x%lX",
slotb, L->C_ADDR);
return;
case EV6__C_STAT__ISTREAM_MEM_DBL:
/* Memory double-bit error on Icache fill */
sprintf(interp, "%s: C_STAT[ISTREAM_MEM_DBL]\n "
"Memory double-bit error on Icache fill at 0x%lX",
membus, L->C_ADDR);
return;
case EV6__C_STAT__DSTREAM_MEM_DBL:
/* Memory double-bit error on Dcache fill */
sprintf(interp, "%s: C_STAT[DSTREAM_MEM_DBL]\n "
"Memory double-bit error on Dcache fill at 0x%lX",
membus, L->C_ADDR);
return;
}
}
static void
ev6_cpu_machine_check(unsigned long vector, struct el_common_EV6_mcheck *L,
struct pt_regs *regs)
{
char interp[80];
/* This is verbose and looks intimidating. Should it be printed for
corrected (CRD) machine checks? */
printk(KERN_CRIT "PALcode logout frame: "
"MCHK_Code %d "
"MCHK_Frame_Rev %d\n"
"I_STAT %016lx "
"DC_STAT %016lx "
"C_ADDR %016lx\n"
"SYND1 %016lx "
"SYND0 %016lx "
"C_STAT %016lx\n"
"C_STS %016lx "
"RES %016lx "
"EXC_ADDR%016lx\n"
"IER_CM %016lx "
"ISUM %016lx "
"MM_STAT %016lx\n"
"PALBASE %016lx "
"I_CTL %016lx "
"PCTX %016lx\n"
"CPU registers: "
"PC %016lx "
"Return %016lx\n",
L->MCHK_Code, L->MCHK_Frame_Rev, L->I_STAT, L->DC_STAT,
L->C_ADDR, L->DC1_SYNDROME, L->DC0_SYNDROME, L->C_STAT,
L->C_STS, L->RESERVED0, L->EXC_ADDR, L->IER_CM, L->ISUM,
L->MM_STAT, L->PAL_BASE, L->I_CTL, L->PCTX,
regs->pc, regs->r26);
/* Attempt an interpretation on the meanings of the fields above. */
sprintf(interp, "No interpretation available!" );
if (vector == SCB_Q_PROCERR)
ev6_crd_interp(interp, L);
else if (vector == SCB_Q_PROCMCHK)
ev6_mchk_interp(interp, L);
printk(KERN_CRIT "interpretation: %s\n\n", interp);
}
/* Perform analysis of a machine check that arrived from the system (NMI) */
static void
......@@ -430,7 +120,6 @@ nautilus_machine_check(unsigned long vector, unsigned long la_ptr,
struct pt_regs *regs)
{
char *mchk_class;
unsigned cpu_analysis=0, sys_analysis=0;
/* Now for some analysis. Machine checks fall into two classes --
those picked up by the system, and those picked up by the CPU.
......@@ -463,39 +152,20 @@ nautilus_machine_check(unsigned long vector, unsigned long la_ptr,
return;
}
switch (vector) {
case SCB_Q_SYSERR:
mchk_class = "Correctable System Machine Check (NMI)";
sys_analysis = 1;
break;
case SCB_Q_SYSMCHK:
mchk_class = "Fatal System Machine Check (NMI)";
sys_analysis = 1;
break;
case SCB_Q_PROCERR:
mchk_class = "Correctable Processor Machine Check";
cpu_analysis = 1;
break;
case SCB_Q_PROCMCHK:
mchk_class = "Fatal Processor Machine Check";
cpu_analysis = 1;
break;
default:
mchk_class = "Unknown vector!";
break;
if (vector == SCB_Q_SYSERR)
mchk_class = "Correctable";
else if (vector == SCB_Q_SYSMCHK)
mchk_class = "Fatal";
else {
ev6_machine_check(vector, la_ptr, regs);
return;
}
printk(KERN_CRIT "NAUTILUS Machine check 0x%lx [%s]\n",
printk(KERN_CRIT "NAUTILUS Machine check 0x%lx "
"[%s System Machine Check (NMI)]\n",
vector, mchk_class);
if (cpu_analysis)
ev6_cpu_machine_check(vector,
(struct el_common_EV6_mcheck *)la_ptr,
regs);
if (sys_analysis)
naut_sys_machine_check(vector, la_ptr, regs);
naut_sys_machine_check(vector, la_ptr, regs);
/* Tell the PALcode to clear the machine check */
draina();
......@@ -504,7 +174,6 @@ nautilus_machine_check(unsigned long vector, unsigned long la_ptr,
}
/*
* The System Vectors
*/
......
......@@ -213,25 +213,25 @@ do_entArith(unsigned long summary, unsigned long write_mask,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, struct pt_regs regs)
{
long si_code = FPE_FLTINV;
siginfo_t info;
if (summary & 1) {
/* Software-completion summary bit is set, so try to
emulate the instruction. */
if (!amask(AMASK_PRECISE_TRAP)) {
/* 21264 (except pass 1) has precise exceptions. */
if (alpha_fp_emul(regs.pc - 4))
return;
} else {
if (alpha_fp_emul_imprecise(&regs, write_mask))
return;
}
emulate the instruction. If the processor supports
precise exceptions, we don't have to search. */
if (!amask(AMASK_PRECISE_TRAP))
si_code = alpha_fp_emul(regs.pc - 4);
else
si_code = alpha_fp_emul_imprecise(&regs, write_mask);
}
#if 0
printk("%s: arithmetic trap at %016lx: %02lx %016lx\n",
current->comm, regs.pc, summary, write_mask);
#endif
die_if_kernel("Arithmetic fault", &regs, 0, 0);
send_sig(SIGFPE, current, 1);
info.si_signo = SIGFPE;
info.si_errno = 0;
info.si_code = si_code;
info.si_addr = (void *) regs.pc;
send_sig_info(SIGFPE, &info, current);
}
asmlinkage void
......@@ -239,6 +239,9 @@ do_entIF(unsigned long type, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, struct pt_regs regs)
{
siginfo_t info;
int signo, code;
if (!opDEC_testing || type != 4) {
if (type == 1) {
const unsigned int *data
......@@ -253,55 +256,99 @@ do_entIF(unsigned long type, unsigned long a1,
switch (type) {
case 0: /* breakpoint */
info.si_signo = SIGTRAP;
info.si_errno = 0;
info.si_code = TRAP_BRKPT;
info.si_trapno = 0;
info.si_addr = (void *) regs.pc;
if (ptrace_cancel_bpt(current)) {
regs.pc -= 4; /* make pc point to former bpt */
}
send_sig(SIGTRAP, current, 1);
send_sig_info(SIGTRAP, &info, current);
return;
case 1: /* bugcheck */
send_sig(SIGTRAP, current, 1);
info.si_signo = SIGTRAP;
info.si_errno = 0;
info.si_code = __SI_FAULT;
info.si_addr = (void *) regs.pc;
info.si_trapno = 0;
send_sig_info(SIGTRAP, &info, current);
return;
case 2: /* gentrap */
/*
* The exception code should be passed on to the signal
* handler as the second argument. Linux doesn't do that
* yet (also notice that Linux *always* behaves like
* DEC Unix with SA_SIGINFO off; see DEC Unix man page
* for sigaction(2)).
*/
info.si_addr = (void *) regs.pc;
info.si_trapno = regs.r16;
switch ((long) regs.r16) {
case GEN_INTOVF: case GEN_INTDIV: case GEN_FLTOVF:
case GEN_FLTDIV: case GEN_FLTUND: case GEN_FLTINV:
case GEN_FLTINE: case GEN_ROPRAND:
send_sig(SIGFPE, current, 1);
return;
case GEN_DECOVF:
case GEN_DECDIV:
case GEN_DECINV:
case GEN_ASSERTERR:
case GEN_NULPTRERR:
case GEN_STKOVF:
case GEN_STRLENERR:
case GEN_SUBSTRERR:
case GEN_RANGERR:
case GEN_SUBRNG:
case GEN_SUBRNG1:
case GEN_SUBRNG2:
case GEN_SUBRNG3:
case GEN_SUBRNG4:
case GEN_SUBRNG5:
case GEN_SUBRNG6:
case GEN_SUBRNG7:
send_sig(SIGTRAP, current, 1);
return;
case GEN_INTOVF:
signo = SIGFPE;
code = FPE_INTOVF;
break;
case GEN_INTDIV:
signo = SIGFPE;
code = FPE_INTDIV;
break;
case GEN_FLTOVF:
signo = SIGFPE;
code = FPE_FLTOVF;
break;
case GEN_FLTDIV:
signo = SIGFPE;
code = FPE_FLTDIV;
break;
case GEN_FLTUND:
signo = SIGFPE;
code = FPE_FLTUND;
break;
case GEN_FLTINV:
signo = SIGFPE;
code = FPE_FLTINV;
break;
case GEN_FLTINE:
signo = SIGFPE;
code = FPE_FLTRES;
break;
case GEN_ROPRAND:
signo = SIGFPE;
code = __SI_FAULT;
break;
case GEN_DECOVF:
case GEN_DECDIV:
case GEN_DECINV:
case GEN_ASSERTERR:
case GEN_NULPTRERR:
case GEN_STKOVF:
case GEN_STRLENERR:
case GEN_SUBSTRERR:
case GEN_RANGERR:
case GEN_SUBRNG:
case GEN_SUBRNG1:
case GEN_SUBRNG2:
case GEN_SUBRNG3:
case GEN_SUBRNG4:
case GEN_SUBRNG5:
case GEN_SUBRNG6:
case GEN_SUBRNG7:
default:
signo = SIGTRAP;
code = __SI_FAULT;
break;
}
break;
info.si_signo = signo;
info.si_errno = 0;
info.si_code = code;
info.si_addr = (void *) regs.pc;
send_sig_info(signo, &info, current);
return;
case 4: /* opDEC */
if (implver() == IMPLVER_EV4) {
long si_code;
/* The some versions of SRM do not handle
the opDEC properly - they return the PC of the
opDEC fault, not the instruction after as the
......@@ -309,8 +356,7 @@ do_entIF(unsigned long type, unsigned long a1,
We do this by intentionally causing an opDEC
fault during the boot sequence and testing if
we get the correct PC. If not, we set a flag
to correct it every time through.
*/
to correct it every time through. */
if (opDEC_testing) {
if (regs.pc == opDEC_test_pc) {
opDEC_fix = 4;
......@@ -324,8 +370,17 @@ do_entIF(unsigned long type, unsigned long a1,
/* EV4 does not implement anything except normal
rounding. Everything else will come here as
an illegal instruction. Emulate them. */
if (alpha_fp_emul(regs.pc-4))
si_code = alpha_fp_emul(regs.pc - 4);
if (si_code == 0)
return;
if (si_code > 0) {
info.si_signo = SIGFPE;
info.si_errno = 0;
info.si_code = si_code;
info.si_addr = (void *) regs.pc;
send_sig_info(SIGFPE, &info, current);
return;
}
}
break;
......@@ -347,7 +402,12 @@ do_entIF(unsigned long type, unsigned long a1,
default: /* unexpected instruction-fault type */
;
}
send_sig(SIGILL, current, 1);
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_ILLOPC;
info.si_addr = regs.pc;
send_sig_info(SIGILL, &info, current);
}
/* There is an ifdef in the PALcode in MILO that enables a
......@@ -362,8 +422,15 @@ do_entDbg(unsigned long type, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, struct pt_regs regs)
{
siginfo_t info;
die_if_kernel("Instruction fault", &regs, type, 0);
force_sig(SIGILL, current);
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_ILLOPC;
info.si_addr = regs.pc;
force_sig_info(SIGILL, &info, current);
}
......@@ -720,6 +787,7 @@ do_entUnaUser(void * va, unsigned long opcode,
unsigned long tmp1, tmp2, tmp3, tmp4;
unsigned long fake_reg, *reg_addr = &fake_reg;
siginfo_t info;
long error;
/* Check the UAC bits to decide what the user wants us to do
......@@ -984,12 +1052,34 @@ do_entUnaUser(void * va, unsigned long opcode,
give_sigsegv:
regs->pc -= 4; /* make pc point to faulting insn */
send_sig(SIGSEGV, current, 1);
info.si_signo = SIGSEGV;
info.si_errno = 0;
/* We need to replicate some of the logic in mm/fault.c,
since we don't have access to the fault code in the
exception handling return path. */
if (!__access_ok((unsigned long)va, 0, USER_DS))
info.si_code = SEGV_ACCERR;
else {
struct mm_struct *mm = current->mm;
down_read(&mm->mmap_sem);
if (find_vma(mm, (unsigned long)va))
info.si_code = SEGV_ACCERR;
else
info.si_code = SEGV_MAPERR;
up_read(&mm->mmap_sem);
}
info.si_addr = va;
send_sig_info(SIGSEGV, &info, current);
return;
give_sigbus:
regs->pc -= 4;
send_sig(SIGBUS, current, 1);
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRALN;
info.si_addr = va;
send_sig_info(SIGBUS, &info, current);
return;
}
......
......@@ -86,11 +86,13 @@ void cleanup_module(void)
/*
* Emulate the floating point instruction at address PC. Returns 0 if
* emulation fails. Notice that the kernel does not and cannot use FP
* regs. This is good because it means that instead of
* saving/restoring all fp regs, we simply stick the result of the
* operation into the appropriate register.
* Emulate the floating point instruction at address PC. Returns -1 if the
* instruction to be emulated is illegal (such as with the opDEC trap), else
* the SI_CODE for a SIGFPE signal, else 0 if everything's ok.
*
* Notice that the kernel does not and cannot use FP regs. This is good
* because it means that instead of saving/restoring all fp regs, we simply
* stick the result of the operation into the appropriate register.
*/
long
alpha_fp_emul (unsigned long pc)
......@@ -102,6 +104,7 @@ alpha_fp_emul (unsigned long pc)
unsigned long fa, fb, fc, func, mode, src;
unsigned long res, va, vb, vc, swcr, fpcr;
__u32 insn;
long si_code;
MOD_INC_USE_COUNT;
......@@ -306,10 +309,19 @@ alpha_fp_emul (unsigned long pc)
wrfpcr(fpcr);
/* Do we generate a signal? */
if (_fex & swcr & IEEE_TRAP_ENABLE_MASK) {
MOD_DEC_USE_COUNT;
return 0;
_fex = _fex & swcr & IEEE_TRAP_ENABLE_MASK;
si_code = 0;
if (_fex) {
if (_fex & IEEE_TRAP_ENABLE_DNO) si_code = FPE_FLTUND;
if (_fex & IEEE_TRAP_ENABLE_INE) si_code = FPE_FLTRES;
if (_fex & IEEE_TRAP_ENABLE_UNF) si_code = FPE_FLTUND;
if (_fex & IEEE_TRAP_ENABLE_OVF) si_code = FPE_FLTOVF;
if (_fex & IEEE_TRAP_ENABLE_DZE) si_code = FPE_FLTDIV;
if (_fex & IEEE_TRAP_ENABLE_INV) si_code = FPE_FLTINV;
}
MOD_DEC_USE_COUNT;
return si_code;
}
/* We used to write the destination register here, but DEC FORTRAN
......@@ -317,20 +329,20 @@ alpha_fp_emul (unsigned long pc)
immediately after the operations above. */
MOD_DEC_USE_COUNT;
return 1;
return 0;
bad_insn:
printk(KERN_ERR "alpha_fp_emul: Invalid FP insn %#x at %#lx\n",
insn, pc);
MOD_DEC_USE_COUNT;
return 0;
return -1;
}
long
alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask)
{
unsigned long trigger_pc = regs->pc - 4;
unsigned long insn, opcode, rc, no_signal = 0;
unsigned long insn, opcode, rc, si_code = 0;
MOD_INC_USE_COUNT;
......@@ -384,7 +396,7 @@ alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask)
if (!write_mask) {
/* Re-execute insns in the trap-shadow. */
regs->pc = trigger_pc + 4;
no_signal = alpha_fp_emul(trigger_pc);
si_code = alpha_fp_emul(trigger_pc);
goto egress;
}
trigger_pc -= 4;
......@@ -392,5 +404,5 @@ alpha_fp_emul_imprecise (struct pt_regs *regs, unsigned long write_mask)
egress:
MOD_DEC_USE_COUNT;
return no_signal;
return si_code;
}
......@@ -89,7 +89,8 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
struct vm_area_struct * vma;
struct mm_struct *mm = current->mm;
unsigned int fixup;
int fault;
int fault, si_code = SEGV_MAPERR;
siginfo_t info;
/* As of EV6, a load into $31/$f31 is a prefetch, and never faults
(or is suppressed by the PALcode). Support that for older CPUs
......@@ -129,6 +130,7 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
/* Ok, we have a good vm_area for this memory access, so
we can handle it. */
good_area:
si_code = SEGV_ACCERR;
if (cause < 0) {
if (!(vma->vm_flags & VM_EXEC))
goto bad_area;
......@@ -148,10 +150,20 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
fault = handle_mm_fault(mm, vma, address, cause > 0);
up_read(&mm->mmap_sem);
if (fault < 0)
goto out_of_memory;
if (fault == 0)
switch (fault) {
case VM_FAULT_MINOR:
current->min_flt++;
break;
case VM_FAULT_MAJOR:
current->maj_flt++;
break;
case VM_FAULT_SIGBUS:
goto do_sigbus;
case VM_FAULT_OOM:
goto out_of_memory;
default:
BUG();
}
return;
/* Something tried to access memory that isn't in our memory map.
......@@ -159,20 +171,14 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
bad_area:
up_read(&mm->mmap_sem);
if (user_mode(regs)) {
force_sig(SIGSEGV, current);
return;
}
if (user_mode(regs))
goto do_sigsegv;
no_context:
/* Are we prepared to handle this fault as an exception? */
if ((fixup = search_exception_table(regs->pc, regs->gp)) != 0) {
unsigned long newpc;
newpc = fixup_exception(dpf_reg, fixup, regs->pc);
#if 0
printk("%s: Exception at [<%lx>] (%lx) handled successfully\n",
current->comm, regs->pc, newpc);
#endif
regs->pc = newpc;
return;
}
......@@ -201,17 +207,28 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
do_sigbus:
/* Send a sigbus, regardless of whether we were in kernel
or user mode. */
force_sig(SIGBUS, current);
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRERR;
info.si_addr = (void *) address;
force_sig_info(SIGBUS, &info, current);
if (!user_mode(regs))
goto no_context;
return;
do_sigsegv:
info.si_signo = SIGSEGV;
info.si_errno = 0;
info.si_code = si_code;
info.si_addr = (void *) address;
force_sig_info(SIGSEGV, &info, current);
return;
#ifdef CONFIG_ALPHA_LARGE_VMALLOC
vmalloc_fault:
if (user_mode(regs)) {
force_sig(SIGSEGV, current);
return;
} else {
if (user_mode(regs))
goto do_sigsegv;
else {
/* Synchronize this task's top level page-table
with the "reference" page table from init. */
long offset = __pgd_offset(address);
......
......@@ -109,6 +109,9 @@ struct percpu_struct {
unsigned long ipc_buffer[21];
unsigned long palcode_avail[16];
unsigned long compatibility;
unsigned long console_data_log_pa;
unsigned long console_data_log_length;
unsigned long bcache_info;
};
struct procdesc_struct {
......
#ifndef _ALPHA_SIGINFO_H
#define _ALPHA_SIGINFO_H
#define SI_PAD_SIZE ((SI_MAX_SIZE/sizeof(int)) - 4)
#define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int))
#define __ARCH_SI_TRAPNO
#define SIGEV_PAD_SIZE ((SIGEV_MAX_SIZE/sizeof(int)) - 4)
#define HAVE_ARCH_COPY_SIGINFO
#define SIGEV_PAD_SIZE ((SIGEV_MAX_SIZE/sizeof(int)) - 4)
#include <asm-generic/siginfo.h>
#ifdef __KERNEL__
#include <linux/string.h>
extern inline void copy_siginfo(siginfo_t *to, siginfo_t *from)
{
if (from->si_code < 0)
memcpy(to, from, sizeof(siginfo_t));
else
/* _sigchld is currently the largest know union member */
memcpy(to, from, 4*sizeof(int) + sizeof(from->_sifields._sigchld));
}
#endif /* __KERNEL__ */
#endif
......@@ -61,7 +61,8 @@ struct el_common {
int retry : 1; /* retry flag */
unsigned int proc_offset; /* processor-specific offset */
unsigned int sys_offset; /* system-specific offset */
unsigned long code; /* machine check code */
unsigned int code; /* machine check code */
unsigned int frame_rev; /* frame revision */
};
/* Machine Check Frame for uncorrectable errors (Large format)
......@@ -117,11 +118,11 @@ struct el_common_EV6_mcheck {
unsigned long DC0_SYNDROME;
unsigned long C_STAT;
unsigned long C_STS;
unsigned long RESERVED0;
unsigned long MM_STAT;
unsigned long EXC_ADDR;
unsigned long IER_CM;
unsigned long ISUM;
unsigned long MM_STAT;
unsigned long RESERVED0;
unsigned long PAL_BASE;
unsigned long I_CTL;
unsigned long PCTX;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment