Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7711ece9 authored by Chris Zankel's avatar Chris Zankel
Browse files

xtensa: fix TLB multihit exceptions

- 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: default avatarChris Zankel <chris@zankel.net>
parent f722406f
Loading
Loading
Loading
Loading
+83 −57
Original line number Diff line number Diff line
@@ -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
@@ -64,41 +64,82 @@
 * 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) */

@@ -108,19 +149,12 @@
#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
#ifdef CONFIG_MMU

#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_PRESENT	   (_PAGE_HW_VALID | _PAGE_CA_WB | _PAGE_ACCESSED)

#define PAGE_NONE	   __pgprot(_PAGE_INVALID | _PAGE_USER | _PAGE_PROTNONE)
#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)
@@ -132,9 +166,9 @@
#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_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_INVALID)		\
	 || ((pte_val(pte) & _PAGE_PROTNONE) == _PAGE_PROTNONE))
	(((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__) */