summaryrefslogtreecommitdiffstats
path: root/arch/sh/mm/fault.c
diff options
context:
space:
mode:
authorStuart Menefy <stuart.menefy@st.com>2006-11-21 15:38:05 +0900
committerPaul Mundt <lethal@linux-sh.org>2006-12-06 10:45:38 +0900
commit99a596f93be10001c50894bcab69e458a49a3b8c (patch)
tree16faf593cc3f7a2c39f3079f0f64f410e530e1c4 /arch/sh/mm/fault.c
parent6e4662ff49c6b94e16a47bfddb920576963b5a20 (diff)
sh: pmd rework.
Remove extra bits from the pmd structure and store a kernel logical address rather than a physical address. This allows it to be directly dereferenced. Another piece of wierdness inherited from x86. Signed-off-by: Stuart Menefy <stuart.menefy@st.com> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/mm/fault.c')
-rw-r--r--arch/sh/mm/fault.c40
1 files changed, 40 insertions, 0 deletions
diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c
index 43bed2cb00e..128907ef7fc 100644
--- a/arch/sh/mm/fault.c
+++ b/arch/sh/mm/fault.c
@@ -46,6 +46,45 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
mm = tsk->mm;
si_code = SEGV_MAPERR;
+ if (unlikely(address >= TASK_SIZE)) {
+ /*
+ * Synchronize this task's top level page-table
+ * with the 'reference' page table.
+ *
+ * Do _not_ use "tsk" here. We might be inside
+ * an interrupt in the middle of a task switch..
+ */
+ int offset = pgd_index(address);
+ pgd_t *pgd, *pgd_k;
+ pud_t *pud, *pud_k;
+ pmd_t *pmd, *pmd_k;
+
+ pgd = get_TTB() + offset;
+ pgd_k = swapper_pg_dir + offset;
+
+ /* This will never happen with the folded page table. */
+ if (!pgd_present(*pgd)) {
+ if (!pgd_present(*pgd_k))
+ goto bad_area_nosemaphore;
+ set_pgd(pgd, *pgd_k);
+ return;
+ }
+
+ pud = pud_offset(pgd, address);
+ pud_k = pud_offset(pgd_k, address);
+ if (pud_present(*pud) || !pud_present(*pud_k))
+ goto bad_area_nosemaphore;
+ set_pud(pud, *pud_k);
+
+ pmd = pmd_offset(pud, address);
+ pmd_k = pmd_offset(pud_k, address);
+ if (pmd_present(*pmd) || !pmd_present(*pmd_k))
+ goto bad_area_nosemaphore;
+ set_pmd(pmd, *pmd_k);
+
+ return;
+ }
+
/*
* If we're in an interrupt or have no user
* context, we must not take the fault..
@@ -109,6 +148,7 @@ survive:
bad_area:
up_read(&mm->mmap_sem);
+bad_area_nosemaphore:
if (user_mode(regs)) {
info.si_signo = SIGSEGV;
info.si_errno = 0;