Source to machdep/ppc/trap.c


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

#include <debug.h>
#include <cpus.h>
#include <kern/thread.h>
#include <mach/exception.h>
#include <kern/syscall_sw.h>
#include <machdep/ppc/cpu_data.h>
#include <mach/thread_status.h>
#include <vm/vm_fault.h>
#include <vm/vm_kern.h> 	/* For kernel_map */
#include <mach/vm_param.h>
#include <machdep/ppc/trap.h>
#include <machdep/ppc/exception.h>
#include <machdep/ppc/proc_reg.h>	/* for SR_xxx definitions */
#include <machdep/ppc/pmap.h>
#include <machine/label_t.h>

#include <sys/param.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>

#include <machdep/ppc/ast.h>
extern void fpu_save(void);
#define NULL 0

#if DEBUG
#define TRAP_ALL 		-1
#define TRAP_ALIGNMENT		0x01
#define TRAP_DATA		0x02
#define TRAP_INSTRUCTION 	0x04
#define TRAP_AST		0x08
#define TRAP_TRACE		0x10
#define TRAP_PROGRAM		0x20
#define TRAP_EXCEPTION		0x40
#define TRAP_UNRESOLVED		0x80
#define TRAP_SYSCALL		0x100	/* all syscalls */
#define TRAP_HW			0x200	/* all in hw_exception.s */
#define TRAP_MACH_SYSCALL	0x400
#define TRAP_SERVER_SYSCALL	0x800

int trapdebug=0;

#define TRAP_DEBUG(LEVEL, A) {if ((trapdebug & LEVEL)==LEVEL){kprintf A;}}
#else
#define TRAP_DEBUG(LEVEL, A)
#endif

/*
 * XXX don't pass VM_PROT_EXECUTE to vm_fault(), execute permission is implied
 * in either R or RW (note: the pmap module knows this).  This is done for the
 * benefit of programs that execute out of their data space (ala lisp).
 * If we didn't do this in that scenerio, the ITLB miss code would call us
 * and we would call vm_fault() with RX permission.  However, the space was
 * probably vm_allocate()ed with just RW and vm_fault would fail.  The "right"
 * solution to me is to have the un*x server always allocate data with RWX for
 * compatibility with existing binaries.
 */

#define	PROT_EXEC	(VM_PROT_READ)
#define PROT_RO		(VM_PROT_READ)
#define PROT_RW		(VM_PROT_READ|VM_PROT_WRITE)

#define IS_KLOADED(pc) ((vm_offset_t)pc > VM_MIN_KERNEL_LOADED_ADDRESS)

/* A useful macro to update the ppc_exception_state in the PCB
 * before calling doexception
 */
#define UPDATE_PPC_EXCEPTION_STATE {					      \
	thread_t th = current_thread();				      \
	struct ppc_exception_state *es = &th->pcb->es;	      \
	es->dar = dar;							      \
	es->dsisr = dsisr;						      \
	es->exception = trapno / EXC_VECTOR_SIZE;	/* back to powerpc */ \
}

static void unresolved_kernel_trap(int trapno,
				   struct ppc_saved_state *ssp,
				   unsigned int dsisr,
				   unsigned int dar,
				   char *message);

struct ppc_saved_state *trap(int trapno,
			     struct ppc_saved_state *ssp,
			     unsigned int dsisr,
			     unsigned int dar)
{
	int exception=0;
	int code;
	int subcode;
	vm_map_t map;
    unsigned int sp;
	unsigned int space,space2;
	unsigned int offset;
	thread_t th = current_thread();
	int error;
    struct proc			*p = th->task->proc;
    struct timeval		syst;

	TRAP_DEBUG(TRAP_ALL,("NMGS TRAP %d srr0=0x%08x, srr1=0x%08x\n",
		     trapno/4,ssp->srr0,ssp->srr1));

	/* Handle kernel traps first */

	if (!USER_MODE(ssp->srr1)) {
		/*
		 * Trap came from system task, ie kernel or collocated server
		 */
	      	switch (trapno) {
			/*
			 * These trap types should never be seen by trap()
			 * in kernel mode, anyway.
			 * Some are interrupts that should be seen by
			 * interrupt() others just don't happen because they
			 * are handled elsewhere. Some could happen but are
			 * considered to be fatal in kernel mode.
			 */
		case EXC_DECREMENTER:
		case EXC_INVALID:
		case EXC_RESET:
		case EXC_MACHINE_CHECK:
		case EXC_SYSTEM_MANAGEMENT:
		case EXC_INTERRUPT:
		case EXC_FP_UNAVAILABLE:
		case EXC_IO_ERROR:
		default:
			unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
			break;
			
		case EXC_TRACE:
#if DEBUG
			enter_debugger(trapno, dsisr, dar, ssp, 0);
#else
			unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
#endif
			break;

		case EXC_RUNMODE_TRACE:
		case EXC_INSTRUCTION_BKPT:
			unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
			break;

		case EXC_PROGRAM:
			if (ssp->srr1 & MASK(SRR1_PRG_TRAP)) {
#if DEBUG
				enter_debugger(trapno, dsisr, dar, ssp, 0);
#else
				unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
#endif /* DEBUG */
			} else {
				unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
			}
			break;
		case EXC_ALIGNMENT:
			TRAP_DEBUG(TRAP_ALIGNMENT,
				   ("NMGS KERNEL ALIGNMENT_ACCESS, "
				     "DAR=0x%08x, DSISR = 0x%08X\n",
				     dar, dsisr,
			     "\20\2HASH\5PROT\6IO_SPC\7WRITE\12WATCH\14EIO"));
			if (alignment(dsisr, dar, ssp)) {
				unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
			}
			break;
		case EXC_DATA_ACCESS:
			TRAP_DEBUG(TRAP_DATA,
				   ("NMGS KERNEL DATA_ACCESS, DAR=0x%08x,"
				     "DSISR = 0x%08X\n", dar, dsisr,
			     "\20\2HASH\5PROT\6IO_SPC\7WRITE\12WATCH\14EIO"));

			if (dsisr & MASK(DSISR_WATCH)) {
				printf("dabr ");
#if DEBUG
				enter_debugger(trapno, dsisr,dar,ssp, 1);
#else
				unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
#endif
				break;
			}
			/* simple case : not SR_COPYIN segment, from kernel */
			if ((dar >> 28) != SR_COPYIN) {
				/* if from kloaded task, use task mapping */
#if notdef_next  /* NeXT kernloaded modules use the kernel map */
				if(IS_KLOADED(ssp->srr0))
					map = th->task->map;
				else 
#endif /* notdef_next */
					map = kernel_map;

				offset = dar;
				TRAP_DEBUG(TRAP_DATA,
					   ("SYSTEM FAULT FROM 0x%08x\n",
					    offset));
				code = vm_fault(map,
						trunc_page(offset),
						dsisr & MASK(DSISR_WRITE) ?
							PROT_RW : PROT_RO,
						FALSE,
						&error);
				if (code != KERN_SUCCESS) {
#if DEBUG
					if (th->recover) {
						label_t *l = (label_t *)th->recover;
						th->recover = (vm_offset_t)NULL;
						longjmp(l,1);
					} else {
					unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
					}
#else
					unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
#endif
				}
				break;
			}

			/* If we get here, the fault was due to a copyin/out */

			map = th->task->map;

			/* Mask out SR_COPYIN and mask in original segment */

			offset = (dar & 0x0fffffff) |
				((mfsrin(dar) & 0xF) << 28);

			TRAP_DEBUG(TRAP_DATA,
				   ("sr=0x%08x, dar=0x%08x, sp=0x%x\n",
				    mfsrin(dar), dar,map->pmap->space));
			assert(((mfsrin(dar) & 0x0FFFFFFF) >> 4) ==
			       map->pmap->space);
			TRAP_DEBUG(TRAP_DATA,
				   ("KERNEL FAULT FROM 0x%x,0x%08x\n",
				    map->pmap->space, offset));

			code = vm_fault(map,
					trunc_page(offset),
					dsisr & MASK(DSISR_WRITE) ?
					PROT_RW : PROT_RO,
					FALSE,
					&error);

			/* If we failed, there should be a recovery
			 * spot to rfi to.
			 */
			if (code != KERN_SUCCESS) {
				TRAP_DEBUG(TRAP_DATA,
					   ("FAULT FAILED- srr0=0x%08x,"
					     "pcb-srr0=0x%08x\n",
					     ssp->srr0,
					     th->pcb->ss.srr0));

				if (th->recover) {
					label_t *l = (label_t *)th->recover;
					th->recover = (vm_offset_t)NULL;
					longjmp(l, 1);
				} else {
					unresolved_kernel_trap(trapno, ssp, dsisr, dar, "copyin/out has no recovery point");
				}
			}
			break;
			
		case EXC_INSTRUCTION_ACCESS:
			/* Colocated tasks aren't necessarily wired and
			 * may page fault
			 */
			TRAP_DEBUG(TRAP_INSTRUCTION,
				   ("NMGS KERNEL INSTRUCTION ACCESS,"
				     "DSISR = 0x%B\n", dsisr,
			     "\20\2HASH\5PROT\6IO_SPC\7WRITE\12WATCH\14EIO"));

#if DEBUG
				enter_debugger(trapno, dsisr,dar,ssp, 1);
#else
				unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
#endif
#if 0
			/* Make sure it's not the kernel itself faulting */
			assert ((ssp->srr0 >= VM_MIN_KERNEL_LOADED_ADDRESS) &&
				(ssp->srr0 <  VM_MAX_KERNEL_LOADED_ADDRESS));

			/* Same as for data access, except fault type
			 * is PROT_EXEC and addr comes from srr0
			 */
			map = th->task->map;
			space = PPC_SID_KERNEL;
			
			code = vm_fault(map, trunc_page(ssp->srr0),
					PROT_EXEC, FALSE,
					&error);
			if (code != KERN_SUCCESS) {
				TRAP_DEBUG(TRAP_INSTRUCTION,
					   ("INSTR FAULT FAILED!\n"));
				exception = EXC_BAD_ACCESS;
				subcode = ssp->srr0;
			}
#endif
			break;

		case EXC_AST:
			TRAP_DEBUG(TRAP_AST,
				   ("EXC_AST FROM KERNEL MODE\n"));
			check_for_ast();
			break;
		}
	} else {
		/*
		 * Trap came from user task
		 */

		if (p)
			syst = p->p_stats->p_ru.ru_stime;


	      	switch (trapno) {
			/*
			 * These trap types should never be seen by trap()
			 * Some are interrupts that should be seen by
			 * interrupt() others just don't happen because they
			 * are handled elsewhere.
			 */
		case EXC_DECREMENTER:
		case EXC_INVALID:
		case EXC_MACHINE_CHECK:
		case EXC_INTERRUPT:
		case EXC_FP_UNAVAILABLE:
		case EXC_SYSTEM_MANAGEMENT:
		case EXC_IO_ERROR:
			
		default:
			panic("Unexpected exception type, %d 0x%x", trapno, trapno);
			break;

		case EXC_RESET:
			powermac_reboot(); 	     /* never returns */

		case EXC_ALIGNMENT:
			TRAP_DEBUG(TRAP_ALIGNMENT,
				   ("NMGS USER ALIGNMENT_ACCESS, "
				     "DAR=0x%08x, DSISR = 0x%B\n", dar, dsisr,
				     "\20\2HASH\5PROT\6IO_SPC\7WRITE\12WATCH\14EIO"));
			if (alignment(dsisr, dar, ssp)) {
				code = EXC_PPC_UNALIGNED;
				exception = EXC_BAD_ACCESS;
				subcode = dar;
			}
			break;

		case EXC_TRACE:			/* Real PPC chips */
		case EXC_INSTRUCTION_BKPT:	/* 603  PPC chips */
		case EXC_RUNMODE_TRACE:		/* 601  PPC chips */
			TRAP_DEBUG(TRAP_TRACE,("NMGS TRACE TRAP\n"));
			exception = EXC_BREAKPOINT;
			code = EXC_PPC_TRACE;
			subcode = ssp->srr0;
			break;

		case EXC_PROGRAM:
			TRAP_DEBUG(TRAP_PROGRAM,
				   ("NMGS PROGRAM TRAP\n"));
			if (ssp->srr1 & MASK(SRR1_PRG_FE)) {
				TRAP_DEBUG(TRAP_PROGRAM,
					   ("FP EXCEPTION\n"));
				fpu_save();
				fpu_disable();
				UPDATE_PPC_EXCEPTION_STATE;
				exception = EXC_ARITHMETIC;
				code = EXC_ARITHMETIC;
				subcode = per_proc_info[cpu_number()].
					fpu_pcb->fs.fpscr;
			} else if (ssp->srr1 & MASK(SRR1_PRG_ILL_INS)) {
				TRAP_DEBUG(TRAP_PROGRAM,
					   ("ILLEGAL INSTRUCTION\n"));

				UPDATE_PPC_EXCEPTION_STATE
				exception = EXC_BAD_INSTRUCTION;
				code = EXC_PPC_UNIPL_INST;
				subcode = ssp->srr0;
			} else if (ssp->srr1 & MASK(SRR1_PRG_PRV_INS)) {
				TRAP_DEBUG(TRAP_PROGRAM,
					   ("PRIVILEGED INSTRUCTION\n"));

				UPDATE_PPC_EXCEPTION_STATE;
				exception = EXC_BAD_INSTRUCTION;
				code = EXC_PPC_PRIVINST;
				subcode = ssp->srr0;
			} else if (ssp->srr1 & MASK(SRR1_PRG_TRAP)) {
#if MACH_KGDB
				/* Give kernel debugger a chance to
				 * claim the breakpoint before passing
				 * it up as an exception
				 */
				if (kgdb_user_breakpoint(ssp)) {
					call_kgdb_with_ctx(EXC_PROGRAM,
							   0,
							   ssp);
					break;
				}
#endif /* MACH_KGDB */

				UPDATE_PPC_EXCEPTION_STATE;
				exception = EXC_BREAKPOINT;
				code = EXC_PPC_BREAKPOINT;
				subcode = ssp->srr0;
			}
			break;

		case EXC_DATA_ACCESS:
			TRAP_DEBUG(TRAP_DATA,
				   ("NMGS USER DATA_ACCESS, DAR=0x%08x, DSISR = 0x%B\n", dar, dsisr,"\20\2HASH\5PROT\6IO_SPC\7WRITE\12WATCH\14EIO"));
			
			if (dsisr & MASK(DSISR_WATCH)) {
				printf("dabr ");
#if DEBUG
				enter_debugger(trapno, dsisr,dar,ssp, 1);
#else
				unresolved_kernel_trap(trapno, ssp, dsisr, dar, NULL);
#endif
				break;
			}
			map = th->task->map;
			space = map->pmap->space;
			assert(space != 0);
#if DEBUG
			mfsr(space2, SR_UNUSED_BY_KERN);
			space2 = (space2 >> 4) & 0x00FFFFFF;

			/* Do a check that at least SR_UNUSED_BY_KERN
			 * is in the correct user space.
			 * TODO NMGS may not be true as ints are on?.
			 */
			assert(space2 == space);
#if 0
			TRAP_DEBUG(TRAP_DATA,("map = 0x%08x, space=0x%08x, addr=0x%08x\n",map, space, trunc_page(dar));
#endif
#endif /* DEBUG */
			
			code = vm_fault(map, trunc_page(dar),
				 dsisr & MASK(DSISR_WRITE) ? PROT_RW : PROT_RO,
				 FALSE,
				 &error);
			if (code != KERN_SUCCESS) {
				TRAP_DEBUG(TRAP_DATA,("FAULT FAILED!\n"));
				UPDATE_PPC_EXCEPTION_STATE;
				exception = EXC_BAD_ACCESS;
				subcode = dar;
			}
			break;
			
		case EXC_INSTRUCTION_ACCESS:
			TRAP_DEBUG(TRAP_INSTRUCTION,("NMGS USER INSTRUCTION ACCESS, DSISR = 0x%B\n", dsisr,"\20\2HASH\5PROT\6IO_SPC\7WRITE\12WATCH\14EIO"));

			/* Same as for data access, except fault type
			 * is PROT_EXEC and addr comes from srr0
			 */
			map = th->task->map;
			space = map->pmap->space;
			assert(space != 0);
#if DEBUG
			mfsr(space2, SR_UNUSED_BY_KERN);
			space2 = (space2 >> 4) & 0x00FFFFFF;

			/* Do a check that at least SR_UNUSED_BY_KERN
			 * is in the correct user space.
			 * TODO NMGS is this always true now ints are on?
			 */
			assert(space2 == space);
#endif /* DEBUG */
			
			code = vm_fault(map, trunc_page(ssp->srr0),
					PROT_EXEC, FALSE,
					&error);
			if (code != KERN_SUCCESS) {
				TRAP_DEBUG(TRAP_INSTRUCTION,
					   ("INSTR FAULT FAILED!\n"));
				UPDATE_PPC_EXCEPTION_STATE;
				exception = EXC_BAD_ACCESS;
				subcode = ssp->srr0;
			}
			break;

		case EXC_AST:
			TRAP_DEBUG(TRAP_AST,("EXC_AST FROM USER MODE\n"));
			check_for_ast();
			break;
			
		}
	}

	if (exception) {
		TRAP_DEBUG(TRAP_EXCEPTION,
		   ("doexception (0x%x, 0x%x, 0x%x, 0x%x, pc = 0x%x in %s)\n",
		   exception,code,subcode, th, th->pcb->ss.srr0,
		   &th->task->proc->p_comm));
		doexception(exception, code, subcode, th);
		return ssp;
	}

    if (USER_MODE(ssp->srr1)) {
		if (p && (p->p_flag & P_PROFIL)) {
			int		ticks;
			struct timeval	*tv = &p->p_stats->p_ru.ru_stime;
					
			ticks = ((tv->tv_sec - syst.tv_sec) * 1000 +
					(tv->tv_usec - syst.tv_usec) / 1000) /
					(tick / 1000);
			if (ticks)
				addupc_task(p, ssp->srr0, ticks);
		}
	}
	return ssp;
}

#if MACH_ASSERT
/* This routine is called from assembly before each and every system call
 * iff MACH_ASSERT is defined. It must preserve r3.
 */

extern int syscall_trace(int, struct ppc_saved_state *);

int syscall_trace(int retval, struct ppc_saved_state *ssp)
{
	int i, argc;

	if (trapdebug & TRAP_SYSCALL)
		trapdebug |= (TRAP_MACH_SYSCALL|TRAP_SERVER_SYSCALL);

	if (!(trapdebug & (TRAP_MACH_SYSCALL|TRAP_SERVER_SYSCALL)))
		return retval;
	if (ssp->r0 & 0x80000000) {
		/* Mach trap */
		if (!(trapdebug & TRAP_MACH_SYSCALL))
			return retval;

		printf("0x%08x : %30s (",
		       ssp->srr0, mach_trap_table[-(ssp->r0)].mach_trap_name);
		argc = mach_trap_table[-(ssp->r0)].mach_trap_arg_count;
		for (i = 0; i < argc; i++)
			printf("%08x ",*(&ssp->r3 + i));
		printf(")\n");
	} else {
		if (!(trapdebug & TRAP_SERVER_SYSCALL))
			return retval;
		printf("0x%08x : RHAPSODY %3d (", ssp->srr0, ssp->r0);
		argc = 4; /* print 4 of 'em! */
		for (i = 0; i < argc; i++)
			printf("%08x ",*(&ssp->r3 + i));
		printf(")\n");
	}
	return retval;
}
#endif /* MACH_ASSERT */

/*
 * called from syscall if there is an error
 */

int syscall_error(
	int exception,
	int code,
	int subcode,
	struct ppc_saved_state *ssp)
{
	register thread_t thread;

	thread = current_thread();

	if (thread == 0)
	    panic("syscall error in boot phase");

	if (!USER_MODE(ssp->srr1))
		panic("system call called from kernel");

	doexception(exception, code, subcode, thread);

	return 0;
}

/* Pass up a server syscall/exception */
void
doexception(
	    int exc,
	    int code,
	    int sub,
	    thread_t th)
{
	exception(exc, code, sub);
}

char *trap_type[] = {
	"0x0000  INVALID EXCEPTION",
	"0x0100  System reset",
	"0x0200  Machine check",
	"0x0300  Data access",
	"0x0400  Instruction access",
	"0x0500  External interrupt",
	"0x0600  Alignment",
	"0x0700  Program",
	"0x0800  Floating point",
	"0x0900  Decrementer",
	"0x0A00  I/O controller interface",
	"0x0B00  INVALID EXCEPTION",
	"0x0C00  System call exception",
	"0x0D00  Trace",
	"0x0E00  FP assist",
	"0x0F00  Performance monitoring",
	"0x1000  Instruction PTE miss",
	"0x1100  Data load PTE miss",
	"0x1200  Data store PTE miss",
	"0x1300  Instruction Breakpoint",
	"0x1400  System Management",
	"0x1500  INVALID EXCEPTION",
	"0x1600  INVALID EXCEPTION",
	"0x1700  INVALID EXCEPTION",
	"0x1800  INVALID EXCEPTION",
	"0x1900  INVALID EXCEPTION",
	"0x1A00  INVALID EXCEPTION",
	"0x1B00  INVALID EXCEPTION",
	"0x1C00  INVALID EXCEPTION",
	"0x1D00  INVALID EXCEPTION",
	"0x1E00  INVALID EXCEPTION",
	"0x1F00  INVALID EXCEPTION",
	"0x2000  Run Mode/Trace"
	"0x2100  INVALID EXCEPTION",
	"0x2200  INVALID EXCEPTION",
	"0x2300  INVALID EXCEPTION",
	"0x2400  INVALID EXCEPTION",
	"0x2500  INVALID EXCEPTION",
	"0x2600  INVALID EXCEPTION",
	"0x2700  INVALID EXCEPTION",
	"0x2800  INVALID EXCEPTION",
	"0x2900  INVALID EXCEPTION",
	"0x2A00  INVALID EXCEPTION",
	"0x2B00  INVALID EXCEPTION",
	"0x2C00  INVALID EXCEPTION",
	"0x2D00  INVALID EXCEPTION",
	"0x2E00  INVALID EXCEPTION",
	"0x2F00  INVALID EXCEPTION",
};

void unresolved_kernel_trap(int trapno,
			    struct ppc_saved_state *ssp,
			    unsigned int dsisr,
			    unsigned int dar,
			    char *message)
{
	char *trap_name;

#if notdef_next
	if(IS_KLOADED(ssp->srr0)) {
		int exception;

		TRAP_DEBUG(TRAP_UNRESOLVED,
			   ("unresolved trap %d in kernel loaded task %d\n",trapno));
		TRAP_DEBUG(TRAP_UNRESOLVED,
			   ("%s: sending exception message\n", message));
		switch(trapno) {
		case EXC_INVALID:
		case EXC_RESET:
		case EXC_MACHINE_CHECK:
		case EXC_INTERRUPT:
		case EXC_DECREMENTER:
		case EXC_IO_ERROR:
		case EXC_SYSTEM_CALL:
		case EXC_RUNMODE_TRACE:
		default:
			exception = EXC_BREAKPOINT;
			break;
		case EXC_ALIGNMENT:
		case EXC_INSTRUCTION_ACCESS:
		case EXC_DATA_ACCESS:
			exception = EXC_BAD_ACCESS;
			break;
		case EXC_PROGRAM:
			exception = EXC_BAD_INSTRUCTION;
			break;
		case EXC_FP_UNAVAILABLE:
			/* TODO NMGS - should this generate EXC_ARITHMETIC?*/
			exception = EXC_ARITHMETIC;
			break;
		}
		UPDATE_PPC_EXCEPTION_STATE;
		doexception(exception, 0, 0, current_thread());
		return;
	}
#if DEBUG
	regDump(ssp);
#endif /* DEBUG */
#endif /* notdef_next */

	if ((unsigned)trapno <= EXC_MAX)
		trap_name = trap_type[trapno / EXC_VECTOR_SIZE];
	else
		trap_name = "???? unrecognised exception";
	if (message == NULL)
		message = trap_name;

	/* no KGDB so display a backtrace */
	printf("\n\nUnresolved kernel trap: %s DSISR=0x%08x DAR=0x%08x\n",
	       trap_name, dsisr, dar);

#ifdef DEBUG
	enter_debugger(trapno, dsisr, dar, ssp, 1);
#else
	{
	unsigned int* stackptr;
	int i;
	printf("generating stack backtrace prior to panic:\n\n");

	printf("backtrace: 0x%08x ", ssp->srr0);

	stackptr = (unsigned int *)(ssp->r1);

	for (i = 0; i < 8; i++) {

		/* Avoid causing page fault */
		if (pmap_extract(kernel_pmap, (vm_offset_t)stackptr) == 0)
			break;

		stackptr = (unsigned int*)*stackptr;
		/*
		 * TODO NMGS - use FM_LR_SAVE constant, but asm.h defines
		 * too much (such as r1, used above)
		 */

		/* Avoid causing page fault */
		if (pmap_extract(kernel_pmap, (vm_offset_t)stackptr+2) == 0)
			break;

		printf("0x%08x ",*(stackptr + 2));
	}
	printf("\n\n");
	}
#endif

	panic(message);
}