1860 lines
48 KiB
C
1860 lines
48 KiB
C
/* $NetBSD: trap.c,v 1.79 1997/11/13 10:37:52 veego Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1988 University of Utah.
|
|
* Copyright (c) 1992, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* the Systems Programming Group of the University of Utah Computer
|
|
* Science Department and Ralph Campbell.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* from: Utah Hdr: trap.c 1.32 91/04/06
|
|
*
|
|
* @(#)trap.c 8.5 (Berkeley) 1/11/94
|
|
*/
|
|
|
|
#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
|
|
__KERNEL_RCSID(0, "$NetBSD: trap.c,v 1.79 1997/11/13 10:37:52 veego Exp $");
|
|
|
|
#if !defined(MIPS1) && !defined(MIPS3)
|
|
#error Neither "MIPS1" (r2000 family), "MIPS3" (r4000 family) was configured.
|
|
#endif
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/device.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/signalvar.h>
|
|
#include <sys/syscall.h>
|
|
#include <sys/user.h>
|
|
#include <sys/buf.h>
|
|
#ifdef KTRACE
|
|
#include <sys/ktrace.h>
|
|
#endif
|
|
|
|
#include <mips/locore.h>
|
|
#include <mips/mips_opcode.h>
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/vm_kern.h>
|
|
#include <vm/vm_page.h>
|
|
|
|
#include <machine/cpu.h>
|
|
#include <mips/trap.h>
|
|
#include <machine/psl.h>
|
|
#include <mips/reg.h>
|
|
#include <mips/regnum.h> /* symbolic register indices */
|
|
#include <mips/pte.h>
|
|
|
|
#include <sys/cdefs.h>
|
|
#include <sys/syslog.h>
|
|
#include <miscfs/procfs/procfs.h>
|
|
|
|
#ifdef DDB
|
|
#include <mips/db_machdep.h>
|
|
#include <ddb/db_sym.h>
|
|
#endif
|
|
|
|
/* all this to get prototypes for ipintr() and arpintr() */
|
|
#include <sys/socket.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_inarp.h>
|
|
#include <netinet/ip_var.h>
|
|
|
|
#include "ppp.h"
|
|
|
|
#if NPPP > 0
|
|
#include <net/ppp_defs.h> /* decls of struct pppstat for.. */
|
|
#include <net/if_pppvar.h> /* decl of enum for... */
|
|
#include <net/if_ppp.h> /* pppintr() prototype */
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Port-specific hardware interrupt handler
|
|
*/
|
|
|
|
int (*mips_hardware_intr) __P((unsigned mask, unsigned pc, unsigned status,
|
|
unsigned cause)) =
|
|
( int (*) __P((unsigned, unsigned, unsigned, unsigned)) ) 0;
|
|
|
|
/*
|
|
* Exception-handling functions, called via ExceptionTable from locore
|
|
*/
|
|
#ifdef MIPS1
|
|
extern void mips1_KernGenException __P((void));
|
|
extern void mips1_UserGenException __P((void));
|
|
extern void mips1_TLBMissException __P((void));
|
|
extern void mips1_SystemCall __P((void));
|
|
extern void mips1_KernIntr __P((void));
|
|
extern void mips1_UserIntr __P((void));
|
|
/* marks end of vector code */
|
|
extern void mips1_UTLBMiss __P((void));
|
|
extern void mips1_exceptionentry_end __P((void));
|
|
#endif
|
|
|
|
#ifdef MIPS3
|
|
extern void mips3_KernGenException __P((void));
|
|
extern void mips3_UserGenException __P((void));
|
|
extern void mips3_SystemCall __P((void));
|
|
extern void mips3_KernIntr __P((void));
|
|
extern void mips3_UserIntr __P((void));
|
|
extern void mips3_TLBModException __P((void));
|
|
extern void mips3_TLBMissException __P((void));
|
|
extern void mips3_TLBInvalidException __P((void));
|
|
extern void mips3_VCED __P((void));
|
|
extern void mips3_VCEI __P((void));
|
|
|
|
/* marks end of vector code */
|
|
extern void mips3_TLBMiss __P((void));
|
|
extern void mips3_exceptionentry_end __P((void));
|
|
#endif
|
|
|
|
|
|
#ifdef MIPS1
|
|
void (*mips1_ExceptionTable[]) __P((void)) = {
|
|
/*
|
|
* The kernel exception handlers.
|
|
*/
|
|
mips1_KernIntr, /* 0 external interrupt */
|
|
mips1_KernGenException, /* 1 TLB modification */
|
|
mips1_TLBMissException, /* 2 TLB miss (load or instr. fetch) */
|
|
mips1_TLBMissException, /* 3 TLB miss (store) */
|
|
mips1_KernGenException, /* 4 address error (load or I-fetch) */
|
|
mips1_KernGenException, /* 5 address error (store) */
|
|
mips1_KernGenException, /* 6 bus error (I-fetch) */
|
|
mips1_KernGenException, /* 7 bus error (load or store) */
|
|
mips1_KernGenException, /* 8 system call */
|
|
mips1_KernGenException, /* 9 breakpoint */
|
|
mips1_KernGenException, /* 10 reserved instruction */
|
|
mips1_KernGenException, /* 11 coprocessor unusable */
|
|
mips1_KernGenException, /* 12 arithmetic overflow */
|
|
mips1_KernGenException, /* 13 r4k trap excpt, r3k reserved */
|
|
mips1_KernGenException, /* 14 r4k virt coherence, r3k reserved */
|
|
mips1_KernGenException, /* 15 r4k FP exception, r3k reserved */
|
|
mips1_KernGenException, /* 16 reserved */
|
|
mips1_KernGenException, /* 17 reserved */
|
|
mips1_KernGenException, /* 18 reserved */
|
|
mips1_KernGenException, /* 19 reserved */
|
|
mips1_KernGenException, /* 20 reserved */
|
|
mips1_KernGenException, /* 21 reserved */
|
|
mips1_KernGenException, /* 22 reserved */
|
|
mips1_KernGenException, /* 23 watch exception */
|
|
mips1_KernGenException, /* 24 reserved */
|
|
mips1_KernGenException, /* 25 reserved */
|
|
mips1_KernGenException, /* 26 reserved */
|
|
mips1_KernGenException, /* 27 reserved */
|
|
mips1_KernGenException, /* 28 reserved */
|
|
mips1_KernGenException, /* 29 reserved */
|
|
mips1_KernGenException, /* 30 reserved */
|
|
mips1_KernGenException, /* 31 virt. coherence exception data */
|
|
/*
|
|
* The user exception handlers.
|
|
*/
|
|
mips1_UserIntr, /* 0 */
|
|
mips1_UserGenException, /* 1 */
|
|
mips1_UserGenException, /* 2 */
|
|
mips1_UserGenException, /* 3 */
|
|
mips1_UserGenException, /* 4 */
|
|
mips1_UserGenException, /* 5 */
|
|
mips1_UserGenException, /* 6 */
|
|
mips1_UserGenException, /* 7 */
|
|
mips1_SystemCall, /* 8 */
|
|
mips1_UserGenException, /* 9 */
|
|
mips1_UserGenException, /* 10 */
|
|
mips1_UserGenException, /* 11 */
|
|
mips1_UserGenException, /* 12 */
|
|
mips1_UserGenException, /* 13 */
|
|
mips1_UserGenException, /* 14 */
|
|
mips1_UserGenException, /* 15 */
|
|
mips1_UserGenException, /* 16 */
|
|
mips1_UserGenException, /* 17 */
|
|
mips1_UserGenException, /* 18 */
|
|
mips1_UserGenException, /* 19 */
|
|
mips1_UserGenException, /* 20 */
|
|
mips1_UserGenException, /* 21 */
|
|
mips1_UserGenException, /* 22 */
|
|
mips1_UserGenException, /* 23 */
|
|
mips1_UserGenException, /* 24 */
|
|
mips1_UserGenException, /* 25 */
|
|
mips1_UserGenException, /* 26 */
|
|
mips1_UserGenException, /* 27 */
|
|
mips1_UserGenException, /* 28 */
|
|
mips1_UserGenException, /* 29 */
|
|
mips1_UserGenException, /* 20 */
|
|
mips1_UserGenException, /* 31 */
|
|
};
|
|
#endif /* MIPS1 */
|
|
|
|
#ifdef MIPS3 /* r4000 family (mips-III cpu) */
|
|
|
|
void (*mips3_ExceptionTable[]) __P((void)) = {
|
|
/*
|
|
* The kernel exception handlers.
|
|
*/
|
|
mips3_KernIntr, /* 0 external interrupt */
|
|
mips3_KernGenException, /* 1 TLB modification */
|
|
mips3_TLBInvalidException, /* 2 TLB miss (load or instr. fetch) */
|
|
mips3_TLBInvalidException, /* 3 TLB miss (store) */
|
|
mips3_KernGenException, /* 4 address error (load or I-fetch) */
|
|
mips3_KernGenException, /* 5 address error (store) */
|
|
mips3_KernGenException, /* 6 bus error (I-fetch) */
|
|
mips3_KernGenException, /* 7 bus error (load or store) */
|
|
mips3_KernGenException, /* 8 system call */
|
|
mips3_KernGenException, /* 9 breakpoint */
|
|
mips3_KernGenException, /* 10 reserved instruction */
|
|
mips3_KernGenException, /* 11 coprocessor unusable */
|
|
mips3_KernGenException, /* 12 arithmetic overflow */
|
|
mips3_KernGenException, /* 13 r4k trap excpt, r3k reserved */
|
|
mips3_VCEI, /* 14 r4k virt coherence, r3k reserved */
|
|
mips3_KernGenException, /* 15 r4k FP exception, r3k reserved */
|
|
mips3_KernGenException, /* 16 reserved */
|
|
mips3_KernGenException, /* 17 reserved */
|
|
mips3_KernGenException, /* 18 reserved */
|
|
mips3_KernGenException, /* 19 reserved */
|
|
mips3_KernGenException, /* 20 reserved */
|
|
mips3_KernGenException, /* 21 reserved */
|
|
mips3_KernGenException, /* 22 reserved */
|
|
mips3_KernGenException, /* 23 watch exception */
|
|
mips3_KernGenException, /* 24 reserved */
|
|
mips3_KernGenException, /* 25 reserved */
|
|
mips3_KernGenException, /* 26 reserved */
|
|
mips3_KernGenException, /* 27 reserved */
|
|
mips3_KernGenException, /* 28 reserved */
|
|
mips3_KernGenException, /* 29 reserved */
|
|
mips3_KernGenException, /* 30 reserved */
|
|
mips3_VCED, /* 31 virt. coherence exception data */
|
|
/*
|
|
* The user exception handlers.
|
|
*/
|
|
mips3_UserIntr, /* 0 */
|
|
mips3_UserGenException, /* 1 */
|
|
mips3_UserGenException, /* 2 */
|
|
mips3_UserGenException, /* 3 */
|
|
mips3_UserGenException, /* 4 */
|
|
mips3_UserGenException, /* 5 */
|
|
mips3_UserGenException, /* 6 */
|
|
mips3_UserGenException, /* 7 */
|
|
mips3_SystemCall, /* 8 */
|
|
mips3_UserGenException, /* 9 */
|
|
mips3_UserGenException, /* 10 */
|
|
mips3_UserGenException, /* 11 */
|
|
mips3_UserGenException, /* 12 */
|
|
mips3_UserGenException, /* 13 */
|
|
mips3_VCEI, /* 14 */
|
|
mips3_UserGenException, /* 15 */
|
|
mips3_UserGenException, /* 16 */
|
|
mips3_UserGenException, /* 17 */
|
|
mips3_UserGenException, /* 18 */
|
|
mips3_UserGenException, /* 19 */
|
|
mips3_UserGenException, /* 20 */
|
|
mips3_UserGenException, /* 21 */
|
|
mips3_UserGenException, /* 22 */
|
|
mips3_UserGenException, /* 23 */
|
|
mips3_UserGenException, /* 24 */
|
|
mips3_UserGenException, /* 25 */
|
|
mips3_UserGenException, /* 26 */
|
|
mips3_UserGenException, /* 27 */
|
|
mips3_UserGenException, /* 28 */
|
|
mips3_UserGenException, /* 29 */
|
|
mips3_UserGenException, /* 20 */
|
|
mips3_VCED, /* 31 virt. coherence exception data */
|
|
};
|
|
#endif /* MIPS3 */
|
|
|
|
|
|
char *trap_type[] = {
|
|
"external interrupt",
|
|
"TLB modification",
|
|
"TLB miss (load or instr. fetch)",
|
|
"TLB miss (store)",
|
|
"address error (load or I-fetch)",
|
|
"address error (store)",
|
|
"bus error (I-fetch)",
|
|
"bus error (load or store)",
|
|
"system call",
|
|
"breakpoint",
|
|
"reserved instruction",
|
|
"coprocessor unusable",
|
|
"arithmetic overflow",
|
|
"r4k trap/r3k reserved 13",
|
|
"r4k virtual coherency instruction/r3k reserved 14",
|
|
"r4k floating point/ r3k reserved 15",
|
|
"reserved 16",
|
|
"reserved 17",
|
|
"reserved 18",
|
|
"reserved 19",
|
|
"reserved 20",
|
|
"reserved 21",
|
|
"reserved 22",
|
|
"r4000 watch",
|
|
"reserved 24",
|
|
"reserved 25",
|
|
"reserved 26",
|
|
"reserved 27",
|
|
"reserved 28",
|
|
"reserved 29",
|
|
"reserved 30",
|
|
"r4000 virtual coherency data",
|
|
};
|
|
extern unsigned intrcnt[];
|
|
|
|
#ifdef DEBUG
|
|
#define TRAPSIZE 10
|
|
struct trapdebug { /* trap history buffer for debugging */
|
|
u_int status;
|
|
u_int cause;
|
|
u_int vadr;
|
|
u_int pc;
|
|
u_int ra;
|
|
u_int sp;
|
|
u_int code;
|
|
} trapdebug[TRAPSIZE], *trp = trapdebug;
|
|
|
|
void trapDump __P((char * msg));
|
|
#endif /* DEBUG */
|
|
|
|
|
|
void mips1_dump_tlb __P((int, int, void (*printfn)(const char*, ...)));
|
|
void mips3_dump_tlb __P((int, int, void (*printfn)(const char*, ...)));
|
|
void mips_dump_tlb __P((int, int));
|
|
|
|
/*
|
|
* Other forward declarations.
|
|
*/
|
|
unsigned MachEmulateBranch __P((unsigned *regsPtr,
|
|
unsigned instPC,
|
|
unsigned fpcCSR,
|
|
int allowNonBranch));
|
|
|
|
struct proc *fpcurproc;
|
|
struct pcb *curpcb;
|
|
|
|
/* extern functions used but not declared elsewhere */
|
|
extern void clearsoftclock __P((void));
|
|
extern void clearsoftnet __P((void));
|
|
extern void MachFPInterrupt __P((unsigned, unsigned, unsigned, int *));
|
|
extern void switchfpregs __P((struct proc *, struct proc *));
|
|
|
|
/* only called by locore */
|
|
extern void trap __P((u_int status, u_int cause, u_int vaddr, u_int opc,
|
|
struct frame frame));
|
|
static void userret __P((struct proc *p, unsigned pc, u_quad_t sticks));
|
|
extern void syscall __P((unsigned status, unsigned cause, unsigned opc,
|
|
struct frame *frame));
|
|
extern void child_return __P((struct proc *p));
|
|
extern void interrupt __P((unsigned status, unsigned cause, unsigned pc,
|
|
struct frame *frame));
|
|
extern void ast __P((unsigned pc));
|
|
|
|
/*
|
|
* stack trace code, also useful to DDB one day
|
|
*/
|
|
#if defined(DEBUG) || defined(DDB)
|
|
int kdbpeek __P((vm_offset_t addr));
|
|
extern void stacktrace __P((void)); /*XXX*/
|
|
extern void logstacktrace __P((void)); /*XXX*/
|
|
|
|
/* extern functions printed by name in stack backtraces */
|
|
extern void idle __P((void)), cpu_switch __P(( struct proc *p));
|
|
extern void MachEmptyWriteBuffer __P((void));
|
|
extern void MachUTLBMiss __P((void));
|
|
extern void setsoftclock __P((void));
|
|
extern int main __P((void*));
|
|
extern void am7990_meminit __P((void*)); /* XXX */
|
|
extern void savectx __P((struct user *));
|
|
#endif /* DEBUG || DDB */
|
|
|
|
|
|
/*
|
|
* Index into intrcnt[], which is defined in locore
|
|
*/
|
|
typedef enum {
|
|
SOFTCLOCK_INTR = 0,
|
|
SOFTNET_INTR,
|
|
SERIAL0_INTR,
|
|
SERIAL1_INTR,
|
|
SERIAL2_INTR,
|
|
LANCE_INTR,
|
|
SCSI_INTR,
|
|
ERROR_INTR,
|
|
HARDCLOCK,
|
|
FPU_INTR,
|
|
SLOT0_INTR,
|
|
SLOT1_INTR,
|
|
SLOT2_INTR,
|
|
DTOP_INTR,
|
|
ISDN_INTR,
|
|
FLOPPY_INTR,
|
|
STRAY_INTR
|
|
} decstation_intr_t;
|
|
|
|
static void
|
|
userret(p, pc, sticks)
|
|
struct proc *p;
|
|
unsigned pc;
|
|
u_quad_t sticks;
|
|
{
|
|
int sig;
|
|
|
|
/* take pending signals */
|
|
while ((sig = CURSIG(p)) != 0)
|
|
postsig(sig);
|
|
p->p_priority = p->p_usrpri;
|
|
if (want_resched) {
|
|
int s;
|
|
/*
|
|
* Since we are curproc, a clock interrupt could
|
|
* change our priority without changing run queues
|
|
* (the running process is not kept on a run queue).
|
|
* If this happened after we setrunqueue ourselves but
|
|
* before we switch()'ed, we might not be on the queue
|
|
* indicated by our priority.
|
|
*/
|
|
s = splstatclock();
|
|
setrunqueue(p);
|
|
p->p_stats->p_ru.ru_nivcsw++;
|
|
mi_switch();
|
|
splx(s);
|
|
while ((sig = CURSIG(p)) != 0)
|
|
postsig(sig);
|
|
}
|
|
/*
|
|
* If profiling, charge system time to the trapped pc.
|
|
*/
|
|
if (p->p_flag & P_PROFIL) {
|
|
extern int psratio;
|
|
|
|
addupc_task(p, pc, (int)(p->p_sticks - sticks) * psratio);
|
|
}
|
|
curpriority = p->p_priority;
|
|
}
|
|
|
|
#define DELAYBRANCH(x) ((int)(x)<0)
|
|
/*
|
|
* Process a system call.
|
|
*
|
|
* System calls are strange beasts. They are passed the syscall number
|
|
* in v0, and the arguments in the registers (as normal). They return
|
|
* an error flag in a3 (if a3 != 0 on return, the syscall had an error),
|
|
* and the return value (if any) in v0 and possibly v1.
|
|
*/
|
|
void
|
|
syscall(status, cause, opc, frame)
|
|
unsigned status, cause, opc;
|
|
struct frame *frame;
|
|
{
|
|
struct proc *p = curproc;
|
|
u_quad_t sticks;
|
|
int args[8], rval[2], error;
|
|
size_t code, numsys, nsaved, argsiz;
|
|
struct sysent *callp;
|
|
|
|
#ifdef DEBUG
|
|
trp->status = status;
|
|
trp->cause = cause;
|
|
trp->vadr = 0;
|
|
trp->pc = opc;
|
|
trp->ra = frame->f_regs[RA];
|
|
trp->sp = frame->f_regs[SP];
|
|
trp->code = 0;
|
|
if (++trp == &trapdebug[TRAPSIZE])
|
|
trp = trapdebug;
|
|
#endif
|
|
|
|
cnt.v_syscall++;
|
|
|
|
if (status & ((CPUISMIPS3) ? MIPS_SR_INT_IE : MIPS1_SR_INT_ENA_PREV))
|
|
splx(MIPS_SR_INT_IE | (status & MIPS_HARD_INT_MASK));
|
|
|
|
sticks = p->p_sticks;
|
|
if (DELAYBRANCH(cause))
|
|
frame->f_regs[PC] = MachEmulateBranch(frame->f_regs, opc, 0, 0);
|
|
else
|
|
frame->f_regs[PC] = opc + sizeof(int);
|
|
callp = p->p_emul->e_sysent;
|
|
numsys = p->p_emul->e_nsysent;
|
|
code = frame->f_regs[V0];
|
|
switch (code) {
|
|
case SYS_syscall:
|
|
/*
|
|
* Code is first argument, followed by actual args.
|
|
*/
|
|
code = frame->f_regs[A0];
|
|
args[0] = frame->f_regs[A1];
|
|
args[1] = frame->f_regs[A2];
|
|
args[2] = frame->f_regs[A3];
|
|
nsaved = 3;
|
|
break;
|
|
case SYS___syscall:
|
|
/*
|
|
* Like syscall, but code is a quad, so as to maintain
|
|
* quad alignment for the rest of the arguments.
|
|
*/
|
|
code = frame->f_regs[A0 + _QUAD_LOWWORD];
|
|
args[0] = frame->f_regs[A2];
|
|
args[1] = frame->f_regs[A3];
|
|
nsaved = 2;
|
|
break;
|
|
default:
|
|
args[0] = frame->f_regs[A0];
|
|
args[1] = frame->f_regs[A1];
|
|
args[2] = frame->f_regs[A2];
|
|
args[3] = frame->f_regs[A3];
|
|
nsaved = 4;
|
|
break;
|
|
}
|
|
if (code >= p->p_emul->e_nsysent)
|
|
callp += p->p_emul->e_nosys;
|
|
else
|
|
callp += code;
|
|
argsiz = callp->sy_argsize / sizeof(int);
|
|
if (argsiz > nsaved) {
|
|
error = copyin(
|
|
(caddr_t)((int *)frame->f_regs[SP] + 4),
|
|
(caddr_t)(args + nsaved),
|
|
(argsiz - nsaved) * sizeof(int));
|
|
if (error)
|
|
goto bad;
|
|
}
|
|
#ifdef SYSCALL_DEBUG
|
|
scdebug_call(p, code, args);
|
|
#endif
|
|
#ifdef KTRACE
|
|
if (KTRPOINT(p, KTR_SYSCALL))
|
|
ktrsyscall(p->p_tracep, code, callp->sy_argsize, args);
|
|
#endif
|
|
rval[0] = 0;
|
|
rval[1] = frame->f_regs[V1];
|
|
#ifdef DEBUG
|
|
/* XXX save code */
|
|
#endif
|
|
error = (*callp->sy_call)(p, args, rval);
|
|
|
|
#ifdef DEBUG
|
|
/* XXX save syscall result in trapdebug */
|
|
#endif
|
|
switch (error) {
|
|
case 0:
|
|
frame->f_regs[V0] = rval[0];
|
|
frame->f_regs[V1] = rval[1];
|
|
frame->f_regs[A3] = 0;
|
|
break;
|
|
case ERESTART:
|
|
frame->f_regs[PC] = opc;
|
|
break;
|
|
case EJUSTRETURN:
|
|
break; /* nothing to do */
|
|
default:
|
|
bad:
|
|
frame->f_regs[V0] = error;
|
|
frame->f_regs[A3] = 1;
|
|
break;
|
|
}
|
|
#ifdef SYSCALL_DEBUG
|
|
scdebug_ret(p, code, error, rval);
|
|
#endif
|
|
userret(p, opc, sticks);
|
|
#ifdef KTRACE
|
|
if (KTRPOINT(p, KTR_SYSRET))
|
|
ktrsysret(p->p_tracep, code, error, rval[0]);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* fork syscall returns directly to user process via proc_trampoline,
|
|
* which will be called the very first time when child gets running.
|
|
* no more FORK_BRAINDAMAGED.
|
|
*/
|
|
void
|
|
child_return(p)
|
|
struct proc *p;
|
|
{
|
|
struct frame *frame = (struct frame *)p->p_md.md_regs;
|
|
|
|
frame->f_regs[V0] = 0;
|
|
frame->f_regs[V1] = 1;
|
|
frame->f_regs[A3] = 0;
|
|
userret(p, frame->f_regs[PC] - sizeof(int), 0); /* XXX */
|
|
#ifdef KTRACE
|
|
if (KTRPOINT(p, KTR_SYSRET))
|
|
ktrsysret(p->p_tracep, SYS_fork, 0, 0);
|
|
#endif
|
|
}
|
|
|
|
#ifdef MIPS3
|
|
#define TRAPTYPE(x) (((x) & MIPS3_CR_EXC_CODE) >> MIPS_CR_EXC_CODE_SHIFT)
|
|
#else
|
|
#define TRAPTYPE(x) (((x) & MIPS1_CR_EXC_CODE) >> MIPS_CR_EXC_CODE_SHIFT)
|
|
#endif
|
|
#define KERNLAND(x) ((int)(x) < 0)
|
|
|
|
/*
|
|
* Trap is called from locore to handle most types of processor traps.
|
|
* System calls are broken out for efficiency. MIPS can handle software
|
|
* interrupts as a part of real interrupt processing.
|
|
*/
|
|
void
|
|
trap(status, cause, vaddr, opc, frame)
|
|
u_int status;
|
|
u_int cause;
|
|
u_int vaddr;
|
|
u_int opc;
|
|
struct frame frame;
|
|
{
|
|
int type, sig;
|
|
int ucode = 0;
|
|
u_quad_t sticks = 0;
|
|
struct proc *p = curproc;
|
|
vm_prot_t ftype;
|
|
extern void fswintrberr __P((void));
|
|
|
|
#ifdef DEBUG
|
|
trp->status = status;
|
|
trp->cause = cause;
|
|
trp->vadr = vaddr;
|
|
trp->pc = opc;
|
|
trp->ra = !USERMODE(status) ? frame.f_regs[RA] : p->p_md.md_regs[RA];
|
|
trp->sp = (int)&frame;
|
|
trp->code = 0;
|
|
if (++trp == &trapdebug[TRAPSIZE])
|
|
trp = trapdebug;
|
|
#endif
|
|
cnt.v_trap++;
|
|
type = TRAPTYPE(cause);
|
|
if (USERMODE(status)) {
|
|
type |= T_USER;
|
|
sticks = p->p_sticks;
|
|
}
|
|
|
|
if (status & ((CPUISMIPS3) ? MIPS_SR_INT_IE : MIPS1_SR_INT_ENA_PREV))
|
|
splx((status & MIPS_HARD_INT_MASK) | MIPS_SR_INT_IE);
|
|
|
|
switch (type) {
|
|
default:
|
|
dopanic:
|
|
type &= ~T_USER;
|
|
if (type >= 0 && type < 32)
|
|
printf("trap: %s", trap_type[type]);
|
|
else
|
|
printf("trap: unknown %d", type);
|
|
printf(" in %s mode\n", USERMODE(status) ? "user" : "kernel");
|
|
printf("status=0x%x, cause=0x%x, pc=0x%x, v=0x%x\n",
|
|
status, cause, opc, vaddr);
|
|
printf("usp=0x%x ksp=%p\n", p->p_md.md_regs[SP], (int*)&frame);
|
|
if (curproc != NULL)
|
|
printf("pid=%d cmd=%s\n", p->p_pid, p->p_comm);
|
|
else
|
|
printf("curproc == NULL\n");
|
|
#ifdef DEBUG
|
|
stacktrace();
|
|
trapDump("trap");
|
|
#endif /* DEBUG */
|
|
panic("trap");
|
|
/*NOTREACHED*/
|
|
case T_TLB_MOD:
|
|
if (KERNLAND(vaddr)) {
|
|
pt_entry_t *pte;
|
|
unsigned entry;
|
|
vm_offset_t pa;
|
|
|
|
pte = kvtopte(vaddr);
|
|
entry = pte->pt_entry;
|
|
if (!mips_pg_v(entry) || (entry & mips_pg_m_bit())) {
|
|
panic("ktlbmod: invalid pte");
|
|
}
|
|
/*XXX MIPS3? */ if (entry & mips_pg_ro_bit()) {
|
|
/* write to read only page in the kernel */
|
|
ftype = VM_PROT_WRITE;
|
|
goto kernelfault;
|
|
}
|
|
entry |= mips_pg_m_bit();
|
|
pte->pt_entry = entry;
|
|
vaddr &= ~PGOFSET;
|
|
MachTLBUpdate(vaddr, entry);
|
|
pa = pfn_to_vad(entry);
|
|
#ifdef ATTR
|
|
pmap_attributes[atop(pa)] |= PMAP_ATTR_MOD;
|
|
#else
|
|
if (!IS_VM_PHYSADDR(pa))
|
|
panic("ktlbmod: unmanaged page");
|
|
PHYS_TO_VM_PAGE(pa)->flags &= ~PG_CLEAN;
|
|
#endif
|
|
return; /* KERN */
|
|
}
|
|
/*FALLTHROUGH*/
|
|
case T_TLB_MOD+T_USER:
|
|
{
|
|
pt_entry_t *pte;
|
|
unsigned entry;
|
|
vm_offset_t pa;
|
|
pmap_t pmap;
|
|
|
|
pmap = p->p_vmspace->vm_map.pmap;
|
|
if (!(pte = pmap_segmap(pmap, vaddr)))
|
|
panic("utlbmod: invalid segmap");
|
|
pte += (vaddr >> PGSHIFT) & (NPTEPG - 1);
|
|
entry = pte->pt_entry;
|
|
if (!mips_pg_v(entry) || (entry & mips_pg_m_bit()))
|
|
panic("utlbmod: invalid pte");
|
|
|
|
/*XXX MIPS3? */ if (entry & mips_pg_ro_bit()) {
|
|
/* write to read only page */
|
|
ftype = VM_PROT_WRITE;
|
|
goto pagefault;
|
|
}
|
|
entry |= mips_pg_m_bit();
|
|
pte->pt_entry = entry;
|
|
vaddr = (vaddr & ~PGOFSET) |
|
|
(pmap->pm_tlbpid << MIPS_TLB_PID_SHIFT);
|
|
MachTLBUpdate(vaddr, entry);
|
|
pa = pfn_to_vad(entry);
|
|
#ifdef ATTR
|
|
pmap_attributes[atop(pa)] |= PMAP_ATTR_MOD;
|
|
#else
|
|
if (!IS_VM_PHYSADDR(pa))
|
|
panic("utlbmod: unmanaged page");
|
|
PHYS_TO_VM_PAGE(pa)->flags &= ~PG_CLEAN;
|
|
#endif
|
|
if (type & T_USER)
|
|
userret(p, opc, sticks);
|
|
return; /* GEN */
|
|
}
|
|
case T_TLB_LD_MISS:
|
|
case T_TLB_ST_MISS:
|
|
ftype = (type == T_TLB_LD_MISS) ? VM_PROT_READ : VM_PROT_WRITE;
|
|
if (KERNLAND(vaddr))
|
|
goto kernelfault;
|
|
/*
|
|
* It is an error for the kernel to access user space except
|
|
* through the copyin/copyout routines.
|
|
*/
|
|
if (curpcb->pcb_onfault == NULL)
|
|
goto dopanic;
|
|
/* check for fuswintr() or suswintr() getting a page fault */
|
|
if (curpcb->pcb_onfault == (caddr_t)fswintrberr) {
|
|
frame.f_regs[PC] = (int)fswintrberr;
|
|
return; /* KERN */
|
|
}
|
|
goto pagefault;
|
|
case T_TLB_LD_MISS+T_USER:
|
|
ftype = VM_PROT_READ;
|
|
goto pagefault;
|
|
case T_TLB_ST_MISS+T_USER:
|
|
ftype = VM_PROT_WRITE;
|
|
pagefault: ;
|
|
{
|
|
vm_offset_t va;
|
|
struct vmspace *vm;
|
|
vm_map_t map;
|
|
int rv;
|
|
|
|
vm = p->p_vmspace;
|
|
map = &vm->vm_map;
|
|
va = trunc_page((vm_offset_t)vaddr);
|
|
rv = vm_fault(map, va, ftype, FALSE);
|
|
#ifdef VMFAULT_TRACE
|
|
printf(
|
|
"vm_fault(%p (pmap %p), %lx (0x%x), %d, %d) -> %d at pc %p\n",
|
|
map, vm->vm_map.pmap, va, vaddr, ftype, FALSE, rv,
|
|
(void*)opc);
|
|
#endif
|
|
/*
|
|
* If this was a stack access we keep track of the maximum
|
|
* accessed stack size. Also, if vm_fault gets a protection
|
|
* failure it is due to accessing the stack region outside
|
|
* the current limit and we need to reflect that as an access
|
|
* error.
|
|
*/
|
|
if ((caddr_t)va >= vm->vm_maxsaddr) {
|
|
if (rv == KERN_SUCCESS) {
|
|
unsigned nss;
|
|
|
|
nss = clrnd(btoc(USRSTACK-(unsigned)va));
|
|
if (nss > vm->vm_ssize)
|
|
vm->vm_ssize = nss;
|
|
}
|
|
else if (rv == KERN_PROTECTION_FAILURE)
|
|
rv = KERN_INVALID_ADDRESS;
|
|
}
|
|
if (rv == KERN_SUCCESS) {
|
|
if (type & T_USER)
|
|
userret(p, opc, sticks);
|
|
return; /* GEN */
|
|
}
|
|
if ((type & T_USER) == 0)
|
|
goto copyfault;
|
|
sig = (rv == KERN_PROTECTION_FAILURE) ? SIGBUS : SIGSEGV;
|
|
ucode = vaddr;
|
|
break; /* SIGNAL */
|
|
}
|
|
kernelfault: ;
|
|
{
|
|
vm_offset_t va;
|
|
int rv;
|
|
|
|
va = trunc_page((vm_offset_t)vaddr);
|
|
rv = vm_fault(kernel_map, va, ftype, FALSE);
|
|
if (rv == KERN_SUCCESS)
|
|
return; /* KERN */
|
|
/*FALLTHROUGH*/
|
|
}
|
|
case T_ADDR_ERR_LD: /* misaligned access */
|
|
case T_ADDR_ERR_ST: /* misaligned access */
|
|
case T_BUS_ERR_LD_ST: /* BERR asserted to cpu */
|
|
copyfault:
|
|
if (curpcb->pcb_onfault == NULL)
|
|
goto dopanic;
|
|
frame.f_regs[PC] = (int)curpcb->pcb_onfault;
|
|
return; /* KERN */
|
|
|
|
case T_ADDR_ERR_LD+T_USER: /* misaligned or kseg access */
|
|
case T_ADDR_ERR_ST+T_USER: /* misaligned or kseg access */
|
|
case T_BUS_ERR_IFETCH+T_USER: /* BERR asserted to cpu */
|
|
case T_BUS_ERR_LD_ST+T_USER: /* BERR asserted to cpu */
|
|
sig = SIGSEGV;
|
|
ucode = vaddr;
|
|
break; /* SIGNAL */
|
|
|
|
case T_BREAK:
|
|
#ifdef DDB
|
|
kdb_trap(type, (struct frame *)&frame.f_regs);
|
|
return; /* KERN */
|
|
#else
|
|
goto dopanic;
|
|
#endif
|
|
case T_BREAK+T_USER:
|
|
{
|
|
unsigned va, instr;
|
|
int rv;
|
|
|
|
/* compute address of break instruction */
|
|
va = (DELAYBRANCH(cause)) ? opc + sizeof(int) : opc;
|
|
|
|
/* read break instruction */
|
|
instr = fuiword((caddr_t)va);
|
|
#ifdef DEBUG
|
|
/*XXX*/ printf("break insn 0x%x\n", instr);
|
|
#endif
|
|
|
|
if (p->p_md.md_ss_addr != va || instr != MIPS_BREAK_SSTEP) {
|
|
sig = SIGTRAP;
|
|
break;
|
|
}
|
|
/*
|
|
* Restore original instruction and clear BP
|
|
*/
|
|
#ifndef NO_PROCFS_SUBR
|
|
rv = suiword((caddr_t)va, p->p_md.md_ss_instr);
|
|
if (rv < 0) {
|
|
vm_offset_t sa, ea;
|
|
sa = trunc_page((vm_offset_t)va);
|
|
ea = round_page((vm_offset_t)va + sizeof(int) - 1);
|
|
rv = vm_map_protect(&p->p_vmspace->vm_map,
|
|
sa, ea, VM_PROT_DEFAULT, FALSE);
|
|
if (rv == KERN_SUCCESS) {
|
|
rv = suiword((caddr_t)va, MIPS_BREAK_SSTEP);
|
|
(void)vm_map_protect(&p->p_vmspace->vm_map,
|
|
sa, ea, VM_PROT_READ|VM_PROT_EXECUTE, FALSE);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
struct uio uio;
|
|
struct iovec iov;
|
|
iov.iov_base = (caddr_t)&p->p_md.md_ss_instr;
|
|
iov.iov_len = sizeof(int);
|
|
uio.uio_iov = &iov;
|
|
uio.uio_iovcnt = 1;
|
|
uio.uio_offset = (off_t)va;
|
|
uio.uio_resid = sizeof(int);
|
|
uio.uio_segflg = UIO_SYSSPACE;
|
|
uio.uio_rw = UIO_WRITE;
|
|
uio.uio_procp = curproc;
|
|
rv = procfs_domem(p, p, NULL, &uio);
|
|
}
|
|
#endif
|
|
MachFlushCache();
|
|
|
|
if (rv < 0)
|
|
printf("Warning: can't restore instruction at 0x%x: 0x%x\n",
|
|
p->p_md.md_ss_addr, p->p_md.md_ss_instr);
|
|
p->p_md.md_ss_addr = 0;
|
|
sig = SIGTRAP;
|
|
break; /* SIGNAL */
|
|
}
|
|
case T_RES_INST+T_USER:
|
|
sig = SIGILL;
|
|
break; /* SIGNAL */
|
|
case T_COP_UNUSABLE+T_USER:
|
|
if ((cause & MIPS_CR_COP_ERR) != 0x10000000) {
|
|
sig = SIGILL; /* only FPU instructions allowed */
|
|
break; /* SIGNAL */
|
|
}
|
|
switchfpregs(fpcurproc, p);
|
|
fpcurproc = p;
|
|
p->p_md.md_regs[PS] |= MIPS_SR_COP_1_BIT;
|
|
p->p_md.md_flags |= MDP_FPUSED;
|
|
userret(p, opc, sticks);
|
|
return; /* GEN */
|
|
case T_FPE+T_USER:
|
|
MachFPInterrupt(status, cause, opc, p->p_md.md_regs);
|
|
userret(p, opc, sticks);
|
|
return; /* GEN */
|
|
case T_OVFLOW+T_USER:
|
|
sig = SIGFPE;
|
|
break; /* SIGNAL */
|
|
}
|
|
p->p_md.md_regs[CAUSE] = cause;
|
|
p->p_md.md_regs[BADVADDR] = vaddr;
|
|
trapsignal(p, sig, ucode);
|
|
if ((type & T_USER) == 0)
|
|
panic("trapsignal");
|
|
userret(p, opc, sticks);
|
|
return;
|
|
}
|
|
|
|
#include <net/netisr.h>
|
|
#include "arp.h"
|
|
#include "ppp.h"
|
|
|
|
/*
|
|
* Handle an interrupt.
|
|
* Called from MachKernIntr() or MachUserIntr()
|
|
* Note: curproc might be NULL.
|
|
*/
|
|
void
|
|
interrupt(status, cause, pc, frame)
|
|
unsigned status, cause, pc;
|
|
struct frame *frame;
|
|
{
|
|
unsigned mask;
|
|
|
|
#ifdef DEBUG
|
|
trp->status = status;
|
|
trp->cause = cause;
|
|
trp->vadr = 0;
|
|
trp->pc = pc;
|
|
trp->ra = 0;
|
|
trp->sp = /* (int)&args */ 0; /* XXX pass args in */
|
|
trp->code = 0;
|
|
if (++trp == &trapdebug[TRAPSIZE])
|
|
trp = trapdebug;
|
|
#endif
|
|
|
|
cnt.v_intr++;
|
|
mask = cause & status; /* pending interrupts & enable mask */
|
|
|
|
/* Device interrupt */
|
|
if (mips_hardware_intr)
|
|
splx((*mips_hardware_intr)(mask, pc, status, cause));
|
|
if (mask & MIPS_INT_MASK_5) {
|
|
intrcnt[FPU_INTR]++;
|
|
if (USERMODE(status))
|
|
MachFPInterrupt(status, cause, pc, frame->f_regs);
|
|
else {
|
|
printf("FPU interrupt: PC %x CR %x SR %x\n",
|
|
pc, cause, status);
|
|
}
|
|
}
|
|
|
|
/* Network software interrupt */
|
|
if ((mask & MIPS_SOFT_INT_MASK_1)
|
|
|| (netisr && (status & MIPS_SOFT_INT_MASK_1))) {
|
|
register isr;
|
|
isr = netisr; netisr = 0; /* XXX need protect? */
|
|
clearsoftnet();
|
|
cnt.v_soft++;
|
|
intrcnt[SOFTNET_INTR]++;
|
|
#ifdef INET
|
|
#if NARP > 0
|
|
if (isr & (1 << NETISR_ARP)) arpintr();
|
|
#endif
|
|
if (isr & (1 << NETISR_IP)) ipintr();
|
|
#endif
|
|
#ifdef NS
|
|
if (isr & (1 << NETISR_NS)) nsintr();
|
|
#endif
|
|
#ifdef ISO
|
|
if (isr & (1 << NETISR_ISO)) clnlintr();
|
|
#endif
|
|
#if NPPP > 0
|
|
if (isr & (1 << NETISR_PPP)) pppintr();
|
|
#endif
|
|
}
|
|
|
|
/* Software clock interrupt */
|
|
if (mask & MIPS_SOFT_INT_MASK_0) {
|
|
clearsoftclock();
|
|
cnt.v_soft++;
|
|
intrcnt[SOFTCLOCK_INTR]++;
|
|
softclock();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle asynchronous software traps.
|
|
* This is called from MachUserIntr() either to deliver signals or
|
|
* to make involuntary context switch (preemption).
|
|
*/
|
|
void
|
|
ast(pc)
|
|
unsigned pc; /* program counter where to continue */
|
|
{
|
|
struct proc *p = curproc;
|
|
|
|
cnt.v_soft++;
|
|
astpending = 0;
|
|
if (p->p_flag & P_OWEUPC) {
|
|
p->p_flag &= ~P_OWEUPC;
|
|
ADDUPROF(p);
|
|
}
|
|
userret(p, pc, p->p_sticks);
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
void
|
|
trapDump(msg)
|
|
char *msg;
|
|
{
|
|
register int i;
|
|
int s;
|
|
int cause;
|
|
|
|
s = splhigh();
|
|
printf("trapDump(%s)\n", msg);
|
|
for (i = 0; i < TRAPSIZE; i++) {
|
|
if (trp == trapdebug)
|
|
trp = &trapdebug[TRAPSIZE - 1];
|
|
else
|
|
trp--;
|
|
if (trp->cause == 0)
|
|
break;
|
|
cause = (trp->cause & ((CPUISMIPS3) ?
|
|
MIPS3_CR_EXC_CODE : MIPS1_CR_EXC_CODE));
|
|
printf("%s: ADR %x PC %x CR %x SR %x\n",
|
|
trap_type[cause >> MIPS_CR_EXC_CODE_SHIFT],
|
|
trp->vadr, trp->pc, trp->cause, trp->status);
|
|
printf(" RA %x SP %x code %d\n", trp->ra, trp->sp, trp->code);
|
|
}
|
|
#ifndef DDB
|
|
bzero(trapdebug, sizeof(trapdebug));
|
|
trp = trapdebug;
|
|
#endif
|
|
splx(s);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* forward declaration
|
|
*/
|
|
static unsigned GetBranchDest __P((InstFmt *InstPtr));
|
|
|
|
|
|
/*
|
|
* Compute destination of a branch instruction.
|
|
* XXX Compute desination of r4000 squashed branches?
|
|
*/
|
|
static unsigned
|
|
GetBranchDest(InstPtr)
|
|
InstFmt *InstPtr;
|
|
{
|
|
return ((unsigned)InstPtr + 4 + ((short)InstPtr->IType.imm << 2));
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the resulting PC as if the branch was executed.
|
|
*/
|
|
unsigned
|
|
MachEmulateBranch(regsPtr, instPC, fpcCSR, allowNonBranch)
|
|
unsigned *regsPtr;
|
|
unsigned instPC;
|
|
unsigned fpcCSR;
|
|
int allowNonBranch;
|
|
{
|
|
InstFmt inst;
|
|
unsigned retAddr;
|
|
int condition;
|
|
|
|
inst.word = (instPC < MIPS_KSEG0_START) ?
|
|
fuiword((caddr_t)instPC) : *(unsigned*)instPC;
|
|
|
|
#if 0
|
|
printf("regsPtr=%x PC=%x Inst=%x fpcCsr=%x\n", regsPtr, instPC,
|
|
inst.word, fpcCSR); /* XXX */
|
|
#endif
|
|
switch ((int)inst.JType.op) {
|
|
case OP_SPECIAL:
|
|
switch ((int)inst.RType.func) {
|
|
case OP_JR:
|
|
case OP_JALR:
|
|
retAddr = regsPtr[inst.RType.rs];
|
|
break;
|
|
|
|
default:
|
|
if (!allowNonBranch)
|
|
panic("MachEmulateBranch: Non-branch");
|
|
retAddr = instPC + 4;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case OP_BCOND:
|
|
switch ((int)inst.IType.rt) {
|
|
case OP_BLTZ:
|
|
case OP_BLTZAL:
|
|
case OP_BLTZL: /* squashed */
|
|
case OP_BLTZALL: /* squashed */
|
|
|
|
if ((int)(regsPtr[inst.RType.rs]) < 0)
|
|
retAddr = GetBranchDest((InstFmt *)instPC);
|
|
else
|
|
retAddr = instPC + 8;
|
|
break;
|
|
|
|
case OP_BGEZ:
|
|
case OP_BGEZAL:
|
|
case OP_BGEZL: /* squashed */
|
|
case OP_BGEZALL: /* squashed */
|
|
|
|
if ((int)(regsPtr[inst.RType.rs]) >= 0)
|
|
retAddr = GetBranchDest((InstFmt *)instPC);
|
|
else
|
|
retAddr = instPC + 8;
|
|
break;
|
|
|
|
default:
|
|
panic("MachEmulateBranch: Bad branch cond");
|
|
}
|
|
break;
|
|
|
|
case OP_J:
|
|
case OP_JAL:
|
|
retAddr = (inst.JType.target << 2) |
|
|
((unsigned)instPC & 0xF0000000);
|
|
break;
|
|
|
|
case OP_BEQ:
|
|
case OP_BEQL: /* squashed */
|
|
|
|
if (regsPtr[inst.RType.rs] == regsPtr[inst.RType.rt])
|
|
retAddr = GetBranchDest((InstFmt *)instPC);
|
|
else
|
|
retAddr = instPC + 8;
|
|
break;
|
|
|
|
case OP_BNE:
|
|
case OP_BNEL: /* squashed */
|
|
|
|
if (regsPtr[inst.RType.rs] != regsPtr[inst.RType.rt])
|
|
retAddr = GetBranchDest((InstFmt *)instPC);
|
|
else
|
|
retAddr = instPC + 8;
|
|
break;
|
|
|
|
case OP_BLEZ:
|
|
case OP_BLEZL: /* squashed */
|
|
|
|
if ((int)(regsPtr[inst.RType.rs]) <= 0)
|
|
retAddr = GetBranchDest((InstFmt *)instPC);
|
|
else
|
|
retAddr = instPC + 8;
|
|
break;
|
|
|
|
case OP_BGTZ:
|
|
case OP_BGTZL: /* squashed */
|
|
|
|
if ((int)(regsPtr[inst.RType.rs]) > 0)
|
|
retAddr = GetBranchDest((InstFmt *)instPC);
|
|
else
|
|
retAddr = instPC + 8;
|
|
break;
|
|
|
|
case OP_COP1:
|
|
switch (inst.RType.rs) {
|
|
case OP_BCx:
|
|
case OP_BCy:
|
|
if ((inst.RType.rt & COPz_BC_TF_MASK) == COPz_BC_TRUE)
|
|
condition = fpcCSR & MIPS_FPU_COND_BIT;
|
|
else
|
|
condition = !(fpcCSR & MIPS_FPU_COND_BIT);
|
|
if (condition)
|
|
retAddr = GetBranchDest((InstFmt *)instPC);
|
|
else
|
|
retAddr = instPC + 8;
|
|
break;
|
|
|
|
default:
|
|
if (!allowNonBranch)
|
|
panic("MachEmulateBranch: Bad coproc branch instruction");
|
|
retAddr = instPC + 4;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (!allowNonBranch)
|
|
panic("MachEmulateBranch: Non-branch instruction");
|
|
retAddr = instPC + 4;
|
|
}
|
|
#if 0
|
|
printf("Target addr=%x\n", retAddr); /* XXX */
|
|
#endif
|
|
return (retAddr);
|
|
}
|
|
|
|
|
|
/*
|
|
* This routine is called by procxmt() to single step one instruction.
|
|
* We do this by storing a break instruction after the current instruction,
|
|
* resuming execution, and then restoring the old instruction.
|
|
*/
|
|
int
|
|
mips_singlestep(p)
|
|
struct proc *p;
|
|
{
|
|
#ifdef NO_PROCFS_SUBR
|
|
struct frame *f = (struct frame *)p->p_md.md_regs;
|
|
unsigned va = 0;
|
|
int rv;
|
|
|
|
if (p->p_md.md_ss_addr) {
|
|
printf("SS %s (%d): breakpoint already set at %x (va %x)\n",
|
|
p->p_comm, p->p_pid, p->p_md.md_ss_addr, va); /* XXX */
|
|
return EFAULT;
|
|
}
|
|
if (fuiword((caddr_t)f->f_regs[PC]) != 0) /* not a NOP instruction */
|
|
va = MachEmulateBranch(f->f_regs, f->f_regs[PC],
|
|
p->p_addr->u_pcb.pcb_fpregs.r_regs[32], 1);
|
|
else
|
|
va = f->f_regs[PC] + sizeof(int);
|
|
p->p_md.md_ss_addr = va;
|
|
p->p_md.md_ss_instr = fuiword((caddr_t)va);
|
|
rv = suiword((caddr_t)va, MIPS_BREAK_SSTEP);
|
|
if (rv < 0) {
|
|
vm_offset_t sa, ea;
|
|
sa = trunc_page((vm_offset_t)va);
|
|
ea = round_page((vm_offset_t)va + sizeof(int) - 1);
|
|
rv = vm_map_protect(&p->p_vmspace->vm_map,
|
|
sa, ea, VM_PROT_DEFAULT, FALSE);
|
|
if (rv == KERN_SUCCESS) {
|
|
rv = suiword((caddr_t)va, MIPS_BREAK_SSTEP);
|
|
(void)vm_map_protect(&p->p_vmspace->vm_map,
|
|
sa, ea, VM_PROT_READ|VM_PROT_EXECUTE, FALSE);
|
|
}
|
|
}
|
|
#else
|
|
struct frame *f = (struct frame *)p->p_md.md_regs;
|
|
unsigned pc = f->f_regs[PC];
|
|
unsigned va = 0;
|
|
int rv;
|
|
int curinstr, bpinstr = MIPS_BREAK_SSTEP;
|
|
struct uio uio;
|
|
struct iovec iov;
|
|
|
|
#define FETCH_INSTRUCTION(i, va) \
|
|
iov.iov_base = (caddr_t)&(i);\
|
|
iov.iov_len = sizeof(int); \
|
|
uio.uio_iov = &iov;\
|
|
uio.uio_iovcnt = 1;\
|
|
uio.uio_offset = (off_t)(va);\
|
|
uio.uio_resid = sizeof(int);\
|
|
uio.uio_segflg = UIO_SYSSPACE;\
|
|
uio.uio_rw = UIO_READ;\
|
|
uio.uio_procp = curproc;\
|
|
rv = procfs_domem(curproc, p, NULL, &uio)
|
|
|
|
#define STORE_INSTRUCTION(i, va) \
|
|
iov.iov_base = (caddr_t)&(i);\
|
|
iov.iov_len = sizeof(int);\
|
|
uio.uio_iov = &iov;\
|
|
uio.uio_iovcnt = 1;\
|
|
uio.uio_offset = (off_t)(va);\
|
|
uio.uio_resid = sizeof(int);\
|
|
uio.uio_segflg = UIO_SYSSPACE;\
|
|
uio.uio_rw = UIO_WRITE;\
|
|
uio.uio_procp = curproc;\
|
|
rv = procfs_domem(curproc, p, NULL, &uio)
|
|
|
|
/* Fetch what's at the current location. */
|
|
FETCH_INSTRUCTION(curinstr, va);
|
|
|
|
/* compute next address after current location */
|
|
if (curinstr != 0)
|
|
va = MachEmulateBranch(f->f_regs, pc,
|
|
p->p_addr->u_pcb.pcb_fpregs.r_regs[32], 1);
|
|
else
|
|
va = pc + sizeof(int);
|
|
|
|
if (p->p_md.md_ss_addr) {
|
|
printf("SS %s (%d): breakpoint already set at %x (va %x)\n",
|
|
p->p_comm, p->p_pid, p->p_md.md_ss_addr, va); /* XXX */
|
|
return (EFAULT);
|
|
}
|
|
p->p_md.md_ss_addr = va;
|
|
|
|
/* Fetch what's at the current location. */
|
|
FETCH_INSTRUCTION(p->p_md.md_ss_instr, va);
|
|
|
|
/* Store breakpoint instruction at the "next" location now. */
|
|
STORE_INSTRUCTION(bpinstr, va);
|
|
MachFlushCache(); /* XXX memory barrier followed by flush icache? */
|
|
#endif
|
|
if (rv < 0)
|
|
return (EFAULT);
|
|
#if 0
|
|
printf("SS %s (%d): breakpoint set at %x: %x (pc %x) br %x\n",
|
|
printf("SS %s (%d): breakpoint set at %x: %x (pc %x) br %x\n",
|
|
p->p_comm, p->p_pid, p->p_md.md_ss_addr,
|
|
p->p_md.md_ss_instr, pc, fuword((caddr_t)va)); /* XXX */
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#if defined(DEBUG) || defined(DDB)
|
|
int
|
|
kdbpeek(addr)
|
|
vm_offset_t addr;
|
|
{
|
|
if (addr & 3) {
|
|
printf("kdbpeek: unaligned address %lx\n", addr);
|
|
/* We might have been called from DDB, so don\'t go there. */
|
|
stacktrace();
|
|
return (-1);
|
|
}
|
|
return (*(int *)addr);
|
|
}
|
|
|
|
#define MIPS_JR_RA 0x03e00008 /* instruction code for jr ra */
|
|
|
|
/* forward */
|
|
char *fn_name(unsigned addr);
|
|
void stacktrace_subr __P((int a0, int a1, int a2, int a3,
|
|
u_int pc, u_int sp, u_int fp, u_int ra,
|
|
void (*)(const char*, ...)));
|
|
|
|
/*
|
|
* Do a stack backtrace.
|
|
* (*printfn)() prints the output to either the system log,
|
|
* the console, or both.
|
|
*/
|
|
void
|
|
stacktrace_subr(a0, a1, a2, a3, pc, sp, fp, ra, printfn)
|
|
int a0, a1, a2, a3;
|
|
u_int pc, sp, fp, ra;
|
|
void (*printfn) __P((const char*, ...));
|
|
{
|
|
unsigned va, subr;
|
|
unsigned instr, mask;
|
|
InstFmt i;
|
|
int more, stksize;
|
|
extern char start[], edata[];
|
|
unsigned int frames = 0;
|
|
int foundframesize = 0;
|
|
|
|
/* Jump here when done with a frame, to start a new one */
|
|
loop:
|
|
/* Jump here after a nonstandard (interrupt handler) frame */
|
|
specialframe:
|
|
stksize = 0;
|
|
subr = 0;
|
|
if (frames++ > 100) {
|
|
(*printfn)("\nstackframe count exceeded\n");
|
|
/* return breaks stackframe-size heuristics with gcc -O2 */
|
|
goto finish; /*XXX*/
|
|
}
|
|
|
|
/* check for bad SP: could foul up next frame */
|
|
if (sp & 3 || sp < 0x80000000) {
|
|
(*printfn)("SP 0x%x: not in kernel\n", sp);
|
|
ra = 0;
|
|
subr = 0;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* check for PC between two entry points
|
|
*/
|
|
# define Between(x, y, z) \
|
|
( ((x) <= (y)) && ((y) < (z)) )
|
|
# define pcBetween(a,b) \
|
|
Between((unsigned)a, pc, (unsigned)b)
|
|
|
|
|
|
/* Backtraces should continue through interrupts from kernel mode */
|
|
#ifdef MIPS1 /* r2000 family (mips-I cpu) */
|
|
if (pcBetween(mips1_KernIntr, mips1_UserIntr)) {
|
|
/* NOTE: the offsets depend on the code in locore.s */
|
|
(*printfn)("r3000 KernIntr+%x: (%x, %x ,%x) -------\n",
|
|
pc-(unsigned)mips1_KernIntr, a0, a1, a2);
|
|
a0 = kdbpeek(sp + 40);
|
|
a1 = kdbpeek(sp + 44);
|
|
a2 = kdbpeek(sp + 48);
|
|
a3 = kdbpeek(sp + 52);
|
|
|
|
pc = kdbpeek(sp + 20); /* exc_pc - pc at time of exception */
|
|
ra = kdbpeek(sp + 140); /* ra at time of exception */
|
|
sp = sp + 176;
|
|
goto specialframe;
|
|
}
|
|
else if (pcBetween(mips1_KernGenException, mips1_UserGenException)) {
|
|
/* NOTE: the offsets depend on the code in locore.s */
|
|
(*printfn)("------ kernel trap+%x: (%x, %x ,%x) -------\n",
|
|
pc-(unsigned)mips1_KernGenException, a0, a1, a2);
|
|
|
|
a0 = kdbpeek(sp + 40);
|
|
a1 = kdbpeek(sp + 44);
|
|
a2 = kdbpeek(sp + 48);
|
|
a3 = kdbpeek(sp + 52);
|
|
|
|
pc = kdbpeek(sp + 172); /* exc_pc - pc at time of exception */
|
|
ra = kdbpeek(sp + 140); /* ra at time of exception */
|
|
sp = sp + 176;
|
|
goto specialframe;
|
|
}
|
|
#endif /* MIPS1 */
|
|
|
|
#ifdef MIPS3 /* r4000 family (mips-III cpu) */
|
|
if (pcBetween(mips3_KernIntr, mips3_UserIntr)) {
|
|
/* NOTE: the offsets depend on the code in locore.s */
|
|
(*printfn)("------ mips3 KernIntr+%x: (%x, %x ,%x) -------\n",
|
|
pc-(unsigned)mips3_KernIntr, a0, a1, a2);
|
|
a0 = kdbpeek(sp + 40);
|
|
a1 = kdbpeek(sp + 44);
|
|
a2 = kdbpeek(sp + 48);
|
|
a3 = kdbpeek(sp + 52);
|
|
|
|
pc = kdbpeek(sp + 20); /* exc_pc - pc at time of exception */
|
|
ra = kdbpeek(sp + 140); /* ra at time of exception */
|
|
sp = sp + 176;
|
|
goto specialframe;
|
|
}
|
|
else if (pcBetween(mips3_KernGenException, mips3_UserGenException)) {
|
|
/* NOTE: the offsets depend on the code in locore.s */
|
|
(*printfn)("------ kernel trap+%x: (%x, %x ,%x) -------\n",
|
|
pc-(unsigned)mips3_KernGenException, a0, a1, a2);
|
|
|
|
a0 = kdbpeek(sp + 40);
|
|
a1 = kdbpeek(sp + 44);
|
|
a2 = kdbpeek(sp + 48);
|
|
a3 = kdbpeek(sp + 52);
|
|
|
|
pc = kdbpeek(sp + 172); /* exc_pc - pc at time of exception */
|
|
ra = kdbpeek(sp + 140); /* ra at time of exception */
|
|
sp = sp + 176;
|
|
goto specialframe;
|
|
}
|
|
#endif /* MIPS3 */
|
|
|
|
|
|
|
|
/*
|
|
* Check for current PC in exception handler code that don't
|
|
* have a preceding "j ra" at the tail of the preceding function.
|
|
* Depends on relative ordering of functions in locore.
|
|
*/
|
|
|
|
/* XXX fixup tests after cutting and pasting in locore.S */
|
|
/* R4000 exception handlers */
|
|
|
|
#ifdef MIPS1 /* r2000 family (mips-I cpu) */
|
|
if (pcBetween(mips1_KernGenException, mips1_UserGenException))
|
|
subr = (unsigned) mips1_KernGenException;
|
|
else if (pcBetween(mips1_UserGenException,mips1_SystemCall))
|
|
subr = (unsigned) mips1_UserGenException;
|
|
else if (pcBetween(mips1_SystemCall,mips1_KernIntr))
|
|
subr = (unsigned) mips1_UserGenException;
|
|
else if (pcBetween(mips1_KernIntr, mips1_UserIntr))
|
|
subr = (unsigned) mips1_KernIntr;
|
|
else if (pcBetween(mips1_UserIntr, mips1_TLBMissException))
|
|
subr = (unsigned) mips1_UserIntr;
|
|
else if (pcBetween(mips1_UTLBMiss, mips1_exceptionentry_end)) {
|
|
(*printfn)("<<mips1 locore>>");
|
|
goto done;
|
|
}
|
|
else
|
|
#endif /* MIPS1 */
|
|
|
|
|
|
#ifdef MIPS3 /* r4000 family (mips-III cpu) */
|
|
/* R4000 exception handlers */
|
|
if (pcBetween(mips3_KernGenException, mips3_UserGenException))
|
|
subr = (unsigned) mips3_KernGenException;
|
|
else if (pcBetween(mips3_UserGenException,mips3_SystemCall))
|
|
subr = (unsigned) mips3_UserGenException;
|
|
else if (pcBetween(mips3_SystemCall,mips3_KernIntr))
|
|
subr = (unsigned) mips3_SystemCall;
|
|
else if (pcBetween(mips3_KernIntr, mips3_UserIntr))
|
|
subr = (unsigned) mips3_KernIntr;
|
|
|
|
else if (pcBetween(mips3_UserIntr, mips3_TLBInvalidException))
|
|
subr = (unsigned) mips3_UserIntr;
|
|
else if (pcBetween(mips3_TLBMiss, mips3_exceptionentry_end)) {
|
|
(*printfn)("<<mips3 locore>>");
|
|
goto done;
|
|
} else
|
|
#endif /* MIPS3 */
|
|
|
|
|
|
if (pcBetween(splx, switchfpregs))
|
|
subr = (unsigned) splx;
|
|
else if (pcBetween(cpu_switch, savectx))
|
|
subr = (unsigned) cpu_switch;
|
|
else if (pcBetween(idle, cpu_switch)) {
|
|
subr = (unsigned) idle;
|
|
ra = 0;
|
|
goto done;
|
|
}
|
|
else if (pcBetween(bcopy, setrunqueue)) {
|
|
subr = (unsigned) bcopy;
|
|
}
|
|
|
|
|
|
|
|
/* Check for bad PC */
|
|
if (pc & 3 || pc < 0x80000000 || pc >= (unsigned)edata) {
|
|
(*printfn)("PC 0x%x: not in kernel space\n", pc);
|
|
ra = 0;
|
|
goto done;
|
|
}
|
|
if (!pcBetween(start, (unsigned) edata)) {
|
|
(*printfn)("PC 0x%x: not in kernel text\n", pc);
|
|
ra = 0;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Find the beginning of the current subroutine by scanning backwards
|
|
* from the current PC for the end of the previous subroutine.
|
|
*/
|
|
if (!subr) {
|
|
va = pc - sizeof(int);
|
|
while ((instr = kdbpeek(va)) != MIPS_JR_RA)
|
|
va -= sizeof(int);
|
|
va += 2 * sizeof(int); /* skip back over branch & delay slot */
|
|
/* skip over nulls which might separate .o files */
|
|
while ((instr = kdbpeek(va)) == 0)
|
|
va += sizeof(int);
|
|
subr = va;
|
|
}
|
|
|
|
/*
|
|
* Jump here for locore entry pointsn for which the preceding
|
|
* function doesn't end in "j ra"
|
|
*/
|
|
#if 0
|
|
stackscan:
|
|
#endif
|
|
/* scan forwards to find stack size and any saved registers */
|
|
stksize = 0;
|
|
more = 3;
|
|
mask = 0;
|
|
foundframesize = 0;
|
|
for (va = subr; more; va += sizeof(int),
|
|
more = (more == 3) ? 3 : more - 1) {
|
|
/* stop if hit our current position */
|
|
if (va >= pc)
|
|
break;
|
|
instr = kdbpeek(va);
|
|
i.word = instr;
|
|
switch (i.JType.op) {
|
|
case OP_SPECIAL:
|
|
switch (i.RType.func) {
|
|
case OP_JR:
|
|
case OP_JALR:
|
|
more = 2; /* stop after next instruction */
|
|
break;
|
|
|
|
case OP_SYSCALL:
|
|
case OP_BREAK:
|
|
more = 1; /* stop now */
|
|
};
|
|
break;
|
|
|
|
case OP_BCOND:
|
|
case OP_J:
|
|
case OP_JAL:
|
|
case OP_BEQ:
|
|
case OP_BNE:
|
|
case OP_BLEZ:
|
|
case OP_BGTZ:
|
|
more = 2; /* stop after next instruction */
|
|
break;
|
|
|
|
case OP_COP0:
|
|
case OP_COP1:
|
|
case OP_COP2:
|
|
case OP_COP3:
|
|
switch (i.RType.rs) {
|
|
case OP_BCx:
|
|
case OP_BCy:
|
|
more = 2; /* stop after next instruction */
|
|
};
|
|
break;
|
|
|
|
case OP_SW:
|
|
/* look for saved registers on the stack */
|
|
if (i.IType.rs != 29)
|
|
break;
|
|
/* only restore the first one */
|
|
if (mask & (1 << i.IType.rt))
|
|
break;
|
|
mask |= (1 << i.IType.rt);
|
|
switch (i.IType.rt) {
|
|
case 4: /* a0 */
|
|
a0 = kdbpeek(sp + (short)i.IType.imm);
|
|
break;
|
|
|
|
case 5: /* a1 */
|
|
a1 = kdbpeek(sp + (short)i.IType.imm);
|
|
break;
|
|
|
|
case 6: /* a2 */
|
|
a2 = kdbpeek(sp + (short)i.IType.imm);
|
|
break;
|
|
|
|
case 7: /* a3 */
|
|
a3 = kdbpeek(sp + (short)i.IType.imm);
|
|
break;
|
|
|
|
case 30: /* fp */
|
|
fp = kdbpeek(sp + (short)i.IType.imm);
|
|
break;
|
|
|
|
case 31: /* ra */
|
|
ra = kdbpeek(sp + (short)i.IType.imm);
|
|
}
|
|
break;
|
|
|
|
case OP_ADDI:
|
|
case OP_ADDIU:
|
|
/* look for stack pointer adjustment */
|
|
if (i.IType.rs != 29 || i.IType.rt != 29)
|
|
break;
|
|
/* don't count pops for mcount */
|
|
if (!foundframesize) {
|
|
stksize = - ((short)i.IType.imm);
|
|
foundframesize = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
(*printfn)("%s+%x (%x,%x,%x,%x) ra %x sz %d\n",
|
|
fn_name(subr), pc - subr, a0, a1, a2, a3, ra, stksize);
|
|
|
|
if (ra) {
|
|
if (pc == ra && stksize == 0)
|
|
(*printfn)("stacktrace: loop!\n");
|
|
else {
|
|
pc = ra;
|
|
sp += stksize;
|
|
ra = 0;
|
|
goto loop;
|
|
}
|
|
} else {
|
|
finish:
|
|
if (curproc)
|
|
(*printfn)("User-level: pid %d\n", curproc->p_pid);
|
|
else
|
|
(*printfn)("User-level: curproc NULL\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Functions ``special'' enough to print by name
|
|
*/
|
|
#ifdef __STDC__
|
|
#define Name(_fn) { (void*)_fn, # _fn }
|
|
#else
|
|
#define Name(_fn) { _fn, "_fn"}
|
|
#endif
|
|
static struct { void *addr; char *name;} names[] = {
|
|
Name(stacktrace),
|
|
Name(stacktrace_subr),
|
|
Name(main),
|
|
Name(interrupt),
|
|
Name(trap),
|
|
#ifdef pmax
|
|
Name(am7990_meminit),
|
|
#endif
|
|
|
|
#ifdef MIPS1 /* r2000 family (mips-I cpu) */
|
|
Name(mips1_KernGenException),
|
|
Name(mips1_UserGenException),
|
|
Name(mips1_SystemCall),
|
|
Name(mips1_KernIntr),
|
|
Name(mips1_UserIntr),
|
|
#endif /* MIPS1 */
|
|
|
|
#ifdef MIPS3 /* r4000 family (mips-III cpu) */
|
|
Name(mips3_KernGenException),
|
|
Name(mips3_UserGenException),
|
|
Name(mips3_SystemCall),
|
|
Name(mips3_KernIntr),
|
|
Name(mips3_UserIntr),
|
|
#endif /* MIPS3 */
|
|
|
|
Name(splx),
|
|
Name(idle),
|
|
Name(cpu_switch),
|
|
{0, 0}
|
|
};
|
|
|
|
/*
|
|
* Map a function address to a string name, if known; or a hex string.
|
|
*/
|
|
char *
|
|
fn_name(unsigned addr)
|
|
{
|
|
static char buf[17];
|
|
int i = 0;
|
|
#ifdef DDB
|
|
db_expr_t diff;
|
|
db_sym_t sym;
|
|
char *symname;
|
|
#endif
|
|
|
|
#ifdef DDB
|
|
diff = 0;
|
|
symname = NULL;
|
|
sym = db_search_symbol(addr, DB_STGY_ANY, &diff);
|
|
db_symbol_values(sym, &symname, 0);
|
|
if (symname && diff == 0)
|
|
return (symname);
|
|
#endif
|
|
for (i = 0; names[i].name; i++)
|
|
if (names[i].addr == (void*)addr)
|
|
return (names[i].name);
|
|
sprintf(buf, "%x", addr);
|
|
return (buf);
|
|
}
|
|
|
|
#endif /* DEBUG */
|
|
|
|
#ifdef MIPS3
|
|
/*
|
|
* Dump TLB contents on mips3 CPU.
|
|
* called by mips3 locore after in-kernel TLB miss.
|
|
*/
|
|
void
|
|
mips3_dump_tlb(first, last, printfn)
|
|
int first;
|
|
int last;
|
|
void (*printfn) __P((const char*, ...));
|
|
{
|
|
int tlbno;
|
|
struct tlb tlb;
|
|
extern void mips3_TLBRead(int, struct tlb *);
|
|
|
|
tlbno = first;
|
|
|
|
while(tlbno <= last) {
|
|
mips3_TLBRead(tlbno, &tlb);
|
|
if (mips_pg_v(tlb.tlb_lo0) || mips_pg_v(tlb.tlb_lo1)) {
|
|
(*printfn)("TLB %2d vad 0x%08x ", tlbno, tlb.tlb_hi);
|
|
}
|
|
else {
|
|
(*printfn)("TLB*%2d vad 0x%08x ", tlbno, tlb.tlb_hi);
|
|
}
|
|
(*printfn)("0=0x%08lx ", pfn_to_vad(tlb.tlb_lo0));
|
|
(*printfn)("%c", tlb.tlb_lo0 & mips_pg_m_bit() ? 'M' : ' ');
|
|
(*printfn)("%c", tlb.tlb_lo0 & mips_pg_global_bit() ? 'G' : ' ');
|
|
(*printfn)(" atr %x ", (tlb.tlb_lo0 >> 3) & 7);
|
|
(*printfn)("1=0x%08lx ", pfn_to_vad(tlb.tlb_lo1));
|
|
(*printfn)("%c", tlb.tlb_lo1 & mips_pg_m_bit() ? 'M' : ' ');
|
|
(*printfn)("%c", tlb.tlb_lo1 & mips_pg_global_bit() ? 'G' : ' ');
|
|
(*printfn)(" atr %x ", (tlb.tlb_lo1 >> 3) & 7);
|
|
(*printfn)(" sz=%x\n", tlb.tlb_mask);
|
|
|
|
tlbno++;
|
|
}
|
|
}
|
|
#endif /* MIPS3 */
|
|
|
|
#ifdef MIPS1
|
|
/*
|
|
* Dump mips1 TLB contents.
|
|
* called by mips3 locore after in-kernel TLB miss.
|
|
*/
|
|
void
|
|
mips1_dump_tlb(first, last, printfn)
|
|
int first;
|
|
int last;
|
|
void (*printfn) __P((const char*, ...));
|
|
{
|
|
int tlbno;
|
|
extern u_int tlbhi, tlblo;
|
|
extern void mips1_TLBRead(int);
|
|
|
|
tlbno = first;
|
|
|
|
while(tlbno <= last) {
|
|
mips1_TLBRead(tlbno);
|
|
if (mips_pg_v(tlblo)) {
|
|
(*printfn)("TLB %2d vad 0x%08x ", tlbno, tlbhi);
|
|
}
|
|
else {
|
|
(*printfn)("TLB*%2d vad 0x%08x ", tlbno, tlbhi);
|
|
}
|
|
(*printfn)("0x%08x ", tlblo & MIPS1_PG_FRAME);
|
|
(*printfn)("%c", tlblo & mips_pg_m_bit() ? 'M' : ' ');
|
|
(*printfn)("%c", tlblo & mips_pg_global_bit() ? 'G' : ' ');
|
|
(*printfn)("%c\n", tlblo & MIPS1_PG_N ? 'N' : ' ');
|
|
|
|
tlbno++;
|
|
}
|
|
}
|
|
#endif /* MIPS1 */
|
|
|
|
/*
|
|
* Dump TLB after panic.
|
|
*/
|
|
void
|
|
mips_dump_tlb(first, last)
|
|
int first;
|
|
int last;
|
|
{
|
|
if (CPUISMIPS3) {
|
|
#ifdef MIPS3
|
|
mips3_dump_tlb(first,last, printf);
|
|
#endif
|
|
} else {
|
|
#ifdef MIPS1
|
|
mips1_dump_tlb(first,last, printf);
|
|
#endif
|
|
}
|
|
}
|