patch-1.3.4 linux/arch/sparc/mm/fault.c
Next file: linux/arch/sparc/mm/init.c
Previous file: linux/arch/sparc/mm/Makefile
Back to the patch index
Back to the overall index
- Lines: 616
- Date:
Sun Jun 25 07:06:31 1995
- Orig file:
v1.3.3/linux/arch/sparc/mm/fault.c
- Orig date:
Sun Feb 26 20:46:20 1995
diff -u --recursive --new-file v1.3.3/linux/arch/sparc/mm/fault.c linux/arch/sparc/mm/fault.c
@@ -1,3 +1,8 @@
+/* fault.c: Page fault handlers for the Sparc.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ */
+
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
@@ -8,11 +13,18 @@
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/openprom.h>
+#include <asm/idprom.h>
#include <asm/page.h>
#include <asm/pgtable.h>
+#include <asm/memreg.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/kdebug.h>
-extern unsigned long pg0[1024]; /* page table for 0-4MB for everybody */
-extern struct sparc_phys_banks sp_banks[14];
+#define ELEMENTS(arr) (sizeof (arr)/sizeof (arr[0]))
+
+extern struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
+extern int prom_node_root;
extern void die_if_kernel(char *,struct pt_regs *,long);
@@ -42,46 +54,37 @@
*/
#undef CONFIG_TEST_VERIFY_AREA
-/* Traverse the memory lists in the prom to see how much physical we
- * have.
- */
-
-unsigned long
-probe_memory(void)
+/* Nice, simple, prom library does all the sweating for us. ;) */
+int prom_probe_memory (void)
{
- register struct linux_romvec *lprom;
register struct linux_mlist_v0 *mlist;
register unsigned long bytes, base_paddr, tally;
register int i;
- bytes = tally = 0;
- base_paddr = 0;
- i=0;
- lprom = romvec;
- switch(lprom->pv_romvers)
- {
- case 0:
- mlist=(*(lprom->pv_v0mem.v0_totphys));
- bytes = tally = mlist->num_bytes;
- base_paddr = (unsigned long) mlist->start_adr;
-
- sp_banks[0].base_addr = base_paddr;
- sp_banks[0].num_bytes = bytes;
-
- if(mlist->theres_more != (void *)0) {
- i++;
- mlist=mlist->theres_more;
- bytes=mlist->num_bytes;
- tally += bytes;
- sp_banks[i].base_addr = (unsigned long) mlist->start_adr;
- sp_banks[i].num_bytes = mlist->num_bytes;
- }
- break;
- case 2:
- printk("no v2 memory probe support yet.\n");
- (*(lprom->pv_halt))();
+ i = 0;
+ mlist= *prom_meminfo()->v0_available;
+ bytes = tally = mlist->num_bytes;
+ base_paddr = (unsigned long) mlist->start_adr;
+
+ sp_banks[0].base_addr = base_paddr;
+ sp_banks[0].num_bytes = bytes;
+
+ while (mlist->theres_more != (void *) 0){
+ i++;
+ mlist = mlist->theres_more;
+ bytes = mlist->num_bytes;
+ tally += bytes;
+ if (i >= SPARC_PHYS_BANKS-1) {
+ printk ("The machine has more banks that this kernel can support\n"
+ "Increase the SPARC_PHYS_BANKS setting (currently %d)\n",
+ SPARC_PHYS_BANKS);
+ i = SPARC_PHYS_BANKS-1;
break;
}
+
+ sp_banks[i].base_addr = (unsigned long) mlist->start_adr;
+ sp_banks[i].num_bytes = mlist->num_bytes;
+ }
i++;
sp_banks[i].base_addr = 0xdeadbeef;
@@ -90,84 +93,464 @@
return tally;
}
-/* Sparc routine to reserve the mapping of the open boot prom */
+/* Traverse the memory lists in the prom to see how much physical we
+ * have.
+ */
+unsigned long
+probe_memory(void)
+{
+ int total;
+
+ total = prom_probe_memory();
-/* uncomment this for FAME and FORTUNE! */
-/* #define DEBUG_MAP_PROM */
+ /* Oh man, much nicer, keep the dirt in promlib. */
+ return total;
+}
-int
-map_the_prom(int curr_num_segs)
+asmlinkage void sparc_txtmem_error(int type, unsigned long sync_err_reg,
+ unsigned long sync_vaddr,
+ unsigned long async_err_reg,
+ unsigned long async_vaddr)
{
- register unsigned long prom_va_begin;
- register unsigned long prom_va_end;
- register int segmap_entry, i;
+ printk("Aieee, sparc text page access error, halting\n");
+ printk("type = %d sync_err_reg = 0x%x sync_vaddr = 0x%x\n",
+ type, (unsigned int) sync_err_reg, (unsigned int) sync_vaddr);
+ printk("async_err_reg = 0x%x async_vaddr = 0x%x\n",
+ (unsigned int) async_err_reg, (unsigned int) async_vaddr);
+ halt();
+}
- prom_va_begin = LINUX_OPPROM_BEGVM;
- prom_va_end = LINUX_OPPROM_ENDVM;
+/* #define DEBUG_SPARC_TEXT_ACCESS_FAULT */
-#ifdef DEBUG_MAP_PROM
- printk("\ncurr_num_segs = 0x%x\n", curr_num_segs);
+asmlinkage void sparc_text_access_fault(int type, unsigned long sync_err_reg,
+ unsigned long sync_vaddr,
+ unsigned long pc, unsigned long psr,
+ struct pt_regs *regs)
+{
+ struct vm_area_struct *vma;
+ unsigned long address;
+
+ address = sync_vaddr;
+#ifdef DEBUG_SPARC_TEXT_ACCESS_FAULT
+ printk("Text FAULT: address = %08lx code = %08lx\n",
+ (unsigned long) address, (unsigned long) sync_err_reg);
+ printk("PC = %08lx\n", (unsigned long) regs->pc);
+ SP_ENTER_DEBUGGER;
+ halt();
#endif
+ vma = find_vma(current, address);
+ if(!vma) {
+ goto bad_area;
+ }
+ if(vma->vm_start <= address)
+ goto good_area;
+ goto bad_area;
- while( prom_va_begin < prom_va_end)
- {
- segmap_entry=get_segmap(prom_va_begin);
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+ handle_mm_fault(vma, address, 0);
+ return;
- curr_num_segs = ((segmap_entry<curr_num_segs)
- ? segmap_entry : curr_num_segs);
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+ if((unsigned long) address < PAGE_SIZE) {
+ printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+ } else
+ printk(KERN_ALERT "Unable to handle kernel paging request");
+ printk(" at virtual address %08lx\n",address);
+ printk(KERN_ALERT "current->tss.pgd_ptr = %08lx\n",
+ (unsigned long) current->tss.pgd_ptr);
+ halt();
+}
- for(i = num_contexts; --i > 0;)
- (*romvec->pv_setctxt)(i, (char *) prom_va_begin,
- segmap_entry);
+asmlinkage void sparc_datamem_error(int type, unsigned long sync_err_reg,
+ unsigned long sync_vaddr,
+ unsigned long async_err_reg,
+ unsigned long async_vaddr)
+{
+ printk("Aieee, sparc data page access error, halting\n");
+ printk("type = %d sync_err_reg = 0x%x sync_vaddr = 0x%x\n",
+ type, (unsigned int) sync_err_reg, (unsigned int) sync_vaddr);
+ printk("async_err_reg = 0x%x async_vaddr = 0x%x\n",
+ (unsigned int) async_err_reg, (unsigned int) async_vaddr);
+ printk("SYNC PAGE has MMU entry %08lx\n",
+ (unsigned long) get_pte(sync_vaddr));
+ halt();
+}
- if(segmap_entry == invalid_segment)
- {
+/* #define DEBUG_SPARC_DATA_ACCESS_FAULT */
-#ifdef DEBUG_MAP_PROM
- printk("invalid_segments, virt_addr 0x%x\n", prom_va_begin);
+asmlinkage void sparc_data_access_fault(int type, unsigned long sync_err_reg,
+ unsigned long sync_vaddr,
+ unsigned long pc, unsigned long psr,
+ struct pt_regs *regs)
+{
+ struct vm_area_struct *vma;
+ unsigned long address;
+ int error_code;
+
+ address = sync_vaddr;
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("Data FAULT: address = %08lx code = %08lx\n",
+ (unsigned long) address, (unsigned long) sync_err_reg);
+ printk("PC = %08lx\n", (unsigned long) regs->pc);
+ printk("PTE = %08lx\n", (unsigned long) get_pte(address));
#endif
+ vma = find_vma(current, address);
+ if(!vma) {
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("NULL VMA\n");
+#endif
+ goto bad_area;
+ }
+ if(vma->vm_start <= address)
+ goto good_area;
+
+ if(!(vma->vm_flags & VM_GROWSDOWN)) {
+ goto bad_area;
+ }
+ if(vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur) {
+ goto bad_area;
+ }
- prom_va_begin += 0x40000; /* num bytes per segment entry */
- continue;
- }
+ vma->vm_offset -= vma->vm_start - (address & PAGE_MASK);
+ vma->vm_start = (address & PAGE_MASK);
- /* DUH, prom maps itself so that users can access it. This is
- * broken.
- */
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("Found good_area\n");
+#endif
+ if((sync_err_reg & SUN4C_SYNC_BADWRITE) &&
+ (sync_err_reg & SUN4C_SYNC_NPRESENT)) {
+ if(!(vma->vm_flags & VM_WRITE)) {
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("oops, vma not writable\n");
+#endif
+ goto bad_area;
+ }
+ } else {
+ if(sync_err_reg & SUN4C_SYNC_PROT) {
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("PROT violation\n");
+#endif
+ goto bad_area;
+ }
+ if(!(vma->vm_flags & (VM_READ | VM_EXEC))) {
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("vma not readable nor executable\n");
+#endif
+ goto bad_area;
+ }
+ }
-#ifdef DEBUG_MAP_PROM
- printk("making segmap for prom privileged, va = 0x%x\n",
- prom_va_begin);
+ if(sync_err_reg & SUN4C_SYNC_BADWRITE)
+ error_code = 0x2;
+ else
+ error_code = 0x0;
+
+#ifdef DEBUG_SPARC_DATA_ACCESS_FAULT
+ printk("calling handle_mm_fault vma=%08lx addr=%08lx code=%d\n",
+ (unsigned long) vma, (unsigned long) address,
+ (int) error_code);
#endif
+ handle_mm_fault(vma, address, error_code);
+ return;
- for(i = 0x40; --i >= 0; prom_va_begin+=4096)
- {
- put_pte(prom_va_begin, get_pte(prom_va_begin) | 0x20000000);
- }
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+ if (wp_works_ok < 0 && address == 0x0) {
+ wp_works_ok = 1;
+ pg0[0] = pte_val(mk_pte(PAGE_OFFSET, PAGE_SHARED));
+ put_pte((unsigned long) 0x0, pg0[0]);
+ printk("This Sparc honours the WP bit even when in supervisor mode. Good.\n");
+ return;
+ }
+
+ if((unsigned long) address < PAGE_SIZE) {
+ printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+ } else
+ printk(KERN_ALERT "Unable to handle kernel paging request");
+ printk(" at virtual address %08lx\n",address);
+ printk(KERN_ALERT "current->tss.pgd_ptr = %08lx\n",
+ (unsigned long) current->tss.pgd_ptr);
+ halt();
+}
- }
+/* Dump the contents of the SRMMU fsr in a human readable format. */
+void
+dump_srmmu_fsr(unsigned long fsr)
+{
+ unsigned long ebe, l, at, ft, fav, ow;
- printk("Mapped the PROM in all contexts...\n");
+ ebe = (fsr&SRMMU_FSR_EBE_MASK)>>10;
+ l = (fsr&SRMMU_FSR_L_MASK)>>8;
+ at = (fsr&SRMMU_FSR_AT_MASK)>>5;
+ ft = (fsr&SRMMU_FSR_FT_MASK)>>2;
+ fav = (fsr&SRMMU_FSR_FAV_MASK)>>1;
+ ow = (fsr&SRMMU_FSR_OW_MASK);
+
+ printk("FSR %08lx: ", fsr);
+
+ /* Ugh, the ebe is arch-dep, have to find out the meanings. */
+ printk("EBE<%s> ", (ebe == 0 ? "none" : (ebe == 1 ? "bus err" :
+ (ebe == 2 ? "bus timeout" :
+ (ebe == 4 ? "uncorrectable err" :
+ (ebe == 8 ? "undefined err" :
+ (ebe == 16 ? "parity err" :
+ (ebe == 24 ? "tsunami parity err" :
+ (ebe == 32 ? "store buf err" :
+ (ebe == 64 ? "control space err" :
+ "VIKING emergency response team"))))))))));
+ printk("L<%s> ", (l == 0 ? "context table" : (l == 1 ? "level1" :
+ (l == 2 ? "level2" :
+ "level3"))));
+ printk("AT<%s> ", (at == 0 ? "user load" :
+ (at == 1 ? "priv load" :
+ (at == 2 ? "user rd/exec" :
+ (at == 3 ? "priv rd/exec" :
+ (at == 4 ? "user store data" :
+ (at == 5 ? "priv store data" :
+ (at == 6 ? "user store inst" :
+ "priv store inst"))))))));
+
+ printk("FT<%s> ", (ft == 0 ? "none" :
+ (ft == 1 ? "invalid address" :
+ (ft == 2 ? "prot violation" :
+ (ft == 3 ? "priv violation" :
+ (ft == 4 ? "translation error" :
+ (ft == 5 ? "bus acc error" :
+ (ft == 6 ? "internal error" :
+ "reserved"))))))));
-#ifdef DEBUG_MAP_PROM
- printk("curr_num_segs = 0x%x\n", curr_num_segs);
-#endif
+ printk("FAV<%s> ", (fav == 0 ? "faddr invalid" : "faddr valid"));
+ printk("OW<%s>\n", (ow == 0 ? "fsr not overwritten" : "fsr overwritten"));
+
+ return;
+}
- return curr_num_segs;
+/* #define DEBUG_SRMMU_TEXT_ACCESS_FAULT */
+void
+really_bad_srmmu_fault(int type, unsigned long fstatus, unsigned long faddr)
+{
+ /* Learn how to handle these later... */
+ printk("REALLY BAD SRMMU FAULT DETECTED\n");
+ printk("Bailing out...\n");
+ dump_srmmu_fsr(fstatus);
+ prom_halt();
+ return;
}
+asmlinkage void srmmu_text_access_fault(int type, unsigned long fstatus,
+ unsigned long faddr,
+ unsigned long pc, unsigned long psr,
+ struct pt_regs *regs)
+{
+ struct vm_area_struct *vma;
+ unsigned long address;
+
+ /* Check for external bus errors and uncorrectable errors */
+ if(fstatus&SRMMU_FSR_EBE_MASK)
+ printk("External Bus Error detected during a text fault.\n");
+
+ /* Check for multiple faults... */
+ if(fstatus&SRMMU_FSR_OW_MASK && (fstatus&SRMMU_FSR_FT_TRANS)) {
+ printk("Multiple faults detected in text fault handler\n");
+ printk("Fault number one: Text fault at addr %08lx", faddr);
+ printk("Fault number two: Translation Error\n");
+ printk("Giving up...\n");
+ prom_halt();
+ }
+
+ if(fstatus&(SRMMU_FSR_EBE_BERR | SRMMU_FSR_EBE_BTIMEO | SRMMU_FSR_EBE_UNCOR))
+ really_bad_srmmu_fault(type, fstatus, faddr);
+
+ /* Ok, we should be able to handle it at this point. */
+
+ address = faddr;
+#ifdef DEBUG_SRMMU_TEXT_ACCESS_FAULT
+ printk("Text FAULT: address = %08lx code = %08lx\n",
+ (unsigned long) address, (unsigned long) fstatus);
+ printk("PC = %08lx\n", (unsigned long) regs->pc);
+ dump_srmmu_fsr(fstatus);
+ halt();
+#endif
+
+ /* Ugh, how often does this happen? ;-( */
+ if(!(fstatus&SRMMU_FSR_FAV_MASK)) {
+ printk("Fault address register is INVALID! Since this is a text\n");
+ printk("fault I'll use the value of the trapped PC instead...\n");
+ address = regs->pc;
+ }
+
+ /* Ugh, I don't wanna know... */
+
+ vma = find_vma(current, address);
+ if(!vma) {
+ goto bad_area;
+ }
+ if(vma->vm_start <= address)
+ goto good_area;
+ goto bad_area;
+
/*
- * This routine handles page faults. It determines the address,
- * and the problem, and then passes it off to one of the appropriate
- * routines.
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
*/
-asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code)
-{
- die_if_kernel("Oops", regs, error_code);
- do_exit(SIGKILL);
+good_area:
+ do_no_page(vma, address, 0);
+ return;
+
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+ if((unsigned long) address < PAGE_SIZE) {
+ printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+ } else
+ printk(KERN_ALERT "Unable to handle kernel paging request");
+ printk(" at virtual address %08lx\n",address);
+ printk(KERN_ALERT "current->tss.pgd_ptr = %08lx\n",
+ (unsigned long) current->tss.pgd_ptr);
+ halt();
}
+/* #define DEBUG_SRMMU_DATA_ACCESS_FAULT */
+asmlinkage void srmmu_data_access_fault(int type, unsigned long fstatus,
+ unsigned long faddr,
+ unsigned long afstatus,
+ unsigned long afaddr,
+ struct pt_regs *regs)
+{
+ struct vm_area_struct *vma;
+ unsigned long address, pc, psr;
+ int error_code;
+
+ pc = regs->pc;
+ psr= regs->psr;
+ address = faddr;
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("Data FAULT: address = %08lx code = %08lx\n",
+ (unsigned long) address, (unsigned long) fstatus);
+ printk("PC = %08lx\n", (unsigned long) pc);
+ printk("afsr = %08lx afaddr = %08lx\n", afstatus, afaddr);
+ dump_srmmu_fsr(fstatus);
+#endif
+ /* I figure if I make the panic's look like they came from SunOS or Solaris
+ * my life will be a lot easier ;-)
+ */
+ if(!(fstatus&SRMMU_FSR_FAV_MASK)) {
+ dump_srmmu_fsr(fstatus);
+ panic("hat_pteload: Fault address is invalid on a data fault, I'm confused...\n");
+ }
+
+#if 0 /* I see this all the time on supersparcs ;-( */
+ if(fstatus&SRMMU_FSR_OW_MASK) {
+ dump_srmmu_fsr(fstatus);
+ panic("hat_pteload: Multiple faults at once, giving up...\n");
+ }
+#endif
+
+ vma = find_vma(current, address);
+ if(!vma) {
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("NULL VMA\n");
+#endif
+ goto bad_area;
+ }
+ if(vma->vm_start <= address)
+ goto good_area;
+
+ if(!(vma->vm_flags & VM_GROWSDOWN)) {
+ goto bad_area;
+ }
+ if(vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur) {
+ goto bad_area;
+ }
+ vma->vm_offset -= vma->vm_start - (address & PAGE_MASK);
+ vma->vm_start = (address & PAGE_MASK);
+
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("Found good_area\n");
+#endif
+ if((fstatus & SUN4C_SYNC_BADWRITE) &&
+ (fstatus & SUN4C_SYNC_NPRESENT)) {
+ if(!(vma->vm_flags & VM_WRITE)) {
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("oops, vma not writable\n");
+#endif
+ goto bad_area;
+ }
+ } else {
+ if(fstatus & SUN4C_SYNC_PROT) {
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("PROT violation\n");
+#endif
+ goto bad_area;
+ }
+ if(!(vma->vm_flags & (VM_READ | VM_EXEC))) {
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("vma not readable nor executable\n");
+#endif
+ goto bad_area;
+ }
+ }
+
+ if(fstatus & SUN4C_SYNC_BADWRITE)
+ error_code = 0x2;
+ else
+ error_code = 0x0;
+
+#ifdef DEBUG_SRMMU_DATA_ACCESS_FAULT
+ printk("calling do_no_page vma=%08lx addr=%08lx code=%d\n",
+ (unsigned long) vma, (unsigned long) address,
+ (int) error_code);
+#endif
+ do_no_page(vma, address, error_code);
+ return;
+
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+ if (wp_works_ok < 0 && address == 0x0) {
+ wp_works_ok = 1;
+ /* Advance the PC and NPC over the test store. */
+ regs->pc = regs->npc;
+ regs->npc += 4;
+ printk("This Sparc honours the WP bit even when in supervisor mode. Good.\n");
+ return;
+ }
+
+ if((unsigned long) address < PAGE_SIZE) {
+ printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+ } else
+ printk(KERN_ALERT "Unable to handle kernel paging request");
+ printk(" at virtual address %08lx\n",address);
+ printk(KERN_ALERT "current->tss.pgd_ptr = %08lx\n",
+ (unsigned long) current->tss.pgd_ptr);
+ halt();
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this