Commit 64b703ef276964b160a5e88df0764f254460cafb

Authored by Vineet Gupta
1 parent 4b06ff35fb

ARC: MMUv4 preps/1 - Fold PTE K/U access flags

The current ARC VM code has 13 flags in Page Table entry: some software
(accesed/dirty/non-linear-maps) and rest hardware specific. With 8k MMU
page, we need 19 bits for addressing page frame so remaining 13 bits is
just about enough to accomodate the current flags.

In MMUv4 there are 2 additional flags, SZ (normal or super page) and WT
(cache access mode write-thru) - and additionally PFN is 20 bits (vs. 19
before for 8k). Thus these can't be held in current PTE w/o making each
entry 64bit wide.

It seems there is some scope of compressing the current PTE flags (and
freeing up a few bits). Currently PTE contains fully orthogonal distinct
access permissions for kernel and user mode (Kr, Kw, Kx; Ur, Uw, Ux)
which can be folded into one set (R, W, X). The translation of 3 PTE
bits into 6 TLB bits (when programming the MMU) can be done based on
following pre-requites/assumptions:

1. For kernel-mode-only translations (vmalloc: 0x7000_0000 to
   0x7FFF_FFFF), PTE additionally has PAGE_GLOBAL flag set (and user
   space entries can never be global). Thus such a PTE can translate
   to Kr, Kw, Kx (as appropriate) and zero for User mode counterparts.

2. For non global entries, the PTE flags can be used to create mirrored
   K and U TLB bits. This is true after commit a950549c675f2c8c504
   "ARC: copy_(to|from)_user() to honor usermode-access permissions"
   which ensured that user-space translations _MUST_ have same access
   permissions for both U/K mode accesses so that  copy_{to,from}_user()
   play fair with fault based CoW break and such...

There is no such thing as free lunch - the cost is slightly infalted
TLB-Miss Handlers.

Signed-off-by: Vineet Gupta <vgupta@synopsys.com>

Showing 3 changed files with 54 additions and 48 deletions Side-by-side Diff

arch/arc/include/asm/pgtable.h
... ... @@ -57,27 +57,21 @@
57 57  
58 58 #define _PAGE_ACCESSED (1<<1) /* Page is accessed (S) */
59 59 #define _PAGE_CACHEABLE (1<<2) /* Page is cached (H) */
60   -#define _PAGE_U_EXECUTE (1<<3) /* Page has user execute perm (H) */
61   -#define _PAGE_U_WRITE (1<<4) /* Page has user write perm (H) */
62   -#define _PAGE_U_READ (1<<5) /* Page has user read perm (H) */
63   -#define _PAGE_K_EXECUTE (1<<6) /* Page has kernel execute perm (H) */
64   -#define _PAGE_K_WRITE (1<<7) /* Page has kernel write perm (H) */
65   -#define _PAGE_K_READ (1<<8) /* Page has kernel perm (H) */
  60 +#define _PAGE_EXECUTE (1<<3) /* Page has user execute perm (H) */
  61 +#define _PAGE_WRITE (1<<4) /* Page has user write perm (H) */
  62 +#define _PAGE_READ (1<<5) /* Page has user read perm (H) */
66 63 #define _PAGE_GLOBAL (1<<9) /* Page is global (H) */
67 64 #define _PAGE_MODIFIED (1<<10) /* Page modified (dirty) (S) */
68 65 #define _PAGE_FILE (1<<10) /* page cache/ swap (S) */
69 66 #define _PAGE_PRESENT (1<<11) /* TLB entry is valid (H) */
70 67  
71   -#else
  68 +#else /* MMU v3 onwards */
72 69  
73 70 /* PD1 */
74 71 #define _PAGE_CACHEABLE (1<<0) /* Page is cached (H) */
75   -#define _PAGE_U_EXECUTE (1<<1) /* Page has user execute perm (H) */
76   -#define _PAGE_U_WRITE (1<<2) /* Page has user write perm (H) */
77   -#define _PAGE_U_READ (1<<3) /* Page has user read perm (H) */
78   -#define _PAGE_K_EXECUTE (1<<4) /* Page has kernel execute perm (H) */
79   -#define _PAGE_K_WRITE (1<<5) /* Page has kernel write perm (H) */
80   -#define _PAGE_K_READ (1<<6) /* Page has kernel perm (H) */
  72 +#define _PAGE_EXECUTE (1<<1) /* Page has user execute perm (H) */
  73 +#define _PAGE_WRITE (1<<2) /* Page has user write perm (H) */
  74 +#define _PAGE_READ (1<<3) /* Page has user read perm (H) */
81 75 #define _PAGE_ACCESSED (1<<7) /* Page is accessed (S) */
82 76  
83 77 /* PD0 */
... ... @@ -92,8 +86,8 @@
92 86 #define _PAGE_SHARED_CODE_H (1<<31) /* Hardware counterpart of above */
93 87 #endif
94 88  
95   -/* Kernel allowed all permissions for all pages */
96   -#define _K_PAGE_PERMS (_PAGE_K_EXECUTE | _PAGE_K_WRITE | _PAGE_K_READ | \
  89 +/* vmalloc permissions */
  90 +#define _K_PAGE_PERMS (_PAGE_EXECUTE | _PAGE_WRITE | _PAGE_READ | \
97 91 _PAGE_GLOBAL | _PAGE_PRESENT)
98 92  
99 93 #ifdef CONFIG_ARC_CACHE_PAGES
... ... @@ -109,10 +103,6 @@
109 103 */
110 104 #define ___DEF (_PAGE_PRESENT | _PAGE_DEF_CACHEABLE)
111 105  
112   -#define _PAGE_READ (_PAGE_U_READ | _PAGE_K_READ)
113   -#define _PAGE_WRITE (_PAGE_U_WRITE | _PAGE_K_WRITE)
114   -#define _PAGE_EXECUTE (_PAGE_U_EXECUTE | _PAGE_K_EXECUTE)
115   -
116 106 /* Set of bits not changed in pte_modify */
117 107 #define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_MODIFIED)
118 108  
... ... @@ -126,8 +116,8 @@
126 116  
127 117 #define PAGE_SHARED PAGE_U_W_R
128 118  
129   -/* While kernel runs out of unstrslated space, vmalloc/modules use a chunk of
130   - * kernel vaddr space - visible in all addr spaces, but kernel mode only
  119 +/* While kernel runs out of unstranslated space, vmalloc/modules use a chunk of
  120 + * user vaddr space - visible in all addr spaces, but kernel mode only
131 121 * Thus Global, all-kernel-access, no-user-access, cached
132 122 */
133 123 #define PAGE_KERNEL __pgprot(_K_PAGE_PERMS | _PAGE_DEF_CACHEABLE)
... ... @@ -136,10 +126,9 @@
136 126 #define PAGE_KERNEL_NO_CACHE __pgprot(_K_PAGE_PERMS)
137 127  
138 128 /* Masks for actual TLB "PD"s */
139   -#define PTE_BITS_IN_PD0 (_PAGE_GLOBAL | _PAGE_PRESENT)
140   -#define PTE_BITS_IN_PD1 (PAGE_MASK | _PAGE_CACHEABLE | \
141   - _PAGE_U_EXECUTE | _PAGE_U_WRITE | _PAGE_U_READ | \
142   - _PAGE_K_EXECUTE | _PAGE_K_WRITE | _PAGE_K_READ)
  129 +#define PTE_BITS_IN_PD0 (_PAGE_GLOBAL | _PAGE_PRESENT)
  130 +#define PTE_BITS_RWX (_PAGE_EXECUTE | _PAGE_WRITE | _PAGE_READ)
  131 +#define PTE_BITS_NON_RWX_IN_PD1 (PAGE_MASK | _PAGE_CACHEABLE)
143 132  
144 133 /**************************************************************************
145 134 * Mapping of vm_flags (Generic VM) to PTE flags (arch specific)
... ... @@ -341,7 +341,7 @@
341 341 void create_tlb(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
342 342 {
343 343 unsigned long flags;
344   - unsigned int idx, asid_or_sasid;
  344 + unsigned int idx, asid_or_sasid, rwx;
345 345 unsigned long pd0_flags;
346 346  
347 347 /*
348 348  
... ... @@ -393,8 +393,23 @@
393 393  
394 394 write_aux_reg(ARC_REG_TLBPD0, address | pd0_flags | asid_or_sasid);
395 395  
  396 + /*
  397 + * ARC MMU provides fully orthogonal access bits for K/U mode,
  398 + * however Linux only saves 1 set to save PTE real-estate
  399 + * Here we convert 3 PTE bits into 6 MMU bits:
  400 + * -Kernel only entries have Kr Kw Kx 0 0 0
  401 + * -User entries have mirrored K and U bits
  402 + */
  403 + rwx = pte_val(*ptep) & PTE_BITS_RWX;
  404 +
  405 + if (pte_val(*ptep) & _PAGE_GLOBAL)
  406 + rwx <<= 3; /* r w x => Kr Kw Kx 0 0 0 */
  407 + else
  408 + rwx |= (rwx << 3); /* r w x => Kr Kw Kx Ur Uw Ux */
  409 +
396 410 /* Load remaining info in PD1 (Page Frame Addr and Kx/Kw/Kr Flags) */
397   - write_aux_reg(ARC_REG_TLBPD1, (pte_val(*ptep) & PTE_BITS_IN_PD1));
  411 + write_aux_reg(ARC_REG_TLBPD1,
  412 + rwx | (pte_val(*ptep) & PTE_BITS_NON_RWX_IN_PD1));
398 413  
399 414 /* First verify if entry for this vaddr+ASID already exists */
400 415 write_aux_reg(ARC_REG_TLBCOMMAND, TLBProbe);
... ... @@ -218,9 +218,16 @@
218 218 ; IN: r0 = PTE, r1 = ptr to PTE
219 219  
220 220 .macro CONV_PTE_TO_TLB
221   - and r3, r0, PTE_BITS_IN_PD1 ; Extract permission flags+PFN from PTE
222   - sr r3, [ARC_REG_TLBPD1] ; these go in PD1
  221 + and r3, r0, PTE_BITS_RWX ; r w x
  222 + lsl r2, r3, 3 ; r w x 0 0 0
  223 + and.f 0, r0, _PAGE_GLOBAL
  224 + or.z r2, r2, r3 ; r w x r w x
223 225  
  226 + and r3, r0, PTE_BITS_NON_RWX_IN_PD1 ; Extract PFN+cache bits from PTE
  227 + or r3, r3, r2
  228 +
  229 + sr r3, [ARC_REG_TLBPD1] ; these go in PD1
  230 +
224 231 and r2, r0, PTE_BITS_IN_PD0 ; Extract other PTE flags: (V)alid, (G)lb
225 232 #if (CONFIG_ARC_MMU_VER <= 2) /* Neednot be done with v3 onwards */
226 233 lsr r2, r2 ; shift PTE flags to match layout in PD0
... ... @@ -272,8 +279,8 @@
272 279 ;----------------------------------------------------------------
273 280 ; VERIFY_PTE: Check if PTE permissions approp for executing code
274 281 cmp_s r2, VMALLOC_START
275   - mov.lo r2, (_PAGE_PRESENT | _PAGE_U_EXECUTE)
276   - mov.hs r2, (_PAGE_PRESENT | _PAGE_K_EXECUTE)
  282 + mov_s r2, (_PAGE_PRESENT | _PAGE_EXECUTE)
  283 + or.hs r2, r2, _PAGE_GLOBAL
277 284  
278 285 and r3, r0, r2 ; Mask out NON Flag bits from PTE
279 286 xor.f r3, r3, r2 ; check ( ( pte & flags_test ) == flags_test )
280 287  
281 288  
... ... @@ -312,26 +319,21 @@
312 319 ;----------------------------------------------------------------
313 320 ; VERIFY_PTE: Chk if PTE permissions approp for data access (R/W/R+W)
314 321  
315   - mov_s r2, 0
  322 + cmp_s r2, VMALLOC_START
  323 + mov_s r2, _PAGE_PRESENT ; common bit for K/U PTE
  324 + or.hs r2, r2, _PAGE_GLOBAL ; kernel PTE only
  325 +
  326 + ; Linux PTE [RWX] bits are semantically overloaded:
  327 + ; -If PAGE_GLOBAL set, they refer to kernel-only flags (vmalloc)
  328 + ; -Otherwise they are user-mode permissions, and those are exactly
  329 + ; same for kernel mode as well (e.g. copy_(to|from)_user)
  330 +
316 331 lr r3, [ecr]
317 332 btst_s r3, ECR_C_BIT_DTLB_LD_MISS ; Read Access
318   - or.nz r2, r2, _PAGE_U_READ ; chk for Read flag in PTE
  333 + or.nz r2, r2, _PAGE_READ ; chk for Read flag in PTE
319 334 btst_s r3, ECR_C_BIT_DTLB_ST_MISS ; Write Access
320   - or.nz r2, r2, _PAGE_U_WRITE ; chk for Write flag in PTE
321   - ; Above laddering takes care of XCHG access
322   - ; which is both Read and Write
323   -
324   - ; If kernel mode access, ; make _PAGE_xx flags as _PAGE_K_xx
325   - ; For copy_(to|from)_user, despite exception taken in kernel mode,
326   - ; this code is not hit, because EFA would still be the user mode
327   - ; address (EFA < 0x6000_0000).
328   - ; This code is for legit kernel mode faults, vmalloc specifically
329   - ; (EFA: 0x7000_0000 to 0x7FFF_FFFF)
330   -
331   - lr r3, [efa]
332   - cmp r3, VMALLOC_START - 1 ; If kernel mode access
333   - asl.hi r2, r2, 3 ; make _PAGE_xx flags as _PAGE_K_xx
334   - or r2, r2, _PAGE_PRESENT ; Common flag for K/U mode
  335 + or.nz r2, r2, _PAGE_WRITE ; chk for Write flag in PTE
  336 + ; Above laddering takes care of XCHG access (both R and W)
335 337  
336 338 ; By now, r2 setup with all the Flags we need to check in PTE
337 339 and r3, r0, r2 ; Mask out NON Flag bits from PTE