/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: trap.c 1.61 90/10/01 $"

/*
 * trap.c
 *	trap() and syscall() trap handlers.
 *
 * TODO:
 *	Consider ways to merge SFSWAP usage and SW traps.
 */

/* $Log:	trap.c,v $
 */

#include "sys/signal.h"
#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/systm.h"
#include "sys/mutex.h"
#include "sys/conf.h"
#include "sys/errno.h"
#include "sys/user.h"
#include "sys/timer.h"
#include "sys/proc.h"
#include "sys/acct.h"
#include "sys/vm.h"
#include "sys/var.h"
#include "sys/cmn_err.h"
#include "sys/arch.h"
#ifdef lint
#include "sys/file.h"
#endif

#include "sys/engine.h"
#include "sys/slic.h"
#include "sys/slicreg.h"
#include "sys/cfg.h"
#include "sys/bic.h"

#include "sys/psl.h"
#include "sys/reg.h"
#include "sys/mmu.h"
#include "sys/pte.h"
#include "sys/plocal.h"
#include "sys/trap.h"
#include "sys/intctl.h"
#include "sys/inline.h"
#include "sys/mftpr.h"
#include "sys/hwparam.h"

#include "syscall.h"

#ifdef	MODELD
#include "sys/scan.h"
#endif	/* MODELD */

#ifdef	DEBUG
int	syscalltrace = 0;
int	traptrace = 0;
int	abtdebug = 0;
int	emuladebug = 0;
#endif	/* DEBUG  */

#define	B1BUG21			/* 80386 B-1 Errata # 21 (see 9/1/87 errata) */
#define LOCKBUG			/* bug on lock instructions (386 only) */
#ifdef LOCKBUG
static int ok2lock();
#endif

char	*trap_type[] = {
	"Integer Zero-Divide",
	"Debug Exception",
	"Non-Maskable Interrupt",
	"Single-Byte Interrupt (breakpoint)",
	"Interrupt On Overflow",
	"Array Bounds Check",
	"Undefined/Illegal Op-Code",
	"Device Not Available (FPU)",
	"System Error",
	"Reserved Trap/Interrupt Vector",	/* T_RES == 9 */
	"Invalid Task-State Segment",
	"Segment/Gate Not Present",
	"Stack Fault",
	"General Protection Fault",
	"Page Fault",
	"Reserved Vector (15)",			/* "can't" happen */
	"Co-Processor Error (FPU)"
};
#define	TRAP_TYPES	(sizeof trap_type / sizeof trap_type[0])

/*
 * trap()
 *	Called from the trap handler when a processor trap occurs.
 *
 * See machine/reg.h for order of parameters.
 */

/*ARGSUSED*/
trap(type, edi, esi, ebp, unused, ebx, edx, ecx, eax, eip, cs, flags, sp, ss)
	volatile int	type;	/* type of trap */
	volatile unsigned ss;	/* only if inter-segment (user->kernel) */
	volatile unsigned sp;	/* only if inter-segment (user->kernel) */
	volatile unsigned flags;	/* extended-flags (see machine/psl.h) */
	volatile unsigned cs;	/* sense user-mode entry from RPL field */
	volatile unsigned eip;	/* return context */
	volatile unsigned eax;	/* scratch registers */
	volatile unsigned ecx;	/* ditto */
	volatile unsigned edx;	/* more such */
	volatile unsigned ebx;	/* register variable */
	volatile unsigned unused;	/* temp SP (from push-all instruction) */
	volatile unsigned ebp;	/* of interrupted frame */
	volatile unsigned esi;	/* register variable */
	volatile unsigned edi;	/* register variable */
{
	register struct proc *p;
	unsigned fltaddr;			/* address that faulted */
	int	i;
#ifdef	MODELD
	register int sgs2 = l.eng->e_flags&E_SGS2;
#endif	/* MODELD */
	unchar	my_slic;
	unchar	bic_slic;
	unchar	accerr_reg;
	extern	struct	ctlr_desc *slic_to_config[];
	extern	(*swtrapvec[])();
	extern	void sys_call();
	extern	void sig_clean();
#ifdef lint
	extern unsigned READ_PTROOT();
	extern void WRITE_DSR();
#endif /*lint*/

	p = u.u_procp;

	if (u.u_prof.pr_scale) {
		/*
		 * Avoid race with hardclock.
		 */
		DISABLE();
		u.u_syst = u.u_pst.ps_stime;
		ENABLE();
	}
	l.cnt.v_trap++;

	if (USERMODE(cs)) {
		type |= T_USER;
		u.u_ar0 = (int *) &eax;
		/*
		 * always provide code (overwritten by
		 * SIGILL,SIGSEGV and SIGFPE).
		 */
	}

#ifdef	DEBUG
	if (traptrace && (type & ~T_USER) != T_PGFLT) {
		printf("<%d,%s>: Trap type %d from %s mode, error code = 0x%x\n",
				p-proc, u.u_comm, type & ~T_USER,
				(type & T_USER) ? "user" : "kernel",
				l.trap_err_code);
		if (traptrace > 1)
			trap_dumpregs((int *)&eax);
	}
#endif	/* DEBUG  */

	switch (type) {

	default:
		cmn_err(CE_CONT, "%s trap type %d, error code=0x%x\n",
				(type & T_USER) ? "User" : "Kernel",
				(type & ~T_USER),
				l.trap_err_code);
		type &= ~T_USER;
		trap_dumpregs((int *)&eax);
		if ((unsigned)type < TRAP_TYPES)
			cmn_err(CE_PANIC, "trap: %s", trap_type[type]);
			/*
			 *+ The processor took one of the listed traps
			 *+ and the kernel couldn't recover from it.
			 */
		else
			cmn_err(CE_PANIC, "unexpected trap type");
			/*
			  *+ The processor took an unknown trap type and
			 *+ the kernel couldn't recover from it.
		 	 */
		/*NOTREACHED*/

	case T_DIVERR+T_USER:			/* integer zero-divide */
		u.u_code = ((T_DIVERR+T_USER) << 16) | FPE_INTDIV_FAULT;
		u.u_segvcode = eip;	/* pc when faulted */
		i = SIGFPE;
		break;

	case T_DBG+T_USER:			/* debug exceptions (user) */
		/*
		 * Handle single-step.
		 */
		u.u_code = (T_DBG+T_USER) << 16;
		u.u_segvcode = eip;	/* pc when faulted */
		i = READ_DSR();
		if (i & DSR_BS) {
			WRITE_DSR(DSR_CLEAR);
			flags &= ~FLAGS_TF;
			i = SIGTRAP;
			break;
		}
		/*
		 * Not single-step, must be watchpoint debug trap.
		 * Watchpoint() turns OFF debug traps and does
		 * mpt_stop() fussing.
		 */
#define	F1BUG
#ifdef F1BUG
		/*
		 * Chip bug. Certain undefined opcodes vector to T_DBG
		 * rather than T_UND. If all DSR bits are clear, assume
		 * it's a bogus interrupt and signal process.
		 */
#ifdef	MODELD
		/*
		 * It's safe to assume that i386 chip bugs won't be present
		 * in i486 processors.
		 */
		if (!sgs2 && (i & (DSR_WATCHPT|DSR_BD|DSR_BT)) == 0) {
#else	/* !MODELD */
		if ((i & (DSR_WATCHPT|DSR_BD|DSR_BT)) == 0) {
#endif	/* MODELD */
			WRITE_DSR(DSR_CLEAR);
			u.u_code |= ILL_RESOP_FAULT;
			u.u_segvcode = eip;	/* pc when faulted */
			i = SIGILL;
			break;
		}
#endif	/* F1BUG */
		watchpoint(i, 0, eip);
		goto out;

	case T_DBG:				/* debug exceptions (kernel) */
		i = READ_DSR();
		/*
		 * A kernel debug exception will occur on the first kernel
		 * instruction if the single-step flag bit is set when
		 * executing a lcall from user mode.  This is possible when
		 * running BCS binaries under the debugger (single stepping).
		 *
		 * Recover by clearing the Trap-Flag bit in flags, zapping
		 * the DR6 register, and setting u_debugpend.
		 * When we complete the system call we will signal a SIGTRAP.
		 */
		if ((i & DSR_BS) &&
		    (ushort)cs == KERNEL_CS &&
		    (eip == (uint)sys_call || eip == (uint)sig_clean)) {
			WRITE_DSR(DSR_CLEAR);
			flags &= ~FLAGS_TF;
			u.u_debugpend = 1;
			return;
		}
		ASSERT((i & DSR_BS) == 0, "trap: kernel single-step");
		/*
		 *+ The processor took a single-step trap while
		 *+ executing in kernel mode.  Only user processes
		 *+ should take these traps.
		 */
		/*
		 * Not single-step, must be watchpoint debug trap.
		 * These can happen from within kernel mode if kernel
		 * does data transfer to/from location being debugged at.
		 */
		watchpoint(i, 1, eip);
		return;

	case T_NMI+T_USER:			/* NMI from user mode */
		/*
		 * Processor interrupts are OFF at entry, since enter
		 * thru interrupt-gate. NMI's are disabled at processor.
		 */
		u.u_code = (T_NMI+T_USER) << 16;
		u.u_segvcode = eip;	/* pc when faulted */
		/*
		 * If it was an access error I/O space, clear access error
		 * and signal user process rather than panicing.
		 */
		my_slic = l.eng->e_slicaddr;
#ifdef	MODELD
		if (sgs2) {
			register int flag = accerr_flag(my_slic);
			if (anyaccerr(flag) && !isio(flag)) {
				/*
				 * Toggle nmi accept in the processor control register
				 * so an NMI that arrived concurrently will be seen
				 * when NMI's are reenabled.
				 */
				reset_snmi(my_slic);
				enable_nmi();		/* proc NMI on */
				i = SIGBUS;
				break;
			}
		} else {
#endif	/* MODELD */
		bic_slic = BIC_SLIC(my_slic, slic_to_config[my_slic]->cd_flags);
		accerr_reg = (my_slic & 0x01) ? BIC_ACCERR1 : BIC_ACCERR0;
		i = rdSubslave(bic_slic, PROC_BIC, accerr_reg);
		if ((i & (BICAE_OCC|BICAE_IO)) == 0) {
			wrSubslave(bic_slic, PROC_BIC, accerr_reg, 0xbb);
			/*
			 * Toggle nmi accept in the processor control register
			 * so an NMI that arrived concurrently will be seen
			 * when NMI's are reenabled.
			 */
			wrslave(my_slic, PROC_CTL,
				PROC_CTL_NO_NMI | PROC_CTL_NO_SSTEP | 
				PROC_CTL_NO_HOLD | PROC_CTL_NO_RESET);
			wrslave(my_slic, PROC_CTL,
				PROC_CTL_NO_SSTEP | PROC_CTL_NO_HOLD |
				PROC_CTL_NO_RESET);
			enable_nmi();		/* proc NMI on */
			i = SIGBUS;
			break;
		}
#ifdef	MODELD
		}
#endif	/* MODELD */
#ifdef	B1BUG21
		/*
		 * If process using the FPU got a reserved-space access error,
		 * assume this is B-1 Errata # 21 (386 generated a 387 IO
		 * reference with address bit 31 off).  Recover from this in
		 * hardware, and give the process a SIGFPE.
		 */
#ifdef	MODELD
		if (!sgs2 && l.usingfpu &&
#else	/* !MODELD */
		if (l.usingfpu &&
#endif	/* MODELD */
		    (rdslave(my_slic, PROC_FLT) & PROC_FLT_ACC_ERR) == 0) {
			recover_bug_21(p);
			u.u_code |= FPE_B1_BUG21;
			i = SIGFPE;
			break;
		}
#endif	/* B1BUG21  */
		/* fall into... */
	case T_NMI:				/* NMI from kernel mode */
#ifdef	B1BUG21
		/*
		 * Similar to user case, if processor isn't idle, was using the
		 * FPU, and got a reserved-space access error, assume this is
		 * B-1 Errata # 21 (386 generated a 387 IO reference with
		 * address bit 31 off).  Recover from this in hardware, and
		 * give the process a SIGFPE.
		 *
		 * Have test here in case process managed to enter kernel
		 * before taking NMI (eg, thru single-step trap).
		 */
#ifdef	MODELD
		if (!sgs2 && !l.noproc && l.usingfpu &&
#else	/* !MODELD */
		if (!l.noproc && l.usingfpu &&
#endif	/* MODELD */
		    (rdslave(l.eng->e_slicaddr, PROC_FLT) & PROC_FLT_ACC_ERR) == 0) {
			recover_bug_21(p);
			u.u_code = (T_NMI<<16) | FPE_B1_BUG21;
			psignal(p, SIGFPE);
			return;
		}
#endif	/* B1BUG21  */
		/* Disable SLIC interrupts. Already off at
		 * proc and donmi() turns off NMI.
		 */
		(void) splhi();
		
#ifdef	DEBUG
		printf("Kernel NMI eip=0x%x\n", eip);
#endif	/* DEBUG  */
		donmi();
		goto out;

	case T_INT3+T_USER:	 		/* single-byte intr (bkpt) */
		/*
		 * report exec of /etc/init failure
		 */
		u.u_code = (T_INT3+T_USER) << 16;
		u.u_segvcode = eip;	/* pc when faulted */
		if (p == &proc[1] && v.ve_proc == &proc[3]) {
			cmn_err(CE_WARN, "exec of /etc/init failed\n");
			/*
			 *+ The kernel couldn't exec the program /etc/init,
			 *+ which is required to bring the system up.
			 */
			return_fw();
		}
		--eip;				/* point at INT3 again */
		flags &= ~FLAGS_TF;
		i = SIGTRAP;
		break;

	case T_INTO+T_USER:			/* interrupt on overflow */
		u.u_code = ((T_INTO+T_USER) << 16) | FPE_INTOVF_TRAP;
		u.u_segvcode = eip;	/* pc when faulted */
		i = SIGFPE;
		break;

	case T_CHECK+T_USER:			/* array bounds check */
		u.u_code = ((T_CHECK+T_USER) << 16) | SEGV_ARRAY_FAULT;
		u.u_segvcode = eip;	/* pc when faulted */
		i = SIGSEGV;
		break;

	case T_UND+T_USER:			/* undefined/illegal op-code */
#ifdef LOCKBUG
		/*
		 * check to see if this was a lock instuction.
		 */
		if (fubyte((char *)eip) == 0xf0) {
			if (ok2lock(eip)) {
				/*
				 * wasn't an illegal instruction
				 * so fault in the next page.
				 */
				if (u.u_segvcode != eip) {
					u.u_segvcode = eip;
					(void)fubyte((char *)(eip+15));
					goto out;
				}
			}
		}
#endif /* LOCKBUG */
		u.u_code = ((T_UND+T_USER) << 16) | ILL_RESOP_FAULT;
		u.u_segvcode = eip;	/* pc when faulted */
		i = SIGILL;
		break;
#ifdef	EMULA_FPA
	case T_DNA:
		ASSERT(l.eng->e_flags & E_FPU387, "no i387 for FPA emulation");
		/*
		 *+ FPA emulation requires an i387 in order to operate
		 *+ correctly.  An FPA operation was attempted on a system
		 *+ with no i387s present.
		 */
		ASSERT_DEBUG(!l.usingfpu, "trap: kernel T_DNA usingfpu");
		restore_fpu();
		goto out;
#endif	/* EMULA_FPA */

	case T_DNA+T_USER:		 	/* device not available (FPU) */
		/*
		 * Process quits using FPU at each context switch.
		 * This speeds up context switch for FPU users when there
		 * is no use of FPU between switches, at the expense of up
		 * to an additional trap per dispatch of an FPU using process.
		 */
		u.u_code = (T_DNA+T_USER) << 16;
		if (l.eng->e_flags & E_FPU387) {	/* if 387... */
			ASSERT_DEBUG(!l.usingfpu, "trap: T_DNA usingfpu");
			restore_fpu();			/* turn it back on */
			goto out;
		} else {
#ifdef EMULA_387
			/*
			 * no fpu or fpa so emulate.
			 */
			int	tos;

			tos = (u.u_fpusave.fpu_status & FPE_SW_TOP) >> 
					FPE_SW_TOS_SHFT;
			
			/* 
			 * Change the u-area save area to the
			 * emulator's view of the stack
			 */
			if (tos) {
				ovbcopy((char *)u.u_fpusave.fpu_stack[0],
				        (char *)u.u_fpusave.fpu_stack[tos], 
				        (unsigned)(8 - tos) * 10);
			}
			
			switch (emula_387(&u.u_ar0[EDI], &eip, cs, 
					   flags, sp, ss)) {
			case FPE_EM_OK:
				tos = (u.u_fpusave.fpu_status & 
					FPE_SW_TOP) >> 
					FPE_SW_TOS_SHFT;
				/*
				 * Change from the emulator's view of the
				 * stack to the kernel's view of the save
				 * area
				 */
				if (tos) {
					bcopy((char *)u.u_fpusave.fpu_stack[tos], 
					      (char *)u.u_fpusave.fpu_stack[0], 
					      (unsigned)(8 - tos) * 10);
				}
				if (flags & FLAGS_TF) {
					flags &= ~FLAGS_TF;
					i = SIGTRAP;
					break;
				} else {
					goto out;
				}
			case FPE_EM_ERR:
#ifdef DEBUG
				if (emuladebug) {
					printf("emulator detected a floating point error\n");
				}
#endif
				u.u_code |= FPE_EM_ERR;
				u.u_segvcode = (u.u_fpusave.fpu_status&FSRTT)
				 &~ (u.u_fpusave.fpu_control & FSRTM);
				i = SIGFPE;
				if (u.u_ptype != BCS_PROC)
					/* set eip to be pc of floating point */
					eip = u.u_fpusave.fpu_ip;
				break;
			case FPE_EM_DNA:
#ifdef DEBUG
				if (emuladebug) {
					printf("There is no 387 chip and no emulator\n");
				}
#endif
				u.u_code |= FPE_COP_DNA;
				i = SIGFPE;
				break;
			case FPE_EM_SEG:
#ifdef DEBUG
				if (emuladebug) {
					printf("The emulator detected a segmentation violation\n");
				}
#endif
				u.u_code |= SEGV_PAGE_FAULT;
				i = SIGSEGV;
				break;
			}
		}
		break;
#else /* !MBF */
			cmn_err(CE_PANIC, "trap:  no i387 on board!");
			/*
			 *+ A CPU has taken a DNA trap (device not 
			 *+ available).  A problem exists with the 80387
			 *+ floating point coprocessor, or it has been
			 *+ been disconfigured and no emulator package
			 *+ is present.
			 */
	      	}
#endif /* MBF */

	case T_STKFLT+T_USER:			/* stack fault */
		u.u_code = ((T_STKFLT+T_USER) << 16) | SEGV_STACK_FAULT;
		u.u_segvcode = eip;	/* pc when faulted */
		i = SIGSEGV;
		break;

	case T_GPFLT+T_USER:			/* general protection fault */
		/*
		 * In Dynix, these typically only happen when executing
		 * privileged instructions (l.trap_err_code == 0), or by
		 * violating segment limits or otherwise mis-using
		 * segmentation (l.trap_err_code == selector).
		 *
		 * Unfortunately, segment limits violation gives error code
		 * == 0, so this can't be distinguished from an illegal
		 * user mode instruction.
		 *
		 * When/if support Virtual-Mode 8086, some additional fuss
		 * goes here.
		 */
#ifdef LOCKBUG
		/*
		 * check to see if this was a lock instuction.
		 */
		if (fubyte((char *)eip) == 0xf0) {
			if (ok2lock(eip)) {
				/*
				 * wasn't an illegal instruction
				 * so fault in the next page.
				 */
				if (u.u_segvcode != eip) {
					u.u_segvcode = eip;
					(void)fubyte((char *)(eip+15));
					goto out;
				}
			}
		}
#endif /* LOCKBUG */
#ifdef	DEBUG
		if (traptrace > 1) dumpUPT(p);
#endif	/* DEBUG  */
		u.u_code = ((T_GPFLT+T_USER) << 16) | SEGV_PRIVIN_FAULT;
		u.u_segvcode = eip;	/* pc when faulted */
		i = SIGSEGV;
		break;

	case T_COPERR+T_USER:			/* co-processor error (FPU) */
		ASSERT(l.usingfpu, "trap: !usingfpu");
		/*
		 *+ The processor took a coprocessor error trap and the
		 *+ kernel's data structures indicated that this processor
		 *+ was not using the coprocessor.
		 */
		save_fpu();			/* save state */
		disable_fpu();			/* turn off in current process*/
		u.u_segvcode =  (u.u_fpusave.fpu_status & FSRTT)
			 &~ (u.u_fpusave.fpu_control & FSRTM);
#ifdef	DEBUG
		if (traptrace) dump_fpu(&u.u_fpusave);
#endif	/* DEBUG  */
		u.u_code = ((T_COPERR+T_USER) << 16) | FPE_COP_FAULT;
		i = SIGFPE;
		if (u.u_ptype != BCS_PROC)
			/* set segvcode to be pc of floating point */
			eip = u.u_fpusave.fpu_ip;
		break;

	case T_SWTCH+T_USER:			/* think about context switch */
		/*
		 * Check SW traps -- reschedule and SW traps come here.
		 * l.runrun==-1 ==> SW traps only; l.runrun==1 ==> maybe both.
		 * This relies on the "&=" below being one instruction to
		 * avoid reentrancy problems (interrupt procedure setting
		 * another bit could "race"); can only happen with SWTON()
		 * calls in interrupts from kernel mode.
		 * Do not set u.u_code as this SW tarp could come from
		 * fpa_trap that has already set u.u-code.
		 */
		while (u.u_swtrap) {
			i = ffs((int)u.u_swtrap) - 1;
			u.u_swtrap &= ~(1 << i);
			(*swtrapvec[i])();
		}
		/*
		 * If only scheduled for SW traps and not reschedule,
		 * avoid a reschedule.  Need to avoid race with interrupts
		 * which might set l.runrun=1.
		 */
		DISABLE();
		if (l.runrun == -1)
			l.runrun = 0;
		ENABLE();
		goto out;

	case T_PGFLT:				/* page fault in kernel mode */
		++l.cnt.v_faults;		/* count the fault */
		/*
		 * Chip bug (thru at least B1's) makes page-fault error code
		 * unreliable; thus locore.s (t_pgflt) puts faulting address
		 * in l.trap_err_code.  This assumes pagein() can't fault.
		 */
		fltaddr = l.trap_err_code;	/* faulting address */
#ifdef	DEBUG
		if (abtdebug)
			printf("<%d,%s>: KPF fltaddr=0x%x, eip=0x%x\n",
					p-proc, u.u_comm, fltaddr, eip);
#endif	/* DEBUG  */

		/*
		 * If faulting address is in the self-mapped page-table,
		 * user gave a bogus address for useracc/copyin, etc.
		 */

		if (fltaddr - (VA_PT + KSOFF) < (unsigned)SZ_FULL_PT) {
			ASSERT(u.u_fltaddr != 0, "trap: PT null u_fltaddr");
			/*
			 *+ The kernel made a reference to an invalid address
			 *+ through a function that wasn't expecting to access
			 *+ nonkernel space.  Functions that refer to addresses
			 *+ provided by user processes protect themselves
			 *+ against such faults.
			 */
			eip = u.u_fltaddr;
#ifdef	DEBUG
			if (traptrace)
				printf("trap: error kernel PT fault\n");
#endif	/* DEBUG  */
			return;
		}

		/*
		 * If it's in user address space, try to page it in.
		 * Else try to grow.
		 * Else fail whatever was going on.
		 *
		 * pagein() checks for valid/read-only or PG_INVAL pte
		 * (since PFEC unreliable).
		 */

		if (fltaddr - USOFF < (unsigned)(USER_SPACE-ctob(UPAGES))) {
			if (pagein(fltaddr-USOFF) || grow(fltaddr-USOFF))
				return;
			ASSERT(u.u_fltaddr != 0, "trap: User null u_fltaddr");
			/*
			 *+ The kernel made a reference to an invalid address
			 *+ through a function that wasn't expecting to access
			 *+ nonkernel space.  Functions that refer to addresses
			 *+ provided by user processes protect themselves
			 *+ against such faults.
			 */
			eip = u.u_fltaddr;
#ifdef	DEBUG
			if (traptrace)
				printf("trap: error kernel user-space fault\n");
#endif	/* DEBUG  */
			return;
		}

		/*
		 * If it's a ref to FPA space:
		 *	if process isn't FPA user, make it one.
		 *	else, fail the request.
		 */

		if (fltaddr - (VA_FPA+FPA_START+KSOFF) < (unsigned)FPA_SIZE) {
			/*
			 * If process has used FPA before or can turn it on,
			 * just return.  Else error.
			 *
			 * Can fail to turn on FPA if none in the system.
			 */
			if (p->p_fpa || enable_fpa())
				restore_fpa();
			else {
#ifdef	EMULA_FPA
				/*
				 * It is known that the kernel will not
				 * execute any FPA instructions unless it
				 * is known that the processor has an FPA
				 * (as in t_fpa).  Thus, no emulation is being
				 * provided here.
				 */
#endif	/* EMULA_FPA */
				ASSERT(u.u_fltaddr != 0, "trap: FPA null u_fltaddr");
				/*
				 *+ The kernel made a reference to an invalid address
				 *+ through a function that wasn't expecting to access
				 *+ nonkernel space.  Functions that refer to addresses
				 *+ provided by user processes protect themselves
				 *+ against such faults.
			 	*/
				eip = u.u_fltaddr;
			}
			return;
		}

		/*
		 * None of the above ==> kernel made a mistake.  Oh, well...
		 */

		cmn_err(CE_CONT, "MMU fault: fault address = 0x%x\n", fltaddr);
		cmn_err(CE_CONT, "Page-Table Root = 0x%x\n", READ_PTROOT());
		trap_dumpregs((int *)&eax);
		cmn_err(CE_PANIC, "MMU Fault");
		/*
		 *+ The system took a page fault while executing in the
		 *+ kernel.  The faulting address was not a valid
		 *+ reference to user text, data, stack or FPA space.
		 */
		/*NOTREACHED*/

	case T_PGFLT+T_USER:			/* page fault from user mode */
		++l.cnt.v_faults;		/* count the fault */
		/*
		 * Chip bug (thru at least B1's) makes page-fault error code
		 * unreliable; thus locore.s (t_pgflt) puts faulting address
		 * in l.trap_err_code.  This assumes pagein() can't fault.
		 */
		fltaddr = l.trap_err_code;	/* faulting address */
#ifdef	DEBUG
		if (abtdebug)
			printf("<%d,%s>: UPF fltaddr=0x%x, eip=0x%x\n",
					p-proc, u.u_comm, fltaddr, eip);
#endif	/* DEBUG  */
		/*
		 * If it's in user address space, try to page it in.
		 * Else try to grow.
		 *
		 * pagein() checks for valid/read-only or PG_INVAL pte
		 * (since PFEC unreliable).
		 */

		if (fltaddr - USOFF < (unsigned)(USER_SPACE-ctob(UPAGES))
		&& (pagein(fltaddr-USOFF) || grow(fltaddr-USOFF)))
			goto out;

		/*
		 * If it's a ref to FPA space:
		 *	if process isn't FPA user, make it one.
		 *	else, SIGSEGV.
		 */

		u.u_code = (T_PGFLT+T_USER) << 16;
		if (fltaddr - (VA_FPA+FPA_START+KSOFF) < (unsigned)FPA_SIZE) {
#ifdef	EMULA_FPA
			switch (p->p_fpa) {
			case FPA_NONE:
				if (enable_fpa()) {
					restore_fpa();
					goto out;
				}
				/*
				 * May race with online of an FPA, however
				 * will be back on an FPA next fault.
				 */
				p->p_fpa = FPA_SW;
				fpa_init();
				emula_fpa_hw2sw();
				break;

			case FPA_HW:
				if (l.fpa) {
					/*
					 * No race with offline, as
					 * p_fpa == FPA_HW implies that
					 * NFPAproc has been incremented.
					 */
					restore_fpa();
					goto out;
				}
				/*
				 * Must have affinitied to a processor which
				 * doesn't have an FPA or we've taken the last
				 * FPA offline (shouldn't happen, but this
				 * code should handle it).
				 * May race with bringing an FPA back online,
				 * however, the next fault, we'll discover
				 * this and switch back to hardware.
				 */
				deref_fpa();
				p->p_fpa = FPA_SW;
				emula_fpa_hw2sw();
				break;

			case FPA_SW:
				if (reenable_fpa()) {
					/*
					 * No reason we can't be on a processor
					 * with an FPA.  May have just turned
					 * off affinity or had an FPA processor
					 * come online.
					 */
					restore_fpa();
					goto out;
				}
				break;

			case FPA_FRCSW:
			default:
				/*
				 * We want the emulation (probably for testing).
				 */
				break;
			}

			/*
			 * Either there is no fpa on the system, or the
			 * process is affinitied to a processor which does
			 * not have an fpa.  Need to emulate the instruction.
			 */
			switch (emula_fpa(fltaddr)) {
			case FPA_EM_OK:
				goto out;
			case FPA_EM_EXCEPTION:
				/*
				 * Some sort of unmasked exception was
				 * generated.
				 */
				emula_fpa_trap();
				i = SIGFPE;
				break;
			case FPA_EM_SEG:
				/*
				 * Encountered some sort of error when
				 * accessing memory; send the process a
				 * segmentation violation.
				 */
				u.u_code |= SEGV_PAGE_FAULT;
				i = SIGSEGV;
				break;
			case FPA_EM_ILLINSTR:
			default:
				/*
				 * Emulation didn't understand the instruction
				 * which was being used to access the FPA.
				 * Seems best to indicate this by indicating
				 * there is no emulation to the program
				 * and shooting it down with a SIGFPE.  This
				 * is more likely to indicate that the failure
				 * was due to the emulation rather than the
				 * program.
				 */
				u.u_code |= FPE_NOFPA_AVAIL;
				i = SIGFPE;
				break;
			}
			goto sig;
#else	/* !EMULA_FPA */
			/*
			 * If process has used FPA before or can turn it
			 * on, restore context and return.  Else SIGFPE.
			 *
			 * Can fail to turn on FPA if none in the system.
			 */

			if (p->p_fpa || enable_fpa()) {
				restore_fpa();
				goto out;
			} else {
				u.u_code |= FPE_NOFPA_AVAIL;
				i = SIGFPE;
				break;
			}
#endif	/* EMULA_FPA */
		}

		u.u_segvcode = fltaddr - USOFF;
		u.u_code |= SEGV_PAGE_FAULT;
		i = SIGSEGV;
		break;
	}
#ifdef	DEBUG
	if (traptrace) {
		type &= ~T_USER;
		printf("<%d,%s>: trap type %d (%s) signal %d u_code 0x%x, ip=0x%x, flags=0x%x\n",
			p-proc, u.u_comm, type,
			(type < TRAP_TYPES) ? trap_type[type] : "unknown",
			i, u.u_code, eip, flags);
	}
#endif	/* DEBUG  */
#ifdef	EMULA_FPA
sig:
#endif	/* EMULA_FPA */
	psignal(p, i);
out:
	/*
	 * If necessary, swap self.
	 * Do before signal/qswtch to decrease latency for swapper.
	 */

	if (p->p_flag & SFSWAP)
		(void) swapout(p, (size_t) p->p_dsize, (size_t) p->p_ssize);

	/*
	 * Pre-emptive context switch?
	 */

	if (l.runrun) {
		ASSERT_DEBUG(USERMODE(cs), "trap: !user redispatch");
		p->p_pri = p->p_usrpri;
		qswtch();
	}

	/*
	 * if (p->p_cursig || ISSIG(p))
	 *	psig();
	 *
	 * No need to lock state to test p_sig.
	 */

	if (p->p_cursig)
		psig();
	else if (QUEUEDSIG(p)) {
		p->p_pri = p->p_usrpri;			/* in case stop */
		i = p_lock(&p->p_state, SPLHI);
		if (issig((lock_t *)NULL) > 0) {
			v_lock(&p->p_state, i);
			psig();
		} else
			v_lock(&p->p_state, i);
	}


	if (u.u_prof.pr_scale) {
		timeval_t stime;

		DISABLE();
		stime = u.u_pst.ps_stime;
		ENABLE();
		/* calculate number of ticks in system */
		i = ((stime.tv_sec - u.u_syst.tv_sec) * 1000 +
			(stime.tv_usec - u.u_syst.tv_usec) / 1000) / (tick / 1000);
		if (i)
			addupc((int)eip, &u.u_prof, i);
	}

	/*
	 * Going back to user mode so fix p_pri, and e_pri (process may have
	 * blocked at higher priority while handling the trap).
	 */

	p->p_pri = p->p_usrpri;
	l.eng->e_pri = p->p_pri;
	if (arch_type != SYSTYP_S1)
		SLICPRI(l.eng->e_pri>>2);

}

/*
 * syscall()
 *	Called from the trap handler when a system call occurs
 *
 * Arguments are already copied into u.u_arg[] by syscall entries
 * (see machine/locore.s).
 *
 * See machine/reg.h for order of parameters.
 */

/*ARGSUSED*/
syscall(edi, esi, ebp, unused, ebx, edx, ecx, eax, eip, cs, flags, sp, ss)
	volatile unsigned ss;	/* only if inter-segment (user->kernel) */
	volatile unsigned sp;	/* only if inter-segment (user->kernel) */
	volatile unsigned flags;/* extended-flags (see machine/psl.h) */
	volatile unsigned cs;	/* sense user-mode entry from RPL field */
	volatile unsigned eip;	/* return context */
	volatile unsigned eax;	/* syscall #, 1st return value */
	volatile unsigned ecx;	/* 2nd return value */
	volatile unsigned edx;	/* more such */
	volatile unsigned ebx;	/* register variable */
	volatile unsigned unused;/* temp SP (from push-all instruction) */
	volatile unsigned ebp;	/* of interrupted frame */
	volatile unsigned esi;	/* register variable */
	volatile unsigned edi;	/* register variable */
{
	register struct proc *p;
	int	syscall_num = SYS_IDX(eax);	/* get syscall number */
	int	syscall_typ = SYS_TBL(eax);	/* get syscall table */
	struct sysent *sysent = syscall_handler[syscall_typ].sysentp;
	int	nsysent = *(syscall_handler[syscall_typ].nsysentp);


	/*
	 * Always sample ru_stime, since syscall may be SYS_profil.
	 */

	u.u_syst = u.u_pst.ps_stime;
	l.cnt.v_syscall++;
	u.u_ar0 = (int *) &eax;
	u.u_error = 0;
	u.u_code = eax << 8;		/* for SIGSYS trap */
	u.u_segvcode = eip;		/* for SIGSYS trap */

	/*
	 * Arguments are already in place in u.u_arg[], via syscall entry.
	 */

	u.u_r.r_val1 = 0;

	/*
	 * Set up signal return and do sys call.
	 * Signal wakeup will return here unless changed.
	 * Can alter to RESTARTSYS, etc.
	 *
	 * NOTE: fsetjmp() doesn't save registers.
	 * Note that stacked EIP could change if process stopped in debugger,
	 * debugger altered EIP, then process takes signal -- in this case,
	 * saving entry EIP doesn't help.
	 */

	flags &= ~FLAGS_CF;			/* no error! */
	if (fsetjmp(&u.u_qsave)) {		/* return from signal */
		if (u.u_error == 0)
			u.u_error = EINTR;
	} else if (syscall_num >= nsysent) {	/* bad syscall number */
#ifdef	DEBUG
		if (syscalltrace) {
			printf("<%d,%s>syscall[%x] - bad syscall #\n",
					u.u_procp-proc, u.u_comm, syscall_num);
		}
#endif	/* DEBUG  */
		nosys();
	} else {				/* do real syscall */
#ifdef	DEBUG
		if (syscalltrace) {
			int	i;
			printf("<%d,%s>%s(", u.u_procp-proc,
					u.u_comm, syscallnames[syscall_num]);
			for(i = 0; i < sysent[syscall_num].sy_narg; i++) {
				if (i > 0)
					printf(",");
				printf("%x", u.u_arg[i]);
			}
			printf(") ");
		}
#endif	/* DEBUG  */
		u.u_pst.ps_syscall++;
		(*(sysent[syscall_num].sy_call))();
	}

	/*
	 * Returned from syscall.  Set up return to user.
	 */

	if (u.u_error) {
		eax = u.u_error;		/* error # */
		flags |= FLAGS_CF;		/* error indicator */
	} else {
		eax = u.u_r.r_val1;		/* 1st return value */
		ecx = u.u_r.r_val2;		/* maybe garbage */
#ifdef	lint
		lint_ref_int((int)ecx);
#endif	/* lint  */
	}

	/*
	 * If system call referenced a file-table entry in a shared open file
	 * table, deref it here.
	 */
	if (u.u_fpref) {
		deref_file(u.u_fpref);
		u.u_fpref = NULL;
	}

	/*
	 * Done.  See about swapping/switching/etc & return.
	 *
	 * Check swap before signal/qswtch to decrease latency for swapper.
	 */

	p = u.u_procp;			/* do here in case fork/signal */

	if (p->p_flag & SFSWAP)
		(void) swapout(p, (size_t) p->p_dsize, (size_t) p->p_ssize);

	/*
	 * Pre-emptive context switch?
	 */

	if (l.runrun) {
		p->p_pri = p->p_usrpri;
		qswtch();
	}

	/*
	 * if (p->p_cursig || ISSIG(p))
	 *	psig();
	 *
	 * No need to lock state to test p_sig.
	 */

	if (p->p_cursig)
		psig();
	else if (QUEUEDSIG(p)) {
		p->p_pri = p->p_usrpri;			/* in case stop */
		(void) p_lock(&p->p_state, SPLHI);
		if (issig((lock_t *)NULL) > 0) {
			v_lock(&p->p_state, SPL0);
			psig();
		} else
			v_lock(&p->p_state, SPL0);
	}

	if (u.u_prof.pr_scale) {
		timeval_t stime;
		int	i;

		DISABLE();
		stime = u.u_pst.ps_stime;
		ENABLE();
		/* calculate number of ticks in system */
		i = ((stime.tv_sec - u.u_syst.tv_sec) * 1000 +
			(stime.tv_usec - u.u_syst.tv_usec) / 1000) / (tick / 1000);
		if (i)
			addupc((int)eip, &u.u_prof, i);
	}

	/*
	 * Going back to user mode so fix p->pri, and e_pri (process may have
	 * blocked at higher priority while in the system-call).
	 */

	p->p_pri = p->p_usrpri;
	l.eng->e_pri = p->p_pri;
	if (arch_type != SYSTYP_S1)
		SLICPRI(l.eng->e_pri>>2);


#ifdef	DEBUG
	if (syscalltrace) {
		if (flags & FLAGS_CF)
			printf("<%d,%s>:Error %d\n", p-proc, u.u_comm, eax);
		else
			printf("<%d,%s>:%x,%x ret to %x\n", p-proc,
				u.u_comm, u.u_r.r_val1, u.u_r.r_val2, eip);
	}
#endif	/* DEBUG  */
}

/*
 * nosys()
 *	nonexistent system call-- signal process (may want to handle it).
 *
 * Flag error if process won't see signal immediately.
 */

nosys()
{
	if (u.u_sa[SIGSYS].sa_handler == SIG_IGN
	             || (SIGM_ISMEMBER(u.u_procp->p_sigmask,sigmask(SIGSYS))))
		u.u_error = EINVAL;
	psignal(u.u_procp, SIGSYS);
}

/*
 * nosyscall()
 *	Null system-call handler for binary configuration of alternate
 *	system-call entries (primarily backwards binary compatibility).
 *
 * Print a message and exit the process.
 */

/*ARGSUSED*/
nosyscall(edi, esi, ebp, unused, ebx, edx, ecx, eax, eip, cs, flags, sp, ss)
{
	register struct proc *p = u.u_procp;

	u.u_ar0 = (int *) &eax;		/* can block in exit() for debugger */

	cmn_err(CE_NOTE,
	"pid %d command %s killed due to unsupported system call 0x%x\n",
		p->p_pid, u.u_comm, eax);
	/*
	 *+ A user process attempted to make a system call for which there is
	 *+ no system call handler.
	 */
	cmn_err(CE_TO_USER,
		"sorry, pid %d was killed due to unsupported system call\n",
		p->p_pid);
	/*
	 *+ A process made a system call to an unsupported kernel
	 *+ function.  The process has been killed.  This is an
	 *+ application software bug.
	 */
	exit(077<<8);
}

/*
 * trap_dumpregs()
 *	Dump register contents at time of kernel trap-panic.
 */

trap_dumpregs(eax)
	register int	*eax;
{
	cmn_err(CE_CONT, "flags=%x eip=%x ebp=%x ksp=%x usp=%x\n",
			eax[FLAGS], eax[EIP], eax[EBP], (&eax[SS])+1, eax[ESP]);
	cmn_err(CE_CONT, "eax=%x ebx=%x ecx=%x edx=%x esi=%x edi=%x\n",
			eax[EAX], eax[EBX], eax[ECX],
			eax[EDX], eax[ESI], eax[EDI]);
}

#ifdef	B1BUG21
/*
 * recover_bug_21()
 *	Recover the hardware from 80386 B1 Errata # 21 (see 9/1/87 Errata).
 *
 * If this problem occurs, the 386 generated a reference to the 387 with
 * address bit 31 *off* (ie, IO access to 0x000000F8 or 0x000000FC).  Hardware
 * generates an NMI, with PROC_FLT register showing "reserved space access
 * error".  There are other sources of this error, but since it's rare, assume
 * it's this problem.
 *
 * Caller insures process was using the FPU.
 *
 * Clear the access error, re-initialize the 387 (this re-sync's the 386 and
 * 387), and let the world know.
 *
 * Interrupts and NMI's are still OFF at the processor at entry.
 */

static
recover_bug_21(p)
	register struct proc *p;
{
	static	char	mesg[] = "386/387 Error Recovery;\nContact Sequent Field Service";

	wrslave(l.eng->e_slicaddr, PROC_FLT, 0xbb);	/* clear err */
	init_fpu();					/* re-init 387 */
	/* togle nmi accept bit */
	wrslave(l.eng->e_slicaddr, PROC_CTL, PROC_CTL_NO_NMI |
		PROC_CTL_NO_SSTEP | PROC_CTL_NO_HOLD | PROC_CTL_NO_RESET);
	wrslave(l.eng->e_slicaddr, PROC_CTL, PROC_CTL_NO_SSTEP |
		PROC_CTL_NO_HOLD | PROC_CTL_NO_RESET);
	enable_nmi();					/* ON int's from NMI */
	cmn_err(CE_WARN, "pid %d: killed because of %s\n", p->p_pid, mesg);
	/*
	 *+ The processor recovered from an error indicating a 386 chip bug
	 *+ (Errata # 21).  Corrective action:  contact service.
	 */
	cmn_err(CE_TO_USER, "pid %d was killed due to %s\n", p->p_pid, mesg);
	/*
	 *+ The processor recovered from an error indicating a 386 chip bug
	 *+ (Errata # 21).  Corrective action:  contact service.
	 */
}
#endif	/* B1BUG21  */

#ifdef LOCKBUG

/*
 * ok2lock() will return true if the instruction sequenece
 * is legal for a lock prefix according to the Intel 80386 Programmer's
 * Reference Manual (1988) page 17-100
 *
 * The LOCK prefix funcions only with the following instructions:
 * BT,BTS,BTR,BTC			mem,reg/imm
 * XCHG					reg,mem
 * XCHG					mem,reg
 * ADD,OR,ADC,SBB,AND,SUB,XOR		mem,reg/imm
 * NOT,NEG,INC,DEC			mem
 *
 */

#define TWO_BYTE 	0x0f	/* 2 byte instruction */
#define ADDR_PREF	0x67	/* address size prefix */
#define OPER_PREF	0x66	/* operand size prefix */
#define SEG_CS		0x2e	/* C segment overide */
#define SEG_DS		0x3e	/* D segment overide */
#define SEG_ES		0x26	/* E segment overide */
#define SEG_FS		0x64	/* F segment overide */
#define SEG_GS		0x65	/* G segment overide */
#define SEG_SS		0x36	/* S segment overide */

#define MODR_MASK	0x38		/* mask to extract MODR opcode bits */
#define MODRO		3		/* shift to MODR opcode */

#define BT_MSET		0xba		/* BT BTS BTC BTC */
#define MT_MRO_BT	(4<<MODRO)	/* BT_MSET modifier for BT */
#define MT_MRO_BTS	(5<<MODRO)	/* BT_MSET modifier for BTS */
#define MT_MRO_BTR	(6<<MODRO)	/* BT_MSET modifier for BTR */
#define MT_MRO_BTC	(7<<MODRO)	/* BT_MSET modifier for BTC */
#define BT		0xa3		/* 8bit BT */
#define BTS		0xab		/* 8bit BTS */
#define BTR		0xb3		/* 8bit BTR */
#define BTC		0xbb		/* 8bit BTC */

#define XCHG8		0x86		/* 8 bit XCHG */
#define XCHG16		0x87		/* 16 or 32 but XCHG */

#define ADD8		0x00
#define ADD16		0x01
#define OR8		0x08
#define OR16		0x09
#define ADC8		0x10
#define ADC16		0x11
#define SBB8		0x18
#define SBB16		0x19
#define AND8		0x20
#define AND16		0x21
#define SUB8		0x28
#define SUB16		0x29
#define XOR8		0x30
#define XOR16		0x31

#define ARITH_MSET_8_8	0x80		/* arithmatic set 8 - 8 */
#define ARITH_MSET_16_16	0x81	/* arithmatic set 16,32 - 16,32 */
#define ARITH_MSET_16_8	0x83		/* arithmatic set 16,32 - 8 */

#define ADD_MRO		(0<<MODRO)
#define OR_MRO		(1<<MODRO)
#define ADC_MRO		(2<<MODRO)
#define SBB_MRO		(3<<MODRO)
#define AND_MRO		(4<<MODRO)
#define SUB_MRO		(5<<MODRO)
#define XOR_MRO		(6<<MODRO)

#define MON_GROUP_1	0xf6	/* not,neg */
#define MON_GROUP_2	0xf7	/* not,neg */
#define MON_GROUP_3	0xfe	/* inc,dec */
#define MON_GROUP_4	0xff	/* inc,dec */

#define NOT_MRO		(2<<MODRO)
#define NEG_MRO		(3<<MODRO)
#define INCA_MRO	(0<<MODRO)
#define INCB_MRO	(6<<MODRO)
#define DEC_MRO		(1<<MODRO)

static int
ok2lock(eip) 
	unsigned	eip;
{	
	char	*pc;
	unsigned i;

	/* 
	 * Lock prefix error recovery
	 * look at next byte.
	 * If it is not supposed to be locked continue with
	 * the illegal instruction fault
	 * otherwise it is likely to be the chip
	 * bug, in which just restart the instruction.
	 * We hope this will resolve since we have
	 * fubyted the rest of instruction in - no
	 * guarentees so maybe cap the operation.
	 */
	pc = (char *)eip;
	for(;;) {
		pc++;
		i = fubyte(pc);
		switch (i) {
		case ADDR_PREF:
		case OPER_PREF:
		case SEG_CS:
		case SEG_DS:
		case SEG_ES:
		case SEG_FS:
		case SEG_GS:
		case SEG_SS:
			/*
			 * skip to next byte on segment overide.
			 * or address or operand size prefix.
			 */
			continue;
		case TWO_BYTE:
			pc++;
			i = fubyte(pc);
			switch (i) {
			case BT:
			case BTS:
			case BTR:
			case BTC:
				return 1;
			case BT_MSET:
				pc++;
				i = fubyte(pc);
				switch (i & MODR_MASK) {
				case MT_MRO_BT:
				case MT_MRO_BTS:
				case MT_MRO_BTR:
				case MT_MRO_BTC:
					return 1;
				}
				return 0;
			default:
				return 0;
			}
		case XCHG8:
		case XCHG16:
		case ADD8:
		case ADD16:
		case OR8:
		case OR16:
		case ADC8:
		case ADC16:
		case SBB8:
		case SBB16:
		case AND8:
		case AND16:
		case SUB8:
		case SUB16:
		case XOR8:
		case XOR16:
			return 1;
		case ARITH_MSET_8_8:
		case ARITH_MSET_16_16:
		case ARITH_MSET_16_8:
			pc++;
			i = fubyte(pc);
			switch (i & MODR_MASK) {
			case ADD_MRO:
			case OR_MRO:
			case ADC_MRO:
			case SBB_MRO:
			case AND_MRO:
			case SUB_MRO:
			case XOR_MRO:
				return 1;
			}
			return 0;
		case MON_GROUP_1:
		case MON_GROUP_2:
			pc++;
			i = fubyte(pc);
			switch (i & MODR_MASK) {
			case NOT_MRO:
			case NEG_MRO:
				return 1;
			}
			return 0;
		case MON_GROUP_3:
		case MON_GROUP_4:	
			pc++;
			i = fubyte(pc);
			switch (i & MODR_MASK) {
			case INCA_MRO:
			case INCB_MRO:
			case DEC_MRO:
				return 1;
			}
			return 0;
		default:
			return 0;
		}
	}
}
#endif /* LOCKBUG */
