summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-11-27 02:40:02 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-11-27 02:40:02 +0000
commita9d7bff9a84dba79609a0002e5321b74c4d64c64 (patch)
tree367fc99c77ad8679039867a7e864f63d9a1f0a3f
parent681da38cd51623972fce0f22d20000d004f423b7 (diff)
Fix vmalloc which got broken by test10. Still fishy, dunno why.
-rw-r--r--arch/mips/mm/fault.c36
1 files changed, 36 insertions, 0 deletions
diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c
index 1d9308848..5d9ecbc4d 100644
--- a/arch/mips/mm/fault.c
+++ b/arch/mips/mm/fault.c
@@ -49,6 +49,18 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long write,
unsigned long fixup;
siginfo_t info;
+ /*
+ * We fault-in kernel-space virtual memory on-demand. The
+ * 'reference' page table is init_mm.pgd.
+ *
+ * NOTE! We MUST NOT take any locks for this case. We may
+ * be in an interrupt or a critical region, and should
+ * only copy the information from the master page table,
+ * nothing more.
+ */
+ if (address >= TASK_SIZE)
+ goto vmalloc_fault;
+
info.si_code = SEGV_MAPERR;
/*
* If we're in an interrupt or have no user
@@ -113,6 +125,8 @@ good_area:
bad_area:
up(&mm->mmap_sem);
+bad_area_nosemaphore:
+ /* User mode accesses just cause a SIGSEGV */
if (user_mode(regs)) {
tsk->thread.cp0_badvaddr = address;
tsk->thread.error_code = write;
@@ -186,4 +200,26 @@ do_sigbus:
/* Kernel mode? Handle exceptions or die */
if (!user_mode(regs))
goto no_context;
+
+ return;
+
+vmalloc_fault:
+ {
+ /*
+ * Synchronize this task's top level page-table
+ * with the 'reference' page table.
+ */
+ int offset = pgd_index(address);
+ pgd_t *pgd, *pgd_k;
+ pmd_t *pmd, *pmd_k;
+
+ pgd = tsk->active_mm->pgd + offset;
+ pgd_k = init_mm.pgd + offset;
+
+ if (!pgd_present(*pgd)) {
+ if (!pgd_present(*pgd_k))
+ goto bad_area_nosemaphore;
+ set_pgd(pgd, *pgd_k);
+ }
+ }
}