sh: Preliminary support for SH-X2 MMU.
authorPaul Mundt <lethal@linux-sh.org>
Mon, 20 Nov 2006 05:30:26 +0000 (14:30 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Wed, 6 Dec 2006 01:45:37 +0000 (10:45 +0900)
This adds some preliminary support for the SH-X2 MMU, used by
newer SH-4A parts (particularly SH7785).

This MMU implements a 'compat' mode with SH-X MMUs and an
'extended' mode for SH-X2 extended features. Extended features
include additional page sizes (8kB, 4MB, 64MB), as well as the
addition of page execute permissions.

The extended mode attributes are placed in a second data array,
which requires us to switch to 64-bit PTEs when in X2 mode.

With the addition of the exec perms, we also overhaul the mmap
prots somewhat, now that it's possible to handle them more
intelligently.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
arch/sh/mm/Kconfig
arch/sh/mm/init.c
arch/sh/mm/ioremap.c
arch/sh/mm/pg-sh4.c
include/asm-sh/elf.h
include/asm-sh/page.h
include/asm-sh/pgtable-2level.h [deleted file]
include/asm-sh/pgtable.h

index 27463e2..88e9663 100644 (file)
@@ -235,13 +235,22 @@ config MEMORY_SIZE
 
 config 32BIT
        bool "Support 32-bit physical addressing through PMB"
-       depends on CPU_SH4A && MMU
+       depends on CPU_SH4A && MMU && (!X2TLB || BROKEN)
        default y
        help
          If you say Y here, physical addressing will be extended to
          32-bits through the SH-4A PMB. If this is not set, legacy
          29-bit physical addressing will be used.
 
+config X2TLB
+       bool "Enable extended TLB mode"
+       depends on CPU_SUBTYPE_SH7785 && MMU && EXPERIMENTAL
+       help
+         Selecting this option will enable the extended mode of the SH-X2
+         TLB. For legacy SH-X behaviour and interoperability, say N. For
+         all of the fun new features and a willingless to submit bug reports,
+         say Y.
+
 config VSYSCALL
        bool "Support vsyscall page"
        depends on MMU
@@ -255,17 +264,53 @@ config VSYSCALL
          For systems with an MMU that can afford to give up a page,
          (the default value) say Y.
 
+choice
+       prompt "Kernel page size"
+       default PAGE_SIZE_4KB
+
+config PAGE_SIZE_4KB
+       bool "4kB"
+       help
+         This is the default page size used by all SuperH CPUs.
+
+config PAGE_SIZE_8KB
+       bool "8kB"
+       depends on EXPERIMENTAL && X2TLB
+       help
+         This enables 8kB pages as supported by SH-X2 and later MMUs.
+
+config PAGE_SIZE_64KB
+       bool "64kB"
+       depends on EXPERIMENTAL && CPU_SH4
+       help
+         This enables support for 64kB pages, possible on all SH-4
+         CPUs and later. Highly experimental, not recommended.
+
+endchoice
+
 choice
        prompt "HugeTLB page size"
        depends on HUGETLB_PAGE && CPU_SH4 && MMU
        default HUGETLB_PAGE_SIZE_64K
 
 config HUGETLB_PAGE_SIZE_64K
-       bool "64K"
+       bool "64kB"
+
+config HUGETLB_PAGE_SIZE_256K
+       bool "256kB"
+       depends on X2TLB
 
 config HUGETLB_PAGE_SIZE_1MB
        bool "1MB"
 
+config HUGETLB_PAGE_SIZE_4MB
+       bool "4MB"
+       depends on X2TLB
+
+config HUGETLB_PAGE_SIZE_64MB
+       bool "64MB"
+       depends on X2TLB
+
 endchoice
 
 source "mm/Kconfig"
index 7154d1c..8b27516 100644 (file)
@@ -93,7 +93,7 @@ static void set_pte_phys(unsigned long addr, unsigned long phys, pgprot_t prot)
        pud = pud_offset(pgd, addr);
        if (pud_none(*pud)) {
                pmd = (pmd_t *)get_zeroed_page(GFP_ATOMIC);
-               set_pud(pud, __pud(__pa(pmd) | _KERNPG_TABLE | _PAGE_USER));
+               set_pud(pud, __pud(__pa(pmd) | _PAGE_TABLE));
                if (pmd != pmd_offset(pud, 0)) {
                        pud_ERROR(*pud);
                        return;
@@ -103,7 +103,7 @@ static void set_pte_phys(unsigned long addr, unsigned long phys, pgprot_t prot)
        pmd = pmd_offset(pud, addr);
        if (pmd_none(*pmd)) {
                pte = (pte_t *)get_zeroed_page(GFP_ATOMIC);
-               set_pmd(pmd, __pmd(__pa(pte) | _KERNPG_TABLE | _PAGE_USER));
+               set_pmd(pmd, __pmd(__pa(pte) | _PAGE_TABLE));
                if (pte != pte_offset_kernel(pmd, 0)) {
                        pmd_ERROR(*pmd);
                        return;
index a9fe80c..11d54c1 100644 (file)
@@ -28,9 +28,7 @@ static inline void remap_area_pte(pte_t * pte, unsigned long address,
 {
        unsigned long end;
        unsigned long pfn;
-       pgprot_t pgprot = __pgprot(_PAGE_PRESENT | _PAGE_RW |
-                                  _PAGE_DIRTY | _PAGE_ACCESSED |
-                                  _PAGE_HW_SHARED | _PAGE_FLAGS_HARD | flags);
+       pgprot_t pgprot = __pgprot(pgprot_val(PAGE_KERNEL_NOCACHE) | flags);
 
        address &= ~PMD_MASK;
        end = address + size;
index 07371ed..e973ac3 100644 (file)
@@ -37,10 +37,6 @@ void clear_user_page(void *to, unsigned long address, struct page *page)
        if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0)
                clear_page(to);
        else {
-               pgprot_t pgprot = __pgprot(_PAGE_PRESENT |
-                                          _PAGE_RW | _PAGE_CACHABLE |
-                                          _PAGE_DIRTY | _PAGE_ACCESSED |
-                                          _PAGE_HW_SHARED | _PAGE_FLAGS_HARD);
                unsigned long phys_addr = PHYSADDR(to);
                unsigned long p3_addr = P3SEG + (address & CACHE_ALIAS);
                pgd_t *pgd = pgd_offset_k(p3_addr);
@@ -50,7 +46,7 @@ void clear_user_page(void *to, unsigned long address, struct page *page)
                pte_t entry;
                unsigned long flags;
 
-               entry = pfn_pte(phys_addr >> PAGE_SHIFT, pgprot);
+               entry = pfn_pte(phys_addr >> PAGE_SHIFT, PAGE_KERNEL);
                down(&p3map_sem[(address & CACHE_ALIAS)>>12]);
                set_pte(pte, entry);
                local_irq_save(flags);
@@ -77,10 +73,6 @@ void copy_user_page(void *to, void *from, unsigned long address,
        if (((address ^ (unsigned long)to) & CACHE_ALIAS) == 0)
                copy_page(to, from);
        else {
-               pgprot_t pgprot = __pgprot(_PAGE_PRESENT |
-                                          _PAGE_RW | _PAGE_CACHABLE |
-                                          _PAGE_DIRTY | _PAGE_ACCESSED |
-                                          _PAGE_HW_SHARED | _PAGE_FLAGS_HARD);
                unsigned long phys_addr = PHYSADDR(to);
                unsigned long p3_addr = P3SEG + (address & CACHE_ALIAS);
                pgd_t *pgd = pgd_offset_k(p3_addr);
@@ -90,7 +82,7 @@ void copy_user_page(void *to, void *from, unsigned long address,
                pte_t entry;
                unsigned long flags;
 
-               entry = pfn_pte(phys_addr >> PAGE_SHIFT, pgprot);
+               entry = pfn_pte(phys_addr >> PAGE_SHIFT, PAGE_KERNEL);
                down(&p3map_sem[(address & CACHE_ALIAS)>>12]);
                set_pte(pte, entry);
                local_irq_save(flags);
index fc050fd..43ca244 100644 (file)
@@ -74,7 +74,7 @@ typedef struct user_fpu_struct elf_fpregset_t;
 #define ELF_ARCH       EM_SH
 
 #define USE_ELF_CORE_DUMP
-#define ELF_EXEC_PAGESIZE      4096
+#define ELF_EXEC_PAGESIZE      PAGE_SIZE
 
 /* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
    use of this is to invoke "./ld.so someprog" to test out a new version of
index ca8b26d..380fd62 100644 (file)
    [ P4 control   ]            0xE0000000
  */
 
-
 /* PAGE_SHIFT determines the page size */
-#define PAGE_SHIFT     12
+#if defined(CONFIG_PAGE_SIZE_4KB)
+# define PAGE_SHIFT    12
+#elif defined(CONFIG_PAGE_SIZE_8KB)
+# define PAGE_SHIFT    13
+#elif defined(CONFIG_PAGE_SIZE_64KB)
+# define PAGE_SHIFT    16
+#else
+# error "Bogus kernel page size?"
+#endif
 
 #ifdef __ASSEMBLY__
 #define PAGE_SIZE      (1 << PAGE_SHIFT)
 
 #if defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
 #define HPAGE_SHIFT    16
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_256K)
+#define HPAGE_SHIFT    18
 #elif defined(CONFIG_HUGETLB_PAGE_SIZE_1MB)
 #define HPAGE_SHIFT    20
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_4MB)
+#define HPAGE_SHIFT    22
+#elif defined(CONFIG_HUGETLB_PAGE_SIZE_64MB)
+#define HPAGE_SHIFT    26
 #endif
 
 #ifdef CONFIG_HUGETLB_PAGE
@@ -69,15 +82,25 @@ extern void __copy_user_page(void *to, void *from, void *orig_to);
 /*
  * These are used to make use of C type-checking..
  */
-typedef struct { unsigned long pte; } pte_t;
-typedef struct { unsigned long pgd; } pgd_t;
+#ifdef CONFIG_X2TLB
+typedef struct { unsigned long pte_low, pte_high; } pte_t;
+typedef struct { unsigned long long pgprot; } pgprot_t;
+#define pte_val(x) \
+       ((x).pte_low | ((unsigned long long)(x).pte_high << 32))
+#define __pte(x) \
+       ({ pte_t __pte = {(x), ((unsigned long long)(x)) >> 32}; __pte; })
+#else
+typedef struct { unsigned long pte_low; } pte_t;
 typedef struct { unsigned long pgprot; } pgprot_t;
+#define pte_val(x)     ((x).pte_low)
+#define __pte(x) ((pte_t) { (x) } )
+#endif
+
+typedef struct { unsigned long pgd; } pgd_t;
 
-#define pte_val(x)     ((x).pte)
 #define pgd_val(x)     ((x).pgd)
 #define pgprot_val(x)  ((x).pgprot)
 
-#define __pte(x) ((pte_t) { (x) } )
 #define __pgd(x) ((pgd_t) { (x) } )
 #define __pgprot(x)    ((pgprot_t) { (x) } )
 
diff --git a/include/asm-sh/pgtable-2level.h b/include/asm-sh/pgtable-2level.h
deleted file mode 100644 (file)
index b525db6..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef __ASM_SH_PGTABLE_2LEVEL_H
-#define __ASM_SH_PGTABLE_2LEVEL_H
-
-/*
- * traditional two-level paging structure:
- */
-
-#define PGDIR_SHIFT    22
-#define PTRS_PER_PGD   1024
-
-/*
- * this is two-level, so we don't really have any
- * PMD directory physically.
- */
-#define PMD_SHIFT      22
-#define PTRS_PER_PMD   1
-
-#define PTRS_PER_PTE   1024
-
-#ifndef __ASSEMBLY__
-#define pte_ERROR(e) \
-       printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e))
-#define pmd_ERROR(e) \
-       printk("%s:%d: bad pmd %08lx.\n", __FILE__, __LINE__, pmd_val(e))
-#define pgd_ERROR(e) \
-       printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
-
-/*
- * 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)
- */
-static inline int pgd_none(pgd_t pgd)          { return 0; }
-static inline int pgd_bad(pgd_t pgd)           { return 0; }
-static inline int pgd_present(pgd_t pgd)       { return 1; }
-static inline void pgd_clear (pgd_t * pgdp)    { }
-
-/*
- * Certain architectures need to do special things when PTEs
- * within a page table are directly modified.  Thus, the following
- * hook is made available.
- */
-#define set_pte(pteptr, pteval) (*(pteptr) = pteval)
-#define set_pte_at(mm,addr,ptep,pteval) set_pte(ptep,pteval)
-
-/*
- * (pmds are folded into pgds so this doesn't get actually called,
- * but the define is needed for a generic inline function.)
- */
-#define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval)
-#define set_pgd(pgdptr, pgdval) (*(pgdptr) = pgdval)
-
-#define pgd_page_vaddr(pgd) \
-((unsigned long) __va(pgd_val(pgd) & PAGE_MASK))
-
-#define pgd_page(pgd) \
-       (phys_to_page(pgd_val(pgd)))
-
-static inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address)
-{
-       return (pmd_t *) dir;
-}
-
-#define pte_pfn(x)             ((unsigned long)(((x).pte >> PAGE_SHIFT)))
-#define pfn_pte(pfn, prot)     __pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
-#define pfn_pmd(pfn, prot)     __pmd(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
-
-#endif /* !__ASSEMBLY__ */
-
-#endif /* __ASM_SH_PGTABLE_2LEVEL_H */
index 2c8682a..22c3d0b 100644 (file)
 #include <asm-generic/pgtable-nopmd.h>
 #include <asm/page.h>
 
-#define PTRS_PER_PGD           1024
-
 #ifndef __ASSEMBLY__
 #include <asm/addrspace.h>
 #include <asm/fixmap.h>
 
-extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
-extern void paging_init(void);
-
 /*
  * ZERO_PAGE is a global shared page that is always zero: used
  * for zero-mapped memory areas etc..
@@ -33,15 +28,28 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
 
 #endif /* !__ASSEMBLY__ */
 
-/* traditional two-level paging structure */
-#define PGDIR_SHIFT    22
-#define PTRS_PER_PMD   1
-#define PTRS_PER_PTE   1024
-#define PMD_SIZE       (1UL << PMD_SHIFT)
-#define PMD_MASK       (~(PMD_SIZE-1))
+/*
+ * traditional two-level paging structure
+ */
+/* PTE bits */
+#ifdef CONFIG_X2TLB
+# define PTE_MAGNITUDE 3       /* 64-bit PTEs on extended mode SH-X2 TLB */
+#else
+# define PTE_MAGNITUDE 2       /* 32-bit PTEs */
+#endif
+#define PTE_SHIFT      PAGE_SHIFT
+#define PTE_BITS       (PTE_SHIFT - PTE_MAGNITUDE)
+
+/* PGD bits */
+#define PGDIR_SHIFT    (PTE_SHIFT + PTE_BITS)
+#define PGDIR_BITS     (32 - PGDIR_SHIFT)
 #define PGDIR_SIZE     (1UL << PGDIR_SHIFT)
 #define PGDIR_MASK     (~(PGDIR_SIZE-1))
 
+/* Entries per level */
+#define PTRS_PER_PTE   (1UL << PTE_BITS)
+#define PTRS_PER_PGD   (1UL << PGDIR_BITS)
+
 #define USER_PTRS_PER_PGD      (TASK_SIZE/PGDIR_SIZE)
 #define FIRST_USER_ADDRESS     0
 
@@ -57,7 +65,8 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
 /*
  * Linux PTEL encoding.
  *
- * Hardware and software bit definitions for the PTEL value:
+ * Hardware and software bit definitions for the PTEL value (see below for
+ * notes on SH-X2 MMUs and 64-bit PTEs):
  *
  * - Bits 0 and 7 are reserved on SH-3 (_PAGE_WT and _PAGE_SZ1 on SH-4).
  *
@@ -76,20 +85,57 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
  *
  * - Bits 31, 30, and 29 remain unused by everyone and can be used for future
  *   software flags, although care must be taken to update _PAGE_CLEAR_FLAGS.
+ *
+ * XXX: Leave the _PAGE_FILE and _PAGE_WT overhaul for a rainy day.
+ *
+ * SH-X2 MMUs and extended PTEs
+ *
+ * SH-X2 supports an extended mode TLB with split data arrays due to the
+ * number of bits needed for PR and SZ (now EPR and ESZ) encodings. The PR and
+ * SZ bit placeholders still exist in data array 1, but are implemented as
+ * reserved bits, with the real logic existing in data array 2.
+ *
+ * The downside to this is that we can no longer fit everything in to a 32-bit
+ * PTE encoding, so a 64-bit pte_t is necessary for these parts. On the plus
+ * side, this gives us quite a few spare bits to play with for future usage.
  */
+/* Legacy and compat mode bits */
 #define        _PAGE_WT        0x001           /* WT-bit on SH-4, 0 on SH-3 */
 #define _PAGE_HW_SHARED        0x002           /* SH-bit  : shared among processes */
 #define _PAGE_DIRTY    0x004           /* D-bit   : page changed */
 #define _PAGE_CACHABLE 0x008           /* C-bit   : cachable */
-#define _PAGE_SZ0      0x010           /* SZ0-bit : Size of page */
-#define _PAGE_RW       0x020           /* PR0-bit : write access allowed */
-#define _PAGE_USER     0x040           /* PR1-bit : user space access allowed */
-#define _PAGE_SZ1      0x080           /* SZ1-bit : Size of page (on SH-4) */
+#ifndef CONFIG_X2TLB
+# define _PAGE_SZ0     0x010           /* SZ0-bit : Size of page */
+# define _PAGE_RW      0x020           /* PR0-bit : write access allowed */
+# define _PAGE_USER    0x040           /* PR1-bit : user space access allowed*/
+# define _PAGE_SZ1     0x080           /* SZ1-bit : Size of page (on SH-4) */
+#endif
 #define _PAGE_PRESENT  0x100           /* V-bit   : page is valid */
 #define _PAGE_PROTNONE 0x200           /* software: if not present  */
 #define _PAGE_ACCESSED 0x400           /* software: page referenced */
 #define _PAGE_FILE     _PAGE_WT        /* software: pagecache or swap? */
 
+/* Extended mode bits */
+#define _PAGE_EXT_ESZ0         0x0010  /* ESZ0-bit: Size of page */
+#define _PAGE_EXT_ESZ1         0x0020  /* ESZ1-bit: Size of page */
+#define _PAGE_EXT_ESZ2         0x0040  /* ESZ2-bit: Size of page */
+#define _PAGE_EXT_ESZ3         0x0080  /* ESZ3-bit: Size of page */
+
+#define _PAGE_EXT_USER_EXEC    0x0100  /* EPR0-bit: User space executable */
+#define _PAGE_EXT_USER_WRITE   0x0200  /* EPR1-bit: User space writable */
+#define _PAGE_EXT_USER_READ    0x0400  /* EPR2-bit: User space readable */
+
+#define _PAGE_EXT_KERN_EXEC    0x0800  /* EPR3-bit: Kernel space executable */
+#define _PAGE_EXT_KERN_WRITE   0x1000  /* EPR4-bit: Kernel space writable */
+#define _PAGE_EXT_KERN_READ    0x2000  /* EPR5-bit: Kernel space readable */
+
+/* Wrapper for extended mode pgprot twiddling */
+#ifdef CONFIG_X2TLB
+# define _PAGE_EXT(x)          ((unsigned long long)(x) << 32)
+#else
+# define _PAGE_EXT(x)          (0)
+#endif
+
 /* software: moves to PTEA.TC (Timing Control) */
 #define _PAGE_PCC_AREA5        0x00000000      /* use BSC registers for area5 */
 #define _PAGE_PCC_AREA6        0x80000000      /* use BSC registers for area6 */
@@ -114,37 +160,165 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
 
 #define _PAGE_FLAGS_HARDWARE_MASK      (0x1fffffff & ~(_PAGE_CLEAR_FLAGS))
 
-/* Hardware flags: SZ0=1 (4k-byte) */
-#define _PAGE_FLAGS_HARD       _PAGE_SZ0
+/* Hardware flags, page size encoding */
+#if defined(CONFIG_X2TLB)
+# if defined(CONFIG_PAGE_SIZE_4KB)
+#  define _PAGE_FLAGS_HARD     _PAGE_EXT(_PAGE_EXT_ESZ0)
+# elif defined(CONFIG_PAGE_SIZE_8KB)
+#  define _PAGE_FLAGS_HARD     _PAGE_EXT(_PAGE_EXT_ESZ1)
+# elif defined(CONFIG_PAGE_SIZE_64KB)
+#  define _PAGE_FLAGS_HARD     _PAGE_EXT(_PAGE_EXT_ESZ2)
+# endif
+#else
+# if defined(CONFIG_PAGE_SIZE_4KB)
+#  define _PAGE_FLAGS_HARD     _PAGE_SZ0
+# elif defined(CONFIG_PAGE_SIZE_64KB)
+#  define _PAGE_FLAGS_HARD     _PAGE_SZ1
+# endif
+#endif
 
-#if defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
-#define _PAGE_SZHUGE   (_PAGE_SZ1)
-#elif defined(CONFIG_HUGETLB_PAGE_SIZE_1MB)
-#define _PAGE_SZHUGE   (_PAGE_SZ0 | _PAGE_SZ1)
+#if defined(CONFIG_X2TLB)
+# if defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
+#  define _PAGE_SZHUGE (_PAGE_EXT_ESZ2)
+# elif defined(CONFIG_HUGETLB_PAGE_SIZE_256K)
+#  define _PAGE_SZHUGE (_PAGE_EXT_ESZ0 | _PAGE_EXT_ESZ2)
+# elif defined(CONFIG_HUGETLB_PAGE_SIZE_1MB)
+#  define _PAGE_SZHUGE (_PAGE_EXT_ESZ0 | _PAGE_EXT_ESZ1 | _PAGE_EXT_ESZ2)
+# elif defined(CONFIG_HUGETLB_PAGE_SIZE_4MB)
+#  define _PAGE_SZHUGE (_PAGE_EXT_ESZ3)
+# elif defined(CONFIG_HUGETLB_PAGE_SIZE_64MB)
+#  define _PAGE_SZHUGE (_PAGE_EXT_ESZ2 | _PAGE_EXT_ESZ3)
+# endif
+#else
+# if defined(CONFIG_HUGETLB_PAGE_SIZE_64K)
+#  define _PAGE_SZHUGE (_PAGE_SZ1)
+# elif defined(CONFIG_HUGETLB_PAGE_SIZE_1MB)
+#  define _PAGE_SZHUGE (_PAGE_SZ0 | _PAGE_SZ1)
+# endif
 #endif
 
-#define _PAGE_TABLE    (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY)
-#define _KERNPG_TABLE  (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
-#define _PAGE_CHG_MASK (PTE_MASK | _PAGE_ACCESSED | _PAGE_CACHABLE | _PAGE_DIRTY)
+#define _PAGE_CHG_MASK \
+       (PTE_MASK | _PAGE_ACCESSED | _PAGE_CACHABLE | _PAGE_DIRTY)
 
 #ifndef __ASSEMBLY__
 
-#ifdef CONFIG_MMU
-#define PAGE_NONE      __pgprot(_PAGE_PROTNONE | _PAGE_CACHABLE |_PAGE_ACCESSED | _PAGE_FLAGS_HARD)
-#define PAGE_SHARED    __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_CACHABLE |_PAGE_ACCESSED | _PAGE_FLAGS_HARD)
-#define PAGE_COPY      __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_CACHABLE | _PAGE_ACCESSED | _PAGE_FLAGS_HARD)
-#define PAGE_READONLY  __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_CACHABLE | _PAGE_ACCESSED | _PAGE_FLAGS_HARD)
-#define PAGE_KERNEL    __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_CACHABLE | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_HW_SHARED | _PAGE_FLAGS_HARD)
+#if defined(CONFIG_X2TLB) /* SH-X2 TLB */
+#define _PAGE_TABLE \
+       (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY | \
+        _PAGE_EXT(_PAGE_EXT_USER_READ | _PAGE_EXT_USER_WRITE))
+
+#define _KERNPG_TABLE \
+       (_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY | \
+        _PAGE_EXT(_PAGE_EXT_KERN_READ | _PAGE_EXT_KERN_WRITE))
+
+#define PAGE_NONE      __pgprot(_PAGE_PROTNONE | _PAGE_CACHABLE | \
+                                _PAGE_ACCESSED | _PAGE_FLAGS_HARD)
+
+#define PAGE_SHARED    __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | \
+                                _PAGE_CACHABLE | _PAGE_FLAGS_HARD | \
+                                _PAGE_EXT(_PAGE_EXT_USER_READ | \
+                                          _PAGE_EXT_USER_WRITE))
+
+#define PAGE_EXECREAD  __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | \
+                                _PAGE_CACHABLE | _PAGE_FLAGS_HARD | \
+                                _PAGE_EXT(_PAGE_EXT_USER_EXEC | \
+                                          _PAGE_EXT_USER_READ))
+
+#define PAGE_COPY      PAGE_EXECREAD
+
+#define PAGE_READONLY  __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | \
+                                _PAGE_CACHABLE | _PAGE_FLAGS_HARD | \
+                                _PAGE_EXT(_PAGE_EXT_USER_READ))
+
+#define PAGE_WRITEONLY __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | \
+                                _PAGE_CACHABLE | _PAGE_FLAGS_HARD | \
+                                _PAGE_EXT(_PAGE_EXT_USER_WRITE))
+
+#define PAGE_RWX       __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | \
+                                _PAGE_CACHABLE | _PAGE_FLAGS_HARD | \
+                                _PAGE_EXT(_PAGE_EXT_USER_WRITE | \
+                                          _PAGE_EXT_USER_READ  | \
+                                          _PAGE_EXT_USER_EXEC))
+
+#define PAGE_KERNEL    __pgprot(_PAGE_PRESENT | _PAGE_CACHABLE | \
+                                _PAGE_DIRTY | _PAGE_ACCESSED | \
+                                _PAGE_HW_SHARED | _PAGE_FLAGS_HARD | \
+                                _PAGE_EXT(_PAGE_EXT_KERN_READ | \
+                                          _PAGE_EXT_KERN_WRITE | \
+                                          _PAGE_EXT_KERN_EXEC))
+
+#define PAGE_KERNEL_NOCACHE \
+                       __pgprot(_PAGE_PRESENT | _PAGE_DIRTY | \
+                                _PAGE_ACCESSED | _PAGE_HW_SHARED | \
+                                _PAGE_FLAGS_HARD | \
+                                _PAGE_EXT(_PAGE_EXT_KERN_READ | \
+                                          _PAGE_EXT_KERN_WRITE | \
+                                          _PAGE_EXT_KERN_EXEC))
+
+#define PAGE_KERNEL_RO __pgprot(_PAGE_PRESENT | _PAGE_CACHABLE | \
+                                _PAGE_DIRTY | _PAGE_ACCESSED | \
+                                _PAGE_HW_SHARED | _PAGE_FLAGS_HARD | \
+                                _PAGE_EXT(_PAGE_EXT_KERN_READ | \
+                                          _PAGE_EXT_KERN_EXEC))
+
+#define PAGE_KERNEL_PCC(slot, type) \
+                       __pgprot(_PAGE_PRESENT | _PAGE_DIRTY | \
+                                _PAGE_ACCESSED | _PAGE_FLAGS_HARD | \
+                                _PAGE_EXT(_PAGE_EXT_KERN_READ | \
+                                          _PAGE_EXT_KERN_WRITE | \
+                                          _PAGE_EXT_KERN_EXEC) \
+                                (slot ? _PAGE_PCC_AREA5 : _PAGE_PCC_AREA6) | \
+                                (type))
+
+#elif defined(CONFIG_MMU) /* SH-X TLB */
+#define _PAGE_TABLE \
+       (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY)
+#define _KERNPG_TABLE \
+       (_PAGE_PRESENT | _PAGE_RW | _PAGE_ACCESSED | _PAGE_DIRTY)
+
+#define PAGE_NONE      __pgprot(_PAGE_PROTNONE | _PAGE_CACHABLE | \
+                                _PAGE_ACCESSED | _PAGE_FLAGS_HARD)
+
+#define PAGE_SHARED    __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | \
+                                _PAGE_CACHABLE | _PAGE_ACCESSED | \
+                                _PAGE_FLAGS_HARD)
+
+#define PAGE_COPY      __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_CACHABLE | \
+                                _PAGE_ACCESSED | _PAGE_FLAGS_HARD)
+
+#define PAGE_READONLY  __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_CACHABLE | \
+                                _PAGE_ACCESSED | _PAGE_FLAGS_HARD)
+
+#define PAGE_EXECREAD  PAGE_READONLY
+#define PAGE_RWX       PAGE_SHARED
+#define PAGE_WRITEONLY PAGE_SHARED
+
+#define PAGE_KERNEL    __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_CACHABLE | \
+                                _PAGE_DIRTY | _PAGE_ACCESSED | \
+                                _PAGE_HW_SHARED | _PAGE_FLAGS_HARD)
+
 #define PAGE_KERNEL_NOCACHE \
-                       __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_HW_SHARED | _PAGE_FLAGS_HARD)
-#define PAGE_KERNEL_RO __pgprot(_PAGE_PRESENT | _PAGE_CACHABLE | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_HW_SHARED | _PAGE_FLAGS_HARD)
+                       __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | \
+                                _PAGE_ACCESSED | _PAGE_HW_SHARED | \
+                                _PAGE_FLAGS_HARD)
+
+#define PAGE_KERNEL_RO __pgprot(_PAGE_PRESENT | _PAGE_CACHABLE | \
+                                _PAGE_DIRTY | _PAGE_ACCESSED | \
+                                _PAGE_HW_SHARED | _PAGE_FLAGS_HARD)
+
 #define PAGE_KERNEL_PCC(slot, type) \
-                       __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_FLAGS_HARD | (slot ? _PAGE_PCC_AREA5 : _PAGE_PCC_AREA6) | (type))
+                       __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | \
+                                _PAGE_ACCESSED | _PAGE_FLAGS_HARD | \
+                                (slot ? _PAGE_PCC_AREA5 : _PAGE_PCC_AREA6) | \
+                                (type))
 #else /* no mmu */
 #define PAGE_NONE              __pgprot(0)
 #define PAGE_SHARED            __pgprot(0)
 #define PAGE_COPY              __pgprot(0)
+#define PAGE_EXECREAD          __pgprot(0)
+#define PAGE_RWX               __pgprot(0)
 #define PAGE_READONLY          __pgprot(0)
+#define PAGE_WRITEONLY         __pgprot(0)
 #define PAGE_KERNEL            __pgprot(0)
 #define PAGE_KERNEL_NOCACHE    __pgprot(0)
 #define PAGE_KERNEL_RO         __pgprot(0)
@@ -154,27 +328,32 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
 #endif /* __ASSEMBLY__ */
 
 /*
- * As i386 and MIPS, SuperH can't do page protection for execute, and
- * considers that the same as a read.  Also, write permissions imply
- * read permissions. This is the closest we can get..
+ * SH-X and lower (legacy) SuperH parts (SH-3, SH-4, some SH-4A) can't do page
+ * protection for execute, and considers it the same as a read. Also, write
+ * permission implies read permission. This is the closest we can get..
+ *
+ * SH-X2 (SH7785) and later parts take this to the opposite end of the extreme,
+ * not only supporting separate execute, read, and write bits, but having
+ * completely separate permission bits for user and kernel space.
  */
+        /*xwr*/
 #define __P000 PAGE_NONE
 #define __P001 PAGE_READONLY
 #define __P010 PAGE_COPY
 #define __P011 PAGE_COPY
-#define __P100 PAGE_READONLY
-#define __P101 PAGE_READONLY
+#define __P100 PAGE_EXECREAD
+#define __P101 PAGE_EXECREAD
 #define __P110 PAGE_COPY
 #define __P111 PAGE_COPY
 
 #define __S000 PAGE_NONE
 #define __S001 PAGE_READONLY
-#define __S010 PAGE_SHARED
+#define __S010 PAGE_WRITEONLY
 #define __S011 PAGE_SHARED
-#define __S100 PAGE_READONLY
-#define __S101 PAGE_READONLY
-#define __S110 PAGE_SHARED
-#define __S111 PAGE_SHARED
+#define __S100 PAGE_EXECREAD
+#define __S101 PAGE_EXECREAD
+#define __S110 PAGE_RWX
+#define __S111 PAGE_RWX
 
 #ifndef __ASSEMBLY__
 
@@ -183,7 +362,17 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
  * within a page table are directly modified.  Thus, the following
  * hook is made available.
  */
+#ifdef CONFIG_X2TLB
+static inline void set_pte(pte_t *ptep, pte_t pte)
+{
+       ptep->pte_high = pte.pte_high;
+       smp_wmb();
+       ptep->pte_low = pte.pte_low;
+}
+#else
 #define set_pte(pteptr, pteval) (*(pteptr) = pteval)
+#endif
+
 #define set_pte_at(mm,addr,ptep,pteval) set_pte(ptep,pteval)
 
 /*
@@ -192,13 +381,13 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
  */
 #define set_pmd(pmdptr, pmdval) (*(pmdptr) = pmdval)
 
-#define pte_pfn(x)             ((unsigned long)(((x).pte >> PAGE_SHIFT)))
+#define pte_pfn(x)             ((unsigned long)(((x).pte_low >> PAGE_SHIFT)))
 #define pfn_pte(pfn, prot)     __pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
 #define pfn_pmd(pfn, prot)     __pmd(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
 
 #define pte_none(x)    (!pte_val(x))
 #define pte_present(x) (pte_val(x) & (_PAGE_PRESENT | _PAGE_PROTNONE))
-#define pte_clear(mm,addr,xp)  do { set_pte_at(mm, addr, xp, __pte(0)); } while (0)
+#define pte_clear(mm,addr,xp) do { set_pte_at(mm, addr, xp, __pte(0)); } while (0)
 
 #define pmd_none(x)    (!pmd_val(x))
 #define pmd_present(x) (pmd_val(x) & _PAGE_PRESENT)
@@ -212,28 +401,52 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];
  * The following only work if pte_present() is true.
  * Undefined behaviour if not..
  */
-static inline int pte_read(pte_t pte) { return pte_val(pte) & _PAGE_USER; }
-static inline int pte_exec(pte_t pte) { return pte_val(pte) & _PAGE_USER; }
-static inline int pte_dirty(pte_t pte){ return pte_val(pte) & _PAGE_DIRTY; }
-static inline int pte_young(pte_t pte){ return pte_val(pte) & _PAGE_ACCESSED; }
-static inline int pte_file(pte_t pte) { return pte_val(pte) & _PAGE_FILE; }
-static inline int pte_write(pte_t pte){ return pte_val(pte) & _PAGE_RW; }
-static inline int pte_not_present(pte_t pte){ return !(pte_val(pte) & _PAGE_PRESENT); }
-
-static inline pte_t pte_rdprotect(pte_t pte)   { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_USER)); return pte; }
-static inline pte_t pte_exprotect(pte_t pte)   { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_USER)); return pte; }
-static inline pte_t pte_mkclean(pte_t pte)     { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_DIRTY)); return pte; }
-static inline pte_t pte_mkold(pte_t pte)       { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_ACCESSED)); return pte; }
-static inline pte_t pte_wrprotect(pte_t pte)   { set_pte(&pte, __pte(pte_val(pte) & ~_PAGE_RW)); return pte; }
-static inline pte_t pte_mkread(pte_t pte)      { set_pte(&pte, __pte(pte_val(pte) | _PAGE_USER)); return pte; }
-static inline pte_t pte_mkexec(pte_t pte)      { set_pte(&pte, __pte(pte_val(pte) | _PAGE_USER)); return pte; }
-static inline pte_t pte_mkdirty(pte_t pte)     { set_pte(&pte, __pte(pte_val(pte) | _PAGE_DIRTY)); return pte; }
-static inline pte_t pte_mkyoung(pte_t pte)     { set_pte(&pte, __pte(pte_val(pte) | _PAGE_ACCESSED)); return pte; }
-static inline pte_t pte_mkwrite(pte_t pte)     { set_pte(&pte, __pte(pte_val(pte) | _PAGE_RW)); return pte; }
-#ifdef CONFIG_HUGETLB_PAGE
-static inline pte_t pte_mkhuge(pte_t pte)      { set_pte(&pte, __pte(pte_val(pte) | _PAGE_SZHUGE)); return pte; }
+#define pte_not_present(pte)   (!(pte_val(pte) & _PAGE_PRESENT))
+#define pte_dirty(pte)         (pte_val(pte) & _PAGE_DIRTY)
+#define pte_young(pte)         (pte_val(pte) & _PAGE_ACCESSED)
+#define pte_file(pte)          (pte_val(pte) & _PAGE_FILE)
+
+#ifdef CONFIG_X2TLB
+#define pte_read(pte)          ((pte).pte_high & _PAGE_EXT_USER_READ)
+#define pte_exec(pte)          ((pte).pte_high & _PAGE_EXT_USER_EXEC)
+#define pte_write(pte)         ((pte).pte_high & _PAGE_EXT_USER_WRITE)
+#else
+#define pte_read(pte)          (pte_val(pte) & _PAGE_USER)
+#define pte_exec(pte)          (pte_val(pte) & _PAGE_USER)
+#define pte_write(pte)         (pte_val(pte) & _PAGE_RW)
 #endif
 
+#define PTE_BIT_FUNC(h,fn,op) \
+static inline pte_t pte_##fn(pte_t pte) { pte.pte_##h op; return pte; }
+
+#ifdef CONFIG_X2TLB
+/*
+ * We cheat a bit in the SH-X2 TLB case. As the permission bits are
+ * individually toggled (and user permissions are entirely decoupled from
+ * kernel permissions), we attempt to couple them a bit more sanely here.
+ */
+PTE_BIT_FUNC(high, rdprotect, &= ~_PAGE_EXT_USER_READ);
+PTE_BIT_FUNC(high, mkread, |= _PAGE_EXT_USER_READ | _PAGE_EXT_KERN_READ);
+PTE_BIT_FUNC(high, wrprotect, &= ~_PAGE_EXT_USER_WRITE);
+PTE_BIT_FUNC(high, mkwrite, |= _PAGE_EXT_USER_WRITE | _PAGE_EXT_KERN_WRITE);
+PTE_BIT_FUNC(high, exprotect, &= ~_PAGE_EXT_USER_EXEC);
+PTE_BIT_FUNC(high, mkexec, |= _PAGE_EXT_USER_EXEC | _PAGE_EXT_KERN_EXEC);
+PTE_BIT_FUNC(high, mkhuge, |= _PAGE_SZHUGE);
+#else
+PTE_BIT_FUNC(low, rdprotect, &= ~_PAGE_USER);
+PTE_BIT_FUNC(low, mkread, |= _PAGE_USER);
+PTE_BIT_FUNC(low, wrprotect, &= ~_PAGE_RW);
+PTE_BIT_FUNC(low, mkwrite, |= _PAGE_RW);
+PTE_BIT_FUNC(low, exprotect, &= ~_PAGE_USER);
+PTE_BIT_FUNC(low, mkexec, |= _PAGE_USER);
+PTE_BIT_FUNC(low, mkhuge, |= _PAGE_SZHUGE);
+#endif
+
+PTE_BIT_FUNC(low, mkclean, &= ~_PAGE_DIRTY);
+PTE_BIT_FUNC(low, mkdirty, |= _PAGE_DIRTY);
+PTE_BIT_FUNC(low, mkold, &= ~_PAGE_ACCESSED);
+PTE_BIT_FUNC(low, mkyoung, |= _PAGE_ACCESSED);
+
 /*
  * Macro and implementation to make a page protection as uncachable.
  */
@@ -258,7 +471,11 @@ static inline pgprot_t pgprot_noncached(pgprot_t _prot)
 #define mk_pte(page, pgprot)   pfn_pte(page_to_pfn(page), (pgprot))
 
 static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
-{ set_pte(&pte, __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot))); return pte; }
+{
+       set_pte(&pte, __pte((pte_val(pte) & _PAGE_CHG_MASK) |
+                           pgprot_val(newprot)));
+       return pte;
+}
 
 #define pmd_page_vaddr(pmd) \
 ((unsigned long) __va(pmd_val(pmd) & PAGE_MASK))
@@ -283,8 +500,15 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 #define pte_unmap(pte)         do { } while (0)
 #define pte_unmap_nested(pte)  do { } while (0)
 
+#ifdef CONFIG_X2TLB
+#define pte_ERROR(e) \
+       printk("%s:%d: bad pte %p(%08lx%08lx).\n", __FILE__, __LINE__, \
+              &(e), (e).pte_high, (e).pte_low)
+#else
 #define pte_ERROR(e) \
        printk("%s:%d: bad pte %08lx.\n", __FILE__, __LINE__, pte_val(e))
+#endif
+
 #define pgd_ERROR(e) \
        printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, pgd_val(e))
 
@@ -337,6 +561,9 @@ extern unsigned int kobjsize(const void *objp);
 extern pte_t ptep_get_and_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep);
 #endif
 
+extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
+extern void paging_init(void);
+
 #include <asm-generic/pgtable.h>
 
 #endif /* !__ASSEMBLY__ */