patch-1.3.44 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: 717
- Date:
Sat Nov 25 02:59:22 1995
- Orig file:
v1.3.43/linux/arch/sparc/mm/fault.c
- Orig date:
Tue Jun 27 14:11:32 1995
diff -u --recursive --new-file v1.3.43/linux/arch/sparc/mm/fault.c linux/arch/sparc/mm/fault.c
@@ -1,4 +1,5 @@
-/* fault.c: Page fault handlers for the Sparc.
+/* $Id: fault.c,v 1.42 1995/11/25 00:59:20 davem Exp $
+ * fault.c: Page fault handlers for the Sparc.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
@@ -19,6 +20,7 @@
#include <asm/memreg.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
+#include <asm/traps.h>
#include <asm/kdebug.h>
#define ELEMENTS(arr) (sizeof (arr)/sizeof (arr[0]))
@@ -57,40 +59,40 @@
/* Nice, simple, prom library does all the sweating for us. ;) */
int prom_probe_memory (void)
{
- register struct linux_mlist_v0 *mlist;
- register unsigned long bytes, base_paddr, tally;
- register int i;
-
- i = 0;
- mlist= *prom_meminfo()->v0_available;
- bytes = tally = mlist->num_bytes;
- base_paddr = (unsigned long) mlist->start_adr;
+ register struct linux_mlist_v0 *mlist;
+ register unsigned long bytes, base_paddr, tally;
+ register int i;
+
+ 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;
+ 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;
- }
+ 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;
- sp_banks[i].num_bytes = 0;
+ 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;
+ sp_banks[i].num_bytes = 0;
- return tally;
+ return tally;
}
/* Traverse the memory lists in the prom to see how much physical we
@@ -107,450 +109,195 @@
return total;
}
-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)
+/* Whee, a level 15 NMI interrupt memory error. Let's have fun... */
+asmlinkage void sparc_lvl15_nmi(struct pt_regs *regs, unsigned long serr,
+ unsigned long svaddr, unsigned long aerr,
+ unsigned long avaddr)
{
- 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();
-}
-
-/* #define DEBUG_SPARC_TEXT_ACCESS_FAULT */
-
-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;
-
-/*
- * 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;
-
-/*
- * 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();
-}
-
-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();
-}
-
-/* #define DEBUG_SPARC_DATA_ACCESS_FAULT */
-
-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;
- }
-
- 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_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;
- }
- }
-
- 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;
-
-/*
- * 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;
-
- 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"))))))));
-
- printk("FAV<%s> ", (fav == 0 ? "faddr invalid" : "faddr valid"));
- printk("OW<%s>\n", (ow == 0 ? "fsr not overwritten" : "fsr overwritten"));
-
- return;
-}
-
-/* #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);
+ printk("FAULT: NMI received\n");
+ printk("SREGS: Synchronous Error %08lx\n", serr);
+ printk(" Synchronous Vaddr %08lx\n", svaddr);
+ printk(" Asynchronous Error %08lx\n", aerr);
+ printk(" Asynchronous Vaddr %08lx\n", avaddr);
+ printk("REGISTER DUMP:\n");
+ show_regs(regs);
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)
+/* Whee, looks like an i386 to me ;-) */
+asmlinkage void do_sparc_fault(struct pt_regs *regs, unsigned long tbr)
{
- 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;
-
-/*
- * Ok, we have a good vm_area for this memory access, so
- * we can handle it..
- */
+ struct vm_area_struct *vma;
+ unsigned long address, error_code, trap_type;
+ unsigned long from_user;
+
+ from_user = (((regs->psr & PSR_PS) >> 4) ^ FAULT_CODE_USER);
+ if(get_fault_info(&address, &error_code, from_user))
+ goto bad_area;
+ trap_type = ((tbr>>4)&0xff);
+ if(trap_type == SP_TRAP_TFLT) {
+ /* We play it 'safe'... */
+ address = regs->pc;
+ error_code = (from_user); /* no page, read */
+ } else if(trap_type != SP_TRAP_DFLT)
+ panic("Bad sparc trap, trap_type not data or text fault...");
+
+ /* Now actually handle the fault. Do kernel faults special,
+ * because on the sun4c we could have faulted trying to read
+ * the vma area of the task and without the following code
+ * we'd fault recursively until all our stack is gone. ;-(
+ *
+ * XXX I think there are races with this maneuver. XXX
+ */
+ if(!from_user && address >= KERNBASE) {
+ update_mmu_cache(0, address, __pte(0));
+ return;
+ }
+
+ vma = find_vma(current, address);
+ if(!vma)
+ goto bad_area;
+ if(vma->vm_start <= address)
+ goto good_area;
+ if(!(vma->vm_flags & VM_GROWSDOWN))
+ goto bad_area;
+ if(expand_stack(vma, address))
+ goto bad_area;
+ /*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
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..
- */
+ if(error_code & FAULT_CODE_WRITE) {
+ if(!(vma->vm_flags & VM_WRITE))
+ goto bad_area;
+ } else {
+ /* Allow reads even for write-only mappings */
+ if(!(vma->vm_flags & (VM_READ | VM_EXEC)))
+ goto bad_area;
+ }
+ handle_mm_fault(vma, address, error_code & FAULT_CODE_WRITE);
+ 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();
+ if(error_code & FAULT_CODE_USER) {
+ current->tss.sig_address = address;
+ current->tss.sig_desc = SUBSIG_NOMAPPING;
+ send_sig(SIGSEGV, current, 1);
+ return;
+ }
+ /* Uh oh, a kernel fault. Check for bootup wp_test... */
+ if (wp_works_ok < 0 && address == 0x0) {
+ wp_works_ok = 1;
+ printk("This Sparc honours the WP bit even when in supervisor mode. "
+ "Good.\n");
+ /* Advance program counter over the store. */
+ regs->pc = regs->npc;
+ regs->npc += 4;
+ 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("At PC %08lx nPC %08lx\n", (unsigned long) regs->pc,
+ (unsigned long) regs->npc);
+ printk(KERN_ALERT "current->tss.pgd_ptr = %08lx\n",
+ (unsigned long) current->tss.pgd_ptr);
+ show_regs(regs);
+ panic("KERNAL FAULT");
}
-/* #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..
+/* When the user does not have a mapped stack and we either
+ * need to read the users register window from that stack or
+ * we need to save a window to that stack, control flow
+ * ends up here to investigate the situation. This is a
+ * very odd situation where a 'user fault' happens from
+ * kernel space.
*/
-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();
+/* #define DEBUG_WINFAULT */
+extern void show_regwindow(struct reg_window *);
+asmlinkage void do_sparc_winfault(struct pt_regs *regs, int push)
+{
+ int wincount = 0;
+ int signal = 0;
+ struct thread_struct *tsp = ¤t->tss;
+
+ flush_user_windows();
+#ifdef DEBUG_WINFAULT
+ {
+ int i;
+ printk("%s[%d]wfault<%d>: WINDOW DUMP --> ", current->comm,
+ current->pid, push);
+ if(push==1)
+ for(i = 0; i < tsp->w_saved; i++)
+ printk("w[%d]sp<%08lx>, ",
+ i, tsp->rwbuf_stkptrs[i]);
+ else
+ printk("w[0]sp<%08lx>", regs->u_regs[UREG_FP]);
+ if(push!=2)
+ printk("\n");
+ }
+#endif
+ if(push==1) {
+ /* We failed to push a window to users stack. */
+ while(wincount < tsp->w_saved) {
+ if ((tsp->rwbuf_stkptrs[wincount] & 7) ||
+ (tsp->rwbuf_stkptrs[wincount] > KERNBASE) ||
+ verify_area(VERIFY_WRITE,
+ (char *) tsp->rwbuf_stkptrs[wincount],
+ sizeof(struct reg_window))) {
+ signal = SIGILL;
+ break;
+ }
+ /* Do it! */
+ memcpy((char *) tsp->rwbuf_stkptrs[wincount],
+ (char *)&tsp->reg_window[wincount],
+ sizeof(struct reg_window));
+ wincount++;
+ }
+ } else {
+ /* We failed to pull a window from users stack.
+ * For a window underflow from userland we need
+ * to verify two stacks, for a return from trap
+ * we need only inspect the one at UREG_FP.
+ */
+ if((regs->u_regs[UREG_FP] & 7) ||
+ (regs->u_regs[UREG_FP] > KERNBASE) ||
+ verify_area(VERIFY_READ,
+ (char *) regs->u_regs[UREG_FP],
+ sizeof(struct reg_window)))
+ signal = SIGILL;
+ else
+ memcpy((char *)&tsp->reg_window[0],
+ (char *) regs->u_regs[UREG_FP],
+ sizeof(struct reg_window));
+ if(push==2 && !signal) {
+ unsigned long sp = tsp->reg_window[0].ins[6];
+#ifdef DEBUG_WINFAULT
+ printk(", w[1]sp<%08lx>\n", sp);
+ show_regwindow(&tsp->reg_window[0]);
+#endif
+ if((sp & 7) || (sp > KERNBASE) ||
+ verify_area(VERIFY_READ, (char *) sp,
+ sizeof(struct reg_window)))
+ signal = SIGILL;
+ else
+ memcpy((char *)&tsp->reg_window[1],
+ (char *) sp, sizeof(struct reg_window));
+ }
+ }
+ if(signal) {
+ printk("%s[%d]: User has trashed stack pointer pc<%08lx>sp<%08lx>\n",
+ current->comm, current->pid, regs->pc, regs->u_regs[UREG_FP]);
+ tsp->sig_address = regs->pc;
+ tsp->sig_desc = SUBSIG_STACK;
+ send_sig(signal, current, 1);
+ } else
+ tsp->w_saved = 0;
}
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