]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-rmk' of git://git.kernel.org/pub/scm/linux/kernel/git/cmarinas...
authorRussell King <rmk+kernel@arm.linux.org.uk>
Thu, 8 Dec 2011 18:02:04 +0000 (18:02 +0000)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Thu, 8 Dec 2011 18:02:04 +0000 (18:02 +0000)
Conflicts:
arch/arm/mm/ioremap.c

32 files changed:
arch/arm/Kconfig
arch/arm/boot/compressed/head.S
arch/arm/include/asm/assembler.h
arch/arm/include/asm/page.h
arch/arm/include/asm/pgalloc.h
arch/arm/include/asm/pgtable-2level.h
arch/arm/include/asm/pgtable-3level-hwdef.h [new file with mode: 0644]
arch/arm/include/asm/pgtable-3level-types.h [new file with mode: 0644]
arch/arm/include/asm/pgtable-3level.h [new file with mode: 0644]
arch/arm/include/asm/pgtable-hwdef.h
arch/arm/include/asm/pgtable.h
arch/arm/include/asm/proc-fns.h
arch/arm/include/asm/system.h
arch/arm/include/asm/tlb.h
arch/arm/kernel/head.S
arch/arm/kernel/hw_breakpoint.c
arch/arm/kernel/sleep.S
arch/arm/mm/Kconfig
arch/arm/mm/alignment.c
arch/arm/mm/context.c
arch/arm/mm/fault.c
arch/arm/mm/fault.h
arch/arm/mm/fsr-2level.c [new file with mode: 0644]
arch/arm/mm/fsr-3level.c [new file with mode: 0644]
arch/arm/mm/idmap.c
arch/arm/mm/ioremap.c
arch/arm/mm/mmu.c
arch/arm/mm/pgd.c
arch/arm/mm/proc-macros.S
arch/arm/mm/proc-v7-2level.S [new file with mode: 0644]
arch/arm/mm/proc-v7-3level.S [new file with mode: 0644]
arch/arm/mm/proc-v7.S

index e084b7e981e8ff301aaae64ef3a2dc7e66701247..4b01d71bdbd1a1aea1ec4fc1024cb88da9a9e1d4 100644 (file)
@@ -1971,7 +1971,7 @@ endchoice
 
 config XIP_KERNEL
        bool "Kernel Execute-In-Place from ROM"
-       depends on !ZBOOT_ROM
+       depends on !ZBOOT_ROM && !ARM_LPAE
        help
          Execute-In-Place allows the kernel to run from non-volatile storage
          directly addressable by the CPU, such as NOR flash. This saves RAM
index c2effc91725452dcaa93af7b3dc1b0ce20d979c2..c5d60250d43daf44205cd830de3ca6ed941ed348 100644 (file)
@@ -659,6 +659,7 @@ __armv7_mmu_cache_on:
                mcrne   p15, 0, r3, c2, c0, 0   @ load page table pointer
                mcrne   p15, 0, r1, c3, c0, 0   @ load domain access control
 #endif
+               mcr     p15, 0, r0, c7, c5, 4   @ ISB
                mcr     p15, 0, r0, c1, c0, 0   @ load control register
                mrc     p15, 0, r0, c1, c0, 0   @ and read it back
                mov     r0, #0
index 29035e86a59db0d4c46ab315a9350780dbfe2999..b6e65dedfd716db5e336451868af811e34a684dc 100644 (file)
 #define ALT_UP_B(label) b label
 #endif
 
+/*
+ * Instruction barrier
+ */
+       .macro  instr_sync
+#if __LINUX_ARM_ARCH__ >= 7
+       isb
+#elif __LINUX_ARM_ARCH__ == 6
+       mcr     p15, 0, r0, c7, c5, 4
+#endif
+       .endm
+
 /*
  * SMP data memory barrier
  */
index ca94653f1ecbd7ebcbab8079db0d0b74de497ea8..97b440c25c5855977481a40ba18977f1f988dd09 100644 (file)
@@ -151,7 +151,11 @@ extern void __cpu_copy_user_highpage(struct page *to, struct page *from,
 #define clear_page(page)       memset((void *)(page), 0, PAGE_SIZE)
 extern void copy_page(void *to, const void *from);
 
+#ifdef CONFIG_ARM_LPAE
+#include <asm/pgtable-3level-types.h>
+#else
 #include <asm/pgtable-2level-types.h>
+#endif
 
 #endif /* CONFIG_MMU */
 
index 3e08fd3fbb6bc5772688b8001aebfb5ad1e16acd..943504f53f579e58c878165c595d5a110dae00ed 100644 (file)
 #define _PAGE_USER_TABLE       (PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_USER))
 #define _PAGE_KERNEL_TABLE     (PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_KERNEL))
 
+#ifdef CONFIG_ARM_LPAE
+
+static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
+{
+       return (pmd_t *)get_zeroed_page(GFP_KERNEL | __GFP_REPEAT);
+}
+
+static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
+{
+       BUG_ON((unsigned long)pmd & (PAGE_SIZE-1));
+       free_page((unsigned long)pmd);
+}
+
+static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
+{
+       set_pud(pud, __pud(__pa(pmd) | PMD_TYPE_TABLE));
+}
+
+#else  /* !CONFIG_ARM_LPAE */
+
 /*
  * Since we have only two-level page tables, these are trivial
  */
 #define pmd_alloc_one(mm,addr)         ({ BUG(); ((pmd_t *)2); })
 #define pmd_free(mm, pmd)              do { } while (0)
-#define pgd_populate(mm,pmd,pte)       BUG()
+#define pud_populate(mm,pmd,pte)       BUG()
+
+#endif /* CONFIG_ARM_LPAE */
 
 extern pgd_t *pgd_alloc(struct mm_struct *mm);
 extern void pgd_free(struct mm_struct *mm, pgd_t *pgd);
@@ -109,7 +131,9 @@ static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte,
 {
        pmdval_t pmdval = (pte + PTE_HWTABLE_OFF) | prot;
        pmdp[0] = __pmd(pmdval);
+#ifndef CONFIG_ARM_LPAE
        pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));
+#endif
        flush_pmd_entry(pmdp);
 }
 
index 470457e1cfc5e121158037e6a1db6fead1eb3602..2317a71c8f8ec4c2cbc5404d96d1c3953877eda8 100644 (file)
 #define L_PTE_MT_DEV_CACHED    (_AT(pteval_t, 0x0b) << 2)      /* 1011 */
 #define L_PTE_MT_MASK          (_AT(pteval_t, 0x0f) << 2)
 
+#ifndef __ASSEMBLY__
+
+/*
+ * The "pud_xxx()" functions here are trivial when the pmd is folded into
+ * the pud: the pud entry is never bad, always exists, and can't be set or
+ * cleared.
+ */
+#define pud_none(pud)          (0)
+#define pud_bad(pud)           (0)
+#define pud_present(pud)       (1)
+#define pud_clear(pudp)                do { } while (0)
+#define set_pud(pud,pudp)      do { } while (0)
+
+static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
+{
+       return (pmd_t *)pud;
+}
+
+#define pmd_bad(pmd)           (pmd_val(pmd) & 2)
+
+#define copy_pmd(pmdpd,pmdps)          \
+       do {                            \
+               pmdpd[0] = pmdps[0];    \
+               pmdpd[1] = pmdps[1];    \
+               flush_pmd_entry(pmdpd); \
+       } while (0)
+
+#define pmd_clear(pmdp)                        \
+       do {                            \
+               pmdp[0] = __pmd(0);     \
+               pmdp[1] = __pmd(0);     \
+               clean_pmd_entry(pmdp);  \
+       } while (0)
+
+/* we don't need complex calculations here as the pmd is folded into the pgd */
+#define pmd_addr_end(addr,end) (end)
+
+#define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)
+
+#endif /* __ASSEMBLY__ */
+
 #endif /* _ASM_PGTABLE_2LEVEL_H */
diff --git a/arch/arm/include/asm/pgtable-3level-hwdef.h b/arch/arm/include/asm/pgtable-3level-hwdef.h
new file mode 100644 (file)
index 0000000..d795282
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * arch/arm/include/asm/pgtable-3level-hwdef.h
+ *
+ * Copyright (C) 2011 ARM Ltd.
+ * Author: Catalin Marinas <catalin.marinas@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _ASM_PGTABLE_3LEVEL_HWDEF_H
+#define _ASM_PGTABLE_3LEVEL_HWDEF_H
+
+/*
+ * Hardware page table definitions.
+ *
+ * + Level 1/2 descriptor
+ *   - common
+ */
+#define PMD_TYPE_MASK          (_AT(pmdval_t, 3) << 0)
+#define PMD_TYPE_FAULT         (_AT(pmdval_t, 0) << 0)
+#define PMD_TYPE_TABLE         (_AT(pmdval_t, 3) << 0)
+#define PMD_TYPE_SECT          (_AT(pmdval_t, 1) << 0)
+#define PMD_BIT4               (_AT(pmdval_t, 0))
+#define PMD_DOMAIN(x)          (_AT(pmdval_t, 0))
+
+/*
+ *   - section
+ */
+#define PMD_SECT_BUFFERABLE    (_AT(pmdval_t, 1) << 2)
+#define PMD_SECT_CACHEABLE     (_AT(pmdval_t, 1) << 3)
+#define PMD_SECT_S             (_AT(pmdval_t, 3) << 8)
+#define PMD_SECT_AF            (_AT(pmdval_t, 1) << 10)
+#define PMD_SECT_nG            (_AT(pmdval_t, 1) << 11)
+#define PMD_SECT_XN            (_AT(pmdval_t, 1) << 54)
+#define PMD_SECT_AP_WRITE      (_AT(pmdval_t, 0))
+#define PMD_SECT_AP_READ       (_AT(pmdval_t, 0))
+#define PMD_SECT_TEX(x)                (_AT(pmdval_t, 0))
+
+/*
+ * AttrIndx[2:0] encoding (mapping attributes defined in the MAIR* registers).
+ */
+#define PMD_SECT_UNCACHED      (_AT(pmdval_t, 0) << 2) /* strongly ordered */
+#define PMD_SECT_BUFFERED      (_AT(pmdval_t, 1) << 2) /* normal non-cacheable */
+#define PMD_SECT_WT            (_AT(pmdval_t, 2) << 2) /* normal inner write-through */
+#define PMD_SECT_WB            (_AT(pmdval_t, 3) << 2) /* normal inner write-back */
+#define PMD_SECT_WBWA          (_AT(pmdval_t, 7) << 2) /* normal inner write-alloc */
+
+/*
+ * + Level 3 descriptor (PTE)
+ */
+#define PTE_TYPE_MASK          (_AT(pteval_t, 3) << 0)
+#define PTE_TYPE_FAULT         (_AT(pteval_t, 0) << 0)
+#define PTE_TYPE_PAGE          (_AT(pteval_t, 3) << 0)
+#define PTE_BUFFERABLE         (_AT(pteval_t, 1) << 2)         /* AttrIndx[0] */
+#define PTE_CACHEABLE          (_AT(pteval_t, 1) << 3)         /* AttrIndx[1] */
+#define PTE_EXT_SHARED         (_AT(pteval_t, 3) << 8)         /* SH[1:0], inner shareable */
+#define PTE_EXT_AF             (_AT(pteval_t, 1) << 10)        /* Access Flag */
+#define PTE_EXT_NG             (_AT(pteval_t, 1) << 11)        /* nG */
+#define PTE_EXT_XN             (_AT(pteval_t, 1) << 54)        /* XN */
+
+/*
+ * 40-bit physical address supported.
+ */
+#define PHYS_MASK_SHIFT                (40)
+#define PHYS_MASK              ((1ULL << PHYS_MASK_SHIFT) - 1)
+
+#endif
diff --git a/arch/arm/include/asm/pgtable-3level-types.h b/arch/arm/include/asm/pgtable-3level-types.h
new file mode 100644 (file)
index 0000000..921aa30
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * arch/arm/include/asm/pgtable-3level-types.h
+ *
+ * Copyright (C) 2011 ARM Ltd.
+ * Author: Catalin Marinas <catalin.marinas@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _ASM_PGTABLE_3LEVEL_TYPES_H
+#define _ASM_PGTABLE_3LEVEL_TYPES_H
+
+#include <asm/types.h>
+
+typedef u64 pteval_t;
+typedef u64 pmdval_t;
+typedef u64 pgdval_t;
+
+#undef STRICT_MM_TYPECHECKS
+
+#ifdef STRICT_MM_TYPECHECKS
+
+/*
+ * These are used to make use of C type-checking..
+ */
+typedef struct { pteval_t pte; } pte_t;
+typedef struct { pmdval_t pmd; } pmd_t;
+typedef struct { pgdval_t pgd; } pgd_t;
+typedef struct { pteval_t pgprot; } pgprot_t;
+
+#define pte_val(x)      ((x).pte)
+#define pmd_val(x)      ((x).pmd)
+#define pgd_val(x)     ((x).pgd)
+#define pgprot_val(x)   ((x).pgprot)
+
+#define __pte(x)        ((pte_t) { (x) } )
+#define __pmd(x)        ((pmd_t) { (x) } )
+#define __pgd(x)       ((pgd_t) { (x) } )
+#define __pgprot(x)     ((pgprot_t) { (x) } )
+
+#else  /* !STRICT_MM_TYPECHECKS */
+
+typedef pteval_t pte_t;
+typedef pmdval_t pmd_t;
+typedef pgdval_t pgd_t;
+typedef pteval_t pgprot_t;
+
+#define pte_val(x)     (x)
+#define pmd_val(x)     (x)
+#define pgd_val(x)     (x)
+#define pgprot_val(x)  (x)
+
+#define __pte(x)       (x)
+#define __pmd(x)       (x)
+#define __pgd(x)       (x)
+#define __pgprot(x)    (x)
+
+#endif /* STRICT_MM_TYPECHECKS */
+
+#endif /* _ASM_PGTABLE_3LEVEL_TYPES_H */
diff --git a/arch/arm/include/asm/pgtable-3level.h b/arch/arm/include/asm/pgtable-3level.h
new file mode 100644 (file)
index 0000000..759af70
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * arch/arm/include/asm/pgtable-3level.h
+ *
+ * Copyright (C) 2011 ARM Ltd.
+ * Author: Catalin Marinas <catalin.marinas@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _ASM_PGTABLE_3LEVEL_H
+#define _ASM_PGTABLE_3LEVEL_H
+
+/*
+ * With LPAE, there are 3 levels of page tables. Each level has 512 entries of
+ * 8 bytes each, occupying a 4K page. The first level table covers a range of
+ * 512GB, each entry representing 1GB. Since we are limited to 4GB input
+ * address range, only 4 entries in the PGD are used.
+ *
+ * There are enough spare bits in a page table entry for the kernel specific
+ * state.
+ */
+#define PTRS_PER_PTE           512
+#define PTRS_PER_PMD           512
+#define PTRS_PER_PGD           4
+
+#define PTE_HWTABLE_PTRS       (PTRS_PER_PTE)
+#define PTE_HWTABLE_OFF                (0)
+#define PTE_HWTABLE_SIZE       (PTRS_PER_PTE * sizeof(u64))
+
+/*
+ * PGDIR_SHIFT determines the size a top-level page table entry can map.
+ */
+#define PGDIR_SHIFT            30
+
+/*
+ * PMD_SHIFT determines the size a middle-level page table entry can map.
+ */
+#define PMD_SHIFT              21
+
+#define PMD_SIZE               (1UL << PMD_SHIFT)
+#define PMD_MASK               (~(PMD_SIZE-1))
+#define PGDIR_SIZE             (1UL << PGDIR_SHIFT)
+#define PGDIR_MASK             (~(PGDIR_SIZE-1))
+
+/*
+ * section address mask and size definitions.
+ */
+#define SECTION_SHIFT          21
+#define SECTION_SIZE           (1UL << SECTION_SHIFT)
+#define SECTION_MASK           (~(SECTION_SIZE-1))
+
+#define USER_PTRS_PER_PGD      (PAGE_OFFSET / PGDIR_SIZE)
+
+/*
+ * "Linux" PTE definitions for LPAE.
+ *
+ * These bits overlap with the hardware bits but the naming is preserved for
+ * consistency with the classic page table format.
+ */
+#define L_PTE_PRESENT          (_AT(pteval_t, 3) << 0)         /* Valid */
+#define L_PTE_FILE             (_AT(pteval_t, 1) << 2)         /* only when !PRESENT */
+#define L_PTE_BUFFERABLE       (_AT(pteval_t, 1) << 2)         /* AttrIndx[0] */
+#define L_PTE_CACHEABLE                (_AT(pteval_t, 1) << 3)         /* AttrIndx[1] */
+#define L_PTE_USER             (_AT(pteval_t, 1) << 6)         /* AP[1] */
+#define L_PTE_RDONLY           (_AT(pteval_t, 1) << 7)         /* AP[2] */
+#define L_PTE_SHARED           (_AT(pteval_t, 3) << 8)         /* SH[1:0], inner shareable */
+#define L_PTE_YOUNG            (_AT(pteval_t, 1) << 10)        /* AF */
+#define L_PTE_XN               (_AT(pteval_t, 1) << 54)        /* XN */
+#define L_PTE_DIRTY            (_AT(pteval_t, 1) << 55)        /* unused */
+#define L_PTE_SPECIAL          (_AT(pteval_t, 1) << 56)        /* unused */
+
+/*
+ * To be used in assembly code with the upper page attributes.
+ */
+#define L_PTE_XN_HIGH          (1 << (54 - 32))
+#define L_PTE_DIRTY_HIGH       (1 << (55 - 32))
+
+/*
+ * AttrIndx[2:0] encoding (mapping attributes defined in the MAIR* registers).
+ */
+#define L_PTE_MT_UNCACHED      (_AT(pteval_t, 0) << 2) /* strongly ordered */
+#define L_PTE_MT_BUFFERABLE    (_AT(pteval_t, 1) << 2) /* normal non-cacheable */
+#define L_PTE_MT_WRITETHROUGH  (_AT(pteval_t, 2) << 2) /* normal inner write-through */
+#define L_PTE_MT_WRITEBACK     (_AT(pteval_t, 3) << 2) /* normal inner write-back */
+#define L_PTE_MT_WRITEALLOC    (_AT(pteval_t, 7) << 2) /* normal inner write-alloc */
+#define L_PTE_MT_DEV_SHARED    (_AT(pteval_t, 4) << 2) /* device */
+#define L_PTE_MT_DEV_NONSHARED (_AT(pteval_t, 4) << 2) /* device */
+#define L_PTE_MT_DEV_WC                (_AT(pteval_t, 1) << 2) /* normal non-cacheable */
+#define L_PTE_MT_DEV_CACHED    (_AT(pteval_t, 3) << 2) /* normal inner write-back */
+#define L_PTE_MT_MASK          (_AT(pteval_t, 7) << 2)
+
+/*
+ * Software PGD flags.
+ */
+#define L_PGD_SWAPPER          (_AT(pgdval_t, 1) << 55)        /* swapper_pg_dir entry */
+
+#ifndef __ASSEMBLY__
+
+#define pud_none(pud)          (!pud_val(pud))
+#define pud_bad(pud)           (!(pud_val(pud) & 2))
+#define pud_present(pud)       (pud_val(pud))
+
+#define pud_clear(pudp)                        \
+       do {                            \
+               *pudp = __pud(0);       \
+               clean_pmd_entry(pudp);  \
+       } while (0)
+
+#define set_pud(pudp, pud)             \
+       do {                            \
+               *pudp = pud;            \
+               flush_pmd_entry(pudp);  \
+       } while (0)
+
+static inline pmd_t *pud_page_vaddr(pud_t pud)
+{
+       return __va(pud_val(pud) & PHYS_MASK & (s32)PAGE_MASK);
+}
+
+/* Find an entry in the second-level page table.. */
+#define pmd_index(addr)                (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))
+static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
+{
+       return (pmd_t *)pud_page_vaddr(*pud) + pmd_index(addr);
+}
+
+#define pmd_bad(pmd)           (!(pmd_val(pmd) & 2))
+
+#define copy_pmd(pmdpd,pmdps)          \
+       do {                            \
+               *pmdpd = *pmdps;        \
+               flush_pmd_entry(pmdpd); \
+       } while (0)
+
+#define pmd_clear(pmdp)                        \
+       do {                            \
+               *pmdp = __pmd(0);       \
+               clean_pmd_entry(pmdp);  \
+       } while (0)
+
+#define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,__pte(pte_val(pte)|(ext)))
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_PGTABLE_3LEVEL_H */
index 183111164ce93cc3c3ec3e1502c9b4fb8c53934c..8426229ba29282582a20bf65a2c08253f914edf3 100644 (file)
 #ifndef _ASMARM_PGTABLE_HWDEF_H
 #define _ASMARM_PGTABLE_HWDEF_H
 
+#ifdef CONFIG_ARM_LPAE
+#include <asm/pgtable-3level-hwdef.h>
+#else
 #include <asm/pgtable-2level-hwdef.h>
+#endif
 
 #endif
index a784859cc7a93d671c9c8578ec922908750fa2e0..3f2f0eb7621112daf7f7bf93a2f12d22582f9de3 100644 (file)
 #define _ASMARM_PGTABLE_H
 
 #include <linux/const.h>
-#include <asm-generic/4level-fixup.h>
 #include <asm/proc-fns.h>
 
 #ifndef CONFIG_MMU
 
+#include <asm-generic/4level-fixup.h>
 #include "pgtable-nommu.h"
 
 #else
 
+#include <asm-generic/pgtable-nopud.h>
 #include <asm/memory.h>
 #include <asm/pgtable-hwdef.h>
 
+#ifdef CONFIG_ARM_LPAE
+#include <asm/pgtable-3level.h>
+#else
 #include <asm/pgtable-2level.h>
+#endif
 
 /*
  * Just any arbitrary offset to the start of the vmalloc VM area: the
@@ -164,39 +169,8 @@ extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
 /* to find an entry in a kernel page-table-directory */
 #define pgd_offset_k(addr)     pgd_offset(&init_mm, addr)
 
-/*
- * The "pgd_xxx()" functions here are trivial for a folded two-level
- * setup: the pgd is never bad, and a pmd always exists (as it's folded
- * into the pgd entry)
- */
-#define pgd_none(pgd)          (0)
-#define pgd_bad(pgd)           (0)
-#define pgd_present(pgd)       (1)
-#define pgd_clear(pgdp)                do { } while (0)
-#define set_pgd(pgd,pgdp)      do { } while (0)
-#define set_pud(pud,pudp)      do { } while (0)
-
-
-/* Find an entry in the second-level page table.. */
-#define pmd_offset(dir, addr)  ((pmd_t *)(dir))
-
 #define pmd_none(pmd)          (!pmd_val(pmd))
 #define pmd_present(pmd)       (pmd_val(pmd))
-#define pmd_bad(pmd)           (pmd_val(pmd) & 2)
-
-#define copy_pmd(pmdpd,pmdps)          \
-       do {                            \
-               pmdpd[0] = pmdps[0];    \
-               pmdpd[1] = pmdps[1];    \
-               flush_pmd_entry(pmdpd); \
-       } while (0)
-
-#define pmd_clear(pmdp)                        \
-       do {                            \
-               pmdp[0] = __pmd(0);     \
-               pmdp[1] = __pmd(0);     \
-               clean_pmd_entry(pmdp);  \
-       } while (0)
 
 static inline pte_t *pmd_page_vaddr(pmd_t pmd)
 {
@@ -205,10 +179,6 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)
 
 #define pmd_page(pmd)          pfn_to_page(__phys_to_pfn(pmd_val(pmd) & PHYS_MASK))
 
-/* we don't need complex calculations here as the pmd is folded into the pgd */
-#define pmd_addr_end(addr,end) (end)
-
-
 #ifndef CONFIG_HIGHPTE
 #define __pte_map(pmd)         pmd_page_vaddr(*(pmd))
 #define __pte_unmap(pte)       do { } while (0)
@@ -230,7 +200,6 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)
 #define pte_page(pte)          pfn_to_page(pte_pfn(pte))
 #define mk_pte(page,prot)      pfn_pte(page_to_pfn(page), prot)
 
-#define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)
 #define pte_clear(mm,addr,ptep)        set_pte_ext(ptep, __pte(0), 0)
 
 #if __LINUX_ARM_ARCH__ < 6
index 9e92cb205e656e61d1b47a3aed9268dd343db205..f3628fb3d2b331a9dba24fc29fdb8bf4b3c6b3d1 100644 (file)
@@ -65,7 +65,11 @@ extern struct processor {
         * Set a possibly extended PTE.  Non-extended PTEs should
         * ignore 'ext'.
         */
+#ifdef CONFIG_ARM_LPAE
+       void (*set_pte_ext)(pte_t *ptep, pte_t pte);
+#else
        void (*set_pte_ext)(pte_t *ptep, pte_t pte, unsigned int ext);
+#endif
 
        /* Suspend/resume */
        unsigned int suspend_size;
@@ -79,7 +83,11 @@ extern void cpu_proc_fin(void);
 extern int cpu_do_idle(void);
 extern void cpu_dcache_clean_area(void *, int);
 extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
+#ifdef CONFIG_ARM_LPAE
+extern void cpu_set_pte_ext(pte_t *ptep, pte_t pte);
+#else
 extern void cpu_set_pte_ext(pte_t *ptep, pte_t pte, unsigned int ext);
+#endif
 extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
 
 /* These three are private to arch/arm/kernel/suspend.c */
@@ -107,6 +115,18 @@ extern void cpu_resume(void);
 
 #define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm)
 
+#ifdef CONFIG_ARM_LPAE
+#define cpu_get_pgd()  \
+       ({                                              \
+               unsigned long pg, pg2;                  \
+               __asm__("mrrc   p15, 0, %0, %1, c2"     \
+                       : "=r" (pg), "=r" (pg2)         \
+                       :                               \
+                       : "cc");                        \
+               pg &= ~(PTRS_PER_PGD*sizeof(pgd_t)-1);  \
+               (pgd_t *)phys_to_virt(pg);              \
+       })
+#else
 #define cpu_get_pgd()  \
        ({                                              \
                unsigned long pg;                       \
@@ -115,6 +135,7 @@ extern void cpu_resume(void);
                pg &= ~0x3fff;                          \
                (pgd_t *)phys_to_virt(pg);              \
        })
+#endif
 
 #endif
 
index fe7de7571bacb91d2377018b229533e9cb6093d5..53785828744ca687be6a6aec35176289d1a2cbe4 100644 (file)
@@ -80,6 +80,14 @@ struct siginfo;
 void arm_notify_die(const char *str, struct pt_regs *regs, struct siginfo *info,
                unsigned long err, unsigned long trap);
 
+#ifdef CONFIG_ARM_LPAE
+#define FAULT_CODE_ALIGNMENT   33
+#define FAULT_CODE_DEBUG       34
+#else
+#define FAULT_CODE_ALIGNMENT   1
+#define FAULT_CODE_DEBUG       2
+#endif
+
 void hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int,
                                       struct pt_regs *),
                     int sig, int code, const char *name);
index 265f908c4a6e79b98e07d43b7614c5164e86da53..5d3ed7e38561dd43553d1d3bf1b2af07e0d2b747 100644 (file)
@@ -202,8 +202,18 @@ static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
        tlb_remove_page(tlb, pte);
 }
 
+static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
+                                 unsigned long addr)
+{
+#ifdef CONFIG_ARM_LPAE
+       tlb_add_flush(tlb, addr);
+       tlb_remove_page(tlb, virt_to_page(pmdp));
+#endif
+}
+
 #define pte_free_tlb(tlb, ptep, addr)  __pte_free_tlb(tlb, ptep, addr)
-#define pmd_free_tlb(tlb, pmdp, addr)  pmd_free((tlb)->mm, pmdp)
+#define pmd_free_tlb(tlb, pmdp, addr)  __pmd_free_tlb(tlb, pmdp, addr)
+#define pud_free_tlb(tlb, pudp, addr)  pud_free((tlb)->mm, pudp)
 
 #define tlb_migrate_finish(mm)         do { } while (0)
 
index fb2945be5a51386d5a843c6886e7b0e724cf06cc..14e277d2ff911ec7690c4223c5acea4b602e3450 100644 (file)
 #error KERNEL_RAM_VADDR must start at 0xXXXX8000
 #endif
 
+#ifdef CONFIG_ARM_LPAE
+       /* LPAE requires an additional page for the PGD */
+#define PG_DIR_SIZE    0x5000
+#define PMD_ORDER      3
+#else
 #define PG_DIR_SIZE    0x4000
 #define PMD_ORDER      2
+#endif
 
        .globl  swapper_pg_dir
        .equ    swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE
@@ -164,6 +170,25 @@ __create_page_tables:
        teq     r0, r6
        bne     1b
 
+#ifdef CONFIG_ARM_LPAE
+       /*
+        * Build the PGD table (first level) to point to the PMD table. A PGD
+        * entry is 64-bit wide.
+        */
+       mov     r0, r4
+       add     r3, r4, #0x1000                 @ first PMD table address
+       orr     r3, r3, #3                      @ PGD block type
+       mov     r6, #4                          @ PTRS_PER_PGD
+       mov     r7, #1 << (55 - 32)             @ L_PGD_SWAPPER
+1:     str     r3, [r0], #4                    @ set bottom PGD entry bits
+       str     r7, [r0], #4                    @ set top PGD entry bits
+       add     r3, r3, #0x1000                 @ next PMD table
+       subs    r6, r6, #1
+       bne     1b
+
+       add     r4, r4, #0x1000                 @ point to the PMD tables
+#endif
+
        ldr     r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
 
        /*
@@ -219,8 +244,8 @@ __create_page_tables:
 #endif
 
        /*
-        * Then map boot params address in r2 or
-        * the first 1MB of ram if boot params address is not specified.
+        * Then map boot params address in r2 or the first 1MB (2MB with LPAE)
+        * of ram if boot params address is not specified.
         */
        mov     r0, r2, lsr #SECTION_SHIFT
        movs    r0, r0, lsl #SECTION_SHIFT
@@ -251,7 +276,15 @@ __create_page_tables:
        mov     r3, r7, lsr #SECTION_SHIFT
        ldr     r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
        orr     r3, r7, r3, lsl #SECTION_SHIFT
+#ifdef CONFIG_ARM_LPAE
+       mov     r7, #1 << (54 - 32)             @ XN
+#else
+       orr     r3, r3, #PMD_SECT_XN
+#endif
 1:     str     r3, [r0], #4
+#ifdef CONFIG_ARM_LPAE
+       str     r7, [r0], #4
+#endif
        add     r3, r3, #1 << SECTION_SHIFT
        cmp     r0, r6
        blo     1b
@@ -282,6 +315,9 @@ __create_page_tables:
        add     r0, r4, #0xd8000000 >> (SECTION_SHIFT - PMD_ORDER)
        str     r3, [r0]
 #endif
+#endif
+#ifdef CONFIG_ARM_LPAE
+       sub     r4, r4, #0x1000         @ point to the PGD table
 #endif
        mov     pc, lr
 ENDPROC(__create_page_tables)
@@ -374,12 +410,17 @@ __enable_mmu:
 #ifdef CONFIG_CPU_ICACHE_DISABLE
        bic     r0, r0, #CR_I
 #endif
+#ifdef CONFIG_ARM_LPAE
+       mov     r5, #0
+       mcrr    p15, 0, r4, r5, c2              @ load TTBR0
+#else
        mov     r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
                      domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
                      domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
                      domain_val(DOMAIN_IO, DOMAIN_CLIENT))
        mcr     p15, 0, r5, c3, c0, 0           @ load domain access register
        mcr     p15, 0, r4, c2, c0, 0           @ load page table pointer
+#endif
        b       __turn_mmu_on
 ENDPROC(__enable_mmu)
 
@@ -401,8 +442,10 @@ ENDPROC(__enable_mmu)
        .pushsection    .idmap.text, "ax"
 ENTRY(__turn_mmu_on)
        mov     r0, r0
+       instr_sync
        mcr     p15, 0, r0, c1, c0, 0           @ write control reg
        mrc     p15, 0, r3, c0, c0, 0           @ read id reg
+       instr_sync
        mov     r3, r3
        mov     r3, r13
        mov     pc, r3
index 814a52a9dc39abf401629e429879080ca1e2173a..d6a95ef9131d5a567b257c056b6f932ce7b0ac23 100644 (file)
@@ -1016,10 +1016,10 @@ static int __init arch_hw_breakpoint_init(void)
        }
 
        /* Register debug fault handler. */
-       hook_fault_code(2, hw_breakpoint_pending, SIGTRAP, TRAP_HWBKPT,
-                       "watchpoint debug exception");
-       hook_ifault_code(2, hw_breakpoint_pending, SIGTRAP, TRAP_HWBKPT,
-                       "breakpoint debug exception");
+       hook_fault_code(FAULT_CODE_DEBUG, hw_breakpoint_pending, SIGTRAP,
+                       TRAP_HWBKPT, "watchpoint debug exception");
+       hook_ifault_code(FAULT_CODE_DEBUG, hw_breakpoint_pending, SIGTRAP,
+                       TRAP_HWBKPT, "breakpoint debug exception");
 
        /* Register hotplug notifier. */
        register_cpu_notifier(&dbg_reset_nb);
index 9e64231c8cfe1d6b1c6179a9cba501e9ea6ecf7e..1f268bda45528ebd0e52c88ddb9bdf40f5e12e03 100644 (file)
@@ -57,8 +57,10 @@ ENDPROC(cpu_suspend_abort)
        .pushsection    .idmap.text,"ax"
 ENTRY(cpu_resume_mmu)
        ldr     r3, =cpu_resume_after_mmu
+       instr_sync
        mcr     p15, 0, r0, c1, c0, 0   @ turn on MMU, I-cache, etc
        mrc     p15, 0, r0, c0, c0, 0   @ read id reg
+       instr_sync
        mov     r0, r0
        mov     r0, r0
        mov     pc, r3                  @ jump to virtual address
index 67f75a0b66d640c4ffdd44b74fc2ac37e8b0063d..5cf7922ff5e7190e141406019b2d82bd737fe22c 100644 (file)
@@ -629,6 +629,23 @@ config IO_36
 
 comment "Processor Features"
 
+config ARM_LPAE
+       bool "Support for the Large Physical Address Extension"
+       depends on MMU && CPU_V7
+       help
+         Say Y if you have an ARMv7 processor supporting the LPAE page
+         table format and you would like to access memory beyond the
+         4GB limit. The resulting kernel image will not run on
+         processors without the LPA extension.
+
+         If unsure, say N.
+
+config ARCH_PHYS_ADDR_T_64BIT
+       def_bool ARM_LPAE
+
+config ARCH_DMA_ADDR_T_64BIT
+       bool
+
 config ARM_THUMB
        bool "Support Thumb user binaries"
        depends on CPU_ARM720T || CPU_ARM740T || CPU_ARM920T || CPU_ARM922T || CPU_ARM925T || CPU_ARM926T || CPU_ARM940T || CPU_ARM946E || CPU_ARM1020 || CPU_ARM1020E || CPU_ARM1022 || CPU_ARM1026 || CPU_XSCALE || CPU_XSC3 || CPU_MOHAWK || CPU_V6 || CPU_V6K || CPU_V7 || CPU_FEROCEON
index c335c76e0d88665a581da12c4af189e4dc757e69..caf14dc059e5dcfd35289af5e2d3dc6f392a273b 100644 (file)
@@ -968,7 +968,7 @@ static int __init alignment_init(void)
                ai_usermode = safe_usermode(ai_usermode, false);
        }
 
-       hook_fault_code(1, do_alignment, SIGBUS, BUS_ADRALN,
+       hook_fault_code(FAULT_CODE_ALIGNMENT, do_alignment, SIGBUS, BUS_ADRALN,
                        "alignment exception");
 
        /*
index 93aac068da945d5b92987424e579d21953983050..ee9bb363d6064aa89276301940ff3980a15602d3 100644 (file)
@@ -22,6 +22,21 @@ unsigned int cpu_last_asid = ASID_FIRST_VERSION;
 DEFINE_PER_CPU(struct mm_struct *, current_mm);
 #endif
 
+#ifdef CONFIG_ARM_LPAE
+#define cpu_set_asid(asid) {                                           \
+       unsigned long ttbl, ttbh;                                       \
+       asm volatile(                                                   \
+       "       mrrc    p15, 0, %0, %1, c2              @ read TTBR0\n" \
+       "       mov     %1, %2, lsl #(48 - 32)          @ set ASID\n"   \
+       "       mcrr    p15, 0, %0, %1, c2              @ set TTBR0\n"  \
+       : "=&r" (ttbl), "=&r" (ttbh)                                    \
+       : "r" (asid & ~ASID_MASK));                                     \
+}
+#else
+#define cpu_set_asid(asid) \
+       asm("   mcr     p15, 0, %0, c13, c0, 1\n" : : "r" (asid))
+#endif
+
 /*
  * We fork()ed a process, and we need a new context for the child
  * to run in.  We reserve version 0 for initial tasks so we will
@@ -37,7 +52,7 @@ void __init_new_context(struct task_struct *tsk, struct mm_struct *mm)
 static void flush_context(void)
 {
        /* set the reserved ASID before flushing the TLB */
-       asm("mcr        p15, 0, %0, c13, c0, 1\n" : : "r" (0));
+       cpu_set_asid(0);
        isb();
        local_flush_tlb_all();
        if (icache_is_vivt_asid_tagged()) {
@@ -99,7 +114,7 @@ static void reset_context(void *info)
        set_mm_context(mm, asid);
 
        /* set the new ASID */
-       asm("mcr        p15, 0, %0, c13, c0, 1\n" : : "r" (mm->context.id));
+       cpu_set_asid(mm->context.id);
        isb();
 }
 
index aa33949fef608cb4c5b7b9a76e0f4020ba0f1db6..eb5520fc755facac778bfac4da1383cb23794cfb 100644 (file)
 
 #include "fault.h"
 
-/*
- * Fault status register encodings.  We steal bit 31 for our own purposes.
- */
-#define FSR_LNX_PF             (1 << 31)
-#define FSR_WRITE              (1 << 11)
-#define FSR_FS4                        (1 << 10)
-#define FSR_FS3_0              (15)
-
-static inline int fsr_fs(unsigned int fsr)
-{
-       return (fsr & FSR_FS3_0) | (fsr & FSR_FS4) >> 6;
-}
-
 #ifdef CONFIG_MMU
 
 #ifdef CONFIG_KPROBES
@@ -123,8 +110,10 @@ void show_pte(struct mm_struct *mm, unsigned long addr)
 
                pte = pte_offset_map(pmd, addr);
                printk(", *pte=%08llx", (long long)pte_val(*pte));
+#ifndef CONFIG_ARM_LPAE
                printk(", *ppte=%08llx",
                       (long long)pte_val(pte[PTE_HWTABLE_PTRS]));
+#endif
                pte_unmap(pte);
        } while(0);
 
@@ -441,6 +430,12 @@ do_translation_fault(unsigned long addr, unsigned int fsr,
        pmd = pmd_offset(pud, addr);
        pmd_k = pmd_offset(pud_k, addr);
 
+#ifdef CONFIG_ARM_LPAE
+       /*
+        * Only one hardware entry per PMD with LPAE.
+        */
+       index = 0;
+#else
        /*
         * On ARM one Linux PGD entry contains two hardware entries (see page
         * tables layout in pgtable.h). We normally guarantee that we always
@@ -450,6 +445,7 @@ do_translation_fault(unsigned long addr, unsigned int fsr,
         * for the first of pair.
         */
        index = (addr >> SECTION_SHIFT) & 1;
+#endif
        if (pmd_none(pmd_k[index]))
                goto bad_area;
 
@@ -489,55 +485,20 @@ do_bad(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
        return 1;
 }
 
-static struct fsr_info {
+struct fsr_info {
        int     (*fn)(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
        int     sig;
        int     code;
        const char *name;
-} fsr_info[] = {
-       /*
-        * The following are the standard ARMv3 and ARMv4 aborts.  ARMv5
-        * defines these to be "precise" aborts.
-        */
-       { do_bad,               SIGSEGV, 0,             "vector exception"                 },
-       { do_bad,               SIGBUS,  BUS_ADRALN,    "alignment exception"              },
-       { do_bad,               SIGKILL, 0,             "terminal exception"               },
-       { do_bad,               SIGBUS,  BUS_ADRALN,    "alignment exception"              },
-       { do_bad,               SIGBUS,  0,             "external abort on linefetch"      },
-       { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "section translation fault"        },
-       { do_bad,               SIGBUS,  0,             "external abort on linefetch"      },
-       { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "page translation fault"           },
-       { do_bad,               SIGBUS,  0,             "external abort on non-linefetch"  },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "section domain fault"             },
-       { do_bad,               SIGBUS,  0,             "external abort on non-linefetch"  },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "page domain fault"                },
-       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
-       { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "section permission fault"         },
-       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
-       { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "page permission fault"            },
-       /*
-        * The following are "imprecise" aborts, which are signalled by bit
-        * 10 of the FSR, and may not be recoverable.  These are only
-        * supported if the CPU abort handler supports bit 10.
-        */
-       { do_bad,               SIGBUS,  0,             "unknown 16"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 17"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 18"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 19"                       },
-       { do_bad,               SIGBUS,  0,             "lock abort"                       }, /* xscale */
-       { do_bad,               SIGBUS,  0,             "unknown 21"                       },
-       { do_bad,               SIGBUS,  BUS_OBJERR,    "imprecise external abort"         }, /* xscale */
-       { do_bad,               SIGBUS,  0,             "unknown 23"                       },
-       { do_bad,               SIGBUS,  0,             "dcache parity error"              }, /* xscale */
-       { do_bad,               SIGBUS,  0,             "unknown 25"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 26"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 27"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 28"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 29"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 30"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 31"                       }
 };
 
+/* FSR definition */
+#ifdef CONFIG_ARM_LPAE
+#include "fsr-3level.c"
+#else
+#include "fsr-2level.c"
+#endif
+
 void __init
 hook_fault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *),
                int sig, int code, const char *name)
@@ -573,42 +534,6 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
        arm_notify_die("", regs, &info, fsr, 0);
 }
 
-
-static struct fsr_info ifsr_info[] = {
-       { do_bad,               SIGBUS,  0,             "unknown 0"                        },
-       { do_bad,               SIGBUS,  0,             "unknown 1"                        },
-       { do_bad,               SIGBUS,  0,             "debug event"                      },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "section access flag fault"        },
-       { do_bad,               SIGBUS,  0,             "unknown 4"                        },
-       { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "section translation fault"        },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "page access flag fault"           },
-       { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "page translation fault"           },
-       { do_bad,               SIGBUS,  0,             "external abort on non-linefetch"  },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "section domain fault"             },
-       { do_bad,               SIGBUS,  0,             "unknown 10"                       },
-       { do_bad,               SIGSEGV, SEGV_ACCERR,   "page domain fault"                },
-       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
-       { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "section permission fault"         },
-       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
-       { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "page permission fault"            },
-       { do_bad,               SIGBUS,  0,             "unknown 16"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 17"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 18"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 19"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 20"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 21"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 22"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 23"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 24"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 25"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 26"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 27"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 28"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 29"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 30"                       },
-       { do_bad,               SIGBUS,  0,             "unknown 31"                       },
-};
-
 void __init
 hook_ifault_code(int nr, int (*fn)(unsigned long, unsigned int, struct pt_regs *),
                 int sig, int code, const char *name)
@@ -641,6 +566,7 @@ do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
        arm_notify_die("", regs, &info, ifsr, 0);
 }
 
+#ifndef CONFIG_ARM_LPAE
 static int __init exceptions_init(void)
 {
        if (cpu_architecture() >= CPU_ARCH_ARMv6) {
@@ -663,3 +589,4 @@ static int __init exceptions_init(void)
 }
 
 arch_initcall(exceptions_init);
+#endif
index 49e9e3804de41ead9959b676a57279cfdd800baf..cf08bdfbe0d6b6168970e7962341dd407c2823fd 100644 (file)
@@ -1,3 +1,28 @@
-void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
+#ifndef __ARCH_ARM_FAULT_H
+#define __ARCH_ARM_FAULT_H
+
+/*
+ * Fault status register encodings.  We steal bit 31 for our own purposes.
+ */
+#define FSR_LNX_PF             (1 << 31)
+#define FSR_WRITE              (1 << 11)
+#define FSR_FS4                        (1 << 10)
+#define FSR_FS3_0              (15)
+#define FSR_FS5_0              (0x3f)
+
+#ifdef CONFIG_ARM_LPAE
+static inline int fsr_fs(unsigned int fsr)
+{
+       return fsr & FSR_FS5_0;
+}
+#else
+static inline int fsr_fs(unsigned int fsr)
+{
+       return (fsr & FSR_FS3_0) | (fsr & FSR_FS4) >> 6;
+}
+#endif
 
+void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
 unsigned long search_exception_table(unsigned long addr);
+
+#endif /* __ARCH_ARM_FAULT_H */
diff --git a/arch/arm/mm/fsr-2level.c b/arch/arm/mm/fsr-2level.c
new file mode 100644 (file)
index 0000000..18ca74c
--- /dev/null
@@ -0,0 +1,78 @@
+static struct fsr_info fsr_info[] = {
+       /*
+        * The following are the standard ARMv3 and ARMv4 aborts.  ARMv5
+        * defines these to be "precise" aborts.
+        */
+       { do_bad,               SIGSEGV, 0,             "vector exception"                 },
+       { do_bad,               SIGBUS,  BUS_ADRALN,    "alignment exception"              },
+       { do_bad,               SIGKILL, 0,             "terminal exception"               },
+       { do_bad,               SIGBUS,  BUS_ADRALN,    "alignment exception"              },
+       { do_bad,               SIGBUS,  0,             "external abort on linefetch"      },
+       { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "section translation fault"        },
+       { do_bad,               SIGBUS,  0,             "external abort on linefetch"      },
+       { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "page translation fault"           },
+       { do_bad,               SIGBUS,  0,             "external abort on non-linefetch"  },
+       { do_bad,               SIGSEGV, SEGV_ACCERR,   "section domain fault"             },
+       { do_bad,               SIGBUS,  0,             "external abort on non-linefetch"  },
+       { do_bad,               SIGSEGV, SEGV_ACCERR,   "page domain fault"                },
+       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
+       { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "section permission fault"         },
+       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
+       { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "page permission fault"            },
+       /*
+        * The following are "imprecise" aborts, which are signalled by bit
+        * 10 of the FSR, and may not be recoverable.  These are only
+        * supported if the CPU abort handler supports bit 10.
+        */
+       { do_bad,               SIGBUS,  0,             "unknown 16"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 17"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 18"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 19"                       },
+       { do_bad,               SIGBUS,  0,             "lock abort"                       }, /* xscale */
+       { do_bad,               SIGBUS,  0,             "unknown 21"                       },
+       { do_bad,               SIGBUS,  BUS_OBJERR,    "imprecise external abort"         }, /* xscale */
+       { do_bad,               SIGBUS,  0,             "unknown 23"                       },
+       { do_bad,               SIGBUS,  0,             "dcache parity error"              }, /* xscale */
+       { do_bad,               SIGBUS,  0,             "unknown 25"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 26"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 27"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 28"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 29"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 30"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 31"                       },
+};
+
+static struct fsr_info ifsr_info[] = {
+       { do_bad,               SIGBUS,  0,             "unknown 0"                        },
+       { do_bad,               SIGBUS,  0,             "unknown 1"                        },
+       { do_bad,               SIGBUS,  0,             "debug event"                      },
+       { do_bad,               SIGSEGV, SEGV_ACCERR,   "section access flag fault"        },
+       { do_bad,               SIGBUS,  0,             "unknown 4"                        },
+       { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "section translation fault"        },
+       { do_bad,               SIGSEGV, SEGV_ACCERR,   "page access flag fault"           },
+       { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "page translation fault"           },
+       { do_bad,               SIGBUS,  0,             "external abort on non-linefetch"  },
+       { do_bad,               SIGSEGV, SEGV_ACCERR,   "section domain fault"             },
+       { do_bad,               SIGBUS,  0,             "unknown 10"                       },
+       { do_bad,               SIGSEGV, SEGV_ACCERR,   "page domain fault"                },
+       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
+       { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "section permission fault"         },
+       { do_bad,               SIGBUS,  0,             "external abort on translation"    },
+       { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "page permission fault"            },
+       { do_bad,               SIGBUS,  0,             "unknown 16"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 17"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 18"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 19"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 20"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 21"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 22"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 23"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 24"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 25"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 26"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 27"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 28"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 29"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 30"                       },
+       { do_bad,               SIGBUS,  0,             "unknown 31"                       },
+};
diff --git a/arch/arm/mm/fsr-3level.c b/arch/arm/mm/fsr-3level.c
new file mode 100644 (file)
index 0000000..05a4e94
--- /dev/null
@@ -0,0 +1,68 @@
+static struct fsr_info fsr_info[] = {
+       { do_bad,               SIGBUS,  0,             "unknown 0"                     },
+       { do_bad,               SIGBUS,  0,             "unknown 1"                     },
+       { do_bad,               SIGBUS,  0,             "unknown 2"                     },
+       { do_bad,               SIGBUS,  0,             "unknown 3"                     },
+       { do_bad,               SIGBUS,  0,             "reserved translation fault"    },
+       { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 1 translation fault"     },
+       { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 2 translation fault"     },
+       { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "level 3 translation fault"     },
+       { do_bad,               SIGBUS,  0,             "reserved access flag fault"    },
+       { do_bad,               SIGSEGV, SEGV_ACCERR,   "level 1 access flag fault"     },
+       { do_bad,               SIGSEGV, SEGV_ACCERR,   "level 2 access flag fault"     },
+       { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 3 access flag fault"     },
+       { do_bad,               SIGBUS,  0,             "reserved permission fault"     },
+       { do_bad,               SIGSEGV, SEGV_ACCERR,   "level 1 permission fault"      },
+       { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "level 2 permission fault"      },
+       { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 3 permission fault"      },
+       { do_bad,               SIGBUS,  0,             "synchronous external abort"    },
+       { do_bad,               SIGBUS,  0,             "asynchronous external abort"   },
+       { do_bad,               SIGBUS,  0,             "unknown 18"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 19"                    },
+       { do_bad,               SIGBUS,  0,             "synchronous abort (translation table walk)" },
+       { do_bad,               SIGBUS,  0,             "synchronous abort (translation table walk)" },
+       { do_bad,               SIGBUS,  0,             "synchronous abort (translation table walk)" },
+       { do_bad,               SIGBUS,  0,             "synchronous abort (translation table walk)" },
+       { do_bad,               SIGBUS,  0,             "synchronous parity error"      },
+       { do_bad,               SIGBUS,  0,             "asynchronous parity error"     },
+       { do_bad,               SIGBUS,  0,             "unknown 26"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 27"                    },
+       { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk" },
+       { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk" },
+       { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk" },
+       { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk" },
+       { do_bad,               SIGBUS,  0,             "unknown 32"                    },
+       { do_bad,               SIGBUS,  BUS_ADRALN,    "alignment fault"               },
+       { do_bad,               SIGBUS,  0,             "debug event"                   },
+       { do_bad,               SIGBUS,  0,             "unknown 35"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 36"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 37"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 38"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 39"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 40"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 41"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 42"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 43"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 44"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 45"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 46"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 47"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 48"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 49"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 50"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 51"                    },
+       { do_bad,               SIGBUS,  0,             "implementation fault (lockdown abort)" },
+       { do_bad,               SIGBUS,  0,             "unknown 53"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 54"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 55"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 56"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 57"                    },
+       { do_bad,               SIGBUS,  0,             "implementation fault (coprocessor abort)" },
+       { do_bad,               SIGBUS,  0,             "unknown 59"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 60"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 61"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 62"                    },
+       { do_bad,               SIGBUS,  0,             "unknown 63"                    },
+};
+
+#define ifsr_info      fsr_info
index 660f1bc68f998350df44d4d2c1b37a99a5b6a919..feacf4c7671236faf8f343002f1a5a4f601c3d4b 100644 (file)
@@ -8,6 +8,31 @@
 
 pgd_t *idmap_pgd;
 
+#ifdef CONFIG_ARM_LPAE
+static void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end,
+       unsigned long prot)
+{
+       pmd_t *pmd;
+       unsigned long next;
+
+       if (pud_none_or_clear_bad(pud) || (pud_val(*pud) & L_PGD_SWAPPER)) {
+               pmd = pmd_alloc_one(&init_mm, addr);
+               if (!pmd) {
+                       pr_warning("Failed to allocate identity pmd.\n");
+                       return;
+               }
+               pud_populate(&init_mm, pud, pmd);
+               pmd += pmd_index(addr);
+       } else
+               pmd = pmd_offset(pud, addr);
+
+       do {
+               next = pmd_addr_end(addr, end);
+               *pmd = __pmd((addr & PMD_MASK) | prot);
+               flush_pmd_entry(pmd);
+       } while (pmd++, addr = next, addr != end);
+}
+#else  /* !CONFIG_ARM_LPAE */
 static void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end,
        unsigned long prot)
 {
@@ -19,6 +44,7 @@ static void idmap_add_pmd(pud_t *pud, unsigned long addr, unsigned long end,
        pmd[1] = __pmd(addr);
        flush_pmd_entry(pmd);
 }
+#endif /* CONFIG_ARM_LPAE */
 
 static void idmap_add_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
        unsigned long prot)
@@ -36,7 +62,7 @@ static void identity_mapping_add(pgd_t *pgd, unsigned long addr, unsigned long e
 {
        unsigned long prot, next;
 
-       prot = PMD_TYPE_SECT | PMD_SECT_AP_WRITE;
+       prot = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AF;
        if (cpu_architecture() <= CPU_ARCH_ARMv5TEJ && !cpu_is_xscale())
                prot |= PMD_BIT4;
 
index 12c7ad215ce732a743475a75a8c31f6dd3e7f5a9..80632e8d7538f33d5a6539df5a72a90fff52e7ec 100644 (file)
@@ -58,7 +58,7 @@ void __check_kvm_seq(struct mm_struct *mm)
        } while (seq != init_mm.context.kvm_seq);
 }
 
-#ifndef CONFIG_SMP
+#if !defined(CONFIG_SMP) && !defined(CONFIG_ARM_LPAE)
 /*
  * Section support is unsafe on SMP - If you iounmap and ioremap a region,
  * the other CPUs will not see this change until their next context switch.
@@ -73,13 +73,16 @@ static void unmap_area_sections(unsigned long virt, unsigned long size)
 {
        unsigned long addr = virt, end = virt + (size & ~(SZ_1M - 1));
        pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmdp;
 
        flush_cache_vunmap(addr, end);
        pgd = pgd_offset_k(addr);
+       pud = pud_offset(pgd, addr);
+       pmdp = pmd_offset(pud, addr);
        do {
-               pmd_t pmd, *pmdp = pmd_offset(pgd, addr);
+               pmd_t pmd = *pmdp;
 
-               pmd = *pmdp;
                if (!pmd_none(pmd)) {
                        /*
                         * Clear the PMD from the page table, and
@@ -98,8 +101,8 @@ static void unmap_area_sections(unsigned long virt, unsigned long size)
                                pte_free_kernel(&init_mm, pmd_page_vaddr(pmd));
                }
 
-               addr += PGDIR_SIZE;
-               pgd++;
+               addr += PMD_SIZE;
+               pmdp += 2;
        } while (addr < end);
 
        /*
@@ -118,6 +121,8 @@ remap_area_sections(unsigned long virt, unsigned long pfn,
 {
        unsigned long addr = virt, end = virt + size;
        pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmd;
 
        /*
         * Remove and free any PTE-based mapping, and
@@ -126,17 +131,17 @@ remap_area_sections(unsigned long virt, unsigned long pfn,
        unmap_area_sections(virt, size);
 
        pgd = pgd_offset_k(addr);
+       pud = pud_offset(pgd, addr);
+       pmd = pmd_offset(pud, addr);
        do {
-               pmd_t *pmd = pmd_offset(pgd, addr);
-
                pmd[0] = __pmd(__pfn_to_phys(pfn) | type->prot_sect);
                pfn += SZ_1M >> PAGE_SHIFT;
                pmd[1] = __pmd(__pfn_to_phys(pfn) | type->prot_sect);
                pfn += SZ_1M >> PAGE_SHIFT;
                flush_pmd_entry(pmd);
 
-               addr += PGDIR_SIZE;
-               pgd++;
+               addr += PMD_SIZE;
+               pmd += 2;
        } while (addr < end);
 
        return 0;
@@ -148,6 +153,8 @@ remap_area_supersections(unsigned long virt, unsigned long pfn,
 {
        unsigned long addr = virt, end = virt + size;
        pgd_t *pgd;
+       pud_t *pud;
+       pmd_t *pmd;
 
        /*
         * Remove and free any PTE-based mapping, and
@@ -156,6 +163,8 @@ remap_area_supersections(unsigned long virt, unsigned long pfn,
        unmap_area_sections(virt, size);
 
        pgd = pgd_offset_k(virt);
+       pud = pud_offset(pgd, addr);
+       pmd = pmd_offset(pud, addr);
        do {
                unsigned long super_pmd_val, i;
 
@@ -164,14 +173,12 @@ remap_area_supersections(unsigned long virt, unsigned long pfn,
                super_pmd_val |= ((pfn >> (32 - PAGE_SHIFT)) & 0xf) << 20;
 
                for (i = 0; i < 8; i++) {
-                       pmd_t *pmd = pmd_offset(pgd, addr);
-
                        pmd[0] = __pmd(super_pmd_val);
                        pmd[1] = __pmd(super_pmd_val);
                        flush_pmd_entry(pmd);
 
-                       addr += PGDIR_SIZE;
-                       pgd++;
+                       addr += PMD_SIZE;
+                       pmd += 2;
                }
 
                pfn += SUPERSECTION_SIZE >> PAGE_SHIFT;
@@ -189,11 +196,13 @@ void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn,
        unsigned long addr;
        struct vm_struct * area;
 
+#ifndef CONFIG_ARM_LPAE
        /*
         * High mappings must be supersection aligned
         */
        if (pfn >= 0x100000 && (__pfn_to_phys(pfn) & ~SUPERSECTION_MASK))
                return NULL;
+#endif
 
        type = get_mem_type(mtype);
        if (!type)
@@ -237,7 +246,7 @@ void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn,
                return NULL;
        addr = (unsigned long)area->addr;
 
-#ifndef CONFIG_SMP
+#if !defined(CONFIG_SMP) && !defined(CONFIG_ARM_LPAE)
        if (DOMAIN_IO == 0 &&
            (((cpu_architecture() >= CPU_ARCH_ARMv6) && (get_cr() & CR_XP)) ||
               cpu_is_xsc3()) && pfn >= 0x100000 &&
@@ -343,7 +352,7 @@ void __iounmap(volatile void __iomem *io_addr)
                        read_unlock(&vmlist_lock);
                        return;
                }
-#ifndef CONFIG_SMP
+#if !defined(CONFIG_SMP) && !defined(CONFIG_ARM_LPAE)
                /*
                 * If this is a section based mapping we need to handle it
                 * specially as the VM subsystem does not know how to handle
index 27e366af67f9e571eb0d99670f03068f74b2d9be..94c5a0c94f5e2e9a19da0509b5717004188ee31a 100644 (file)
@@ -151,6 +151,7 @@ static int __init early_nowrite(char *__unused)
 }
 early_param("nowb", early_nowrite);
 
+#ifndef CONFIG_ARM_LPAE
 static int __init early_ecc(char *p)
 {
        if (memcmp(p, "on", 2) == 0)
@@ -160,6 +161,7 @@ static int __init early_ecc(char *p)
        return 0;
 }
 early_param("ecc", early_ecc);
+#endif
 
 static int __init noalign_setup(char *__unused)
 {
@@ -229,10 +231,12 @@ static struct mem_type mem_types[] = {
                .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN,
                .domain    = DOMAIN_KERNEL,
        },
+#ifndef CONFIG_ARM_LPAE
        [MT_MINICLEAN] = {
                .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN | PMD_SECT_MINICACHE,
                .domain    = DOMAIN_KERNEL,
        },
+#endif
        [MT_LOW_VECTORS] = {
                .prot_pte  = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
                                L_PTE_RDONLY,
@@ -430,6 +434,7 @@ static void __init build_mem_type_table(void)
         * ARMv6 and above have extended page tables.
         */
        if (cpu_arch >= CPU_ARCH_ARMv6 && (cr & CR_XP)) {
+#ifndef CONFIG_ARM_LPAE
                /*
                 * Mark cache clean areas and XIP ROM read only
                 * from SVC mode and no access from userspace.
@@ -437,6 +442,7 @@ static void __init build_mem_type_table(void)
                mem_types[MT_ROM].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE;
                mem_types[MT_MINICLEAN].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE;
                mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE;
+#endif
 
                if (is_smp()) {
                        /*
@@ -475,6 +481,18 @@ static void __init build_mem_type_table(void)
                mem_types[MT_MEMORY_NONCACHED].prot_sect |= PMD_SECT_BUFFERABLE;
        }
 
+#ifdef CONFIG_ARM_LPAE
+       /*
+        * Do not generate access flag faults for the kernel mappings.
+        */
+       for (i = 0; i < ARRAY_SIZE(mem_types); i++) {
+               mem_types[i].prot_pte |= PTE_EXT_AF;
+               mem_types[i].prot_sect |= PMD_SECT_AF;
+       }
+       kern_pgprot |= PTE_EXT_AF;
+       vecs_pgprot |= PTE_EXT_AF;
+#endif
+
        for (i = 0; i < 16; i++) {
                unsigned long v = pgprot_val(protection_map[i]);
                protection_map[i] = __pgprot(v | user_pgprot);
@@ -578,8 +596,10 @@ static void __init alloc_init_section(pud_t *pud, unsigned long addr,
        if (((addr | end | phys) & ~SECTION_MASK) == 0) {
                pmd_t *p = pmd;
 
+#ifndef CONFIG_ARM_LPAE
                if (addr & SECTION_SIZE)
                        pmd++;
+#endif
 
                do {
                        *pmd = __pmd(phys | type->prot_sect);
@@ -609,6 +629,7 @@ static void alloc_init_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
        } while (pud++, addr = next, addr != end);
 }
 
+#ifndef CONFIG_ARM_LPAE
 static void __init create_36bit_mapping(struct map_desc *md,
                                        const struct mem_type *type)
 {
@@ -668,6 +689,7 @@ static void __init create_36bit_mapping(struct map_desc *md,
                pgd += SUPERSECTION_SIZE >> PGDIR_SHIFT;
        } while (addr != end);
 }
+#endif /* !CONFIG_ARM_LPAE */
 
 /*
  * Create the page directory entries and any necessary
@@ -700,6 +722,7 @@ static void __init create_mapping(struct map_desc *md)
 
        type = &mem_types[md->type];
 
+#ifndef CONFIG_ARM_LPAE
        /*
         * Catch 36-bit addresses
         */
@@ -707,6 +730,7 @@ static void __init create_mapping(struct map_desc *md)
                create_36bit_mapping(md, type);
                return;
        }
+#endif
 
        addr = md->virtual & PAGE_MASK;
        phys = __pfn_to_phys(md->pfn);
@@ -797,6 +821,9 @@ void __init sanity_check_meminfo(void)
                struct membank *bank = &meminfo.bank[j];
                *bank = meminfo.bank[i];
 
+               if (bank->start > ULONG_MAX)
+                       highmem = 1;
+
 #ifdef CONFIG_HIGHMEM
                if (__va(bank->start) >= vmalloc_min ||
                    __va(bank->start) < (void *)PAGE_OFFSET)
@@ -808,7 +835,7 @@ void __init sanity_check_meminfo(void)
                 * Split those memory banks which are partially overlapping
                 * the vmalloc area greatly simplifying things later.
                 */
-               if (__va(bank->start) < vmalloc_min &&
+               if (!highmem && __va(bank->start) < vmalloc_min &&
                    bank->size > vmalloc_min - __va(bank->start)) {
                        if (meminfo.nr_banks >= NR_BANKS) {
                                printk(KERN_CRIT "NR_BANKS too low, "
@@ -828,6 +855,17 @@ void __init sanity_check_meminfo(void)
 #else
                bank->highmem = highmem;
 
+               /*
+                * Highmem banks not allowed with !CONFIG_HIGHMEM.
+                */
+               if (highmem) {
+                       printk(KERN_NOTICE "Ignoring RAM at %.8llx-%.8llx "
+                              "(!CONFIG_HIGHMEM).\n",
+                              (unsigned long long)bank->start,
+                              (unsigned long long)bank->start + bank->size - 1);
+                       continue;
+               }
+
                /*
                 * Check whether this memory bank would entirely overlap
                 * the vmalloc area.
@@ -920,7 +958,13 @@ static inline void prepare_page_table(void)
                pmd_clear(pmd_off_k(addr));
 }
 
+#ifdef CONFIG_ARM_LPAE
+/* the first page is reserved for pgd */
+#define SWAPPER_PG_DIR_SIZE    (PAGE_SIZE + \
+                                PTRS_PER_PGD * PTRS_PER_PMD * sizeof(pmd_t))
+#else
 #define SWAPPER_PG_DIR_SIZE    (PTRS_PER_PGD * sizeof(pgd_t))
+#endif
 
 /*
  * Reserve the special regions of memory
index b2027c154b2a0c946198de99370a89be2f60b6eb..a3e78ccabd655ef871c74b66b6a277eee29290d6 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/mm.h>
 #include <linux/gfp.h>
 #include <linux/highmem.h>
+#include <linux/slab.h>
 
 #include <asm/pgalloc.h>
 #include <asm/page.h>
 
 #include "mm.h"
 
+#ifdef CONFIG_ARM_LPAE
+#define __pgd_alloc()  kmalloc(PTRS_PER_PGD * sizeof(pgd_t), GFP_KERNEL)
+#define __pgd_free(pgd)        kfree(pgd)
+#else
+#define __pgd_alloc()  (pgd_t *)__get_free_pages(GFP_KERNEL, 2)
+#define __pgd_free(pgd)        free_pages((unsigned long)pgd, 2)
+#endif
+
 /*
  * need to get a 16k page for level 1
  */
@@ -27,7 +36,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
        pmd_t *new_pmd, *init_pmd;
        pte_t *new_pte, *init_pte;
 
-       new_pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, 2);
+       new_pgd = __pgd_alloc();
        if (!new_pgd)
                goto no_pgd;
 
@@ -42,10 +51,25 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
 
        clean_dcache_area(new_pgd, PTRS_PER_PGD * sizeof(pgd_t));
 
+#ifdef CONFIG_ARM_LPAE
+       /*
+        * Allocate PMD table for modules and pkmap mappings.
+        */
+       new_pud = pud_alloc(mm, new_pgd + pgd_index(MODULES_VADDR),
+                           MODULES_VADDR);
+       if (!new_pud)
+               goto no_pud;
+
+       new_pmd = pmd_alloc(mm, new_pud, 0);
+       if (!new_pmd)
+               goto no_pmd;
+#endif
+
        if (!vectors_high()) {
                /*
                 * On ARM, first page must always be allocated since it
-                * contains the machine vectors.
+                * contains the machine vectors. The vectors are always high
+                * with LPAE.
                 */
                new_pud = pud_alloc(mm, new_pgd, 0);
                if (!new_pud)
@@ -74,7 +98,7 @@ no_pte:
 no_pmd:
        pud_free(mm, new_pud);
 no_pud:
-       free_pages((unsigned long)new_pgd, 2);
+       __pgd_free(new_pgd);
 no_pgd:
        return NULL;
 }
@@ -111,5 +135,24 @@ no_pud:
        pgd_clear(pgd);
        pud_free(mm, pud);
 no_pgd:
-       free_pages((unsigned long) pgd_base, 2);
+#ifdef CONFIG_ARM_LPAE
+       /*
+        * Free modules/pkmap or identity pmd tables.
+        */
+       for (pgd = pgd_base; pgd < pgd_base + PTRS_PER_PGD; pgd++) {
+               if (pgd_none_or_clear_bad(pgd))
+                       continue;
+               if (pgd_val(*pgd) & L_PGD_SWAPPER)
+                       continue;
+               pud = pud_offset(pgd, 0);
+               if (pud_none_or_clear_bad(pud))
+                       continue;
+               pmd = pmd_offset(pud, 0);
+               pud_clear(pud);
+               pmd_free(mm, pmd);
+               pgd_clear(pgd);
+               pud_free(mm, pud);
+       }
+#endif
+       __pgd_free(pgd_base);
 }
index 307a4def8d3a7d6c89d06211e53f855a7e518fea..2d8ff3ad86d3e1a2b9d9abd83a1bdd3ab42eba3a 100644 (file)
@@ -91,8 +91,9 @@
 #if L_PTE_SHARED != PTE_EXT_SHARED
 #error PTE shared bit mismatch
 #endif
-#if (L_PTE_XN+L_PTE_USER+L_PTE_RDONLY+L_PTE_DIRTY+L_PTE_YOUNG+\
-     L_PTE_FILE+L_PTE_PRESENT) > L_PTE_SHARED
+#if !defined (CONFIG_ARM_LPAE) && \
+       (L_PTE_XN+L_PTE_USER+L_PTE_RDONLY+L_PTE_DIRTY+L_PTE_YOUNG+\
+        L_PTE_FILE+L_PTE_PRESENT) > L_PTE_SHARED
 #error Invalid Linux PTE bit settings
 #endif
 #endif /* CONFIG_MMU */
diff --git a/arch/arm/mm/proc-v7-2level.S b/arch/arm/mm/proc-v7-2level.S
new file mode 100644 (file)
index 0000000..3a4b3e7
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * arch/arm/mm/proc-v7-2level.S
+ *
+ * Copyright (C) 2001 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define TTB_S          (1 << 1)
+#define TTB_RGN_NC     (0 << 3)
+#define TTB_RGN_OC_WBWA        (1 << 3)
+#define TTB_RGN_OC_WT  (2 << 3)
+#define TTB_RGN_OC_WB  (3 << 3)
+#define TTB_NOS                (1 << 5)
+#define TTB_IRGN_NC    ((0 << 0) | (0 << 6))
+#define TTB_IRGN_WBWA  ((0 << 0) | (1 << 6))
+#define TTB_IRGN_WT    ((1 << 0) | (0 << 6))
+#define TTB_IRGN_WB    ((1 << 0) | (1 << 6))
+
+/* PTWs cacheable, inner WB not shareable, outer WB not shareable */
+#define TTB_FLAGS_UP   TTB_IRGN_WB|TTB_RGN_OC_WB
+#define PMD_FLAGS_UP   PMD_SECT_WB
+
+/* PTWs cacheable, inner WBWA shareable, outer WBWA not shareable */
+#define TTB_FLAGS_SMP  TTB_IRGN_WBWA|TTB_S|TTB_NOS|TTB_RGN_OC_WBWA
+#define PMD_FLAGS_SMP  PMD_SECT_WBWA|PMD_SECT_S
+
+/*
+ *     cpu_v7_switch_mm(pgd_phys, tsk)
+ *
+ *     Set the translation table base pointer to be pgd_phys
+ *
+ *     - pgd_phys - physical address of new TTB
+ *
+ *     It is assumed that:
+ *     - we are not using split page tables
+ */
+ENTRY(cpu_v7_switch_mm)
+#ifdef CONFIG_MMU
+       mov     r2, #0
+       ldr     r1, [r1, #MM_CONTEXT_ID]        @ get mm->context.id
+       ALT_SMP(orr     r0, r0, #TTB_FLAGS_SMP)
+       ALT_UP(orr      r0, r0, #TTB_FLAGS_UP)
+#ifdef CONFIG_ARM_ERRATA_430973
+       mcr     p15, 0, r2, c7, c5, 6           @ flush BTAC/BTB
+#endif
+#ifdef CONFIG_ARM_ERRATA_754322
+       dsb
+#endif
+       mcr     p15, 0, r2, c13, c0, 1          @ set reserved context ID
+       isb
+1:     mcr     p15, 0, r0, c2, c0, 0           @ set TTB 0
+       isb
+#ifdef CONFIG_ARM_ERRATA_754322
+       dsb
+#endif
+       mcr     p15, 0, r1, c13, c0, 1          @ set context ID
+       isb
+#endif
+       mov     pc, lr
+ENDPROC(cpu_v7_switch_mm)
+
+/*
+ *     cpu_v7_set_pte_ext(ptep, pte)
+ *
+ *     Set a level 2 translation table entry.
+ *
+ *     - ptep  - pointer to level 2 translation table entry
+ *               (hardware version is stored at +2048 bytes)
+ *     - pte   - PTE value to store
+ *     - ext   - value for extended PTE bits
+ */
+ENTRY(cpu_v7_set_pte_ext)
+#ifdef CONFIG_MMU
+       str     r1, [r0]                        @ linux version
+
+       bic     r3, r1, #0x000003f0
+       bic     r3, r3, #PTE_TYPE_MASK
+       orr     r3, r3, r2
+       orr     r3, r3, #PTE_EXT_AP0 | 2
+
+       tst     r1, #1 << 4
+       orrne   r3, r3, #PTE_EXT_TEX(1)
+
+       eor     r1, r1, #L_PTE_DIRTY
+       tst     r1, #L_PTE_RDONLY | L_PTE_DIRTY
+       orrne   r3, r3, #PTE_EXT_APX
+
+       tst     r1, #L_PTE_USER
+       orrne   r3, r3, #PTE_EXT_AP1
+#ifdef CONFIG_CPU_USE_DOMAINS
+       @ allow kernel read/write access to read-only user pages
+       tstne   r3, #PTE_EXT_APX
+       bicne   r3, r3, #PTE_EXT_APX | PTE_EXT_AP0
+#endif
+
+       tst     r1, #L_PTE_XN
+       orrne   r3, r3, #PTE_EXT_XN
+
+       tst     r1, #L_PTE_YOUNG
+       tstne   r1, #L_PTE_PRESENT
+       moveq   r3, #0
+
+ ARM(  str     r3, [r0, #2048]! )
+ THUMB(        add     r0, r0, #2048 )
+ THUMB(        str     r3, [r0] )
+       mcr     p15, 0, r0, c7, c10, 1          @ flush_pte
+#endif
+       mov     pc, lr
+ENDPROC(cpu_v7_set_pte_ext)
+
+       /*
+        * Memory region attributes with SCTLR.TRE=1
+        *
+        *   n = TEX[0],C,B
+        *   TR = PRRR[2n+1:2n]         - memory type
+        *   IR = NMRR[2n+1:2n]         - inner cacheable property
+        *   OR = NMRR[2n+17:2n+16]     - outer cacheable property
+        *
+        *                      n       TR      IR      OR
+        *   UNCACHED           000     00
+        *   BUFFERABLE         001     10      00      00
+        *   WRITETHROUGH       010     10      10      10
+        *   WRITEBACK          011     10      11      11
+        *   reserved           110
+        *   WRITEALLOC         111     10      01      01
+        *   DEV_SHARED         100     01
+        *   DEV_NONSHARED      100     01
+        *   DEV_WC             001     10
+        *   DEV_CACHED         011     10
+        *
+        * Other attributes:
+        *
+        *   DS0 = PRRR[16] = 0         - device shareable property
+        *   DS1 = PRRR[17] = 1         - device shareable property
+        *   NS0 = PRRR[18] = 0         - normal shareable property
+        *   NS1 = PRRR[19] = 1         - normal shareable property
+        *   NOS = PRRR[24+n] = 1       - not outer shareable
+        */
+.equ   PRRR,   0xff0a81a8
+.equ   NMRR,   0x40e040e0
+
+       /*
+        * Macro for setting up the TTBRx and TTBCR registers.
+        * - \ttb0 and \ttb1 updated with the corresponding flags.
+        */
+       .macro  v7_ttb_setup, zero, ttbr0, ttbr1, tmp
+       mcr     p15, 0, \zero, c2, c0, 2        @ TTB control register
+       ALT_SMP(orr     \ttbr0, \ttbr0, #TTB_FLAGS_SMP)
+       ALT_UP(orr      \ttbr0, \ttbr0, #TTB_FLAGS_UP)
+       ALT_SMP(orr     \ttbr1, \ttbr1, #TTB_FLAGS_SMP)
+       ALT_UP(orr      \ttbr1, \ttbr1, #TTB_FLAGS_UP)
+       mcr     p15, 0, \ttbr1, c2, c0, 1       @ load TTB1
+       .endm
+
+       __CPUINIT
+
+       /*   AT
+        *  TFR   EV X F   I D LR    S
+        * .EEE ..EE PUI. .T.T 4RVI ZWRS BLDP WCAM
+        * rxxx rrxx xxx0 0101 xxxx xxxx x111 xxxx < forced
+        *    1    0 110       0011 1100 .111 1101 < we want
+        */
+       .align  2
+       .type   v7_crval, #object
+v7_crval:
+       crval   clear=0x0120c302, mmuset=0x10c03c7d, ucset=0x00c01c7c
+
+       .previous
diff --git a/arch/arm/mm/proc-v7-3level.S b/arch/arm/mm/proc-v7-3level.S
new file mode 100644 (file)
index 0000000..8de0f1d
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * arch/arm/mm/proc-v7-3level.S
+ *
+ * Copyright (C) 2001 Deep Blue Solutions Ltd.
+ * Copyright (C) 2011 ARM Ltd.
+ * Author: Catalin Marinas <catalin.marinas@arm.com>
+ *   based on arch/arm/mm/proc-v7-2level.S
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define TTB_IRGN_NC    (0 << 8)
+#define TTB_IRGN_WBWA  (1 << 8)
+#define TTB_IRGN_WT    (2 << 8)
+#define TTB_IRGN_WB    (3 << 8)
+#define TTB_RGN_NC     (0 << 10)
+#define TTB_RGN_OC_WBWA        (1 << 10)
+#define TTB_RGN_OC_WT  (2 << 10)
+#define TTB_RGN_OC_WB  (3 << 10)
+#define TTB_S          (3 << 12)
+#define TTB_EAE                (1 << 31)
+
+/* PTWs cacheable, inner WB not shareable, outer WB not shareable */
+#define TTB_FLAGS_UP   (TTB_IRGN_WB|TTB_RGN_OC_WB)
+#define PMD_FLAGS_UP   (PMD_SECT_WB)
+
+/* PTWs cacheable, inner WBWA shareable, outer WBWA not shareable */
+#define TTB_FLAGS_SMP  (TTB_IRGN_WBWA|TTB_S|TTB_RGN_OC_WBWA)
+#define PMD_FLAGS_SMP  (PMD_SECT_WBWA|PMD_SECT_S)
+
+/*
+ * cpu_v7_switch_mm(pgd_phys, tsk)
+ *
+ * Set the translation table base pointer to be pgd_phys (physical address of
+ * the new TTB).
+ */
+ENTRY(cpu_v7_switch_mm)
+#ifdef CONFIG_MMU
+       ldr     r1, [r1, #MM_CONTEXT_ID]        @ get mm->context.id
+       and     r3, r1, #0xff
+       mov     r3, r3, lsl #(48 - 32)          @ ASID
+       mcrr    p15, 0, r0, r3, c2              @ set TTB 0
+       isb
+#endif
+       mov     pc, lr
+ENDPROC(cpu_v7_switch_mm)
+
+/*
+ * cpu_v7_set_pte_ext(ptep, pte)
+ *
+ * Set a level 2 translation table entry.
+ * - ptep - pointer to level 3 translation table entry
+ * - pte - PTE value to store (64-bit in r2 and r3)
+ */
+ENTRY(cpu_v7_set_pte_ext)
+#ifdef CONFIG_MMU
+       tst     r2, #L_PTE_PRESENT
+       beq     1f
+       tst     r3, #1 << (55 - 32)             @ L_PTE_DIRTY
+       orreq   r2, #L_PTE_RDONLY
+1:     strd    r2, r3, [r0]
+       mcr     p15, 0, r0, c7, c10, 1          @ flush_pte
+#endif
+       mov     pc, lr
+ENDPROC(cpu_v7_set_pte_ext)
+
+       /*
+        * Memory region attributes for LPAE (defined in pgtable-3level.h):
+        *
+        *   n = AttrIndx[2:0]
+        *
+        *                      n       MAIR
+        *   UNCACHED           000     00000000
+        *   BUFFERABLE         001     01000100
+        *   DEV_WC             001     01000100
+        *   WRITETHROUGH       010     10101010
+        *   WRITEBACK          011     11101110
+        *   DEV_CACHED         011     11101110
+        *   DEV_SHARED         100     00000100
+        *   DEV_NONSHARED      100     00000100
+        *   unused             101
+        *   unused             110
+        *   WRITEALLOC         111     11111111
+        */
+.equ   PRRR,   0xeeaa4400                      @ MAIR0
+.equ   NMRR,   0xff000004                      @ MAIR1
+
+       /*
+        * Macro for setting up the TTBRx and TTBCR registers.
+        * - \ttbr1 updated.
+        */
+       .macro  v7_ttb_setup, zero, ttbr0, ttbr1, tmp
+       ldr     \tmp, =swapper_pg_dir           @ swapper_pg_dir virtual address
+       cmp     \ttbr1, \tmp                    @ PHYS_OFFSET > PAGE_OFFSET? (branch below)
+       mrc     p15, 0, \tmp, c2, c0, 2         @ TTB control register
+       orr     \tmp, \tmp, #TTB_EAE
+       ALT_SMP(orr     \tmp, \tmp, #TTB_FLAGS_SMP)
+       ALT_UP(orr      \tmp, \tmp, #TTB_FLAGS_UP)
+       ALT_SMP(orr     \tmp, \tmp, #TTB_FLAGS_SMP << 16)
+       ALT_UP(orr      \tmp, \tmp, #TTB_FLAGS_UP << 16)
+       /*
+        * TTBR0/TTBR1 split (PAGE_OFFSET):
+        *   0x40000000: T0SZ = 2, T1SZ = 0 (not used)
+        *   0x80000000: T0SZ = 0, T1SZ = 1
+        *   0xc0000000: T0SZ = 0, T1SZ = 2
+        *
+        * Only use this feature if PHYS_OFFSET <= PAGE_OFFSET, otherwise
+        * booting secondary CPUs would end up using TTBR1 for the identity
+        * mapping set up in TTBR0.
+        */
+       bhi     9001f                           @ PHYS_OFFSET > PAGE_OFFSET?
+       orr     \tmp, \tmp, #(((PAGE_OFFSET >> 30) - 1) << 16) @ TTBCR.T1SZ
+#if defined CONFIG_VMSPLIT_2G
+       /* PAGE_OFFSET == 0x80000000, T1SZ == 1 */
+       add     \ttbr1, \ttbr1, #1 << 4         @ skip two L1 entries
+#elif defined CONFIG_VMSPLIT_3G
+       /* PAGE_OFFSET == 0xc0000000, T1SZ == 2 */
+       add     \ttbr1, \ttbr1, #4096 * (1 + 3) @ only L2 used, skip pgd+3*pmd
+#endif
+       /* CONFIG_VMSPLIT_1G does not need TTBR1 adjustment */
+9001:  mcr     p15, 0, \tmp, c2, c0, 2         @ TTB control register
+       mcrr    p15, 1, \ttbr1, \zero, c2       @ load TTBR1
+       .endm
+
+       __CPUINIT
+
+       /*
+        *   AT
+        *  TFR   EV X F   IHD LR    S
+        * .EEE ..EE PUI. .TAT 4RVI ZWRS BLDP WCAM
+        * rxxx rrxx xxx0 0101 xxxx xxxx x111 xxxx < forced
+        *   11    0 110    1  0011 1100 .111 1101 < we want
+        */
+       .align  2
+       .type   v7_crval, #object
+v7_crval:
+       crval   clear=0x0120c302, mmuset=0x30c23c7d, ucset=0x00c01c7c
+
+       .previous
index 66a185f018a06ba93f5b98d6860b05983a4317a6..7efa2a721d5dd9dbc0853eab8b9c8980c47c5e17 100644 (file)
 
 #include "proc-macros.S"
 
-#define TTB_S          (1 << 1)
-#define TTB_RGN_NC     (0 << 3)
-#define TTB_RGN_OC_WBWA        (1 << 3)
-#define TTB_RGN_OC_WT  (2 << 3)
-#define TTB_RGN_OC_WB  (3 << 3)
-#define TTB_NOS                (1 << 5)
-#define TTB_IRGN_NC    ((0 << 0) | (0 << 6))
-#define TTB_IRGN_WBWA  ((0 << 0) | (1 << 6))
-#define TTB_IRGN_WT    ((1 << 0) | (0 << 6))
-#define TTB_IRGN_WB    ((1 << 0) | (1 << 6))
-
-/* PTWs cacheable, inner WB not shareable, outer WB not shareable */
-#define TTB_FLAGS_UP   TTB_IRGN_WB|TTB_RGN_OC_WB
-#define PMD_FLAGS_UP   PMD_SECT_WB
-
-/* PTWs cacheable, inner WBWA shareable, outer WBWA not shareable */
-#define TTB_FLAGS_SMP  TTB_IRGN_WBWA|TTB_S|TTB_NOS|TTB_RGN_OC_WBWA
-#define PMD_FLAGS_SMP  PMD_SECT_WBWA|PMD_SECT_S
+#ifdef CONFIG_ARM_LPAE
+#include "proc-v7-3level.S"
+#else
+#include "proc-v7-2level.S"
+#endif
 
 ENTRY(cpu_v7_proc_init)
        mov     pc, lr
@@ -99,127 +86,12 @@ ENTRY(cpu_v7_dcache_clean_area)
        mov     pc, lr
 ENDPROC(cpu_v7_dcache_clean_area)
 
-/*
- *     cpu_v7_switch_mm(pgd_phys, tsk)
- *
- *     Set the translation table base pointer to be pgd_phys
- *
- *     - pgd_phys - physical address of new TTB
- *
- *     It is assumed that:
- *     - we are not using split page tables
- */
-ENTRY(cpu_v7_switch_mm)
-#ifdef CONFIG_MMU
-       mov     r2, #0
-       ldr     r1, [r1, #MM_CONTEXT_ID]        @ get mm->context.id
-       ALT_SMP(orr     r0, r0, #TTB_FLAGS_SMP)
-       ALT_UP(orr      r0, r0, #TTB_FLAGS_UP)
-#ifdef CONFIG_ARM_ERRATA_430973
-       mcr     p15, 0, r2, c7, c5, 6           @ flush BTAC/BTB
-#endif
-#ifdef CONFIG_ARM_ERRATA_754322
-       dsb
-#endif
-       mcr     p15, 0, r2, c13, c0, 1          @ set reserved context ID
-       isb
-1:     mcr     p15, 0, r0, c2, c0, 0           @ set TTB 0
-       isb
-#ifdef CONFIG_ARM_ERRATA_754322
-       dsb
-#endif
-       mcr     p15, 0, r1, c13, c0, 1          @ set context ID
-       isb
-#endif
-       mov     pc, lr
-ENDPROC(cpu_v7_switch_mm)
-
-/*
- *     cpu_v7_set_pte_ext(ptep, pte)
- *
- *     Set a level 2 translation table entry.
- *
- *     - ptep  - pointer to level 2 translation table entry
- *               (hardware version is stored at +2048 bytes)
- *     - pte   - PTE value to store
- *     - ext   - value for extended PTE bits
- */
-ENTRY(cpu_v7_set_pte_ext)
-#ifdef CONFIG_MMU
-       str     r1, [r0]                        @ linux version
-
-       bic     r3, r1, #0x000003f0
-       bic     r3, r3, #PTE_TYPE_MASK
-       orr     r3, r3, r2
-       orr     r3, r3, #PTE_EXT_AP0 | 2
-
-       tst     r1, #1 << 4
-       orrne   r3, r3, #PTE_EXT_TEX(1)
-
-       eor     r1, r1, #L_PTE_DIRTY
-       tst     r1, #L_PTE_RDONLY | L_PTE_DIRTY
-       orrne   r3, r3, #PTE_EXT_APX
-
-       tst     r1, #L_PTE_USER
-       orrne   r3, r3, #PTE_EXT_AP1
-#ifdef CONFIG_CPU_USE_DOMAINS
-       @ allow kernel read/write access to read-only user pages
-       tstne   r3, #PTE_EXT_APX
-       bicne   r3, r3, #PTE_EXT_APX | PTE_EXT_AP0
-#endif
-
-       tst     r1, #L_PTE_XN
-       orrne   r3, r3, #PTE_EXT_XN
-
-       tst     r1, #L_PTE_YOUNG
-       tstne   r1, #L_PTE_PRESENT
-       moveq   r3, #0
-
- ARM(  str     r3, [r0, #2048]! )
- THUMB(        add     r0, r0, #2048 )
- THUMB(        str     r3, [r0] )
-       mcr     p15, 0, r0, c7, c10, 1          @ flush_pte
-#endif
-       mov     pc, lr
-ENDPROC(cpu_v7_set_pte_ext)
-
        string  cpu_v7_name, "ARMv7 Processor"
        .align
 
-       /*
-        * Memory region attributes with SCTLR.TRE=1
-        *
-        *   n = TEX[0],C,B
-        *   TR = PRRR[2n+1:2n]         - memory type
-        *   IR = NMRR[2n+1:2n]         - inner cacheable property
-        *   OR = NMRR[2n+17:2n+16]     - outer cacheable property
-        *
-        *                      n       TR      IR      OR
-        *   UNCACHED           000     00
-        *   BUFFERABLE         001     10      00      00
-        *   WRITETHROUGH       010     10      10      10
-        *   WRITEBACK          011     10      11      11
-        *   reserved           110
-        *   WRITEALLOC         111     10      01      01
-        *   DEV_SHARED         100     01
-        *   DEV_NONSHARED      100     01
-        *   DEV_WC             001     10
-        *   DEV_CACHED         011     10
-        *
-        * Other attributes:
-        *
-        *   DS0 = PRRR[16] = 0         - device shareable property
-        *   DS1 = PRRR[17] = 1         - device shareable property
-        *   NS0 = PRRR[18] = 0         - normal shareable property
-        *   NS1 = PRRR[19] = 1         - normal shareable property
-        *   NOS = PRRR[24+n] = 1       - not outer shareable
-        */
-.equ   PRRR,   0xff0a81a8
-.equ   NMRR,   0x40e040e0
-
 /* Suspend/resume support: derived from arch/arm/mach-s5pv210/sleep.S */
 .globl cpu_v7_suspend_size
-.equ   cpu_v7_suspend_size, 4 * 7
+.equ   cpu_v7_suspend_size, 4 * 8
 #ifdef CONFIG_ARM_CPU_SUSPEND
 ENTRY(cpu_v7_do_suspend)
        stmfd   sp!, {r4 - r10, lr}
@@ -228,10 +100,11 @@ ENTRY(cpu_v7_do_suspend)
        stmia   r0!, {r4 - r5}
        mrc     p15, 0, r6, c3, c0, 0   @ Domain ID
        mrc     p15, 0, r7, c2, c0, 1   @ TTB 1
+       mrc     p15, 0, r11, c2, c0, 2  @ TTB control register
        mrc     p15, 0, r8, c1, c0, 0   @ Control register
        mrc     p15, 0, r9, c1, c0, 1   @ Auxiliary control register
        mrc     p15, 0, r10, c1, c0, 2  @ Co-processor access control
-       stmia   r0, {r6 - r10}
+       stmia   r0, {r6 - r11}
        ldmfd   sp!, {r4 - r10, pc}
 ENDPROC(cpu_v7_do_suspend)
 
@@ -243,13 +116,15 @@ ENTRY(cpu_v7_do_resume)
        ldmia   r0!, {r4 - r5}
        mcr     p15, 0, r4, c13, c0, 0  @ FCSE/PID
        mcr     p15, 0, r5, c13, c0, 3  @ User r/o thread ID
-       ldmia   r0, {r6 - r10}
+       ldmia   r0, {r6 - r11}
        mcr     p15, 0, r6, c3, c0, 0   @ Domain ID
+#ifndef CONFIG_ARM_LPAE
        ALT_SMP(orr     r1, r1, #TTB_FLAGS_SMP)
        ALT_UP(orr      r1, r1, #TTB_FLAGS_UP)
+#endif
        mcr     p15, 0, r1, c2, c0, 0   @ TTB 0
        mcr     p15, 0, r7, c2, c0, 1   @ TTB 1
-       mcr     p15, 0, ip, c2, c0, 2   @ TTB control register
+       mcr     p15, 0, r11, c2, c0, 2  @ TTB control register
        mrc     p15, 0, r4, c1, c0, 1   @ Read Auxiliary control register
        teq     r4, r9                  @ Is it already set?
        mcrne   p15, 0, r9, c1, c0, 1   @ No, so write it
@@ -379,12 +254,7 @@ __v7_setup:
        dsb
 #ifdef CONFIG_MMU
        mcr     p15, 0, r10, c8, c7, 0          @ invalidate I + D TLBs
-       mcr     p15, 0, r10, c2, c0, 2          @ TTB control register
-       ALT_SMP(orr     r4, r4, #TTB_FLAGS_SMP)
-       ALT_UP(orr      r4, r4, #TTB_FLAGS_UP)
-       ALT_SMP(orr     r8, r8, #TTB_FLAGS_SMP)
-       ALT_UP(orr      r8, r8, #TTB_FLAGS_UP)
-       mcr     p15, 0, r8, c2, c0, 1           @ load TTB1
+       v7_ttb_setup r10, r4, r8, r5            @ TTBCR, TTBRx setup
        ldr     r5, =PRRR                       @ PRRR
        ldr     r6, =NMRR                       @ NMRR
        mcr     p15, 0, r5, c10, c2, 0          @ write PRRR
@@ -406,16 +276,7 @@ __v7_setup:
        mov     pc, lr                          @ return to head.S:__ret
 ENDPROC(__v7_setup)
 
-       /*   AT
-        *  TFR   EV X F   I D LR    S
-        * .EEE ..EE PUI. .T.T 4RVI ZWRS BLDP WCAM
-        * rxxx rrxx xxx0 0101 xxxx xxxx x111 xxxx < forced
-        *    1    0 110       0011 1100 .111 1101 < we want
-        */
-       .type   v7_crval, #object
-v7_crval:
-       crval   clear=0x0120c302, mmuset=0x10c03c7d, ucset=0x00c01c7c
-
+       .align  2
 __v7_setup_stack:
        .space  4 * 11                          @ 11 registers
 
@@ -437,11 +298,11 @@ __v7_setup_stack:
         */
 .macro __v7_proc initfunc, mm_mmuflags = 0, io_mmuflags = 0, hwcaps = 0
        ALT_SMP(.long   PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \
-                       PMD_FLAGS_SMP | \mm_mmuflags)
+                       PMD_SECT_AF | PMD_FLAGS_SMP | \mm_mmuflags)
        ALT_UP(.long    PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \
-                       PMD_FLAGS_UP | \mm_mmuflags)
-       .long   PMD_TYPE_SECT | PMD_SECT_XN | PMD_SECT_AP_WRITE | \
-               PMD_SECT_AP_READ | \io_mmuflags
+                       PMD_SECT_AF | PMD_FLAGS_UP | \mm_mmuflags)
+       .long   PMD_TYPE_SECT | PMD_SECT_AP_WRITE | \
+               PMD_SECT_AP_READ | PMD_SECT_AF | \io_mmuflags
        W(b)    \initfunc
        .long   cpu_arch_name
        .long   cpu_elf_name
@@ -454,6 +315,7 @@ __v7_setup_stack:
        .long   v7_cache_fns
 .endm
 
+#ifndef CONFIG_ARM_LPAE
        /*
         * ARM Ltd. Cortex A5 processor.
         */
@@ -473,6 +335,7 @@ __v7_ca9mp_proc_info:
        .long   0xff0ffff0
        __v7_proc __v7_ca9mp_setup
        .size   __v7_ca9mp_proc_info, . - __v7_ca9mp_proc_info
+#endif /* CONFIG_ARM_LPAE */
 
        /*
         * ARM Ltd. Cortex A15 processor.