root/arch/arm/mm/proc-macros.S
/* 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