diff --git a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c index 43bed2cb00e38c73c0c10a8e78e405477f7c6faa..128907ef7fcd69ca4cd66e5784f91c1aca21a799 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 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, bad_area: up_read(&mm->mmap_sem); +bad_area_nosemaphore: if (user_mode(regs)) { info.si_signo = SIGSEGV; info.si_errno = 0; diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index 8c8d39118387ec451c1000e851af8cf5d9a1ad7b..462bfeac6d9ced7b79ee161e944a7f0a016f47b5 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -84,30 +84,22 @@ static void set_pte_phys(unsigned long addr, unsigned long phys, pgprot_t prot) pmd_t *pmd; pte_t *pte; - pgd = swapper_pg_dir + pgd_index(addr); + pgd = pgd_offset_k(addr); if (pgd_none(*pgd)) { pgd_ERROR(*pgd); return; } - pud = pud_offset(pgd, addr); - if (pud_none(*pud)) { - pmd = (pmd_t *)get_zeroed_page(GFP_ATOMIC); - set_pud(pud, __pud(__pa(pmd) | _PAGE_TABLE)); - if (pmd != pmd_offset(pud, 0)) { - pud_ERROR(*pud); - return; - } + pud = pud_alloc(NULL, pgd, addr); + if (unlikely(!pud)) { + pud_ERROR(*pud); + return; } - pmd = pmd_offset(pud, addr); - if (pmd_none(*pmd)) { - pte = (pte_t *)get_zeroed_page(GFP_ATOMIC); - set_pmd(pmd, __pmd(__pa(pte) | _PAGE_TABLE)); - if (pte != pte_offset_kernel(pmd, 0)) { - pmd_ERROR(*pmd); - return; - } + pmd = pmd_alloc(NULL, pud, addr); + if (unlikely(!pmd)) { + pmd_ERROR(*pmd); + return; } pte = pte_offset_kernel(pmd, addr); diff --git a/include/asm-sh/pgalloc.h b/include/asm-sh/pgalloc.h index e841465ab4d24214955704852dde0b99a3da35e8..888e4529e6fe3c4bb2108f134b8b7674435836c3 100644 --- a/include/asm-sh/pgalloc.h +++ b/include/asm-sh/pgalloc.h @@ -1,13 +1,16 @@ #ifndef __ASM_SH_PGALLOC_H #define __ASM_SH_PGALLOC_H -#define pmd_populate_kernel(mm, pmd, pte) \ - set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte))) +static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmd, + pte_t *pte) +{ + set_pmd(pmd, __pmd((unsigned long)pte)); +} static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, struct page *pte) { - set_pmd(pmd, __pmd(_PAGE_TABLE + page_to_phys(pte))); + set_pmd(pmd, __pmd((unsigned long)page_address(pte))); } /* @@ -15,7 +18,16 @@ static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, */ static inline pgd_t *pgd_alloc(struct mm_struct *mm) { - return (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_REPEAT | __GFP_ZERO); + pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_REPEAT); + + if (pgd) { + memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); + memcpy(pgd + USER_PTRS_PER_PGD, + swapper_pg_dir + USER_PTRS_PER_PGD, + (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t)); + } + + return pgd; } static inline void pgd_free(pgd_t *pgd) diff --git a/include/asm-sh/pgtable.h b/include/asm-sh/pgtable.h index 22c3d0b3e11aad1b0e10eacf7924b0a291075bdb..b1f21e765640202e5e3a59617b0d7886145752db 100644 --- a/include/asm-sh/pgtable.h +++ b/include/asm-sh/pgtable.h @@ -203,26 +203,18 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)]; #ifndef __ASSEMBLY__ #if defined(CONFIG_X2TLB) /* SH-X2 TLB */ -#define _PAGE_TABLE \ - (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY | \ - _PAGE_EXT(_PAGE_EXT_USER_READ | _PAGE_EXT_USER_WRITE)) - -#define _KERNPG_TABLE \ - (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY | \ - _PAGE_EXT(_PAGE_EXT_KERN_READ | _PAGE_EXT_KERN_WRITE)) - #define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_CACHABLE | \ _PAGE_ACCESSED | _PAGE_FLAGS_HARD) #define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | \ _PAGE_CACHABLE | _PAGE_FLAGS_HARD | \ _PAGE_EXT(_PAGE_EXT_USER_READ | \ - _PAGE_EXT_USER_WRITE)) + _PAGE_EXT_USER_WRITE)) #define PAGE_EXECREAD __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | \ _PAGE_CACHABLE | _PAGE_FLAGS_HARD | \ _PAGE_EXT(_PAGE_EXT_USER_EXEC | \ - _PAGE_EXT_USER_READ)) + _PAGE_EXT_USER_READ)) #define PAGE_COPY PAGE_EXECREAD @@ -237,14 +229,14 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)]; #define PAGE_RWX __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | \ _PAGE_CACHABLE | _PAGE_FLAGS_HARD | \ _PAGE_EXT(_PAGE_EXT_USER_WRITE | \ - _PAGE_EXT_USER_READ | \ + _PAGE_EXT_USER_READ | \ _PAGE_EXT_USER_EXEC)) #define PAGE_KERNEL __pgprot(_PAGE_PRESENT | _PAGE_CACHABLE | \ _PAGE_DIRTY | _PAGE_ACCESSED | \ _PAGE_HW_SHARED | _PAGE_FLAGS_HARD | \ _PAGE_EXT(_PAGE_EXT_KERN_READ | \ - _PAGE_EXT_KERN_WRITE | \ + _PAGE_EXT_KERN_WRITE | \ _PAGE_EXT_KERN_EXEC)) #define PAGE_KERNEL_NOCACHE \ @@ -252,30 +244,25 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)]; _PAGE_ACCESSED | _PAGE_HW_SHARED | \ _PAGE_FLAGS_HARD | \ _PAGE_EXT(_PAGE_EXT_KERN_READ | \ - _PAGE_EXT_KERN_WRITE | \ + _PAGE_EXT_KERN_WRITE | \ _PAGE_EXT_KERN_EXEC)) #define PAGE_KERNEL_RO __pgprot(_PAGE_PRESENT | _PAGE_CACHABLE | \ _PAGE_DIRTY | _PAGE_ACCESSED | \ _PAGE_HW_SHARED | _PAGE_FLAGS_HARD | \ _PAGE_EXT(_PAGE_EXT_KERN_READ | \ - _PAGE_EXT_KERN_EXEC)) + _PAGE_EXT_KERN_EXEC)) #define PAGE_KERNEL_PCC(slot, type) \ __pgprot(_PAGE_PRESENT | _PAGE_DIRTY | \ _PAGE_ACCESSED | _PAGE_FLAGS_HARD | \ _PAGE_EXT(_PAGE_EXT_KERN_READ | \ - _PAGE_EXT_KERN_WRITE | \ + _PAGE_EXT_KERN_WRITE | \ _PAGE_EXT_KERN_EXEC) \ (slot ? _PAGE_PCC_AREA5 : _PAGE_PCC_AREA6) | \ (type)) #elif defined(CONFIG_MMU) /* SH-X TLB */ -#define _PAGE_TABLE \ - (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY) -#define _KERNPG_TABLE \ - (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY) - #define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_CACHABLE | \ _PAGE_ACCESSED | _PAGE_FLAGS_HARD) @@ -390,9 +377,9 @@ static inline void set_pte(pte_t *ptep, pte_t pte) #define pte_clear(mm,addr,xp) do { set_pte_at(mm, addr, xp, __pte(0)); } while (0) #define pmd_none(x) (!pmd_val(x)) -#define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT) +#define pmd_present(x) (pmd_val(x)) #define pmd_clear(xp) do { set_pmd(xp, __pmd(0)); } while (0) -#define pmd_bad(x) ((pmd_val(x) & (~PAGE_MASK & ~_PAGE_USER)) != _KERNPG_TABLE) +#define pmd_bad(x) (pmd_val(x) & ~PAGE_MASK) #define pages_to_mb(x) ((x) >> (20-PAGE_SHIFT)) #define pte_page(x) phys_to_page(pte_val(x)&PTE_PHYS_MASK) @@ -477,11 +464,8 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) return pte; } -#define pmd_page_vaddr(pmd) \ -((unsigned long) __va(pmd_val(pmd) & PAGE_MASK)) - -#define pmd_page(pmd) \ - (phys_to_page(pmd_val(pmd))) +#define pmd_page_vaddr(pmd) pmd_val(pmd) +#define pmd_page(pmd) (virt_to_page(pmd_val(pmd))) /* to find an entry in a page-table-directory. */ #define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))