patch-2.1.16 linux/arch/sparc/mm/fault.c
Next file: linux/arch/sparc/mm/init.c
Previous file: linux/arch/sparc/mm/extable.c
Back to the patch index
Back to the overall index
- Lines: 167
- Date:
Fri Dec 13 11:37:31 1996
- Orig file:
v2.1.15/linux/arch/sparc/mm/fault.c
- Orig date:
Tue Nov 12 15:56:04 1996
diff -u --recursive --new-file v2.1.15/linux/arch/sparc/mm/fault.c linux/arch/sparc/mm/fault.c
@@ -1,4 +1,4 @@
-/* $Id: fault.c,v 1.77 1996/10/28 00:56:02 davem Exp $
+/* $Id: fault.c,v 1.84 1996/12/10 06:06:23 davem Exp $
* fault.c: Page fault handlers for the Sparc.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -26,6 +26,7 @@
#include <asm/smp.h>
#include <asm/traps.h>
#include <asm/kdebug.h>
+#include <asm/uaccess.h>
#define ELEMENTS(arr) (sizeof (arr)/sizeof (arr[0]))
@@ -70,8 +71,10 @@
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",
+ printk ("The machine has more banks than "
+ "this kernel can support\n"
+ "Increase the SPARC_PHYS_BANKS "
+ "setting (currently %d)\n",
SPARC_PHYS_BANKS);
i = SPARC_PHYS_BANKS-1;
break;
@@ -134,6 +137,8 @@
struct vm_area_struct *vma;
struct task_struct *tsk = current;
struct mm_struct *mm = tsk->mm;
+ unsigned int fixup;
+ unsigned long g2;
int from_user = !(regs->psr & PSR_PS);
#if 0
static unsigned long last_one;
@@ -162,15 +167,12 @@
#endif
}
#endif
- /* 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. ;-(
+
+ /* The kernel referencing a bad kernel pointer can lock up
+ * a sun4c machine completely, so we must attempt recovery.
*/
- if(!from_user && address >= PAGE_OFFSET) {
- quick_kernel_fault(address);
- return;
- }
+ if(!from_user && address >= PAGE_OFFSET)
+ goto bad_area;
vma = find_vma(mm, address);
if(!vma)
@@ -203,6 +205,18 @@
*/
bad_area:
up(&mm->mmap_sem);
+ /* Is this in ex_table? */
+
+ g2 = regs->u_regs[UREG_G2];
+ if (!from_user && (fixup = search_exception_table (regs->pc, &g2))) {
+ printk("Exception: PC<%08lx> faddr<%08lx>\n", regs->pc, address);
+ printk("EX_TABLE: insn<%08lx> fixup<%08x> g2<%08lx>\n",
+ regs->pc, fixup, g2);
+ regs->pc = fixup;
+ regs->npc = regs->pc + 4;
+ regs->u_regs[UREG_G2] = g2;
+ return;
+ }
/* Did we have an exception handler installed? */
if(current->tss.ex.count == 1) {
if(from_user) {
@@ -235,10 +249,12 @@
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(KERN_ALERT " at virtual address %08lx\n",address);
+ printk(KERN_ALERT "Unable to handle kernel NULL "
+ "pointer dereference");
+ } else {
+ printk(KERN_ALERT "Unable to handle kernel paging request "
+ "at virtual address %08lx\n", address);
+ }
printk(KERN_ALERT "tsk->mm->context = %08lx\n",
(unsigned long) tsk->mm->context);
printk(KERN_ALERT "tsk->mm->pgd = %08lx\n",
@@ -249,27 +265,62 @@
asmlinkage void do_sun4c_fault(struct pt_regs *regs, int text_fault, int write,
unsigned long address)
{
- extern void sun4c_update_mmu_cache(struct vm_area_struct *,unsigned long,pte_t);
+ extern void sun4c_update_mmu_cache(struct vm_area_struct *,
+ unsigned long,pte_t);
extern pgd_t *sun4c_pgd_offset(struct mm_struct *,unsigned long);
extern pte_t *sun4c_pte_offset(pmd_t *,unsigned long);
struct task_struct *tsk = current;
struct mm_struct *mm = tsk->mm;
- pgd_t *pgd;
- pte_t *pte;
+ pgd_t *pgdp;
+ pte_t *ptep;
- if(text_fault)
+ if (text_fault)
address = regs->pc;
- pgd = sun4c_pgd_offset(mm, address);
- pte = sun4c_pte_offset((pmd_t *) pgd, address);
+ pgdp = sun4c_pgd_offset(mm, address);
+ ptep = sun4c_pte_offset((pmd_t *) pgdp, address);
+
+ if (pgd_val(*pgdp)) {
+ if (write) {
+ if ((pte_val(*ptep) & (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT))
+ == (_SUN4C_PAGE_WRITE|_SUN4C_PAGE_PRESENT)) {
+
+ pte_val(*ptep) |= (_SUN4C_PAGE_ACCESSED |
+ _SUN4C_PAGE_MODIFIED |
+ _SUN4C_PAGE_VALID |
+ _SUN4C_PAGE_DIRTY);
+
+ if (sun4c_get_segmap(address) != invalid_segment) {
+ sun4c_put_pte(address, pte_val(*ptep));
+ return;
+ }
+ }
+ } else {
+ if ((pte_val(*ptep) & (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT))
+ == (_SUN4C_PAGE_READ|_SUN4C_PAGE_PRESENT)) {
+
+ pte_val(*ptep) |= (_SUN4C_PAGE_ACCESSED |
+ _SUN4C_PAGE_VALID);
+
+ if (sun4c_get_segmap(address) != invalid_segment) {
+ sun4c_put_pte(address, pte_val(*ptep));
+ return;
+ }
+ }
+ }
+ }
/* This conditional is 'interesting'. */
- if(pgd_val(*pgd) && !(write && !(pte_val(*pte) & _SUN4C_PAGE_WRITE))
- && (pte_val(*pte) & _SUN4C_PAGE_VALID))
- /* XXX Very bad, can't do this optimization when VMA arg is actually
- * XXX used by update_mmu_cache()!
+ if (pgd_val(*pgdp) && !(write && !(pte_val(*ptep) & _SUN4C_PAGE_WRITE))
+ && (pte_val(*ptep) & _SUN4C_PAGE_VALID))
+ /* Note: It is safe to not grab the MMAP semaphore here because
+ * we know that update_mmu_cache() will not sleep for
+ * any reason (at least not in the current implementation)
+ * and therefore there is no danger of another thread getting
+ * on the CPU and doing a shrink_mmap() on this vma.
*/
- sun4c_update_mmu_cache((struct vm_area_struct *) 0, address, *pte);
+ sun4c_update_mmu_cache (find_vma(current->mm, address), address,
+ *ptep);
else
do_sparc_fault(regs, text_fault, write, address);
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov