patch-2.4.0-test12 linux/arch/parisc/kernel/traps.c

Next file: linux/arch/parisc/lib/Makefile
Previous file: linux/arch/parisc/kernel/time.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test11/linux/arch/parisc/kernel/traps.c linux/arch/parisc/kernel/traps.c
@@ -0,0 +1,895 @@
+/*
+ *  linux/arch/parisc/traps.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *  Copyright (C) 1999, 2000  Philipp Rumpf <prumpf@tux.org>
+ */
+
+/*
+ * 'Traps.c' handles hardware traps and faults after we have saved some
+ * state in 'asm.s'.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/atomic.h>
+
+#include <asm/smp.h>
+#include <asm/pdc.h>
+
+#ifdef CONFIG_KWDB
+#include <kdb/break.h>		/* for BI2_KGDB_GDB */
+#include <kdb/kgdb_types.h>	/* for __() */
+#include <kdb/save_state.h>	/* for struct save_state */
+#include <kdb/kgdb_machine.h>	/* for pt_regs_to_ssp and ssp_to_pt_regs */
+#include <kdb/trap.h>		/* for I_BRK_INST */
+#endif /* CONFIG_KWDB */
+
+
+static inline void console_verbose(void)
+{
+	extern int console_loglevel;
+	console_loglevel = 15;
+}
+
+
+void page_exception(void);
+
+/*
+ * These constants are for searching for possible module text
+ * segments.  VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is
+ * a guess of how much space is likely to be vmalloced.
+ */
+#define VMALLOC_OFFSET (8*1024*1024)
+#define MODULE_RANGE (8*1024*1024)
+
+int kstack_depth_to_print = 24;
+
+static void printbinary(unsigned long x, int nbits)
+{
+	unsigned long mask = 1UL << (nbits - 1);
+	while (mask != 0) {
+		printk(mask & x ? "1" : "0");
+		mask >>= 1;
+	}
+}
+
+void show_regs(struct pt_regs *regs)
+{
+	int i;
+#ifdef __LP64__
+#define RFMT " %016lx"
+#else
+#define RFMT " %08lx"
+#endif
+
+	printk("\n"); /* don't want to have that pretty register dump messed up */
+
+	printk("     YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI\nPSW: ");
+	printbinary(regs->gr[0], 32);
+	printk("\n");
+
+	for (i = 0; i < 32; i += 4) {
+		int j;
+		printk("r%d-%d\t", i, i + 3);
+		for (j = 0; j < 4; j++) {
+			printk(RFMT, i + j == 0 ? 0 : regs->gr[i + j]);
+		}
+		printk("\n");
+	}
+
+	for (i = 0; i < 8; i += 4) {
+		int j;
+		printk("sr%d-%d\t", i, i + 4);
+		for (j = 0; j < 4; j++) {
+			printk(RFMT, regs->sr[i + j]);
+		}
+		printk("\n");
+	}
+
+#if REDICULOUSLY_VERBOSE
+	for (i = 0; i < 32; i++) {
+		printk("FR%2d : %016lx  ", i, regs->fr[i]);
+		if ((i & 1) == 1)
+			printk("\n");
+	}
+#endif
+
+	printk("\nIASQ:" RFMT RFMT " IAOQ:" RFMT RFMT "\n",
+	       regs->iasq[0], regs->iasq[1], regs->iaoq[0], regs->iaoq[1]);
+	printk(" IIR: %08lx    ISR:" RFMT "  IOR:" RFMT "\nORIG_R28:" RFMT
+	       "\n", regs->iir, regs->isr, regs->ior, regs->orig_r28);
+}
+
+void
+die_if_kernel (char *str, struct pt_regs *regs, long err)
+{
+	if (user_mode(regs)) {
+#if 1
+		if (err == 0)
+			return; /* STFU */
+
+		/* XXX for debugging only */
+		printk ("!!die_if_kernel: %s(%d): %s %ld\n",
+			current->comm, current->pid, str, err);
+		show_regs(regs);
+#endif
+		return;
+	}
+
+	printk("%s[%d]: %s %ld\n", current->comm, current->pid, str, err);
+
+	show_regs(regs);
+
+	/* Wot's wrong wif bein' racy? */
+	if (current->thread.flags & PARISC_KERNEL_DEATH) {
+		printk("die_if_kernel recursion detected.\n");
+		sti();
+		while (1);
+	}
+	current->thread.flags |= PARISC_KERNEL_DEATH;
+	do_exit(SIGSEGV);
+}
+
+asmlinkage void cache_flush_denied(struct pt_regs * regs, long error_code)
+{
+}
+
+asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
+{
+}
+
+#ifndef CONFIG_MATH_EMULATION
+
+asmlinkage void math_emulate(long arg)
+{
+}
+
+#endif /* CONFIG_MATH_EMULATION */
+
+int syscall_ipi(int (*syscall) (struct pt_regs *), struct pt_regs *regs)
+{
+	return syscall(regs);
+}
+
+struct {
+	int retval;
+
+	int (*func) (void *, struct pt_regs *);
+	void * data;
+} ipi_action[NR_CPUS];
+
+void ipi_interrupt(int irq, void *unused, struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+
+	if(!ipi_action[cpu].func)
+		BUG();
+
+	ipi_action[cpu].retval =
+		ipi_action[cpu].func(ipi_action[cpu].data, regs);
+}
+
+/* gdb uses break 4,8 */
+#define GDB_BREAK_INSN 0x10004
+void handle_gdb_break(struct pt_regs *regs, int wot)
+{
+	struct siginfo si;
+
+	si.si_code = wot;
+	si.si_addr = (void *) (regs->iaoq[0] & ~3);
+	si.si_signo = SIGTRAP;
+	si.si_errno = 0;
+	force_sig_info(SIGTRAP, &si, current);
+}
+
+void handle_break(unsigned iir, struct pt_regs *regs)
+{
+	struct siginfo si;
+#ifdef CONFIG_KWDB
+	struct save_state ssp;
+#endif /* CONFIG_KWDB */   
+
+	flush_all_caches();
+	switch(iir) {
+	case 0x00:
+		/* show registers, halt */
+		cli();
+		printk("break 0,0: pid=%d command='%s'\n",
+		       current->pid, current->comm);
+		die_if_kernel("Breakpoint", regs, 0);
+		show_regs(regs);
+		si.si_code = TRAP_BRKPT;
+		si.si_addr = (void *) (regs->iaoq[0] & ~3);
+		si.si_signo = SIGTRAP;
+		force_sig_info(SIGTRAP, &si, current);
+		break;
+
+	case GDB_BREAK_INSN:
+		die_if_kernel("Breakpoint", regs, 0);
+		handle_gdb_break(regs, TRAP_BRKPT);
+		break;
+
+#ifdef CONFIG_KWDB
+
+	case KGDB_BREAK_INSN:
+		mtctl(0, 15);
+		pt_regs_to_ssp(regs, &ssp);
+		kgdb_trap(I_BRK_INST, &ssp, 1);
+		ssp_to_pt_regs(&ssp, regs);
+		break;
+
+	case KGDB_INIT_BREAK_INSN:
+		mtctl(0, 15);
+		pt_regs_to_ssp(regs, &ssp);
+		kgdb_trap(I_BRK_INST, &ssp, 1);
+		ssp_to_pt_regs(&ssp, regs);
+
+		/* Advance pcoq to skip break */
+		regs->iaoq[0] = regs->iaoq[1];
+		regs->iaoq[1] += 4;
+		break;
+
+#endif /* CONFIG_KWDB */
+
+	default:
+		set_eiem(0);
+		printk("break %#08x: pid=%d command='%s'\n",
+		       iir, current->pid, current->comm);
+		show_regs(regs);
+		si.si_signo = SIGTRAP;
+		si.si_code = TRAP_BRKPT;
+		si.si_addr = (void *) (regs->iaoq[0] & ~3);
+		force_sig_info(SIGTRAP, &si, current);
+		return;
+	}
+}
+
+/* Format of the floating-point exception registers. */
+struct exc_reg {
+	unsigned int exception : 6;
+	unsigned int ei : 26;
+};
+
+/* Macros for grabbing bits of the instruction format from the 'ei'
+   field above. */
+/* Major opcode 0c and 0e */
+#define FP0CE_UID(i) (((i) >> 6) & 3)
+#define FP0CE_CLASS(i) (((i) >> 9) & 3)
+#define FP0CE_SUBOP(i) (((i) >> 13) & 7)
+#define FP0CE_SUBOP1(i) (((i) >> 15) & 7) /* Class 1 subopcode */
+#define FP0C_FORMAT(i) (((i) >> 11) & 3)
+#define FP0E_FORMAT(i) (((i) >> 11) & 1)
+
+/* Major opcode 0c, uid 2 (performance monitoring) */
+#define FPPM_SUBOP(i) (((i) >> 9) & 0x1f)
+
+/* Major opcode 2e (fused operations).   */
+#define FP2E_SUBOP(i)  (((i) >> 5) & 1)
+#define FP2E_FORMAT(i) (((i) >> 11) & 1)
+
+/* Major opcode 26 (FMPYSUB) */
+/* Major opcode 06 (FMPYADD) */
+#define FPx6_FORMAT(i) ((i) & 0x1f)
+
+/* Flags and enable bits of the status word. */
+#define FPSW_FLAGS(w) ((w) >> 27)
+#define FPSW_ENABLE(w) ((w) & 0x1f)
+#define FPSW_V (1<<4)
+#define FPSW_Z (1<<3)
+#define FPSW_O (1<<2)
+#define FPSW_U (1<<1)
+#define FPSW_I (1<<0)
+
+/* Emulate a floating point instruction if necessary and possible
+   (this will be moved elsewhere eventually).  Return zero if
+   successful or if emulation was not required, -1 if the instruction
+   is actually illegal or unimplemented.  The status word passed as
+   the first parameter will be modified to signal exceptions, if
+   any. */
+
+/* FIXME!!!  This is really incomplete and, at the moment, most
+   illegal FP instructions will simply act as no-ops.  Obviously that
+   is *not* what we want.  Also we don't even try to handle exception
+   types other than the 'unimplemented' ones. */
+int
+fp_emul_insn(u32 *sw, struct exc_reg exc, struct pt_regs *regs)
+{
+	switch (exc.exception) {
+	case 0x3:  /* Unimplemented, opcode 06 */
+		break;
+	case 0x9:  /* Unimplemented, opcode 0c */
+		/* We do not support quadword operations, end of
+                   story.  There's no support for them in GCC. */
+		if (FP0C_FORMAT(exc.ei) == 3)
+			return -1; /* SIGILL */
+		/* Fall through. */
+	case 0xa:  /* Unimplemented, opcode 0e */
+		if (FP0CE_CLASS(exc.ei) == 1) {
+			/* FCNV instructions of various sorts. */
+		} else {
+			if (FP0CE_CLASS(exc.ei == 0)
+			    && FP0CE_SUBOP(exc.ei == 5)) {
+				/* FRND instructions should be
+                                   emulated, at some point, I
+                                   guess. */
+				return -1; /* SIGILL */
+			}
+		}
+		break;
+	case 0x23: /* Unimplemented, opcode 26 */
+		break;
+	case 0x2b: /* Unimplemented, opcode 2e */
+		break;
+	case 0x1:  /* Unimplemented, opcode 0e/0c */
+		/* FIXME: How the hell are we supposed to tell which
+                   opcode it is? */
+		break;
+	default:
+		return -1; /* Punt */
+	}
+
+	return 0;
+}
+
+/* Handle a floating point exception.  Return zero if the faulting
+   instruction can be completed successfully. */
+int
+handle_fpe(struct pt_regs *regs)
+{
+	struct siginfo si;
+	union {
+		struct fpsw {
+			/* flag bits */
+			unsigned int fv : 1;
+			unsigned int fz : 1;
+			unsigned int fo : 1;
+			unsigned int fu : 1;
+			unsigned int fi : 1;
+
+			unsigned int c : 1;
+			unsigned int pad1 : 4;
+			unsigned int cq : 11;
+			unsigned int rm : 2;
+			unsigned int pad2 : 2;
+			unsigned int t : 1;
+			unsigned int d : 1;
+
+			/* enable bits */
+			unsigned int ev : 1;
+			unsigned int ez : 1;
+			unsigned int eo : 1;
+			unsigned int eu : 1;
+			unsigned int ei : 1;
+		} status;
+		u32 word;
+	} sw;
+	struct exc_reg excepts[7];
+	unsigned int code = 0;
+	unsigned int throw;
+
+	/* Status word = FR0L. */
+	memcpy(&sw, regs->fr, sizeof(sw));
+	/* Exception words = FR0R-FR3R. */
+	memcpy(excepts, ((char *) regs->fr) + 4, sizeof(excepts));
+
+	/* This is all CPU dependent.  Since there is no public
+           documentation on the PA2.0 processors we will just assume
+           everything is like the 7100/7100LC/7300LC for now.
+
+	   Specifically: All exceptions are marked as "unimplemented"
+	   in the exception word, and the only exception word used is
+	   excepts[1]. */
+
+	/* Try to emulate the instruction.  Also determine if it is
+           really an illegal instruction in the process.
+
+	   FIXME: fp_emul_insn() only checks for the "unimplemented"
+	   exceptions at the moment.  So this may break horribly on
+	   PA2.0, where we may want to also check to see if we should
+	   just send SIGFPE (or maybe not, let's see the documentation
+	   first...) */
+	if (fp_emul_insn(&sw.word, excepts[1], regs) == -1)
+		goto send_sigill;
+
+	/* Take the intersection of the flag bits in the FPSW and the
+           enable bits in the FPSW. */
+	throw = FPSW_FLAGS(sw.word) & FPSW_ENABLE(sw.word);
+
+	/* Concoct an appropriate si_code.  Of course we don't know
+           what to do if multiple exceptions were enabled and multiple
+           flags were set.  Maybe that's why HP/UX doesn't implement
+           feenableexcept(). */
+
+	if (throw == 0)
+		goto success; /* Duh. */
+	else if (throw & FPSW_V)
+		code = FPE_FLTINV;
+	else if (throw & FPSW_Z)
+		code = FPE_FLTDIV;
+	else if (throw & FPSW_O)
+		code = FPE_FLTOVF;
+	else if (throw & FPSW_U)
+		code = FPE_FLTUND;
+	else if (throw & FPSW_I)
+		code = FPE_FLTRES;
+
+#if 1 /* Debugging... */
+	printk("Unemulated floating point exception, pid=%d (%s)\n",
+	       current->pid, current->comm);
+	show_regs(regs);
+	{
+		int i;
+		printk("FP Status: %08x\n", sw.word);
+		printk("FP Exceptions:\n");
+		for (i = 0; i < 7; i++) {
+			printk("\tExcept%d: exception %03x insn %06x\n",
+			       i, excepts[i].exception, excepts[i].ei);
+		}
+	}
+#endif
+
+	/* FIXME: Should we clear the flag bits, T bit, and exception
+           registers here? */
+
+	si.si_signo = SIGFPE;
+	si.si_errno = 0;
+	si.si_code = code;
+	si.si_addr = (void *) regs->iaoq[0];
+	force_sig_info(SIGFPE, &si, current);
+	return -1;
+
+ send_sigill:
+	si.si_signo = SIGILL;
+	si.si_errno = 0;
+	si.si_code = ILL_COPROC;
+	si.si_addr = (void *) regs->iaoq[0];
+	force_sig_info(SIGILL, &si, current);
+	return -1;
+
+ success:
+	/* We absolutely have to clear the T bit and exception
+           registers to allow the process to recover.  Otherwise every
+           subsequent floating point instruction will trap. */
+	sw.status.t = 0;
+	memset(excepts, 0, sizeof(excepts));
+
+	memcpy(regs->fr, &sw, sizeof(sw));
+	memcpy(((char *) regs->fr) + 4,excepts , sizeof(excepts));
+	return 0;
+}
+
+int handle_toc(void)
+{
+	return 0;
+}
+
+void default_trap(int code, struct pt_regs *regs)
+{
+	printk("Trap %d on CPU %d\n", code, smp_processor_id());
+
+	show_regs(regs);
+}
+
+void (*cpu_lpmc) (int code, struct pt_regs *regs) = default_trap;
+
+
+#ifdef CONFIG_KWDB
+int
+debug_call (void) {
+    printk ("Debug call.\n");
+    return 0;
+}
+
+int
+debug_call_leaf (void) {
+    return 0;
+}
+#endif /* CONFIG_KWDB */
+
+extern void do_page_fault(struct pt_regs *, int, unsigned long);
+extern void parisc_terminate(char *, struct pt_regs *, int, unsigned long);
+extern void transfer_pim_to_trap_frame(struct pt_regs *);
+extern void pdc_console_restart(void);
+
+void handle_interruption(int code, struct pt_regs *regs)
+{
+	unsigned long fault_address = 0;
+	unsigned long fault_space = 0;
+	struct siginfo si;
+#ifdef CONFIG_KWDB
+	struct save_state ssp;
+#endif /* CONFIG_KWDB */   
+
+	if (code == 1)
+	    pdc_console_restart();  /* switch back to pdc if HPMC */
+	else
+	    sti();
+
+#ifdef __LP64__
+
+	/*
+	 * FIXME:
+	 * For 32 bit processes we don't want the b bits (bits 0 & 1)
+	 * in the ior. This is more appropriately handled in the tlb
+	 * miss handlers. Changes need to be made to support addresses
+	 * >32 bits for 64 bit processes.
+	 */
+
+	regs->ior &= 0x3FFFFFFFFFFFFFFFUL;
+#endif
+
+#if 0
+	printk("interrupted with code %d, regs %p\n", code, regs);
+	show_regs(regs);
+#endif
+
+	switch(code) {
+	case  1:
+		parisc_terminate("High Priority Machine Check (HPMC)",regs,code,0);
+		/* NOT REACHED */
+	case  3: /* Recovery counter trap */
+		regs->gr[0] &= ~PSW_R;
+		if (regs->iasq[0])
+			handle_gdb_break(regs, TRAP_TRACE);
+		/* else this must be the start of a syscall - just let it
+		 * run.
+		 */
+		return;
+
+	case  5:
+		flush_all_caches();
+		cpu_lpmc(5, regs);
+		return;
+
+	case  6:
+		fault_address = regs->iaoq[0];
+		fault_space   = regs->iasq[0];
+		break;
+
+	case  9: /* Break Instruction */
+		handle_break(regs->iir,regs);
+		return;
+
+	case 14:
+		/* Assist Exception Trap, i.e. floating point exception. */
+		die_if_kernel("Floating point exception", regs, 0); /* quiet */
+		handle_fpe(regs);
+		return;
+	case 15:
+	case 16:  /* Non-Access TLB miss faulting address is in IOR */
+	case 17:
+	case 26:
+		fault_address = regs->ior;
+		fault_space   = regs->isr;
+
+		if (code == 26 && fault_space == 0)
+		    parisc_terminate("Data access rights fault in kernel",regs,code,fault_address);
+		break;
+
+	case 19:
+		regs->gr[0] |= PSW_X; /* So we can single-step over the trap */
+		/* fall thru */
+	case 21:
+		handle_gdb_break(regs, TRAP_HWBKPT);
+		return;
+
+	case 25: /* Taken branch trap */
+		regs->gr[0] &= ~PSW_T;
+		if (regs->iasq[0])
+			handle_gdb_break(regs, TRAP_BRANCH);
+		/* else this must be the start of a syscall - just let it
+		 * run.
+		 */
+		return;
+
+#if 0 /* def CONFIG_KWDB */
+	case I_TAKEN_BR:	/* 25 */
+		mtctl(0, 15);
+		pt_regs_to_ssp(regs, &ssp);
+		kgdb_trap(I_TAKEN_BR, &ssp, 1);
+		ssp_to_pt_regs(&ssp, regs);
+		break;
+#endif /* CONFIG_KWDB */
+
+	case  8:
+		die_if_kernel("Illegal instruction", regs, code);
+		si.si_code = ILL_ILLOPC;
+		goto give_sigill;
+
+	case 10:
+		die_if_kernel("Priviledged operation - shouldn't happen!", regs, code);
+		si.si_code = ILL_PRVOPC;
+		goto give_sigill;
+	case 11:
+		die_if_kernel("Priviledged register - shouldn't happen!", regs, code);
+		si.si_code = ILL_PRVREG;
+	give_sigill:
+		si.si_signo = SIGILL;
+		si.si_errno = 0;
+		si.si_addr = (void *) regs->iaoq[0];
+		force_sig_info(SIGILL, &si, current);
+		return;
+
+	case 28:  /* Unaligned just causes SIGBUS for now */
+		die_if_kernel("Unaligned data reference", regs, code);
+		si.si_code = BUS_ADRALN;
+		si.si_signo = SIGBUS;
+		si.si_errno = 0;
+		si.si_addr = (void *) regs->ior;
+		force_sig_info(SIGBUS, &si, current);
+		return;
+
+	default:
+		if (user_mode(regs)) {
+			printk("\nhandle_interruption() pid=%d command='%s'\n",
+			    current->pid, current->comm);
+			show_regs(regs);
+			/* SIGBUS, for lack of a better one. */
+			si.si_signo = SIGBUS;
+			si.si_code = BUS_OBJERR;
+			si.si_errno = 0;
+			si.si_addr = (void *) regs->ior;
+			force_sig_info(SIGBUS, &si, current);
+			return;
+		}
+		parisc_terminate("Unexpected Interruption!",regs,code,0);
+		/* NOT REACHED */
+	}
+
+	if (user_mode(regs)) {
+	    if (fault_space != regs->sr[7]) {
+		if (fault_space == 0)
+			printk("User Fault on Kernel Space ");
+		else /* this case should never happen, but whatever... */
+			printk("User Fault (long pointer) ");
+		printk("pid=%d command='%s'\n", current->pid, current->comm);
+		show_regs(regs);
+		si.si_signo = SIGSEGV;
+		si.si_errno = 0;
+		si.si_code = SEGV_MAPERR;
+		si.si_addr = (void *) regs->ior;
+		force_sig_info(SIGSEGV, &si, current);
+		return;
+	    }
+	}
+	else {
+
+	    /*
+	     * The kernel should never fault on its own address space.
+	     */
+
+	    if (fault_space == 0)
+		    parisc_terminate("Kernel Fault",regs,code,fault_address);
+	}
+
+#ifdef CONFIG_KWDB
+	debug_call_leaf ();
+#endif /* CONFIG_KWDB */
+
+	do_page_fault(regs, code, fault_address);
+
+	/*
+	 * This should not be necessary.
+	 * However, we do not currently
+	 * implement flush_page_to_ram.
+	 *
+	 * The problem is that if we just
+	 * brought in some code through the
+	 * D-cache, the I-cache may not see
+	 * it since it hasn't been flushed
+	 * to ram.
+	 */
+
+/* 	flush_all_caches(); */
+
+#if 0
+	printk("returning %p\n", regs);
+/*	show_regs(regs); */
+#endif
+
+	return;
+
+}
+
+void show_stack(unsigned long sp)
+{
+#if 1
+	if ((sp & 0xc0000000UL) == 0xc0000000UL) {
+
+	    __u32 *stackptr;
+	    __u32 *dumpptr;
+
+	    /* Stack Dump! */
+
+	    stackptr = (__u32 *)sp;
+	    dumpptr  = (__u32 *)(sp & ~(INIT_TASK_SIZE - 1));
+	    printk("\nDumping Stack from %p to %p:\n",dumpptr,stackptr);
+	    while (dumpptr < stackptr) {
+		printk("%04x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+		    ((__u32)dumpptr) & 0xffff,
+		    dumpptr[0], dumpptr[1], dumpptr[2], dumpptr[3],
+		    dumpptr[4], dumpptr[5], dumpptr[6], dumpptr[7]);
+		dumpptr += 8;
+	    }
+	}
+#endif
+}
+
+
+void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long offset)
+{
+	set_eiem(0);
+	cli();
+
+	if (code == 1)
+	    transfer_pim_to_trap_frame(regs);
+
+#if 1
+	show_stack(regs->gr[30]);
+#endif
+
+	printk("\n%s: Code=%d regs=%p (Addr=%08lx)\n",msg,code,regs,offset);
+	show_regs(regs);
+
+	for(;;)
+	    ;
+}
+
+void transfer_pim_to_trap_frame(struct pt_regs *regs)
+{
+    register int i;
+    extern unsigned int hpmc_pim_data[];
+    struct pdc_hpmc_pim_11 *pim_narrow;
+    struct pdc_hpmc_pim_20 *pim_wide;
+
+    if (boot_cpu_data.cpu_type >= pcxu) {
+
+	pim_wide = (struct pdc_hpmc_pim_20 *)hpmc_pim_data;
+
+	/*
+	 * Note: The following code will probably generate a
+	 * bunch of truncation error warnings from the compiler.
+	 * Could be handled with an ifdef, but perhaps there
+	 * is a better way.
+	 */
+
+	regs->gr[0] = pim_wide->cr[22];
+
+	for (i = 1; i < 32; i++)
+	    regs->gr[i] = pim_wide->gr[i];
+
+	for (i = 0; i < 32; i++)
+	    regs->fr[i] = pim_wide->fr[i];
+
+	for (i = 0; i < 8; i++)
+	    regs->sr[i] = pim_wide->sr[i];
+
+	regs->iasq[0] = pim_wide->cr[17];
+	regs->iasq[1] = pim_wide->iasq_back;
+	regs->iaoq[0] = pim_wide->cr[18];
+	regs->iaoq[1] = pim_wide->iaoq_back;
+
+	regs->cr30 = pim_wide->cr[30];
+	regs->sar  = pim_wide->cr[11];
+	regs->iir  = pim_wide->cr[19];
+	regs->isr  = pim_wide->cr[20];
+	regs->ior  = pim_wide->cr[21];
+    }
+    else {
+	pim_narrow = (struct pdc_hpmc_pim_11 *)hpmc_pim_data;
+
+	regs->gr[0] = pim_narrow->cr[22];
+
+	for (i = 1; i < 32; i++)
+	    regs->gr[i] = pim_narrow->gr[i];
+
+	for (i = 0; i < 32; i++)
+	    regs->fr[i] = pim_narrow->fr[i];
+
+	for (i = 0; i < 8; i++)
+	    regs->sr[i] = pim_narrow->sr[i];
+
+	regs->iasq[0] = pim_narrow->cr[17];
+	regs->iasq[1] = pim_narrow->iasq_back;
+	regs->iaoq[0] = pim_narrow->cr[18];
+	regs->iaoq[1] = pim_narrow->iaoq_back;
+
+	regs->cr30 = pim_narrow->cr[30];
+	regs->sar  = pim_narrow->cr[11];
+	regs->iir  = pim_narrow->cr[19];
+	regs->isr  = pim_narrow->cr[20];
+	regs->ior  = pim_narrow->cr[21];
+    }
+
+    /*
+     * The following fields only have meaning if we came through
+     * another path. So just zero them here.
+     */
+
+    regs->ksp = 0;
+    regs->kpc = 0;
+    regs->orig_r28 = 0;
+}
+
+int __init check_ivt(void *iva)
+{
+	int i;
+	u32 check = 0;
+	u32 *ivap;
+	u32 *hpmcp;
+	u32 length;
+	extern void os_hpmc(void);
+	extern void os_hpmc_end(void);
+
+	if(strcmp((char *)iva, "cows can fly"))
+		return -1;
+
+	ivap = (u32 *)iva;
+
+	for (i = 0; i < 8; i++)
+	    *ivap++ = 0;
+
+	/* Compute Checksum for HPMC handler */
+
+	length = (u32)((unsigned long)os_hpmc_end - (unsigned long)os_hpmc);
+	ivap[7] = length;
+
+	hpmcp = (u32 *)os_hpmc;
+
+	for(i=0; i<length/4; i++)
+	    check += *hpmcp++;
+
+	for(i=0; i<8; i++)
+	    check += ivap[i];
+
+	ivap[5] = -check;
+
+	return 0;
+}
+	
+#ifndef __LP64__
+extern const void fault_vector_11;
+#endif
+extern const void fault_vector_20;
+
+void __init trap_init(void)
+{
+	volatile long eiem;
+	void *iva;
+
+	printk("trap_init\n");
+	
+	if (boot_cpu_data.cpu_type >= pcxu)
+		iva = (void *) &fault_vector_20;
+	else
+#ifdef __LP64__
+		panic("Can't boot 64-bit OS on PA1.1 processor!");
+#else
+		iva = (void *) &fault_vector_11;
+#endif
+
+	if(check_ivt(iva))
+		panic("IVT invalid");
+
+	mtctl(0, 30);
+	mtctl(90000000, 16);
+	set_eiem(-1L);
+	mtctl(-1L, 23);
+	asm volatile ("rsm 0,%0" : "=r" (eiem));
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)