MIPS: mm: Fix MIPS32 36b physical addressing (alchemy, netlogic)
authorPaul Burton <paul.burton@imgtec.com>
Tue, 19 Apr 2016 08:25:05 +0000 (09:25 +0100)
committerRalf Baechle <ralf@linux-mips.org>
Fri, 13 May 2016 13:30:25 +0000 (15:30 +0200)
There are 2 distinct cases in which a kernel for a MIPS32 CPU
(CONFIG_CPU_MIPS32=y) may use 64 bit physical addresses
(CONFIG_PHYS_ADDR_T_64BIT=y):

  - 36 bit physical addressing as used by RMI Alchemy & Netlogic XLP/XLR
    CPUs.

  - MIPS32r5 eXtended Physical Addressing (XPA).

These 2 cases are distinct in that they require different behaviour from
the kernel - the EntryLo registers have different formats. Until Linux
v4.1 we only supported the first case, with code conditional upon the 2
aforementioned Kconfig variables being set. Commit c5b367835cfc ("MIPS:
Add support for XPA.") added support for the second case, but did so by
modifying the code that existed for the first case rather than treating
the 2 cases as distinct. Since the EntryLo registers have different
formats this breaks the 36 bit Alchemy/XLP/XLR case. Fix this by
splitting the 2 cases, with XPA cases now being conditional upon
CONFIG_XPA and the non-XPA case matching the code as it existed prior to
commit c5b367835cfc ("MIPS: Add support for XPA.").

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Reported-by: Manuel Lauss <manuel.lauss@gmail.com>
Tested-by: Manuel Lauss <manuel.lauss@gmail.com>
Fixes: c5b367835cfc ("MIPS: Add support for XPA.")
Cc: James Hogan <james.hogan@imgtec.com>
Cc: David Daney <david.daney@cavium.com>
Cc: Huacai Chen <chenhc@lemote.com>
Cc: Maciej W. Rozycki <macro@linux-mips.org>
Cc: Paul Gortmaker <paul.gortmaker@windriver.com>
Cc: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Cc: Peter Zijlstra (Intel) <peterz@infradead.org>
Cc: David Hildenbrand <dahi@linux.vnet.ibm.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Alex Smith <alex.smith@imgtec.com>
Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: stable@vger.kernel.org # v4.1+
Cc: linux-mips@linux-mips.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/13119/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/include/asm/pgtable-32.h
arch/mips/include/asm/pgtable-bits.h
arch/mips/include/asm/pgtable.h
arch/mips/mm/init.c
arch/mips/mm/tlbex.c

index 181bd8e7db7d625197c2ccf37950877370cb72d7..d21f3da7bdb619402a438b923fda454b7525d204 100644 (file)
@@ -103,7 +103,7 @@ static inline void pmd_clear(pmd_t *pmdp)
        pmd_val(*pmdp) = ((unsigned long) invalid_pte_table);
 }
 
-#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
+#if defined(CONFIG_XPA)
 
 #define pte_pfn(x)             (((unsigned long)((x).pte_high >> _PFN_SHIFT)) | (unsigned long)((x).pte_low << _PAGE_PRESENT_SHIFT))
 static inline pte_t
@@ -118,6 +118,20 @@ pfn_pte(unsigned long pfn, pgprot_t prot)
        return pte;
 }
 
+#elif defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
+
+#define pte_pfn(x)             ((unsigned long)((x).pte_high >> 6))
+
+static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot)
+{
+       pte_t pte;
+
+       pte.pte_high = (pfn << 6) | (pgprot_val(prot) & 0x3f);
+       pte.pte_low = pgprot_val(prot);
+
+       return pte;
+}
+
 #else
 
 #ifdef CONFIG_CPU_VR41XX
@@ -166,7 +180,7 @@ pfn_pte(unsigned long pfn, pgprot_t prot)
 
 #else
 
-#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
+#if defined(CONFIG_XPA)
 
 /* Swap entries must have VALID and GLOBAL bits cleared. */
 #define __swp_type(x)                  (((x).val >> 4) & 0x1f)
@@ -175,6 +189,15 @@ pfn_pte(unsigned long pfn, pgprot_t prot)
 #define __pte_to_swp_entry(pte)                ((swp_entry_t) { (pte).pte_high })
 #define __swp_entry_to_pte(x)          ((pte_t) { 0, (x).val })
 
+#elif defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
+
+/* Swap entries must have VALID and GLOBAL bits cleared. */
+#define __swp_type(x)                  (((x).val >> 2) & 0x1f)
+#define __swp_offset(x)                         ((x).val >> 7)
+#define __swp_entry(type, offset)      ((swp_entry_t)  { ((type) << 2) | ((offset) << 7) })
+#define __pte_to_swp_entry(pte)                ((swp_entry_t) { (pte).pte_high })
+#define __swp_entry_to_pte(x)          ((pte_t) { 0, (x).val })
+
 #else
 /*
  * Constraints:
index 5bc663d330d210aa97f0f882c55ef84c57033fcf..58e8bf8154043c10008d1f4c25a37f9633373ae7 100644 (file)
  * unpredictable things.  The code (when it is written) to deal with
  * this problem will be in the update_mmu_cache() code for the r4k.
  */
-#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
+#if defined(CONFIG_XPA)
 
 /*
- * Page table bit offsets used for 64 bit physical addressing on MIPS32,
- * for example with Alchemy, Netlogic XLP/XLR or XPA.
+ * Page table bit offsets used for 64 bit physical addressing on
+ * MIPS32r5 with XPA.
  */
 enum pgtable_bits {
        /* Used by TLB hardware (placed in EntryLo*) */
@@ -59,6 +59,27 @@ enum pgtable_bits {
  */
 #define _PFNX_MASK             0xffffff
 
+#elif defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
+
+/*
+ * Page table bit offsets used for 36 bit physical addressing on MIPS32,
+ * for example with Alchemy or Netlogic XLP/XLR.
+ */
+enum pgtable_bits {
+       /* Used by TLB hardware (placed in EntryLo*) */
+       _PAGE_GLOBAL_SHIFT,
+       _PAGE_VALID_SHIFT,
+       _PAGE_DIRTY_SHIFT,
+       _CACHE_SHIFT,
+
+       /* Used only by software (masked out before writing EntryLo*) */
+       _PAGE_PRESENT_SHIFT = _CACHE_SHIFT + 3,
+       _PAGE_NO_READ_SHIFT,
+       _PAGE_WRITE_SHIFT,
+       _PAGE_ACCESSED_SHIFT,
+       _PAGE_MODIFIED_SHIFT,
+};
+
 #elif defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_TX39XX)
 
 /* Page table bits used for r3k systems */
@@ -116,7 +137,7 @@ enum pgtable_bits {
 #endif
 
 /* Used by TLB hardware (placed in EntryLo*) */
-#if (defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32))
+#if defined(CONFIG_XPA)
 # define _PAGE_NO_EXEC         (1 << _PAGE_NO_EXEC_SHIFT)
 #elif defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_CPU_MIPSR6)
 # define _PAGE_NO_EXEC         (cpu_has_rixi ? (1 << _PAGE_NO_EXEC_SHIFT) : 0)
index f6de4f49b1b2d4b8596604e337c7e4af6884b7ce..e07a105cafc26fca81d3f1d1fb80d91201a40559 100644 (file)
@@ -133,7 +133,12 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
 
 #if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
 
-#define pte_none(pte)          (!(((pte).pte_high) & ~_PAGE_GLOBAL))
+#ifdef CONFIG_XPA
+# define pte_none(pte)         (!(((pte).pte_high) & ~_PAGE_GLOBAL))
+#else
+# define pte_none(pte)         (!(((pte).pte_low | (pte).pte_high) & ~_PAGE_GLOBAL))
+#endif
+
 #define pte_present(pte)       ((pte).pte_low & _PAGE_PRESENT)
 #define pte_no_exec(pte)       ((pte).pte_low & _PAGE_NO_EXEC)
 
@@ -143,14 +148,21 @@ static inline void set_pte(pte_t *ptep, pte_t pte)
        smp_wmb();
        ptep->pte_low = pte.pte_low;
 
+#ifdef CONFIG_XPA
        if (pte.pte_high & _PAGE_GLOBAL) {
+#else
+       if (pte.pte_low & _PAGE_GLOBAL) {
+#endif
                pte_t *buddy = ptep_buddy(ptep);
                /*
                 * Make sure the buddy is global too (if it's !none,
                 * it better already be global)
                 */
-               if (pte_none(*buddy))
+               if (pte_none(*buddy)) {
+                       if (!config_enabled(CONFIG_XPA))
+                               buddy->pte_low |= _PAGE_GLOBAL;
                        buddy->pte_high |= _PAGE_GLOBAL;
+               }
        }
 }
 
@@ -160,8 +172,13 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *pt
 
        htw_stop();
        /* Preserve global status for the pair */
-       if (ptep_buddy(ptep)->pte_high & _PAGE_GLOBAL)
-               null.pte_high = _PAGE_GLOBAL;
+       if (config_enabled(CONFIG_XPA)) {
+               if (ptep_buddy(ptep)->pte_high & _PAGE_GLOBAL)
+                       null.pte_high = _PAGE_GLOBAL;
+       } else {
+               if (ptep_buddy(ptep)->pte_low & _PAGE_GLOBAL)
+                       null.pte_low = null.pte_high = _PAGE_GLOBAL;
+       }
 
        set_pte_at(mm, addr, ptep, null);
        htw_start();
@@ -302,6 +319,8 @@ static inline int pte_young(pte_t pte)      { return pte.pte_low & _PAGE_ACCESSED; }
 static inline pte_t pte_wrprotect(pte_t pte)
 {
        pte.pte_low  &= ~_PAGE_WRITE;
+       if (!config_enabled(CONFIG_XPA))
+               pte.pte_low &= ~_PAGE_SILENT_WRITE;
        pte.pte_high &= ~_PAGE_SILENT_WRITE;
        return pte;
 }
@@ -309,6 +328,8 @@ static inline pte_t pte_wrprotect(pte_t pte)
 static inline pte_t pte_mkclean(pte_t pte)
 {
        pte.pte_low  &= ~_PAGE_MODIFIED;
+       if (!config_enabled(CONFIG_XPA))
+               pte.pte_low &= ~_PAGE_SILENT_WRITE;
        pte.pte_high &= ~_PAGE_SILENT_WRITE;
        return pte;
 }
@@ -316,6 +337,8 @@ static inline pte_t pte_mkclean(pte_t pte)
 static inline pte_t pte_mkold(pte_t pte)
 {
        pte.pte_low  &= ~_PAGE_ACCESSED;
+       if (!config_enabled(CONFIG_XPA))
+               pte.pte_low &= ~_PAGE_SILENT_READ;
        pte.pte_high &= ~_PAGE_SILENT_READ;
        return pte;
 }
@@ -323,24 +346,33 @@ static inline pte_t pte_mkold(pte_t pte)
 static inline pte_t pte_mkwrite(pte_t pte)
 {
        pte.pte_low |= _PAGE_WRITE;
-       if (pte.pte_low & _PAGE_MODIFIED)
+       if (pte.pte_low & _PAGE_MODIFIED) {
+               if (!config_enabled(CONFIG_XPA))
+                       pte.pte_low |= _PAGE_SILENT_WRITE;
                pte.pte_high |= _PAGE_SILENT_WRITE;
+       }
        return pte;
 }
 
 static inline pte_t pte_mkdirty(pte_t pte)
 {
        pte.pte_low |= _PAGE_MODIFIED;
-       if (pte.pte_low & _PAGE_WRITE)
+       if (pte.pte_low & _PAGE_WRITE) {
+               if (!config_enabled(CONFIG_XPA))
+                       pte.pte_low |= _PAGE_SILENT_WRITE;
                pte.pte_high |= _PAGE_SILENT_WRITE;
+       }
        return pte;
 }
 
 static inline pte_t pte_mkyoung(pte_t pte)
 {
        pte.pte_low |= _PAGE_ACCESSED;
-       if (!(pte.pte_low & _PAGE_NO_READ))
+       if (!(pte.pte_low & _PAGE_NO_READ)) {
+               if (!config_enabled(CONFIG_XPA))
+                       pte.pte_low |= _PAGE_SILENT_READ;
                pte.pte_high |= _PAGE_SILENT_READ;
+       }
        return pte;
 }
 #else
@@ -438,7 +470,7 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
  */
 #define mk_pte(page, pgprot)   pfn_pte(page_to_pfn(page), (pgprot))
 
-#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
+#if defined(CONFIG_XPA)
 static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 {
        pte.pte_low  &= (_PAGE_MODIFIED | _PAGE_ACCESSED | _PFNX_MASK);
@@ -447,6 +479,15 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
        pte.pte_high |= pgprot_val(newprot) & ~_PFN_MASK;
        return pte;
 }
+#elif defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
+static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
+{
+       pte.pte_low  &= _PAGE_CHG_MASK;
+       pte.pte_high &= (_PFN_MASK | _CACHE_MASK);
+       pte.pte_low  |= pgprot_val(newprot);
+       pte.pte_high |= pgprot_val(newprot) & ~(_PFN_MASK | _CACHE_MASK);
+       return pte;
+}
 #else
 static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 {
index 6c6a843b0d1783051098733fdac64ea4154230b9..134c988bc61fb15c9fff99306ee502f339fc43b8 100644 (file)
@@ -98,8 +98,10 @@ static void *__kmap_pgprot(struct page *page, unsigned long addr, pgprot_t prot)
        idx += in_interrupt() ? FIX_N_COLOURS : 0;
        vaddr = __fix_to_virt(FIX_CMAP_END - idx);
        pte = mk_pte(page, prot);
-#if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
+#if defined(CONFIG_XPA)
        entrylo = pte_to_entrylo(pte.pte_high);
+#elif defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32)
+       entrylo = pte.pte_high;
 #else
        entrylo = pte_to_entrylo(pte_val(pte));
 #endif
index 67966f30c5227a3eb14cb003f31ecfdfad3240a2..db4adf9cc65c44bd206326afd811e980d8d82d06 100644 (file)
@@ -1011,25 +1011,21 @@ static void build_get_ptep(u32 **p, unsigned int tmp, unsigned int ptr)
 
 static void build_update_entries(u32 **p, unsigned int tmp, unsigned int ptep)
 {
-       /*
-        * 64bit address support (36bit on a 32bit CPU) in a 32bit
-        * Kernel is a special case. Only a few CPUs use it.
-        */
-       if (config_enabled(CONFIG_PHYS_ADDR_T_64BIT) && !cpu_has_64bits) {
+       if (config_enabled(CONFIG_XPA)) {
                int pte_off_even = sizeof(pte_t) / 2;
                int pte_off_odd = pte_off_even + sizeof(pte_t);
-#ifdef CONFIG_XPA
                const int scratch = 1; /* Our extra working register */
 
                uasm_i_addu(p, scratch, 0, ptep);
-#endif
+
                uasm_i_lw(p, tmp, pte_off_even, ptep); /* even pte */
-               uasm_i_lw(p, ptep, pte_off_odd, ptep); /* odd pte */
                UASM_i_ROTR(p, tmp, tmp, ilog2(_PAGE_GLOBAL));
-               UASM_i_ROTR(p, ptep, ptep, ilog2(_PAGE_GLOBAL));
                UASM_i_MTC0(p, tmp, C0_ENTRYLO0);
+
+               uasm_i_lw(p, ptep, pte_off_odd, ptep); /* odd pte */
+               UASM_i_ROTR(p, ptep, ptep, ilog2(_PAGE_GLOBAL));
                UASM_i_MTC0(p, ptep, C0_ENTRYLO1);
-#ifdef CONFIG_XPA
+
                uasm_i_lw(p, tmp, 0, scratch);
                uasm_i_lw(p, ptep, sizeof(pte_t), scratch);
                uasm_i_lui(p, scratch, 0xff);
@@ -1038,7 +1034,22 @@ static void build_update_entries(u32 **p, unsigned int tmp, unsigned int ptep)
                uasm_i_and(p, ptep, scratch, ptep);
                uasm_i_mthc0(p, tmp, C0_ENTRYLO0);
                uasm_i_mthc0(p, ptep, C0_ENTRYLO1);
-#endif
+               return;
+       }
+
+       /*
+        * 64bit address support (36bit on a 32bit CPU) in a 32bit
+        * Kernel is a special case. Only a few CPUs use it.
+        */
+       if (config_enabled(CONFIG_PHYS_ADDR_T_64BIT) && !cpu_has_64bits) {
+               int pte_off_even = sizeof(pte_t) / 2;
+               int pte_off_odd = pte_off_even + sizeof(pte_t);
+
+               uasm_i_lw(p, tmp, pte_off_even, ptep); /* even pte */
+               UASM_i_MTC0(p, tmp, C0_ENTRYLO0);
+
+               uasm_i_lw(p, ptep, pte_off_odd, ptep); /* odd pte */
+               UASM_i_MTC0(p, ptep, C0_ENTRYLO1);
                return;
        }
 
@@ -1637,7 +1648,7 @@ iPTE_SW(u32 **p, struct uasm_reloc **r, unsigned int pte, unsigned int ptr,
 #ifdef CONFIG_PHYS_ADDR_T_64BIT
        unsigned int hwmode = mode & (_PAGE_VALID | _PAGE_DIRTY);
 
-       if (!cpu_has_64bits) {
+       if (config_enabled(CONFIG_XPA) && !cpu_has_64bits) {
                const int scratch = 1; /* Our extra working register */
 
                uasm_i_lui(p, scratch, (mode >> 16));