| /* SPDX-License-Identifier: GPL-2.0 */ |
| /* |
| * We need constants.h for: |
| * VMA_VM_MM |
| * VMA_VM_FLAGS |
| * VM_EXEC |
| */ |
| #include <asm/asm-offsets.h> |
| #include <asm/pgtable.h> |
| #include <asm/thread_info.h> |
| |
| #ifdef CONFIG_CPU_V7M |
| #include <asm/v7m.h> |
| #endif |
| |
| /* |
| * vma_vm_mm - get mm pointer from vma pointer (vma->vm_mm) |
| */ |
| .macro vma_vm_mm, rd, rn |
| ldr \rd, [\rn, #VMA_VM_MM] |
| .endm |
| |
| /* |
| * vma_vm_flags - get vma->vm_flags |
| */ |
| .macro vma_vm_flags, rd, rn |
| ldr \rd, [\rn, #VMA_VM_FLAGS] |
| .endm |
| |
| /* |
| * act_mm - get current->active_mm |
| */ |
| .macro act_mm, rd |
| get_current \rd |
| .if (TSK_ACTIVE_MM > IMM12_MASK) |
| add \rd, \rd, #TSK_ACTIVE_MM & ~IMM12_MASK |
| .endif |
| ldr \rd, [\rd, #TSK_ACTIVE_MM & IMM12_MASK] |
| .endm |
| |
| /* |
| * mmid - get context id from mm pointer (mm->context.id) |
| * note, this field is 64bit, so in big-endian the two words are swapped too. |
| */ |
| .macro mmid, rd, rn |
| #ifdef __ARMEB__ |
| ldr \rd, [\rn, #MM_CONTEXT_ID + 4 ] |
| #else |
| ldr \rd, [\rn, #MM_CONTEXT_ID] |
| #endif |
| .endm |
| |
| /* |
| * mask_asid - mask the ASID from the context ID |
| */ |
| .macro asid, rd, rn |
| and \rd, \rn, #255 |
| .endm |
| |
| .macro crval, clear, mmuset, ucset |
| #ifdef CONFIG_MMU |
| .word \clear |
| .word \mmuset |
| #else |
| .word \clear |
| .word \ucset |
| #endif |
| .endm |
| |
| /* |
| * dcache_line_size - get the minimum D-cache line size from the CTR register |
| * on ARMv7. |
| */ |
| .macro dcache_line_size, reg, tmp |
| #ifdef CONFIG_CPU_V7M |
| movw \tmp, #:lower16:BASEADDR_V7M_SCB + V7M_SCB_CTR |
| movt \tmp, #:upper16:BASEADDR_V7M_SCB + V7M_SCB_CTR |
| ldr \tmp, [\tmp] |
| #else |
| mrc p15, 0, \tmp, c0, c0, 1 @ read ctr |
| #endif |
| lsr \tmp, \tmp, #16 |
| and \tmp, \tmp, #0xf @ cache line size encoding |
| mov \reg, #4 @ bytes per word |
| mov \reg, \reg, lsl \tmp @ actual cache line size |
| .endm |
| |
| /* |
| * icache_line_size - get the minimum I-cache line size from the CTR register |
| * on ARMv7. |
| */ |
| .macro icache_line_size, reg, tmp |
| #ifdef CONFIG_CPU_V7M |
| movw \tmp, #:lower16:BASEADDR_V7M_SCB + V7M_SCB_CTR |
| movt \tmp, #:upper16:BASEADDR_V7M_SCB + V7M_SCB_CTR |
| ldr \tmp, [\tmp] |
| #else |
| mrc p15, 0, \tmp, c0, c0, 1 @ read ctr |
| #endif |
| and \tmp, \tmp, #0xf @ cache line size encoding |
| mov \reg, #4 @ bytes per word |
| mov \reg, \reg, lsl \tmp @ actual cache line size |
| .endm |
| |
| /* |
| * Sanity check the PTE configuration for the code below - which makes |
| * certain assumptions about how these bits are laid out. |
| */ |
| #ifdef CONFIG_MMU |
| #if L_PTE_SHARED != PTE_EXT_SHARED |
| #error PTE shared bit mismatch |
| #endif |
| #if !defined (CONFIG_ARM_LPAE) && \ |
| (L_PTE_XN+L_PTE_USER+L_PTE_RDONLY+L_PTE_DIRTY+L_PTE_YOUNG+\ |
| L_PTE_PRESENT) > L_PTE_SHARED |
| #error Invalid Linux PTE bit settings |
| #endif |
| #endif /* CONFIG_MMU */ |
| |
| /* |
| * The ARMv6 and ARMv7 set_pte_ext translation function. |
| * |
| * Permission translation: |
| * YUWD APX AP1 AP0 SVC User |
| * 0xxx 0 0 0 no acc no acc |
| * 100x 1 0 1 r/o no acc |
| * 10x0 1 0 1 r/o no acc |
| * 1011 0 0 1 r/w no acc |
| * 110x 1 1 1 r/o r/o |
| * 11x0 1 1 1 r/o r/o |
| * 1111 0 1 1 r/w r/w |
| */ |
| .macro armv6_mt_table pfx |
| \pfx\()_mt_table: |
| .long 0x00 @ L_PTE_MT_UNCACHED |
| .long PTE_EXT_TEX(1) @ L_PTE_MT_BUFFERABLE |
| .long PTE_CACHEABLE @ L_PTE_MT_WRITETHROUGH |
| .long PTE_CACHEABLE | PTE_BUFFERABLE @ L_PTE_MT_WRITEBACK |
| .long PTE_BUFFERABLE @ L_PTE_MT_DEV_SHARED |
| .long 0x00 @ unused |
| .long 0x00 @ L_PTE_MT_MINICACHE (not present) |
| .long PTE_EXT_TEX(1) | PTE_CACHEABLE | PTE_BUFFERABLE @ L_PTE_MT_WRITEALLOC |
| .long 0x00 @ unused |
| .long PTE_EXT_TEX(1) @ L_PTE_MT_DEV_WC |
| .long 0x00 @ unused |
| .long PTE_CACHEABLE | PTE_BUFFERABLE @ L_PTE_MT_DEV_CACHED |
| .long PTE_EXT_TEX(2) @ L_PTE_MT_DEV_NONSHARED |
| .long 0x00 @ unused |
| .long 0x00 @ unused |
| .long PTE_CACHEABLE | PTE_BUFFERABLE | PTE_EXT_APX @ L_PTE_MT_VECTORS |
| .endm |
| |
| .macro armv6_set_pte_ext pfx |
| str r1, [r0], #2048 @ linux version |
| |
| bic r3, r1, #0x000003fc |
| bic r3, r3, #PTE_TYPE_MASK |
| orr r3, r3, r2 |
| orr r3, r3, #PTE_EXT_AP0 | 2 |
| |
| adr ip, \pfx\()_mt_table |
| and r2, r1, #L_PTE_MT_MASK |
| ldr r2, [ip, r2] |
| |
| eor r1, r1, #L_PTE_DIRTY |
| tst r1, #L_PTE_DIRTY|L_PTE_RDONLY |
| orrne r3, r3, #PTE_EXT_APX |
| |
| tst r1, #L_PTE_USER |
| orrne r3, r3, #PTE_EXT_AP1 |
| tstne r3, #PTE_EXT_APX |
| |
| @ user read-only -> kernel read-only |
| bicne r3, r3, #PTE_EXT_AP0 |
| |
| tst r1, #L_PTE_XN |
| orrne r3, r3, #PTE_EXT_XN |
| |
| eor r3, r3, r2 |
| |
| tst r1, #L_PTE_YOUNG |
| tstne r1, #L_PTE_PRESENT |
| moveq r3, #0 |
| tstne r1, #L_PTE_NONE |
| movne r3, #0 |
| |
| str r3, [r0] |
| mcr p15, 0, r0, c7, c10, 1 @ flush_pte |
| .endm |
| |
| |
| /* |
| * The ARMv3, ARMv4 and ARMv5 set_pte_ext translation function, |
| * covering most CPUs except Xscale and Xscale 3. |
| * |
| * Permission translation: |
| * YUWD AP SVC User |
| * 0xxx 0x00 no acc no acc |
| * 100x 0x00 r/o no acc |
| * 10x0 0x00 r/o no acc |
| * 1011 0x55 r/w no acc |
| * 110x 0xaa r/w r/o |
| * 11x0 0xaa r/w r/o |
| * 1111 0xff r/w r/w |
| */ |
| .macro armv3_set_pte_ext wc_disable=1 |
| str r1, [r0], #2048 @ linux version |
| |
| eor r3, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
| |
| bic r2, r1, #PTE_SMALL_AP_MASK @ keep C, B bits |
| bic r2, r2, #PTE_TYPE_MASK |
| orr r2, r2, #PTE_TYPE_SMALL |
| |
| tst r3, #L_PTE_USER @ user? |
| orrne r2, r2, #PTE_SMALL_AP_URO_SRW |
| |
| tst r3, #L_PTE_RDONLY | L_PTE_DIRTY @ write and dirty? |
| orreq r2, r2, #PTE_SMALL_AP_UNO_SRW |
| |
| tst r3, #L_PTE_PRESENT | L_PTE_YOUNG @ present and young? |
| movne r2, #0 |
| |
| .if \wc_disable |
| #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH |
| tst r2, #PTE_CACHEABLE |
| bicne r2, r2, #PTE_BUFFERABLE |
| #endif |
| .endif |
| str r2, [r0] @ hardware version |
| .endm |
| |
| |
| /* |
| * Xscale set_pte_ext translation, split into two halves to cope |
| * with work-arounds. r3 must be preserved by code between these |
| * two macros. |
| * |
| * Permission translation: |
| * YUWD AP SVC User |
| * 0xxx 00 no acc no acc |
| * 100x 00 r/o no acc |
| * 10x0 00 r/o no acc |
| * 1011 01 r/w no acc |
| * 110x 10 r/w r/o |
| * 11x0 10 r/w r/o |
| * 1111 11 r/w r/w |
| */ |
| .macro xscale_set_pte_ext_prologue |
| str r1, [r0] @ linux version |
| |
| eor r3, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
| |
| bic r2, r1, #PTE_SMALL_AP_MASK @ keep C, B bits |
| orr r2, r2, #PTE_TYPE_EXT @ extended page |
| |
| tst r3, #L_PTE_USER @ user? |
| orrne r2, r2, #PTE_EXT_AP_URO_SRW @ yes -> user r/o, system r/w |
| |
| tst r3, #L_PTE_RDONLY | L_PTE_DIRTY @ write and dirty? |
| orreq r2, r2, #PTE_EXT_AP_UNO_SRW @ yes -> user n/a, system r/w |
| @ combined with user -> user r/w |
| .endm |
| |
| .macro xscale_set_pte_ext_epilogue |
| tst r3, #L_PTE_PRESENT | L_PTE_YOUNG @ present and young? |
| movne r2, #0 @ no -> fault |
| |
| str r2, [r0, #2048]! @ hardware version |
| mov ip, #0 |
| mcr p15, 0, r0, c7, c10, 1 @ clean L1 D line |
| mcr p15, 0, ip, c7, c10, 4 @ data write barrier |
| .endm |
| |
| .macro define_processor_functions name:req, dabort:req, pabort:req, nommu=0, suspend=0, bugs=0 |
| /* |
| * If we are building for big.Little with branch predictor hardening, |
| * we need the processor function tables to remain available after boot. |
| */ |
| #if defined(CONFIG_BIG_LITTLE) && defined(CONFIG_HARDEN_BRANCH_PREDICTOR) |
| .section ".rodata" |
| #endif |
| .type \name\()_processor_functions, #object |
| .align 2 |
| ENTRY(\name\()_processor_functions) |
| .word \dabort |
| .word \pabort |
| .word cpu_\name\()_proc_init |
| .word \bugs |
| .word cpu_\name\()_proc_fin |
| .word cpu_\name\()_reset |
| .word cpu_\name\()_do_idle |
| .word cpu_\name\()_dcache_clean_area |
| .word cpu_\name\()_switch_mm |
| |
| .if \nommu |
| .word 0 |
| .else |
| .word cpu_\name\()_set_pte_ext |
| .endif |
| |
| .if \suspend |
| .word cpu_\name\()_suspend_size |
| #ifdef CONFIG_ARM_CPU_SUSPEND |
| .word cpu_\name\()_do_suspend |
| .word cpu_\name\()_do_resume |
| #else |
| .word 0 |
| .word 0 |
| #endif |
| .else |
| .word 0 |
| .word 0 |
| .word 0 |
| .endif |
| |
| .size \name\()_processor_functions, . - \name\()_processor_functions |
| #if defined(CONFIG_BIG_LITTLE) && defined(CONFIG_HARDEN_BRANCH_PREDICTOR) |
| .previous |
| #endif |
| .endm |
| |
| .macro globl_equ x, y |
| .globl \x |
| .equ \x, \y |
| .endm |
| |
| .macro initfn, func, base |
| .long \func - \base |
| .endm |
| |
| /* |
| * Macro to calculate the log2 size for the protection region |
| * registers. This calculates rd = log2(size) - 1. tmp must |
| * not be the same register as rd. |
| */ |
| .macro pr_sz, rd, size, tmp |
| mov \tmp, \size, lsr #12 |
| mov \rd, #11 |
| 1: movs \tmp, \tmp, lsr #1 |
| addne \rd, \rd, #1 |
| bne 1b |
| .endm |
| |
| /* |
| * Macro to generate a protection region register value |
| * given a pre-masked address, size, and enable bit. |
| * Corrupts size. |
| */ |
| .macro pr_val, dest, addr, size, enable |
| pr_sz \dest, \size, \size @ calculate log2(size) - 1 |
| orr \dest, \addr, \dest, lsl #1 @ mask in the region size |
| orr \dest, \dest, \enable |
| .endm |