root/arch/arc/mm/tlbex.S

/* [<][>][^][v][top][bottom][index][help] */
   1 /* SPDX-License-Identifier: GPL-2.0-only */
   2 /*
   3  * TLB Exception Handling for ARC
   4  *
   5  * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
   6  *
   7  * Vineetg: April 2011 :
   8  *  -MMU v1: moved out legacy code into a seperate file
   9  *  -MMU v3: PD{0,1} bits layout changed: They don't overlap anymore,
  10  *      helps avoid a shift when preparing PD0 from PTE
  11  *
  12  * Vineetg: July 2009
  13  *  -For MMU V2, we need not do heuristics at the time of commiting a D-TLB
  14  *   entry, so that it doesn't knock out it's I-TLB entry
  15  *  -Some more fine tuning:
  16  *   bmsk instead of add, asl.cc instead of branch, delay slot utilise etc
  17  *
  18  * Vineetg: July 2009
  19  *  -Practically rewrote the I/D TLB Miss handlers
  20  *   Now 40 and 135 instructions a peice as compared to 131 and 449 resp.
  21  *   Hence Leaner by 1.5 K
  22  *   Used Conditional arithmetic to replace excessive branching
  23  *   Also used short instructions wherever possible
  24  *
  25  * Vineetg: Aug 13th 2008
  26  *  -Passing ECR (Exception Cause REG) to do_page_fault( ) for printing
  27  *   more information in case of a Fatality
  28  *
  29  * Vineetg: March 25th Bug #92690
  30  *  -Added Debug Code to check if sw-ASID == hw-ASID
  31 
  32  * Rahul Trivedi, Amit Bhor: Codito Technologies 2004
  33  */
  34 
  35 #include <linux/linkage.h>
  36 #include <asm/entry.h>
  37 #include <asm/mmu.h>
  38 #include <asm/pgtable.h>
  39 #include <asm/arcregs.h>
  40 #include <asm/cache.h>
  41 #include <asm/processor.h>
  42 #include <asm/tlb-mmu1.h>
  43 
  44 #ifdef CONFIG_ISA_ARCOMPACT
  45 ;-----------------------------------------------------------------
  46 ; ARC700 Exception Handling doesn't auto-switch stack and it only provides
  47 ; ONE scratch AUX reg "ARC_REG_SCRATCH_DATA0"
  48 ;
  49 ; For Non-SMP, the scratch AUX reg is repurposed to cache task PGD, so a
  50 ; "global" is used to free-up FIRST core reg to be able to code the rest of
  51 ; exception prologue (IRQ auto-disabled on Exceptions, so it's IRQ-safe).
  52 ; Since the Fast Path TLB Miss handler is coded with 4 regs, the remaining 3
  53 ; need to be saved as well by extending the "global" to be 4 words. Hence
  54 ;       ".size   ex_saved_reg1, 16"
  55 ; [All of this dance is to avoid stack switching for each TLB Miss, since we
  56 ; only need to save only a handful of regs, as opposed to complete reg file]
  57 ;
  58 ; For ARC700 SMP, the "global" obviously can't be used for free up the FIRST
  59 ; core reg as it will not be SMP safe.
  60 ; Thus scratch AUX reg is used (and no longer used to cache task PGD).
  61 ; To save the rest of 3 regs - per cpu, the global is made "per-cpu".
  62 ; Epilogue thus has to locate the "per-cpu" storage for regs.
  63 ; To avoid cache line bouncing the per-cpu global is aligned/sized per
  64 ; L1_CACHE_SHIFT, despite fundamentally needing to be 12 bytes only. Hence
  65 ;       ".size   ex_saved_reg1, (CONFIG_NR_CPUS << L1_CACHE_SHIFT)"
  66 
  67 ; As simple as that....
  68 ;--------------------------------------------------------------------------
  69 
  70 ; scratch memory to save [r0-r3] used to code TLB refill Handler
  71 ARCFP_DATA ex_saved_reg1
  72         .align 1 << L1_CACHE_SHIFT
  73         .type   ex_saved_reg1, @object
  74 #ifdef CONFIG_SMP
  75         .size   ex_saved_reg1, (CONFIG_NR_CPUS << L1_CACHE_SHIFT)
  76 ex_saved_reg1:
  77         .zero (CONFIG_NR_CPUS << L1_CACHE_SHIFT)
  78 #else
  79         .size   ex_saved_reg1, 16
  80 ex_saved_reg1:
  81         .zero 16
  82 #endif
  83 
  84 .macro TLBMISS_FREEUP_REGS
  85 #ifdef CONFIG_SMP
  86         sr  r0, [ARC_REG_SCRATCH_DATA0] ; freeup r0 to code with
  87         GET_CPU_ID  r0                  ; get to per cpu scratch mem,
  88         asl r0, r0, L1_CACHE_SHIFT      ; cache line wide per cpu
  89         add r0, @ex_saved_reg1, r0
  90 #else
  91         st    r0, [@ex_saved_reg1]
  92         mov_s r0, @ex_saved_reg1
  93 #endif
  94         st_s  r1, [r0, 4]
  95         st_s  r2, [r0, 8]
  96         st_s  r3, [r0, 12]
  97 
  98         ; VERIFY if the ASID in MMU-PID Reg is same as
  99         ; one in Linux data structures
 100 
 101         tlb_paranoid_check_asm
 102 .endm
 103 
 104 .macro TLBMISS_RESTORE_REGS
 105 #ifdef CONFIG_SMP
 106         GET_CPU_ID  r0                  ; get to per cpu scratch mem
 107         asl r0, r0, L1_CACHE_SHIFT      ; each is cache line wide
 108         add r0, @ex_saved_reg1, r0
 109         ld_s  r3, [r0,12]
 110         ld_s  r2, [r0, 8]
 111         ld_s  r1, [r0, 4]
 112         lr    r0, [ARC_REG_SCRATCH_DATA0]
 113 #else
 114         mov_s r0, @ex_saved_reg1
 115         ld_s  r3, [r0,12]
 116         ld_s  r2, [r0, 8]
 117         ld_s  r1, [r0, 4]
 118         ld_s  r0, [r0]
 119 #endif
 120 .endm
 121 
 122 #else   /* ARCv2 */
 123 
 124 .macro TLBMISS_FREEUP_REGS
 125         PUSH  r0
 126         PUSH  r1
 127         PUSH  r2
 128         PUSH  r3
 129 .endm
 130 
 131 .macro TLBMISS_RESTORE_REGS
 132         POP   r3
 133         POP   r2
 134         POP   r1
 135         POP   r0
 136 .endm
 137 
 138 #endif
 139 
 140 ;============================================================================
 141 ;  Troubleshooting Stuff
 142 ;============================================================================
 143 
 144 ; Linux keeps ASID (Address Space ID) in task->active_mm->context.asid
 145 ; When Creating TLB Entries, instead of doing 3 dependent loads from memory,
 146 ; we use the MMU PID Reg to get current ASID.
 147 ; In bizzare scenrios SW and HW ASID can get out-of-sync which is trouble.
 148 ; So we try to detect this in TLB Mis shandler
 149 
 150 .macro tlb_paranoid_check_asm
 151 
 152 #ifdef CONFIG_ARC_DBG_TLB_PARANOIA
 153 
 154         GET_CURR_TASK_ON_CPU  r3
 155         ld r0, [r3, TASK_ACT_MM]
 156         ld r0, [r0, MM_CTXT+MM_CTXT_ASID]
 157         breq r0, 0, 55f ; Error if no ASID allocated
 158 
 159         lr r1, [ARC_REG_PID]
 160         and r1, r1, 0xFF
 161 
 162         and r2, r0, 0xFF        ; MMU PID bits only for comparison
 163         breq r1, r2, 5f
 164 
 165 55:
 166         ; Error if H/w and S/w ASID don't match, but NOT if in kernel mode
 167         lr  r2, [erstatus]
 168         bbit0 r2, STATUS_U_BIT, 5f
 169 
 170         ; We sure are in troubled waters, Flag the error, but to do so
 171         ; need to switch to kernel mode stack to call error routine
 172         GET_TSK_STACK_BASE   r3, sp
 173 
 174         ; Call printk to shoutout aloud
 175         mov r2, 1
 176         j print_asid_mismatch
 177 
 178 5:      ; ASIDs match so proceed normally
 179         nop
 180 
 181 #endif
 182 
 183 .endm
 184 
 185 ;============================================================================
 186 ;TLB Miss handling Code
 187 ;============================================================================
 188 
 189 ;-----------------------------------------------------------------------------
 190 ; This macro does the page-table lookup for the faulting address.
 191 ; OUT: r0 = PTE faulted on, r1 = ptr to PTE, r2 = Faulting V-address
 192 .macro LOAD_FAULT_PTE
 193 
 194         lr  r2, [efa]
 195 
 196 #ifndef CONFIG_SMP
 197         lr  r1, [ARC_REG_SCRATCH_DATA0] ; current pgd
 198 #else
 199         GET_CURR_TASK_ON_CPU  r1
 200         ld  r1, [r1, TASK_ACT_MM]
 201         ld  r1, [r1, MM_PGD]
 202 #endif
 203 
 204         lsr     r0, r2, PGDIR_SHIFT     ; Bits for indexing into PGD
 205         ld.as   r3, [r1, r0]            ; PGD entry corresp to faulting addr
 206         tst     r3, r3
 207         bz      do_slow_path_pf         ; if no Page Table, do page fault
 208 
 209 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
 210         and.f   0, r3, _PAGE_HW_SZ      ; Is this Huge PMD (thp)
 211         add2.nz r1, r1, r0
 212         bnz.d   2f              ; YES: PGD == PMD has THP PTE: stop pgd walk
 213         mov.nz  r0, r3
 214 
 215 #endif
 216         and     r1, r3, PAGE_MASK
 217 
 218         ; Get the PTE entry: The idea is
 219         ; (1) x = addr >> PAGE_SHIFT    -> masks page-off bits from @fault-addr
 220         ; (2) y = x & (PTRS_PER_PTE - 1) -> to get index
 221         ; (3) z = (pgtbl + y * 4)
 222 
 223 #ifdef CONFIG_ARC_HAS_PAE40
 224 #define PTE_SIZE_LOG    3       /* 8 == 2 ^ 3 */
 225 #else
 226 #define PTE_SIZE_LOG    2       /* 4 == 2 ^ 2 */
 227 #endif
 228 
 229         ; multiply in step (3) above avoided by shifting lesser in step (1)
 230         lsr     r0, r2, ( PAGE_SHIFT - PTE_SIZE_LOG )
 231         and     r0, r0, ( (PTRS_PER_PTE - 1) << PTE_SIZE_LOG )
 232         ld.aw   r0, [r1, r0]            ; r0: PTE (lower word only for PAE40)
 233                                         ; r1: PTE ptr
 234 
 235 2:
 236 
 237 .endm
 238 
 239 ;-----------------------------------------------------------------
 240 ; Convert Linux PTE entry into TLB entry
 241 ; A one-word PTE entry is programmed as two-word TLB Entry [PD0:PD1] in mmu
 242 ;    (for PAE40, two-words PTE, while three-word TLB Entry [PD0:PD1:PD1HI])
 243 ; IN: r0 = PTE, r1 = ptr to PTE
 244 
 245 .macro CONV_PTE_TO_TLB
 246         and    r3, r0, PTE_BITS_RWX     ;          r  w  x
 247         asl    r2, r3, 3                ; Kr Kw Kx 0  0  0 (GLOBAL, kernel only)
 248         and.f  0,  r0, _PAGE_GLOBAL
 249         or.z   r2, r2, r3               ; Kr Kw Kx Ur Uw Ux (!GLOBAL, user page)
 250 
 251         and r3, r0, PTE_BITS_NON_RWX_IN_PD1 ; Extract PFN+cache bits from PTE
 252         or  r3, r3, r2
 253 
 254         sr  r3, [ARC_REG_TLBPD1]        ; paddr[31..13] | Kr Kw Kx Ur Uw Ux | C
 255 #ifdef  CONFIG_ARC_HAS_PAE40
 256         ld      r3, [r1, 4]             ; paddr[39..32]
 257         sr      r3, [ARC_REG_TLBPD1HI]
 258 #endif
 259 
 260         and r2, r0, PTE_BITS_IN_PD0 ; Extract other PTE flags: (V)alid, (G)lb
 261 
 262         lr  r3,[ARC_REG_TLBPD0]     ; MMU prepares PD0 with vaddr and asid
 263 
 264         or  r3, r3, r2              ; S | vaddr | {sasid|asid}
 265         sr  r3,[ARC_REG_TLBPD0]     ; rewrite PD0
 266 .endm
 267 
 268 ;-----------------------------------------------------------------
 269 ; Commit the TLB entry into MMU
 270 
 271 .macro COMMIT_ENTRY_TO_MMU
 272 #if (CONFIG_ARC_MMU_VER < 4)
 273 
 274 #ifdef CONFIG_EZNPS_MTM_EXT
 275         /* verify if entry for this vaddr+ASID already exists */
 276         sr    TLBProbe, [ARC_REG_TLBCOMMAND]
 277         lr    r0, [ARC_REG_TLBINDEX]
 278         bbit0 r0, 31, 88f
 279 #endif
 280 
 281         /* Get free TLB slot: Set = computed from vaddr, way = random */
 282         sr  TLBGetIndex, [ARC_REG_TLBCOMMAND]
 283 
 284         /* Commit the Write */
 285 #if (CONFIG_ARC_MMU_VER >= 2)   /* introduced in v2 */
 286         sr TLBWriteNI, [ARC_REG_TLBCOMMAND]
 287 #else
 288         sr TLBWrite, [ARC_REG_TLBCOMMAND]
 289 #endif
 290 
 291 #else
 292         sr TLBInsertEntry, [ARC_REG_TLBCOMMAND]
 293 #endif
 294 
 295 88:
 296 .endm
 297 
 298 
 299 ARCFP_CODE      ;Fast Path Code, candidate for ICCM
 300 
 301 ;-----------------------------------------------------------------------------
 302 ; I-TLB Miss Exception Handler
 303 ;-----------------------------------------------------------------------------
 304 
 305 ENTRY(EV_TLBMissI)
 306 
 307         TLBMISS_FREEUP_REGS
 308 
 309         ;----------------------------------------------------------------
 310         ; Get the PTE corresponding to V-addr accessed, r2 is setup with EFA
 311         LOAD_FAULT_PTE
 312 
 313         ;----------------------------------------------------------------
 314         ; VERIFY_PTE: Check if PTE permissions approp for executing code
 315         cmp_s   r2, VMALLOC_START
 316         mov_s   r2, (_PAGE_PRESENT | _PAGE_EXECUTE)
 317         or.hs   r2, r2, _PAGE_GLOBAL
 318 
 319         and     r3, r0, r2  ; Mask out NON Flag bits from PTE
 320         xor.f   r3, r3, r2  ; check ( ( pte & flags_test ) == flags_test )
 321         bnz     do_slow_path_pf
 322 
 323         ; Let Linux VM know that the page was accessed
 324         or      r0, r0, _PAGE_ACCESSED  ; set Accessed Bit
 325         st_s    r0, [r1]                ; Write back PTE
 326 
 327         CONV_PTE_TO_TLB
 328         COMMIT_ENTRY_TO_MMU
 329         TLBMISS_RESTORE_REGS
 330 EV_TLBMissI_fast_ret:   ; additional label for VDK OS-kit instrumentation
 331         rtie
 332 
 333 END(EV_TLBMissI)
 334 
 335 ;-----------------------------------------------------------------------------
 336 ; D-TLB Miss Exception Handler
 337 ;-----------------------------------------------------------------------------
 338 
 339 ENTRY(EV_TLBMissD)
 340 
 341         TLBMISS_FREEUP_REGS
 342 
 343         ;----------------------------------------------------------------
 344         ; Get the PTE corresponding to V-addr accessed
 345         ; If PTE exists, it will setup, r0 = PTE, r1 = Ptr to PTE, r2 = EFA
 346         LOAD_FAULT_PTE
 347 
 348         ;----------------------------------------------------------------
 349         ; VERIFY_PTE: Chk if PTE permissions approp for data access (R/W/R+W)
 350 
 351         cmp_s   r2, VMALLOC_START
 352         mov_s   r2, _PAGE_PRESENT       ; common bit for K/U PTE
 353         or.hs   r2, r2, _PAGE_GLOBAL    ; kernel PTE only
 354 
 355         ; Linux PTE [RWX] bits are semantically overloaded:
 356         ; -If PAGE_GLOBAL set, they refer to kernel-only flags (vmalloc)
 357         ; -Otherwise they are user-mode permissions, and those are exactly
 358         ;  same for kernel mode as well (e.g. copy_(to|from)_user)
 359 
 360         lr      r3, [ecr]
 361         btst_s  r3, ECR_C_BIT_DTLB_LD_MISS      ; Read Access
 362         or.nz   r2, r2, _PAGE_READ              ; chk for Read flag in PTE
 363         btst_s  r3, ECR_C_BIT_DTLB_ST_MISS      ; Write Access
 364         or.nz   r2, r2, _PAGE_WRITE             ; chk for Write flag in PTE
 365         ; Above laddering takes care of XCHG access (both R and W)
 366 
 367         ; By now, r2 setup with all the Flags we need to check in PTE
 368         and     r3, r0, r2              ; Mask out NON Flag bits from PTE
 369         brne.d  r3, r2, do_slow_path_pf ; is ((pte & flags_test) == flags_test)
 370 
 371         ;----------------------------------------------------------------
 372         ; UPDATE_PTE: Let Linux VM know that page was accessed/dirty
 373         lr      r3, [ecr]
 374         or      r0, r0, _PAGE_ACCESSED        ; Accessed bit always
 375         btst_s  r3,  ECR_C_BIT_DTLB_ST_MISS   ; See if it was a Write Access ?
 376         or.nz   r0, r0, _PAGE_DIRTY           ; if Write, set Dirty bit as well
 377         st_s    r0, [r1]                      ; Write back PTE
 378 
 379         CONV_PTE_TO_TLB
 380 
 381 #if (CONFIG_ARC_MMU_VER == 1)
 382         ; MMU with 2 way set assoc J-TLB, needs some help in pathetic case of
 383         ; memcpy where 3 parties contend for 2 ways, ensuing a livelock.
 384         ; But only for old MMU or one with Metal Fix
 385         TLB_WRITE_HEURISTICS
 386 #endif
 387 
 388         COMMIT_ENTRY_TO_MMU
 389         TLBMISS_RESTORE_REGS
 390 EV_TLBMissD_fast_ret:   ; additional label for VDK OS-kit instrumentation
 391         rtie
 392 
 393 ;-------- Common routine to call Linux Page Fault Handler -----------
 394 do_slow_path_pf:
 395 
 396 #ifdef CONFIG_ISA_ARCV2
 397         ; Set Z flag if exception in U mode. Hardware micro-ops do this on any
 398         ; taken interrupt/exception, and thus is already the case at the entry
 399         ; above, but ensuing code would have already clobbered.
 400         ; EXCEPTION_PROLOGUE called in slow path, relies on correct Z flag set
 401 
 402         lr      r2, [erstatus]
 403         and     r2, r2, STATUS_U_MASK
 404         bxor.f  0, r2, STATUS_U_BIT
 405 #endif
 406 
 407         ; Restore the 4-scratch regs saved by fast path miss handler
 408         TLBMISS_RESTORE_REGS
 409 
 410         ; Slow path TLB Miss handled as a regular ARC Exception
 411         ; (stack switching / save the complete reg-file).
 412         b  call_do_page_fault
 413 END(EV_TLBMissD)

/* [<][>][^][v][top][bottom][index][help] */