955 lines
26 KiB
C
955 lines
26 KiB
C
/* $NetBSD: fault.c,v 1.13 1997/02/04 07:12:31 mark Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1994-1997 Mark Brinicombe.
|
|
* Copyright (c) 1994 Brini.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software written for Brini by Mark Brinicombe
|
|
*
|
|
* 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 Brini.
|
|
* 4. The name of the company nor the name of the author may be used to
|
|
* endorse or promote products derived from this software without specific
|
|
* prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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.
|
|
*
|
|
* RiscBSD kernel project
|
|
*
|
|
* fault.c
|
|
*
|
|
* Fault handlers
|
|
*
|
|
* Created : 28/11/94
|
|
*/
|
|
|
|
/*
|
|
* Special compilation symbols
|
|
*
|
|
* DEBUG_FAULT_CORRECTION - Add debug code used to develop the register
|
|
* correction following a data abort.
|
|
*
|
|
* CONTINUE_AFTER_SVC_PREFETCH - Do not panic following a prefetch abort
|
|
* in SVC mode. Used during developement.
|
|
*/
|
|
|
|
#define DEBUG_FAULT_CORRECTION
|
|
/*#define CONTINUE_AFTER_SVC_PREFETCH*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/user.h>
|
|
#include <sys/kernel.h>
|
|
|
|
#include <vm/vm_kern.h>
|
|
|
|
#include <machine/frame.h>
|
|
#include <machine/katelib.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/pte.h>
|
|
#include <machine/irqhandler.h>
|
|
|
|
extern int pmap_debug_level;
|
|
static int onfault_count = 0;
|
|
|
|
int pmap_modified_emulation __P((pmap_t, vm_offset_t));
|
|
int pmap_handled_emulation __P((pmap_t, vm_offset_t));
|
|
pt_entry_t *pmap_pte __P((pmap_t pmap, vm_offset_t va));
|
|
|
|
u_int disassemble __P((u_int));
|
|
int fetchuserword __P((u_int address, u_int *location));
|
|
extern char fusubailout[];
|
|
|
|
/* Abort code */
|
|
|
|
/* Define text descriptions of the different aborts */
|
|
|
|
static char *aborts[16] = {
|
|
"Write buffer fault",
|
|
"Alignment fault",
|
|
"Write buffer fault",
|
|
"Alignment fault",
|
|
"Bus error (LF section)",
|
|
"Translation fault (section)",
|
|
"Bus error (page)",
|
|
"Translation fault (page)",
|
|
"Bus error (section)",
|
|
"Domain error (section)",
|
|
"Bus error (page)",
|
|
"Domain error (page)",
|
|
"Bus error trans (L1)",
|
|
"Permission error (section)",
|
|
"Bus error trans (L2)",
|
|
"Permission error (page)"
|
|
};
|
|
|
|
/*
|
|
* void data_abort_handler(trapframe_t *frame)
|
|
*
|
|
* Abort handler called when read/write occurs at an address of
|
|
* a non existant or restricted (access permissions) memory page.
|
|
* We first need to identify the type of page fault.
|
|
*/
|
|
|
|
#define TRAP_CODE ((fault_status & 0x0f) | (fault_address & 0xfffffff0))
|
|
|
|
void
|
|
data_abort_handler(frame)
|
|
trapframe_t *frame;
|
|
{
|
|
struct proc *p;
|
|
struct pcb *pcb;
|
|
u_int fault_address;
|
|
u_int fault_status;
|
|
u_int fault_pc;
|
|
u_int fault_instruction;
|
|
u_int s;
|
|
int fault_code;
|
|
u_quad_t sticks = 0;
|
|
int saved_lr = 0;
|
|
|
|
/*
|
|
* Enable IRQ's and FIQ's (disabled by CPU on abort) if trapframe
|
|
* shows they were enabled.
|
|
*/
|
|
|
|
#ifndef BLOCK_IRQS
|
|
if (!(frame->tf_spsr & I32_bit))
|
|
enable_interrupts(I32_bit);
|
|
#endif /* BLOCK_IRQS */
|
|
|
|
/* Update vmmeter statistics */
|
|
|
|
cnt.v_trap++;
|
|
|
|
/* Get fault address and status from the CPU */
|
|
|
|
fault_address = cpu_faultaddress();
|
|
fault_status = cpu_faultstatus();
|
|
fault_pc = frame->tf_pc;
|
|
|
|
fault_instruction = ReadWord(fault_pc);
|
|
|
|
/* More debug stuff */
|
|
|
|
s = spltty();
|
|
if (pmap_debug_level >= 0) {
|
|
printf("Data abort: '%s' status = %03x address = %08x PC = %08x\n",
|
|
aborts[fault_status & 0xf], fault_status & 0xfff,
|
|
fault_address, fault_pc);
|
|
|
|
printf("Instruction @V%08x = %08x\n",
|
|
fault_pc, fault_instruction);
|
|
}
|
|
|
|
if (cpu_dataabt_fixup(frame))
|
|
panic("fixup failed\n");
|
|
|
|
(void)splx(s);
|
|
|
|
/* Extract the fault code from the fault status */
|
|
|
|
fault_code = fault_status & FAULT_TYPE_MASK;
|
|
|
|
/* Get the current proc structure or proc0 if there is none */
|
|
|
|
if ((p = curproc) == 0)
|
|
p = &proc0;
|
|
|
|
if (pmap_debug_level >= 0)
|
|
printf("fault in process %08x\n", (u_int)p);
|
|
|
|
/* can't use curpcb, as it might be NULL; and we have p in a register anyway */
|
|
|
|
pcb = &p->p_addr->u_pcb;
|
|
if (pcb == 0) {
|
|
vm_offset_t va;
|
|
|
|
va = trunc_page((vm_offset_t)fault_address);
|
|
if (pmap_handled_emulation(kernel_pmap, va))
|
|
return;
|
|
if (pmap_modified_emulation(kernel_pmap, va))
|
|
return;
|
|
printf("data_abort_handler: pc=%08x fault addr=%08x faultcode=%08x\n",
|
|
fault_pc, fault_address, fault_status);
|
|
panic("data_abort_handler: no pcb ... we're toast !\n");
|
|
}
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (pcb != curpcb) {
|
|
printf("data_abort: Alert ! pcb(%08x) != curpcb(%08x)\n", (u_int)pcb,
|
|
(u_int)curpcb);
|
|
printf("data_abort: Alert ! proc(%08x), curproc(%08x)\n", (u_int)p,
|
|
(u_int)curproc);
|
|
}
|
|
#endif /* DIAGNOSTIC */
|
|
|
|
/* fusubail is used by [fs]uswintr to avoid page faulting */
|
|
|
|
if ((pcb->pcb_onfault
|
|
&& (fault_code != FAULT_TRANS_S && fault_code != FAULT_TRANS_P))
|
|
|| pcb->pcb_onfault == fusubailout) {
|
|
copyfault:
|
|
printf("Using pcb_onfault=%08x addr=%08x st=%08x curproc=%x\n",
|
|
(u_int)pcb->pcb_onfault, fault_address, fault_status, (u_int)curproc);
|
|
frame->tf_pc = (u_int)pcb->pcb_onfault;
|
|
if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE)
|
|
panic("Yikes pcb_onfault=%08x during USR mode fault\n",
|
|
(u_int)pcb->pcb_onfault);
|
|
#ifdef VALIDATE_TRAPFRAME
|
|
validate_trapframe(frame, 1);
|
|
#endif
|
|
#ifdef PORTMASTER
|
|
++onfault_count;
|
|
if (onfault_count == 10) {
|
|
printf("Bummer: OD'ing on onfault_count\n");
|
|
#ifdef DDB
|
|
/* Debugger();*/
|
|
onfault_count = 0;
|
|
#endif /* DDB */
|
|
}
|
|
#endif /* PORTMASTER */
|
|
return;
|
|
}
|
|
|
|
/* Were we in user mode when the abort occurred ? */
|
|
|
|
if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) {
|
|
sticks = p->p_sticks;
|
|
|
|
/*
|
|
* Modify the fault_code to reflect the USR/SVC state
|
|
* at time of fault
|
|
*/
|
|
|
|
fault_code |= FAULT_USER;
|
|
p->p_md.md_regs = frame;
|
|
}
|
|
|
|
/* Now act of the fault type */
|
|
|
|
switch (fault_code) {
|
|
case FAULT_WRTBUF_0 | FAULT_USER: /* Write Buffer Fault */
|
|
case FAULT_WRTBUF_1 | FAULT_USER: /* Write Buffer Fault */
|
|
case FAULT_WRTBUF_0: /* Write Buffer Fault */
|
|
case FAULT_WRTBUF_1: /* Write Buffer Fault */
|
|
/* If this happens forget it no point in continuing */
|
|
|
|
panic("Write Buffer Fault [%d] - Halting\n", fault_code);
|
|
break;
|
|
|
|
case FAULT_ALIGN_0 | FAULT_USER: /* Alignment Fault */
|
|
case FAULT_ALIGN_1 | FAULT_USER: /* Alignment Fault */
|
|
case FAULT_ALIGN_0: /* Alignment Fault */
|
|
case FAULT_ALIGN_1: /* Alignment Fault */
|
|
|
|
/*
|
|
* Really this should just kill the process.
|
|
* Alignment faults are turned off in the kernel
|
|
* in order to get better performance from shorts with
|
|
* GCC so an alignment fault means somebody has played
|
|
* with the control register in the CPU. Might as well
|
|
* panic as the kernel was not compiled for aligned accesses.
|
|
*/
|
|
panic("Alignment fault [%d] - Halting\n", fault_code);
|
|
break;
|
|
|
|
case FAULT_BUSERR_0 | FAULT_USER: /* Bus Error LF Section */
|
|
case FAULT_BUSERR_1 | FAULT_USER: /* Bus Error Page */
|
|
case FAULT_BUSERR_2 | FAULT_USER: /* Bus Error Section */
|
|
case FAULT_BUSERR_3 | FAULT_USER: /* Bus Error Page */
|
|
case FAULT_BUSERR_0: /* Bus Error LF Section */
|
|
case FAULT_BUSERR_1: /* Bus Error Page */
|
|
case FAULT_BUSERR_2: /* Bus Error Section */
|
|
case FAULT_BUSERR_3: /* Bus Error Page */
|
|
|
|
/* What will accutally cause a bus error ? */
|
|
/* Real bus errors are not a process problem but hardware */
|
|
|
|
panic("Bus Error [%d]- Halting\n", fault_code);
|
|
break;
|
|
|
|
case FAULT_DOMAIN_S | FAULT_USER: /* Section Domain Error Fault */
|
|
case FAULT_DOMAIN_P | FAULT_USER: /* Page Domain Error Fault*/
|
|
case FAULT_DOMAIN_S: /* Section Domain Error Fault */
|
|
case FAULT_DOMAIN_P: /* Page Domain Error Fault*/
|
|
|
|
/*
|
|
* Right well we dont use domains, everything is
|
|
* always a client and thus subject to access permissions.
|
|
* If we get a domain error then we have corrupts PTE's
|
|
* so we might as well die !
|
|
* I suppose eventually this should just kill the process
|
|
* who owns the PTE's but if this happens it implies a
|
|
* kernel problem.
|
|
*/
|
|
|
|
panic("Domain Error [%d] - Halting\n", fault_code);
|
|
break;
|
|
|
|
case FAULT_PERM_P: /* Page Permission Fault*/
|
|
case FAULT_PERM_P | FAULT_USER: /* Page Permission Fault*/
|
|
/* Ok we have a permission fault in user or kernel mode */
|
|
{
|
|
register vm_offset_t va;
|
|
register struct vmspace *vm = p->p_vmspace;
|
|
register vm_map_t map;
|
|
int rv;
|
|
vm_prot_t ftype;
|
|
|
|
/*
|
|
* Ok we have a permission fault in user mode. The only cause must be
|
|
* that a read only page has been written to. This may be genuine or it
|
|
* may be a bad access. In the future it may also be cause by the software
|
|
* emulation of the modified flag.
|
|
*/
|
|
|
|
va = trunc_page((vm_offset_t)fault_address);
|
|
|
|
if (pmap_debug_level >= 0)
|
|
printf("ok we have a page permission fault - addr=V%08x ",
|
|
(u_int)va);
|
|
|
|
/*
|
|
* It is only a kernel address space fault iff:
|
|
* 1. (fault_code & FAULT_USER) == 0 and
|
|
* 2. pcb_onfault not set or
|
|
* 3. pcb_onfault set but supervisor space fault
|
|
* The last can occur during an exec() copyin where the
|
|
* argument space is lazy-allocated.
|
|
*/
|
|
|
|
if ((fault_code & FAULT_USER) == 0
|
|
&& (va >= KERNEL_BASE || va <= VM_MIN_ADDRESS)) {
|
|
/* Was the fault due to the FPE/IPKDB ? */
|
|
|
|
if ((frame->tf_spsr & PSR_MODE) == PSR_UND32_MODE) {
|
|
printf("UND32 Data abort: '%s' status = %03x address = %08x PC = %08x\n",
|
|
aborts[fault_status & 0xf], fault_status & 0xfff, fault_address,
|
|
fault_pc);
|
|
postmortem(frame);
|
|
trapsignal(p, SIGSEGV, TRAP_CODE);
|
|
goto out;
|
|
}
|
|
|
|
printf("Data abort: '%s' status = %03x address = %08x PC = %08x\n",
|
|
aborts[fault_status & 0xf], fault_status & 0xfff, fault_address,
|
|
fault_pc);
|
|
postmortem(frame);
|
|
panic("permission fault in kernel by kernel\n");
|
|
} else
|
|
map = &vm->vm_map;
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (va == 0 && map == kernel_map) {
|
|
printf("fault: bad kernel access at %x\n", (u_int)va);
|
|
goto we_re_toast;
|
|
}
|
|
#endif
|
|
|
|
if (pmap_debug_level >= 0)
|
|
printf("vmmap=%08x ", (u_int)map);
|
|
|
|
/*
|
|
* We need to know whether the page should be mapped as R or R/W.
|
|
* The MMU does not give us the info as to whether the fault was caused
|
|
* by a read or a write. This means we need to disassemble the instruction
|
|
* responcible and determine if it was a read or write instruction.
|
|
*/
|
|
|
|
ftype = VM_PROT_READ;
|
|
|
|
if ((fault_instruction & 0x0c100000) == 0x04000000)
|
|
ftype |= VM_PROT_WRITE;
|
|
else if ((fault_instruction & 0x0a100000) == 0x08000000)
|
|
ftype |= VM_PROT_WRITE;
|
|
else if ((fault_instruction & 0x0fb00ff0) == 0x01000090)
|
|
ftype |= VM_PROT_WRITE;
|
|
|
|
/* if (!(ftype & VM_PROT_WRITE)) {
|
|
panic("permission fault on a read !\n");
|
|
}*/
|
|
|
|
if (pmap_modified_emulation(map->pmap, va))
|
|
goto out;
|
|
else {
|
|
|
|
/* The page must be mapped to cause a permission fault. */
|
|
|
|
rv = vm_fault(map, va, ftype, FALSE);
|
|
if (pmap_debug_level >= 0)
|
|
printf("fault result=%d\n", rv);
|
|
if (rv == KERN_SUCCESS)
|
|
goto out;
|
|
printf("Data abort: '%s' status = %03x address = %08x PC = %08x\n",
|
|
aborts[fault_status & 0xf], fault_status & 0xfff, fault_address,
|
|
fault_pc);
|
|
postmortem(frame);
|
|
trapsignal(p, SIGSEGV, TRAP_CODE);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FAULT_PERM_S | FAULT_USER: /* Section Permission Fault */
|
|
/*
|
|
* Section permission faults should not happen often.
|
|
* Only from user processes mis-behaving
|
|
*/
|
|
printf("Data abort: '%s' status = %03x address = %08x PC = %08x\n",
|
|
aborts[fault_status & 0xf], fault_status & 0xfff, fault_address,
|
|
fault_pc);
|
|
disassemble(fault_pc);
|
|
postmortem(frame);
|
|
trapsignal(p, SIGSEGV, TRAP_CODE);
|
|
break;
|
|
|
|
case FAULT_BUSTRNL1 | FAULT_USER: /* Bus Error Trans L1 Fault */
|
|
case FAULT_BUSTRNL2 | FAULT_USER: /* Bus Error Trans L2 Fault */
|
|
case FAULT_BUSTRNL1: /* Bus Error Trans L1 Fault */
|
|
case FAULT_BUSTRNL2: /* Bus Error Trans L2 Fault */
|
|
/*
|
|
* These faults imply that the PTE is corrupt.
|
|
* Likely to be a kernel fault so we had better stop.
|
|
*/
|
|
panic("Bus Error Translation [%d] - Halting\n", fault_code);
|
|
break;
|
|
|
|
case FAULT_TRANS_P: /* Page Translation Fault */
|
|
case FAULT_TRANS_P | FAULT_USER: /* Page Translation Fault */
|
|
/* Ok page translation fault - The page does not exist */
|
|
{
|
|
register vm_offset_t va;
|
|
register struct vmspace *vm = p->p_vmspace;
|
|
register vm_map_t map;
|
|
int rv;
|
|
vm_prot_t ftype;
|
|
extern vm_map_t kernel_map;
|
|
u_int nss;
|
|
|
|
va = trunc_page((vm_offset_t)fault_address);
|
|
|
|
if (pmap_debug_level >= 0)
|
|
printf("ok we have a page fault - addr=V%08x ", (u_int)va);
|
|
|
|
/*
|
|
* It is only a kernel address space fault iff:
|
|
* 1. (fault_code & FAULT_USER) == 0 and
|
|
* 2. pcb_onfault not set or
|
|
* 3. pcb_onfault set but supervisor space fault
|
|
* The last can occur during an exec() copyin where the
|
|
* argument space is lazy-allocated.
|
|
*/
|
|
|
|
if (fault_code == FAULT_TRANS_P
|
|
&& (va >= KERNEL_BASE || va < VM_MIN_ADDRESS))
|
|
map = kernel_map;
|
|
else
|
|
map = &vm->vm_map;
|
|
|
|
if (pmap_debug_level >= 0)
|
|
printf("vmmap=%08x ", (u_int)map);
|
|
|
|
if (pmap_handled_emulation(map->pmap, va))
|
|
goto out;
|
|
|
|
/* debug_show_vm_map(map, "fault");*/
|
|
|
|
/* We need to know whether the page should be mapped as R or R/W.
|
|
* The MMU does not give us the info as to whether the fault was caused
|
|
* by a read or a write. This means we need to disassemble the instruction
|
|
* responcible and determine if it was a read or write instruction.
|
|
* For the moment we will cheat and make it read only. If it was a write
|
|
* When the instruction is re-executed we will get a permission fault
|
|
* instead.
|
|
*/
|
|
|
|
ftype = VM_PROT_READ;
|
|
|
|
/* STR instruction ? */
|
|
if ((fault_instruction & 0x0c100000) == 0x04000000)
|
|
ftype |= VM_PROT_WRITE;
|
|
/* STM instruction ? */
|
|
else if ((fault_instruction & 0x0a100000) == 0x08000000)
|
|
ftype |= VM_PROT_WRITE;
|
|
/* SWP instruction ? */
|
|
else if ((fault_instruction & 0x0fb00ff0) == 0x01000090)
|
|
ftype |= VM_PROT_WRITE;
|
|
|
|
if (pmap_debug_level >= 0)
|
|
printf("fault protection = %d\n", ftype);
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (va == 0 && map == kernel_map) {
|
|
printf("trap: bad kernel access at %x\n", (u_int)va);
|
|
goto we_re_toast;
|
|
}
|
|
#endif /* DIAGNOSTIC */
|
|
|
|
nss = 0;
|
|
if ((caddr_t)va >= vm->vm_maxsaddr
|
|
&& (caddr_t)va < (caddr_t)VM_MAXUSER_ADDRESS
|
|
&& map != kernel_map) {
|
|
nss = clrnd(btoc(USRSTACK-(u_int)va));
|
|
if (nss > btoc(p->p_rlimit[RLIMIT_STACK].rlim_cur)) {
|
|
rv = KERN_FAILURE;
|
|
goto nogo;
|
|
}
|
|
}
|
|
|
|
/* check if page table is mapped, if not, fault it first */
|
|
|
|
/*
|
|
if (*(((pt_entry_t **)(PROCESS_PAGE_TBLS_BASE + va >> (PD_SHIFT+2)))[]) == 0)
|
|
panic("vm_fault: Page table is needed first\n")
|
|
*/
|
|
|
|
rv = vm_fault(map, va, ftype, FALSE);
|
|
/*printf("fault result=%d\n", rv);*/
|
|
if (rv == KERN_SUCCESS) {
|
|
if (nss > vm->vm_ssize)
|
|
vm->vm_ssize = nss;
|
|
va = trunc_page(vtopte(va));
|
|
/*
|
|
* for page table, increment wiring as long as not a page
|
|
* table fault as well
|
|
*/
|
|
if (map != kernel_map)
|
|
vm_map_pageable(map, va, round_page(va+1), FALSE);
|
|
if (fault_code == FAULT_TRANS_P)
|
|
return;
|
|
goto out;
|
|
}
|
|
nogo:
|
|
if (fault_code == FAULT_TRANS_P) {
|
|
printf("Failed page fault in kernel\n");
|
|
if (pcb->pcb_onfault)
|
|
goto copyfault;
|
|
printf("vm_fault(%x, %x, %x, 0) -> %x\n",
|
|
(u_int)map, (u_int)va, ftype, rv);
|
|
goto we_re_toast;
|
|
}
|
|
printf("nogo, Data abort: '%s' status = %03x address = %08x PC = %08x\n",
|
|
aborts[fault_status & 0xf], fault_status & 0xfff, fault_address,
|
|
fault_pc);
|
|
disassemble(fault_pc);
|
|
postmortem(frame);
|
|
trapsignal(p, SIGSEGV, TRAP_CODE);
|
|
break;
|
|
}
|
|
/* panic("Page Fault - Halting\n");*/
|
|
break;
|
|
|
|
case FAULT_TRANS_S: /* Section Translation Fault */
|
|
case FAULT_TRANS_S | FAULT_USER: /* Section Translation Fault */
|
|
/* Section translation fault - the L1 page table does not exist */
|
|
{
|
|
register vm_offset_t va;
|
|
register struct vmspace *vm = p->p_vmspace;
|
|
register vm_map_t map;
|
|
int rv;
|
|
vm_prot_t ftype;
|
|
u_int nss, v;
|
|
|
|
va = trunc_page((vm_offset_t)fault_address);
|
|
|
|
if (pmap_debug_level >= 0)
|
|
printf("ok we have a section fault page addr=V%08x\n",
|
|
(u_int)va);
|
|
|
|
/*
|
|
* It is only a kernel address space fault iff:
|
|
* 1. (fault_code & FAULT_USER) == 0 and
|
|
* 2. pcb_onfault not set or
|
|
* 3. pcb_onfault set but supervisor space fault
|
|
* The last can occur during an exec() copyin where the
|
|
* argument space is lazy-allocated.
|
|
*/
|
|
|
|
if (fault_code == FAULT_TRANS_S && va >= KERNEL_BASE)
|
|
map = kernel_map;
|
|
else
|
|
map = &vm->vm_map;
|
|
|
|
/*
|
|
debug_show_vm_map(map, "fault");
|
|
debug_show_vm_map(kernel_map, "kernel");
|
|
*/
|
|
|
|
/* We are mapping a page table so this must be kernel r/w */
|
|
|
|
ftype = VM_PROT_READ | VM_PROT_WRITE;
|
|
#ifdef DIAGNOSTIC
|
|
if (map == kernel_map && va == 0) {
|
|
printf("trap: bad kernel access at %x\n", (u_int)va);
|
|
goto we_re_toast;
|
|
}
|
|
#endif /* DIAGNOSTIC */
|
|
|
|
nss = 0;
|
|
if ((caddr_t)va >= vm->vm_maxsaddr
|
|
&& (caddr_t)va < (caddr_t)VM_MAXUSER_ADDRESS
|
|
&& map != kernel_map) {
|
|
/* printf("Address is in the stack\n");*/
|
|
nss = clrnd(btoc(USRSTACK-(u_int)va));
|
|
if (nss > btoc(p->p_rlimit[RLIMIT_STACK].rlim_cur)) {
|
|
printf("Stack limit exceeded %08x %08x\n",
|
|
nss, btoc(p->p_rlimit[RLIMIT_STACK].rlim_cur));
|
|
rv = KERN_FAILURE;
|
|
goto nogo1;
|
|
}
|
|
}
|
|
|
|
/* check if page table is mapped, if not, fault it first */
|
|
|
|
v = trunc_page(vtopte(va));
|
|
if (pmap_debug_level >= 0)
|
|
printf("v=%08x\n", v);
|
|
rv = vm_fault(map, v, ftype, FALSE);
|
|
if (rv != KERN_SUCCESS)
|
|
goto nogo1;
|
|
|
|
if (pmap_debug_level >= 0)
|
|
printf("vm_fault succeeded\n");
|
|
|
|
/* update increment wiring as this is a page table fault */
|
|
|
|
vm_map_pageable(map, v, round_page(v+1), FALSE);
|
|
|
|
if (pmap_debug_level >= 0)
|
|
printf("faulting in page %08x\n", (u_int)va);
|
|
|
|
ftype = VM_PROT_READ;
|
|
|
|
rv = vm_fault(map, va, ftype, FALSE);
|
|
if (rv == KERN_SUCCESS) {
|
|
if (nss > vm->vm_ssize)
|
|
vm->vm_ssize = nss;
|
|
va = trunc_page(vtopte(va));
|
|
/*
|
|
* for page table, increment wiring as long as not a page
|
|
* table fault as well
|
|
*/
|
|
if (!v && map != kernel_map)
|
|
vm_map_pageable(map, va, round_page(va+1), FALSE);
|
|
if (fault_code == FAULT_TRANS_S)
|
|
return;
|
|
goto out;
|
|
}
|
|
nogo1:
|
|
printf("nogo1, Data abort: '%s' status = %03x address = %08x PC = %08x\n",
|
|
aborts[fault_status & 0xf], fault_status & 0xfff, fault_address,
|
|
fault_pc);
|
|
disassemble(fault_pc);
|
|
if (fault_code == FAULT_TRANS_S) {
|
|
printf("Section fault in SVC mode\n");
|
|
if (pcb->pcb_onfault)
|
|
goto copyfault;
|
|
printf("vm_fault(%x, %x, %x, 0) -> %x\n",
|
|
(u_int)map, (u_int)va, ftype, rv);
|
|
goto we_re_toast;
|
|
}
|
|
postmortem(frame);
|
|
trapsignal(p, SIGSEGV, TRAP_CODE);
|
|
break;
|
|
}
|
|
/* panic("Section Fault - Halting\n");
|
|
break;*/
|
|
|
|
default :
|
|
/* Are there any combinations I have missed ? */
|
|
|
|
printf("fault status = %08x fault code = %08x\n",
|
|
fault_status, fault_code);
|
|
|
|
we_re_toast:
|
|
/* Were are dead, try and provide some debug infomation before dying */
|
|
|
|
postmortem(frame);
|
|
|
|
panic("Fault cannot be handled\n");
|
|
break;
|
|
}
|
|
|
|
out:
|
|
if ((fault_code & FAULT_USER) == 0)
|
|
return;
|
|
|
|
#ifdef VALIDATE_TRAPFRAME
|
|
validate_trapframe(frame, 1);
|
|
#endif
|
|
userret(p, frame->tf_pc, sticks);
|
|
|
|
#ifdef VALIDATE_TRAPFRAME
|
|
validate_trapframe(frame, 1);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* void prefetch_abort_handler(trapframe_t *frame)
|
|
*
|
|
* Abort handler called when instruction execution occurs at
|
|
* a non existant or restricted (access permissions) memory page.
|
|
* If the address is invalid and we were in SVC mode then panic as
|
|
* the kernel should never prefetch abort.
|
|
* If the address is invalid and the page is mapped then the user process
|
|
* does no have read permission so send it a signal.
|
|
* Otherwise fault the page in and try again.
|
|
*/
|
|
|
|
extern int kernel_debug;
|
|
|
|
void
|
|
prefetch_abort_handler(frame)
|
|
trapframe_t *frame;
|
|
{
|
|
register u_int fault_pc;
|
|
register struct proc *p;
|
|
register struct pcb *pcb;
|
|
u_int fault_instruction;
|
|
u_int s;
|
|
int fault_code;
|
|
u_quad_t sticks;
|
|
pt_entry_t *pte;
|
|
|
|
/* Debug code */
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if ((GetCPSR() & PSR_MODE) != PSR_SVC32_MODE) {
|
|
s = spltty();
|
|
printf("fault being handled in non SVC32 mode\n");
|
|
postmortem(frame);
|
|
pmap_debug_level = 0;
|
|
(void)splx(s);
|
|
panic("Fault handler not in SVC mode\n");
|
|
}
|
|
#endif /* DIAGNOSTIC */
|
|
|
|
/*
|
|
* Enable IRQ's & FIQ's (disabled by the abort) This always comes
|
|
* from user mode so we know interrupts were not disabled.
|
|
* But we check anyway.
|
|
*/
|
|
|
|
#ifndef BLOCK_IRQS
|
|
if (!(frame->tf_spsr & I32_bit))
|
|
enable_interrupts(I32_bit);
|
|
#endif
|
|
|
|
/* Update vmmeter statistics */
|
|
|
|
cnt.v_trap++;
|
|
|
|
#ifdef notyet
|
|
if (cpu_prefetchabt_fixup(frame))
|
|
panic("fixup failed\n");
|
|
#endif
|
|
|
|
/* Get the current proc structure or proc0 if there is none */
|
|
|
|
if ((p = curproc) == 0) {
|
|
p = &proc0;
|
|
printf("Prefetch about with curproc == 0\n");
|
|
}
|
|
|
|
if (pmap_debug_level >= 0)
|
|
printf("prefetch fault in process %08x %s\n", (u_int)p, p->p_comm);
|
|
|
|
/*
|
|
* can't use curpcb, as it might be NULL; and we have p in a
|
|
* register anyway
|
|
*/
|
|
|
|
pcb = &p->p_addr->u_pcb;
|
|
if (pcb == 0)
|
|
panic("prefetch_abort_handler: no pcb ... we're toast !\n");
|
|
|
|
#ifdef DIAGNOSTIC
|
|
if (pcb != curpcb) {
|
|
printf("data_abort: Alert ! pcb(%08x) != curpcb(%08x)\n",
|
|
(u_int)pcb, (u_int)curpcb);
|
|
printf("data_abort: Alert ! proc(%08x), curproc(%08x)\n",
|
|
(u_int)p, (u_int)curproc);
|
|
}
|
|
#endif /* DIAGNOSTIC */
|
|
|
|
if ((frame->tf_spsr & PSR_MODE) == PSR_USR32_MODE) {
|
|
sticks = p->p_sticks;
|
|
|
|
/* Modify the fault_code to reflect the USR/SVC state at time of fault */
|
|
|
|
fault_code |= FAULT_USER;
|
|
p->p_md.md_regs = frame;
|
|
} else {
|
|
/* All the kernel code pages are loaded at boot and do not get paged */
|
|
|
|
s = spltty();
|
|
printf("Prefetch address = %08x\n", frame->tf_pc);
|
|
|
|
postmortem(frame);
|
|
|
|
#ifdef CONTINUE_AFTER_SVC_PREFETCH
|
|
printf("prefetch abort in SVC mode !\n");
|
|
printf("The system should now be considered very unstable :-)\n");
|
|
sigexit(curproc, SIGILL);
|
|
/* Not reached */
|
|
panic("prefetch_abort_handler: How did we get here ?\n");
|
|
#else
|
|
panic("Prefetch abort in SVC mode\n");
|
|
#endif /* CONTINUE_AFTER_SVC_PREFETCH */
|
|
}
|
|
|
|
/* Get fault address */
|
|
|
|
fault_pc = frame->tf_pc;
|
|
|
|
if (pmap_debug_level >= 0)
|
|
printf("prefetch_abort: PC = %08x\n", fault_pc);
|
|
|
|
/* Ok validate the address, can only execute in USER space */
|
|
|
|
if (fault_pc < VM_MIN_ADDRESS || fault_pc >= VM_MAXUSER_ADDRESS) {
|
|
s = spltty();
|
|
printf("prefetch: pc (%08x) not in user process space\n",
|
|
fault_pc);
|
|
postmortem(frame);
|
|
trapsignal(p, SIGSEGV, fault_pc);
|
|
(void)splx(s);
|
|
userret(p, frame->tf_pc, sticks);
|
|
return;
|
|
}
|
|
|
|
/* Ok read the fault address. This will fault the page in for us */
|
|
|
|
if (fetchuserword(fault_pc, &fault_instruction) != 0) {
|
|
s = spltty();
|
|
printf("prefetch: faultin failed for address %08x!!\n",
|
|
fault_pc);
|
|
postmortem(frame);
|
|
trapsignal(p, SIGSEGV, fault_pc);
|
|
(void)splx(s);
|
|
} else {
|
|
|
|
#ifdef DIAGNOSTIC
|
|
/* More debug stuff */
|
|
|
|
if (pmap_debug_level >= 0) {
|
|
s = spltty();
|
|
printf("Instruction @V%08x = %08x\n", fault_pc,
|
|
fault_instruction);
|
|
disassemble(fault_pc);
|
|
printf("return addr=%08x", frame->tf_pc);
|
|
pte = pmap_pte(p->p_vmspace->vm_map.pmap,
|
|
(vm_offset_t)fault_pc);
|
|
if (pte)
|
|
printf(" pte=%08x *pte=%08x\n", pte, *pte);
|
|
else
|
|
printf("\n");
|
|
|
|
(void)splx(s);
|
|
}
|
|
#endif /* DIAGNOSTIC */
|
|
}
|
|
|
|
#ifdef VALIDATE_TRAPFRAME
|
|
validate_trapframe(frame, 4);
|
|
#endif
|
|
|
|
userret(p, frame->tf_pc, sticks);
|
|
|
|
#ifdef VALIDATE_TRAPFRAME
|
|
validate_trapframe(frame, 2);
|
|
#endif
|
|
}
|
|
|
|
#ifdef VALIDATE_TRAPFRAME
|
|
|
|
void
|
|
validate_trapframe(frame, where)
|
|
trapframe_t *frame;
|
|
int where;
|
|
{
|
|
char *ptr;
|
|
u_int mode;
|
|
|
|
if ((GetCPSR() & PSR_MODE) != PSR_SVC32_MODE)
|
|
printf("VTF Warning : validate_trapframe : Not in SVC32 mode\n");
|
|
|
|
mode = frame->tf_spsr & PSR_MODE;
|
|
|
|
switch (where) {
|
|
case 1:
|
|
ptr = "data abort handler";
|
|
break;
|
|
case 2:
|
|
ptr = "prefetch abort handler";
|
|
if (mode != PSR_USR32_MODE)
|
|
printf("VTF Warning : %s : not USR32 mode\n", ptr);
|
|
break;
|
|
case 3:
|
|
ptr = "ast handler";
|
|
if (mode != PSR_USR32_MODE)
|
|
printf("VTF Warning : %s : not USR32 mode\n", ptr);
|
|
break;
|
|
case 4:
|
|
ptr = "syscall handler";
|
|
if (mode != PSR_USR32_MODE)
|
|
printf("VTF Warning : %s : not USR32 mode\n", ptr);
|
|
break;
|
|
case 5:
|
|
ptr = "undefined handler";
|
|
if (mode != PSR_USR32_MODE)
|
|
printf("VTF Warning : %s : not USR32 mode\n", ptr);
|
|
break;
|
|
case 6:
|
|
ptr = "sigreturn handler";
|
|
if (mode != PSR_USR32_MODE)
|
|
printf("VTF Warning : %s : not USR32 mode\n", ptr);
|
|
break;
|
|
default:
|
|
ptr = "unknown handler";
|
|
break;
|
|
}
|
|
|
|
if (frame->tf_usr_sp >= VM_MAXUSER_ADDRESS)
|
|
printf("VTF WARNING: %s : frame->tf_usr_sp >= VM_MAXUSER_ADDRESS [%08x]\n", ptr, frame->tf_usr_sp);
|
|
if (frame->tf_svc_lr >= 0xf1000000)
|
|
printf("VTF WARNING: %s : frame->tf_svc_lr >= 0xf1000000 [%08x]\n", ptr, frame->tf_svc_lr);
|
|
if (frame->tf_pc >= 0xf1000000)
|
|
printf("VTF WARNING: %s: frame->tf_pc >= 0xf1000000 [%08x]\n", ptr, frame->tf_pc);
|
|
if (frame->tf_pc < VM_MIN_ADDRESS)
|
|
printf("VTF WARNING: %s: frame->tf_pc >= VM_MIN_ADDRESS [%08x]\n", ptr, frame->tf_pc);
|
|
if (mode != PSR_USR32_MODE) {
|
|
if (frame->tf_svc_lr < 0xf0000000)
|
|
printf("VTF WARNING: %s : frame->tf_svc_lr < 0xf0000000 [%08x]\n", ptr, frame->tf_svc_lr);
|
|
if (frame->tf_pc < 0xf0000000)
|
|
printf("VTF WARNING: %s: frame->tf_pc < 0xf0000000 [%08x]\n", ptr, frame->tf_pc);
|
|
}
|
|
}
|
|
#endif /* VALIDATE_TRAPFRAME */
|
|
|
|
/* End of fault.c */
|