]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
xtensa: fix TLB multihit exceptions
authorChris Zankel <chris@zankel.net>
Mon, 20 May 2013 18:09:13 +0000 (11:09 -0700)
committerChris Zankel <chris@zankel.net>
Mon, 20 May 2013 18:09:13 +0000 (11:09 -0700)
- set _PAGE_USER in the pte_clear to avoid having TLB multihit exceptions
  (see following threads for more details);
  http://lists.linux-xtensa.org/pipermail/linux-xtensa/Week-of-Mon-20130401/
  http://lists.linux-xtensa.org/pipermail/linux-xtensa/Week-of-Mon-20130408/
- improved documentation of the PTE layout
- fix PTE mapping for present and 'prot_none' pages for T1050 hw and earlier
- fix pte_file offset and size
- add check for the correct number of bits for swap type

CC: piet.delaney@gmail.com
CC: jcmvbkbc@gmail.com
Signed-off-by: Chris Zankel <chris@zankel.net>
arch/xtensa/include/asm/pgtable.h

index d7546c94da520625d7ae02c65c8e05e025535816..7e09f7011370095f78c182eac5b86abbf67a3091 100644 (file)
@@ -5,7 +5,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  *
- * Copyright (C) 2001 - 2007 Tensilica Inc.
+ * Copyright (C) 2001 - 2013 Tensilica Inc.
  */
 
 #ifndef _XTENSA_PGTABLE_H
  * Virtual memory area. We keep a distance to other memory regions to be
  * on the safe side. We also use this area for cache aliasing.
  */
-
 #define VMALLOC_START          0xC0000000
 #define VMALLOC_END            0xC7FEFFFF
 #define TLBTEMP_BASE_1         0xC7FF0000
 #define TLBTEMP_BASE_2         0xC7FF8000
 
 /*
- * Xtensa Linux config PTE layout (when present):
- *     31-12:  PPN
- *     11-6:   Software
- *     5-4:    RING
- *     3-0:    CA
+ * For the Xtensa architecture, the PTE layout is as follows:
+ *
+ *             31------12  11  10-9   8-6  5-4  3-2  1-0
+ *             +-----------------------------------------+
+ *             |           |   Software   |   HARDWARE   |
+ *             |    PPN    |          ADW | RI |Attribute|
+ *             +-----------------------------------------+
+ *   pte_none  |             MBZ          | 01 | 11 | 00 |
+ *             +-----------------------------------------+
+ *   present   |    PPN    | 0 | 00 | ADW | RI | CA | wx |
+ *             +- - - - - - - - - - - - - - - - - - - - -+
+ *   (PAGE_NONE)|    PPN    | 0 | 00 | ADW | 01 | 11 | 11 |
+ *             +-----------------------------------------+
+ *   swap      |     index     |   type   | 01 | 11 | 00 |
+ *             +- - - - - - - - - - - - - - - - - - - - -+
+ *   file      |        file offset       | 01 | 11 | 10 |
+ *             +-----------------------------------------+
+ *
+ * For T1050 hardware and earlier the layout differs for present and (PAGE_NONE)
+ *             +-----------------------------------------+
+ *   present   |    PPN    | 0 | 00 | ADW | RI | CA | w1 |
+ *             +-----------------------------------------+
+ *   (PAGE_NONE)|    PPN    | 0 | 00 | ADW | 01 | 01 | 00 |
+ *             +-----------------------------------------+
  *
- * Similar to the Alpha and MIPS ports, we need to keep track of the ref
- * and mod bits in software.  We have a software "you can read
- * from this page" bit, and a hardware one which actually lets the
- * process read from the page.  On the same token we have a software
- * writable bit and the real hardware one which actually lets the
- * process write to the page.
+ *  Legend:
+ *   PPN        Physical Page Number
+ *   ADW       software: accessed (young) / dirty / writable
+ *   RI         ring (0=privileged, 1=user, 2 and 3 are unused)
+ *   CA                cache attribute: 00 bypass, 01 writeback, 10 writethrough
+ *             (11 is invalid and used to mark pages that are not present)
+ *   w         page is writable (hw)
+ *   x         page is executable (hw)
+ *   index      swap offset / PAGE_SIZE (bit 11-31: 21 bits -> 8 GB)
+ *             (note that the index is always non-zero)
+ *   type       swap type (5 bits -> 32 types)
+ *   file offset 26-bit offset into the file, in increments of PAGE_SIZE
  *
- * See further below for PTE layout for swapped-out pages.
+ *  Notes:
+ *   - (PROT_NONE) is a special case of 'present' but causes an exception for
+ *     any access (read, write, and execute).
+ *   - 'multihit-exception' has the highest priority of all MMU exceptions,
+ *     so the ring must be set to 'RING_USER' even for 'non-present' pages.
+ *   - on older hardware, the exectuable flag was not supported and
+ *     used as a 'valid' flag, so it needs to be always set.
+ *   - we need to keep track of certain flags in software (dirty and young)
+ *     to do this, we use write exceptions and have a separate software w-flag.
+ *   - attribute value 1101 (and 1111 on T1050 and earlier) is reserved
  */
 
+#define _PAGE_ATTRIB_MASK      0xf
+
 #define _PAGE_HW_EXEC          (1<<0)  /* hardware: page is executable */
 #define _PAGE_HW_WRITE         (1<<1)  /* hardware: page is writable */
 
-#define _PAGE_FILE             (1<<1)  /* non-linear mapping, if !present */
-#define _PAGE_PROTNONE         (3<<0)  /* special case for VM_PROT_NONE */
-
-/* None of these cache modes include MP coherency:  */
 #define _PAGE_CA_BYPASS                (0<<2)  /* bypass, non-speculative */
 #define _PAGE_CA_WB            (1<<2)  /* write-back */
 #define _PAGE_CA_WT            (2<<2)  /* write-through */
 #define _PAGE_CA_MASK          (3<<2)
-#define _PAGE_INVALID          (3<<2)
+#define _PAGE_CA_INVALID       (3<<2)
+
+/* We use invalid attribute values to distinguish special pte entries */
+#if XCHAL_HW_VERSION_MAJOR < 2000
+#define _PAGE_HW_VALID         0x01    /* older HW needed this bit set */
+#define _PAGE_NONE             0x04
+#else
+#define _PAGE_HW_VALID         0x00
+#define _PAGE_NONE             0x0f
+#endif
+#define _PAGE_FILE             (1<<1)  /* file mapped page, only if !present */
 
 #define _PAGE_USER             (1<<4)  /* user access (ring=1) */
 
 #define _PAGE_DIRTY            (1<<7)  /* software: page dirty */
 #define _PAGE_ACCESSED         (1<<8)  /* software: page accessed (read) */
 
-/* On older HW revisions, we always have to set bit 0 */
-#if XCHAL_HW_VERSION_MAJOR < 2000
-# define _PAGE_VALID           (1<<0)
-#else
-# define _PAGE_VALID           0
-#endif
-
-#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
-#define _PAGE_PRESENT  (_PAGE_VALID | _PAGE_CA_WB | _PAGE_ACCESSED)
-
 #ifdef CONFIG_MMU
 
-#define PAGE_NONE         __pgprot(_PAGE_INVALID | _PAGE_USER | _PAGE_PROTNONE)
+#define _PAGE_CHG_MASK    (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
+#define _PAGE_PRESENT     (_PAGE_HW_VALID | _PAGE_CA_WB | _PAGE_ACCESSED)
+
+#define PAGE_NONE         __pgprot(_PAGE_NONE | _PAGE_USER)
 #define PAGE_COPY         __pgprot(_PAGE_PRESENT | _PAGE_USER)
 #define PAGE_COPY_EXEC    __pgprot(_PAGE_PRESENT | _PAGE_USER | _PAGE_HW_EXEC)
 #define PAGE_READONLY     __pgprot(_PAGE_PRESENT | _PAGE_USER)
 #define PAGE_KERNEL_EXEC   __pgprot(_PAGE_PRESENT|_PAGE_HW_WRITE|_PAGE_HW_EXEC)
 
 #if (DCACHE_WAY_SIZE > PAGE_SIZE)
-# define _PAGE_DIRECTORY (_PAGE_VALID | _PAGE_ACCESSED)
+# define _PAGE_DIRECTORY   (_PAGE_HW_VALID | _PAGE_ACCESSED | _PAGE_CA_BYPASS)
 #else
-# define _PAGE_DIRECTORY (_PAGE_VALID | _PAGE_ACCESSED | _PAGE_CA_WB)
+# define _PAGE_DIRECTORY   (_PAGE_HW_VALID | _PAGE_ACCESSED | _PAGE_CA_WB)
 #endif
 
 #else /* no mmu */
@@ -202,12 +236,16 @@ static inline void pgtable_cache_init(void) { }
 /*
  * pte status.
  */
-#define pte_none(pte)   (pte_val(pte) == _PAGE_INVALID)
-#define pte_present(pte)                                               \
-       (((pte_val(pte) & _PAGE_CA_MASK) != _PAGE_INVALID)              \
-        || ((pte_val(pte) & _PAGE_PROTNONE) == _PAGE_PROTNONE))
+# define pte_none(pte)  (pte_val(pte) == (_PAGE_CA_INVALID | _PAGE_USER))
+#if XCHAL_HW_VERSION_MAJOR < 2000
+# define pte_present(pte) ((pte_val(pte) & _PAGE_CA_MASK) != _PAGE_CA_INVALID)
+#else
+# define pte_present(pte)                                              \
+       (((pte_val(pte) & _PAGE_CA_MASK) != _PAGE_CA_INVALID)           \
+        || ((pte_val(pte) & _PAGE_ATTRIB_MASK) == _PAGE_NONE))
+#endif
 #define pte_clear(mm,addr,ptep)                                                \
-       do { update_pte(ptep, __pte(_PAGE_INVALID)); } while(0)
+       do { update_pte(ptep, __pte(_PAGE_CA_INVALID | _PAGE_USER)); } while (0)
 
 #define pmd_none(pmd)   (!pmd_val(pmd))
 #define pmd_present(pmd) (pmd_val(pmd) & PAGE_MASK)
@@ -328,35 +366,23 @@ ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
 
 
 /*
- * Encode and decode a swap entry.
- *
- * Format of swap pte:
- *  bit           0       MBZ
- *  bit           1       page-file (must be zero)
- *  bits   2 -  3  page hw access mode (must be 11: _PAGE_INVALID)
- *  bits   4 -  5  ring protection (must be 01: _PAGE_USER)
- *  bits   6 - 10  swap type (5 bits -> 32 types)
- *  bits  11 - 31  swap offset / PAGE_SIZE (21 bits -> 8GB)
- * Format of file pte:
- *  bit           0       MBZ
- *  bit           1       page-file (must be one: _PAGE_FILE)
- *  bits   2 -  3  page hw access mode (must be 11: _PAGE_INVALID)
- *  bits   4 -  5  ring protection (must be 01: _PAGE_USER)
- *  bits   6 - 31  file offset / PAGE_SIZE
+ * Encode and decode a swap and file entry.
  */
+#define SWP_TYPE_BITS          5
+#define MAX_SWAPFILES_CHECK() BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > SWP_TYPE_BITS)
 
 #define __swp_type(entry)      (((entry).val >> 6) & 0x1f)
 #define __swp_offset(entry)    ((entry).val >> 11)
 #define __swp_entry(type,offs) \
-       ((swp_entry_t) {((type) << 6) | ((offs) << 11) | _PAGE_INVALID})
+       ((swp_entry_t){((type) << 6) | ((offs) << 11) | \
+        _PAGE_CA_INVALID | _PAGE_USER})
 #define __pte_to_swp_entry(pte)        ((swp_entry_t) { pte_val(pte) })
 #define __swp_entry_to_pte(x)  ((pte_t) { (x).val })
 
-#define PTE_FILE_MAX_BITS      28
-#define pte_to_pgoff(pte)      (pte_val(pte) >> 4)
+#define PTE_FILE_MAX_BITS      26
+#define pte_to_pgoff(pte)      (pte_val(pte) >> 6)
 #define pgoff_to_pte(off)      \
-       ((pte_t) { ((off) << 4) | _PAGE_INVALID | _PAGE_FILE })
+       ((pte_t) { ((off) << 6) | _PAGE_CA_INVALID | _PAGE_FILE | _PAGE_USER })
 
 #endif /*  !defined (__ASSEMBLY__) */