summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2001-05-23 12:52:58 +0000
committerRalf Baechle <ralf@linux-mips.org>2001-05-23 12:52:58 +0000
commit5e1f225fc0d4793e1fdc85660b1fdf69095c38f3 (patch)
tree229660073999b8e43dfee99eff59975bc0e7be88
parentb02fba343994b22476ff5daf48028d8f2cf7dc1a (diff)
Fix vmalloc for mips64.
-rw-r--r--arch/mips64/mm/fault.c51
-rw-r--r--include/asm-mips64/pgtable.h7
2 files changed, 51 insertions, 7 deletions
diff --git a/arch/mips64/mm/fault.c b/arch/mips64/mm/fault.c
index 42e810176..a070ae50c 100644
--- a/arch/mips64/mm/fault.c
+++ b/arch/mips64/mm/fault.c
@@ -84,6 +84,18 @@ do_page_fault(struct pt_regs *regs, unsigned long write, unsigned long address)
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
@@ -148,13 +160,7 @@ good_area:
bad_area:
up_read(&mm->mmap_sem);
- /*
- * Quickly check for vmalloc range faults.
- */
- if ((!vma) && (address >= VMALLOC_START) && (address < VMALLOC_END)) {
- printk("Fix vmalloc invalidate fault\n");
- while(1);
- }
+bad_area_nosemaphore:
if (user_mode(regs)) {
tsk->thread.cp0_badvaddr = address;
tsk->thread.error_code = write;
@@ -232,4 +238,35 @@ 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);
+ return;
+ }
+
+ pmd = pmd_offset(pgd, address);
+ pmd_k = pmd_offset(pgd_k, address);
+
+ if (pmd_present(*pmd) || !pmd_present(*pmd_k))
+ goto bad_area_nosemaphore;
+ set_pmd(pmd, *pmd_k);
+ return;
+ }
}
diff --git a/include/asm-mips64/pgtable.h b/include/asm-mips64/pgtable.h
index bd925fd6b..db48fc09e 100644
--- a/include/asm-mips64/pgtable.h
+++ b/include/asm-mips64/pgtable.h
@@ -314,6 +314,13 @@ extern inline void pte_clear(pte_t *ptep)
}
/*
+ * (pmds are folded into pgds so this doesnt get actually called,
+ * but the define is needed for a generic inline function.)
+ */
+#define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval)
+#define set_pgd(pgdptr, pgdval) (*(pgdptr) = pgdval)
+
+/*
* Empty pmd entries point to the invalid_pte_table.
*/
extern inline int pmd_none(pmd_t pmd)