mirror of https://github.com/wolfSSL/wolfBoot.git
1804 lines
57 KiB
ArmAsm
1804 lines
57 KiB
ArmAsm
/**
|
|
* Aarch64 bootup
|
|
* Copyright (C) 2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfBoot.
|
|
*
|
|
* wolfBoot is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfBoot is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
/* Code is adapted from the default AMD/Xilinx boot.S, translation_table.S and
|
|
* asm_vectors.S*/
|
|
|
|
#ifdef TARGET_zynq
|
|
#include "hal/zynq.h"
|
|
#endif
|
|
|
|
#ifdef TARGET_versal
|
|
#include "hal/versal.h"
|
|
#endif
|
|
|
|
#ifdef TARGET_nxp_ls1028a
|
|
#include "hal/nxp_ls1028a.h"
|
|
#endif
|
|
|
|
#ifdef TARGET_raspi3
|
|
#include "hal/raspi3.h"
|
|
#endif
|
|
|
|
#ifdef TARGET_tegra234
|
|
#include "hal/tegra234.h"
|
|
#endif
|
|
|
|
#ifdef TARGET_cm4
|
|
#include "hal/cm4.h"
|
|
#endif
|
|
|
|
/* GICv2 Register Offsets */
|
|
#ifndef GICD_BASE
|
|
#define GICD_BASE 0xF9010000
|
|
#endif
|
|
#define GICD_CTLR 0x0000
|
|
#define GICD_TYPER 0x0004
|
|
#define GICD_SGIR 0x0F00
|
|
#define GICD_IGROUPRn 0x0080
|
|
|
|
#ifndef GICC_BASE
|
|
#define GICC_BASE 0xF9020000
|
|
#endif
|
|
#define GICC_PMR 0x0004
|
|
|
|
|
|
#ifndef USE_BUILTIN_STARTUP
|
|
|
|
.globl MMUTableL0
|
|
.globl MMUTableL1
|
|
.globl MMUTableL2
|
|
.global _prestart
|
|
.global _boot
|
|
|
|
.global __el3_stack
|
|
.global __el2_stack
|
|
.global __el1_stack
|
|
.global __el0_stack
|
|
.global _vector_table
|
|
|
|
.globl FIQInterrupt
|
|
.globl IRQInterrupt
|
|
.globl SErrorInterrupt
|
|
.globl SynchronousInterrupt
|
|
.globl FPUStatus
|
|
|
|
.set EL3_stack, __el3_stack
|
|
.set EL2_stack, __el2_stack
|
|
.set EL1_stack, __el1_stack
|
|
.set EL0_stack, __el0_stack
|
|
|
|
.set L0Table, MMUTableL0
|
|
.set L1Table, MMUTableL1
|
|
.set L2Table, MMUTableL2
|
|
.set vector_base, _vector_table
|
|
|
|
#ifdef TARGET_versal
|
|
/* Versal: RVBAR is handled by PLM, not accessible from APU in JTAG mode */
|
|
/* Cortex-A72 timestamp clock frequency (from Versal HW) */
|
|
.set counterfreq, 100000000
|
|
#else
|
|
.set rvbar_base, 0xFD5C0040
|
|
/* Cortex-A53 timestamp clock frequency */
|
|
.set counterfreq, 99990005
|
|
#endif
|
|
|
|
.set MODE_EL1, 0x5
|
|
.set DAIF_BIT, 0x1C0
|
|
|
|
|
|
.section .boot,"ax"
|
|
_boot:
|
|
mov x0, #0
|
|
mov x1, #0
|
|
mov x2, #0
|
|
mov x3, #0
|
|
mov x4, #0
|
|
mov x5, #0
|
|
mov x6, #0
|
|
mov x7, #0
|
|
mov x8, #0
|
|
mov x9, #0
|
|
mov x10, #0
|
|
mov x11, #0
|
|
mov x12, #0
|
|
mov x13, #0
|
|
mov x14, #0
|
|
mov x15, #0
|
|
mov x16, #0
|
|
mov x17, #0
|
|
mov x18, #0
|
|
mov x19, #0
|
|
mov x20, #0
|
|
mov x21, #0
|
|
mov x22, #0
|
|
mov x23, #0
|
|
mov x24, #0
|
|
mov x25, #0
|
|
mov x26, #0
|
|
mov x27, #0
|
|
mov x28, #0
|
|
mov x29, #0
|
|
mov x30, #0
|
|
|
|
/* Init Exception Level */
|
|
mrs x0, currentEL
|
|
cmp x0, #0xC
|
|
beq InitEL3
|
|
|
|
cmp x0, #0x8
|
|
beq InitEL2
|
|
|
|
cmp x0, #0x4
|
|
beq InitEL1
|
|
|
|
/* go to error if current exception level is not EL1-3 */
|
|
b error
|
|
|
|
InitEL3:
|
|
#if defined(EL3_SECURE) && EL3_SECURE == 1
|
|
/* Set vector table base address */
|
|
ldr x1, =vector_base
|
|
msr VBAR_EL3,x1
|
|
|
|
/* Set reset vector address */
|
|
/* Note: On Versal, RVBAR is handled by PLM and APU_DUAL_CSR is not
|
|
* accessible in JTAG boot mode. Skip RVBAR write for Versal. */
|
|
#if !defined(SKIP_RVBAR) || SKIP_RVBAR == 0
|
|
/* Get the cpu ID */
|
|
mrs x0, MPIDR_EL1
|
|
and x0, x0, #0xFF
|
|
mov w0, w0
|
|
ldr w2, =rvbar_base
|
|
/* calculate the RVBAR base address for particular CPU core */
|
|
mov w3, #0x8
|
|
mul w0, w0, w3
|
|
add w2, w2, w0
|
|
/* store vector base address to RVBAR */
|
|
str x1, [x2]
|
|
#endif
|
|
|
|
/* Define stack pointer for current exception level */
|
|
ldr x2,=EL3_stack
|
|
mov sp,x2
|
|
|
|
/* Enable Trapping of SIMD/FPU register for standalone BSP */
|
|
mov x0, #0
|
|
#if defined(FPU_TRAP) && FPU_TRAP == 1
|
|
orr x0, x0, #(0x1 << 10)
|
|
#endif
|
|
msr CPTR_EL3, x0
|
|
isb
|
|
|
|
/* Clear FPUStatus variable to make sure that it contains current
|
|
* status of FPU i.e. disabled. In case of a warm restart execution
|
|
* when bss sections are not cleared, it may contain previously updated
|
|
* value which does not hold true now.
|
|
*/
|
|
#if defined(FPU_TRAP) && FPU_TRAP == 1
|
|
ldr x0,=FPUStatus
|
|
str xzr, [x0]
|
|
#endif
|
|
/* Configure SCR_EL3 */
|
|
mov w1, #0 /* Initial value of register is unknown */
|
|
orr w1, w1, #(1 << 11) /* Set ST bit (Secure EL1 can access CNTPS_TVAL_EL1, CNTPS_CTL_EL1 & CNTPS_CVAL_EL1) */
|
|
orr w1, w1, #(1 << 10) /* Set RW bit (EL1 is AArch64, as this is the Secure world) */
|
|
orr w1, w1, #(1 << 3) /* Set EA bit (SError routed to EL3) */
|
|
orr w1, w1, #(1 << 2) /* Set FIQ bit (FIQs routed to EL3) */
|
|
orr w1, w1, #(1 << 1) /* Set IRQ bit (IRQs routed to EL3) */
|
|
msr SCR_EL3, x1
|
|
|
|
/* Configure CPUACTLR_EL1 - CPU auxiliary control register
|
|
* Read-modify-write to preserve any pre-configured bits and add prefetch settings.
|
|
* Note: On Versal, BL31 will later apply additional errata workarounds after EL3 init:
|
|
* 859971, 1319367, CVE-2017-5715, CVE-2018-3639, CVE-2022-23960 */
|
|
mrs x0, S3_1_C15_C2_0 /* Read current CPUACTLR_EL1 */
|
|
ldr x1,=0x80CA000 /* L1 Data prefetch control - 5, Enable device split throttle, 2 independent data prefetch streams */
|
|
orr x0, x0, x1 /* Merge with existing value */
|
|
#if defined(CONFIG_ARM_ERRATA_855873) && CONFIG_ARM_ERRATA_855873
|
|
/* Set ENDCCASCI bit in CPUACTLR_EL1 register, to execute data
|
|
* cache clean operations as data cache clean and invalidate
|
|
*/
|
|
orr x0, x0, #(1 << 44) /* Set ENDCCASCI bit */
|
|
#endif
|
|
msr S3_1_C15_C2_0, x0 /* CPUACTLR_EL1 */
|
|
|
|
/* Program the counter frequency.
|
|
* For the FSBL-replacement build, do NOT overwrite CNTFRQ_EL0: the BootROM
|
|
* sets it to the actual system-counter rate, which our usleep() needs for
|
|
* accurate psu_init DDR-training delays. Hardcoding 100MHz here is wrong
|
|
* when the counter runs at the BootROM (undivided) rate. */
|
|
#ifndef WOLFBOOT_ZYNQMP_FSBL
|
|
#ifdef TARGET_nxp_ls1028a
|
|
/* Read the true base frequency from the system counter (CNTFID0 @ SYSCNT
|
|
* base+0x20 = SYS_REF_CLK/4); the counterfreq constant is the undivided
|
|
* clock and would make CNTPCT time run ~4x fast. */
|
|
ldr x1, =0x023E0020
|
|
ldr w0, [x1]
|
|
#else
|
|
ldr x0,=counterfreq
|
|
#endif
|
|
msr CNTFRQ_EL0, x0
|
|
#endif
|
|
|
|
/* Enable hardware coherency between cores */
|
|
mrs x0, S3_1_c15_c2_1 /* Read EL1 CPU Extended Control Register */
|
|
orr x0, x0, #(1 << 6) /* Set the SMPEN bit */
|
|
msr S3_1_c15_c2_1, x0 /* Write EL1 CPU Extended Control Register */
|
|
isb
|
|
|
|
tlbi ALLE3
|
|
ic IALLU /* Invalidate ICache to PoU */
|
|
bl invalidate_dcaches
|
|
dsb sy
|
|
isb
|
|
|
|
#ifndef NO_MMU
|
|
ldr x1, =L0Table /* Get address of level 0 for TTBR0_EL3 */
|
|
msr TTBR0_EL3, x1 /* Set TTBR0_EL3 */
|
|
|
|
/**********************************************
|
|
* Set up memory attributes
|
|
* This equates to:
|
|
* 0 = b01000100 = Normal, Inner/Outer Non-Cacheable
|
|
* 1 = b11111111 = Normal, Inner/Outer WB/WA/RA
|
|
* 2 = b00000000 = Device-nGnRnE
|
|
* 3 = b00000100 = Device-nGnRE
|
|
* 4 = b10111011 = Normal, Inner/Outer WT/WA/RA
|
|
**********************************************/
|
|
ldr x1, =0x000000BB0400FF44
|
|
msr MAIR_EL3, x1
|
|
|
|
/**********************************************
|
|
* Set up TCR_EL3
|
|
* Granual Size TG0 = 00 -> 4KB
|
|
***************************************************/
|
|
#ifdef TARGET_versal
|
|
/* Versal: Physical Address Size PS = 100 -> 44bits 16TB
|
|
* T0SZ = 20 -> (region size 2^(64-20) = 2^44) */
|
|
ldr x1,=0x80843514
|
|
#else
|
|
/* ZynqMP: Physical Address Size PS = 010 -> 40bits 1TB
|
|
* T0SZ = 24 -> (region size 2^(64-24) = 2^40) */
|
|
ldr x1,=0x80823518
|
|
#endif
|
|
|
|
msr TCR_EL3, x1
|
|
isb
|
|
#endif /* !NO_MMU */
|
|
|
|
/* Enable SError Exception for asynchronous abort */
|
|
mrs x1,DAIF
|
|
bic x1,x1,#(0x1<<8)
|
|
msr DAIF,x1
|
|
|
|
/* Configure SCTLR_EL3 */
|
|
mov x1, #0 /* Most of the SCTLR_EL3 bits are unknown at reset */
|
|
#ifndef NO_MMU
|
|
orr x1, x1, #(1 << 12) /* Enable I cache */
|
|
orr x1, x1, #(1 << 3) /* Enable SP alignment check */
|
|
orr x1, x1, #(1 << 2) /* Enable caches */
|
|
orr x1, x1, #(1 << 0) /* Enable MMU */
|
|
#else
|
|
orr x1, x1, #(1 << 3) /* Enable SP alignment check */
|
|
#endif
|
|
msr SCTLR_EL3, x1
|
|
dsb sy
|
|
isb
|
|
|
|
bl boot_entry_C /* jump to start */
|
|
#else
|
|
/* present exception level and selected exception level mismatch */
|
|
b error
|
|
#endif
|
|
|
|
InitEL2:
|
|
#if defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
|
|
/* Set vector table base address */
|
|
ldr x1, =vector_base
|
|
msr VBAR_EL2, x1
|
|
|
|
/* Define stack pointer for current exception level */
|
|
ldr x2,=EL2_stack
|
|
mov sp,x2
|
|
|
|
mov x0, #0x33ff
|
|
msr CPTR_EL2, x0 /* Enable FP/SIMD */
|
|
|
|
/* Invalidate TLB */
|
|
tlbi alle2
|
|
/* Invalidate ICache */
|
|
ic ialluis
|
|
isb sy
|
|
/* Invalidate DCache */
|
|
bl invalidate_dcaches
|
|
dsb sy
|
|
isb
|
|
|
|
#ifndef NO_MMU
|
|
ldr x1, =L0Table /* Get address of level 0 for TTBR0_EL2 */
|
|
msr TTBR0_EL2, x1 /* Set TTBR0_EL2 */
|
|
|
|
/**********************************************
|
|
* Set up memory attributes
|
|
* This equates to:
|
|
* 0 = b01000100 = Normal, Inner/Outer Non-Cacheable
|
|
* 1 = b11111111 = Normal, Inner/Outer WB/WA/RA
|
|
* 2 = b00000000 = Device-nGnRnE
|
|
* 3 = b00000100 = Device-nGnRE
|
|
* 4 = b10111011 = Normal, Inner/Outer WT/WA/RA
|
|
**********************************************/
|
|
ldr x1, =0x000000BB0400FF44
|
|
msr MAIR_EL2, x1
|
|
|
|
/**********************************************
|
|
* Set up TCR_EL2
|
|
* Granual Size TG0 = 00 -> 4KB
|
|
***************************************************/
|
|
#ifdef TARGET_versal
|
|
/* Versal: Physical Address Size PS = 100 -> 44bits 16TB
|
|
* T0SZ = 20 -> (region size 2^(64-20) = 2^44) */
|
|
ldr x1,=0x80843514
|
|
#else
|
|
/* ZynqMP: Physical Address Size PS = 010 -> 40bits 1TB
|
|
* T0SZ = 24 -> (region size 2^(64-24) = 2^40) */
|
|
ldr x1,=0x80823518
|
|
#endif
|
|
|
|
msr TCR_EL2, x1
|
|
isb
|
|
|
|
/* Configure SCTLR_EL2 */
|
|
mrs x1, SCTLR_EL2
|
|
orr x1, x1, #(1 << 12) /* Enable ICache */
|
|
orr x1, x1, #(1 << 3) /* Enable SP alignment check */
|
|
orr x1, x1, #(1 << 2) /* Enable DCaches */
|
|
orr x1, x1, #(1 << 0) /* Enable MMU */
|
|
#else
|
|
/* Configure SCTLR_EL2 - no MMU/cache */
|
|
mov x1, #0
|
|
orr x1, x1, #(1 << 3) /* Enable SP alignment check */
|
|
#endif
|
|
msr SCTLR_EL2, x1
|
|
dsb sy
|
|
isb
|
|
|
|
bl boot_entry_C /* jump to start */
|
|
#else
|
|
/* present exception level and selected exception level mismatch */
|
|
b error
|
|
#endif
|
|
|
|
InitEL1:
|
|
#if defined(EL1_NONSECURE) && EL1_NONSECURE == 1
|
|
/* Set vector table base address */
|
|
ldr x1, =vector_base
|
|
msr VBAR_EL1,x1
|
|
|
|
/* Trap floating point access only in case of standalone BSP */
|
|
#if defined(FPU_TRAP) && FPU_TRAP == 0
|
|
mrs x0, CPACR_EL1
|
|
orr x0, x0, #(0x3 << 20)
|
|
msr CPACR_EL1, x0
|
|
#else
|
|
mrs x0, CPACR_EL1
|
|
bic x0, x0, #(0x3 << 20)
|
|
msr CPACR_EL1, x0
|
|
#endif
|
|
isb
|
|
|
|
/* Clear FPUStatus variable to make sure that it contains current
|
|
* status of FPU i.e. disabled. In case of a warm restart execution
|
|
* when bss sections are not cleared, it may contain previously updated
|
|
* value which does not hold true now.
|
|
*/
|
|
#if defined(FPU_TRAP) && FPU_TRAP == 1
|
|
ldr x0,=FPUStatus
|
|
str xzr, [x0]
|
|
#endif
|
|
/* Define stack pointer for current exception level */
|
|
ldr x2,=EL1_stack
|
|
mov sp,x2
|
|
|
|
/* Disable MMU first */
|
|
mov x1,#0x0
|
|
msr SCTLR_EL1, x1
|
|
isb
|
|
|
|
tlbi VMALLE1
|
|
ic IALLU /* Invalidate I cache to PoU */
|
|
bl invalidate_dcaches
|
|
dsb sy
|
|
isb
|
|
|
|
#ifndef NO_MMU
|
|
ldr x1, =L0Table /* Get address of level 0 for TTBR0_EL1 */
|
|
msr TTBR0_EL1, x1 /* Set TTBR0_EL1 */
|
|
|
|
/**********************************************
|
|
* Set up memory attributes
|
|
* This equates to:
|
|
* 0 = b01000100 = Normal, Inner/Outer Non-Cacheable
|
|
* 1 = b11111111 = Normal, Inner/Outer WB/WA/RA
|
|
* 2 = b00000000 = Device-nGnRnE
|
|
* 3 = b00000100 = Device-nGnRE
|
|
* 4 = b10111011 = Normal, Inner/Outer WT/WA/RA
|
|
**********************************************/
|
|
ldr x1, =0x000000BB0400FF44
|
|
msr MAIR_EL1, x1
|
|
|
|
/**********************************************
|
|
* Set up TCR_EL1
|
|
* Granual Size TG0 = 00 -> 4KB
|
|
***************************************************/
|
|
#ifdef TARGET_versal
|
|
/* Versal: Physical Address Size PS = 100 -> 44bits 16TB
|
|
* T0SZ = 20 -> (region size 2^(64-20) = 2^44) */
|
|
ldr x1,=0x485800514
|
|
#else
|
|
/* ZynqMP: Physical Address Size PS = 010 -> 40bits 1TB
|
|
* T0SZ = 24 -> (region size 2^(64-24) = 2^40) */
|
|
ldr x1,=0x285800518
|
|
#endif
|
|
|
|
msr TCR_EL1, x1
|
|
isb
|
|
#endif /* !NO_MMU */
|
|
|
|
/* Enable SError Exception for asynchronous abort */
|
|
mrs x1,DAIF
|
|
bic x1,x1,#(0x1<<8)
|
|
msr DAIF,x1
|
|
|
|
/* Configure SCTLR_EL1 */
|
|
mov x1,#0x0
|
|
orr x1, x1, #(1 << 18) /* Set WFE non trapping */
|
|
orr x1, x1, #(1 << 17) /* Set WFI non trapping */
|
|
orr x1, x1, #(1 << 5) /* Set CP15 barrier enabled */
|
|
#ifndef NO_MMU
|
|
orr x1, x1, #(1 << 12) /* Set I bit (ICache) */
|
|
orr x1, x1, #(1 << 2) /* Set C bit (DCache) */
|
|
orr x1, x1, #(1 << 0) /* Set M bit (MMU) */
|
|
#endif
|
|
msr SCTLR_EL1, x1
|
|
isb
|
|
|
|
bl boot_entry_C /* jump to start */
|
|
#else
|
|
/* present exception level and selected exception level mismatch */
|
|
b error
|
|
#endif
|
|
|
|
/* Assembly startup error handler */
|
|
error:
|
|
wfi
|
|
b error
|
|
|
|
|
|
invalidate_dcaches:
|
|
dmb ISH
|
|
mrs x0, CLIDR_EL1 /* x0 = CLIDR */
|
|
ubfx w2, w0, #24, #3 /* w2 = CLIDR.LoC */
|
|
cmp w2, #0 /* LoC is 0? */
|
|
b.eq invalidatecaches_end /* No cleaning required and enable MMU */
|
|
mov w1, #0 /* w1 = level iterator */
|
|
|
|
invalidatecaches_flush_level:
|
|
add w3, w1, w1, lsl #1 /* w3 = w1 * 3 (right-shift for cache type) */
|
|
lsr w3, w0, w3 /* w3 = w0 >> w3 */
|
|
ubfx w3, w3, #0, #3 /* w3 = cache type of this level */
|
|
cmp w3, #2 /* No cache at this level? */
|
|
b.lt invalidatecaches_next_level
|
|
|
|
lsl w4, w1, #1
|
|
msr CSSELR_EL1, x4 /* Select current cache level in CSSELR */
|
|
isb /* ISB required to reflect new CSIDR */
|
|
mrs x4, CCSIDR_EL1 /* w4 = CSIDR */
|
|
|
|
ubfx w3, w4, #0, #3
|
|
add w3, w3, #2 /* w3 = log2(line size) */
|
|
ubfx w5, w4, #13, #15
|
|
ubfx w4, w4, #3, #10 /* w4 = Way number */
|
|
clz w6, w4 /* w6 = 32 - log2(number of ways) */
|
|
|
|
invalidatecaches_flush_set:
|
|
mov w8, w4 /* w8 = Way number */
|
|
invalidatecaches_flush_way:
|
|
lsl w7, w1, #1 /* Fill level field */
|
|
lsl w9, w5, w3
|
|
orr w7, w7, w9 /* Fill index field */
|
|
lsl w9, w8, w6
|
|
orr w7, w7, w9 /* Fill way field */
|
|
dc CISW, x7 /* Invalidate by set/way to point of coherency */
|
|
subs w8, w8, #1 /* Decrement way */
|
|
b.ge invalidatecaches_flush_way
|
|
subs w5, w5, #1 /* Descrement set */
|
|
b.ge invalidatecaches_flush_set
|
|
|
|
invalidatecaches_next_level:
|
|
add w1, w1, #1 /* Next level */
|
|
cmp w2, w1
|
|
b.gt invalidatecaches_flush_level
|
|
|
|
invalidatecaches_end:
|
|
ret
|
|
|
|
|
|
|
|
/*
|
|
* Below is the static translation page table required by MMU for Cortex-A53.
|
|
* The translation table is flat mapped (input address = output address) with
|
|
* default memory attributes defined for Zynq Ultrascale+ architecture.
|
|
* It utilizes translation granual size of 4KB with 2MB section size for
|
|
* initial 4GB memory and 1GB section size for memory after 4GB.
|
|
* The overview of translation table memory attributes is described below.
|
|
*
|
|
*| | Memory Range | Definition in Translation Table |
|
|
*|-----------------------|-----------------------------|-----------------------------------|
|
|
*| DDR | 0x0000000000 - 0x007FFFFFFF | Normal write-back Cacheable |
|
|
*| PL | 0x0080000000 - 0x00BFFFFFFF | Strongly Ordered |
|
|
*| QSPI, lower PCIe | 0x00C0000000 - 0x00EFFFFFFF | Strongly Ordere |
|
|
*| Reserved | 0x00F0000000 - 0x00F7FFFFFF | Unassigned |
|
|
*| STM Coresight | 0x00F8000000 - 0x00F8FFFFFF | Strongly Ordered |
|
|
*| GIC | 0x00F9000000 - 0x00F91FFFFF | Strongly Ordered |
|
|
*| Reserved | 0x00F9200000 - 0x00FCFFFFFF | Unassigned |
|
|
*| FPS, LPS slaves | 0x00FD000000 - 0x00FFBFFFFF | Strongly Ordered |
|
|
*| CSU, PMU | 0x00FFC00000 - 0x00FFDFFFFF | Strongly Ordered |
|
|
*| TCM, OCM | 0x00FFE00000 - 0x00FFFFFFFF | Normal inner write-back cacheable |
|
|
*| Reserved | 0x0100000000 - 0x03FFFFFFFF | Unassigned |
|
|
*| PL, PCIe | 0x0400000000 - 0x07FFFFFFFF | Strongly Ordered |
|
|
*| DDR | 0x0800000000 - 0x0FFFFFFFFF | Normal inner write-back cacheable |
|
|
*| PL, PCIe | 0x1000000000 - 0xBFFFFFFFFF | Strongly Ordered |
|
|
*| Reserved | 0xC000000000 - 0xFFFFFFFFFF | Unassigned |
|
|
*
|
|
* For DDR region 0x0000000000 - 0x007FFFFFFF, a system where DDR is less than
|
|
* 2GB, region after DDR and before PL is marked as undefined/reserved in
|
|
* translation table. Region 0xF9100000 - 0xF91FFFFF is reserved memory in
|
|
* 0x00F9000000 - 0x00F91FFFFF range, but it is marked as strongly ordered
|
|
* because minimum section size in translation table section is 2MB. Region
|
|
* 0x00FFC00000 - 0x00FFDFFFFF contains CSU and PMU memory which are marked as
|
|
* Device since it is less than 1MB and falls in a region with device memory.
|
|
*/
|
|
|
|
.set reserved, 0x0 /* Fault */
|
|
#if defined(ZYNQMP_DDR_NC) && ZYNQMP_DDR_NC == 1
|
|
.set Memory, 0x401 | (0 << 8) | (0x0) /* DDR as Normal NON-cacheable (MAIR idx0=0x44) - diag */
|
|
#elif defined(ZYNQMP_DDR_DEVICE) && ZYNQMP_DDR_DEVICE == 1
|
|
.set Memory, 0x409 | (1 << 53) | (1 << 54) /* DDR as Device-nGnRnE (no speculation) - diag */
|
|
#elif defined(ZYNQMP_DDR_NONSHARED) && ZYNQMP_DDR_NONSHARED == 1
|
|
.set Memory, 0x405 | (0 << 8) | (0x0) /* normal writeback write allocate NON-shareable read write */
|
|
#elif defined(EL1_NONSECURE) && EL1_NONSECURE == 1
|
|
.set Memory, 0x405 | (2 << 8) | (0x0) /* normal writeback write allocate outer shared read write */
|
|
#else
|
|
.set Memory, 0x405 | (3 << 8) | (0x0) /* normal writeback write allocate inner shared read write */
|
|
#endif
|
|
.set Device, 0x409 | (1 << 53) | (1 << 54) | (0x0) /* strongly ordered read write non executable*/
|
|
/* Outer-shareable Normal WB. A coherent DMA master behind the CCI (LS1028A
|
|
* ENETC) needs Outer-Shareable to snoop the A72 caches; Inner-Shareable only
|
|
* covers the CPU cluster. */
|
|
.set MemoryOSH, 0x405 | (2 << 8) | (0x0)
|
|
|
|
#ifndef TARGET_nxp_ls1028a
|
|
.section .mmu_tbl0,"a"
|
|
|
|
MMUTableL0:
|
|
|
|
.set SECT, MMUTableL1 /* 0x0000_0000 - 0x7F_FFFF_FFFF */
|
|
.8byte SECT + 0x3
|
|
.set SECT, MMUTableL1+0x1000 /* 0x80_0000_0000 - 0xFF_FFFF_FFFF */
|
|
.8byte SECT + 0x3
|
|
|
|
.section .mmu_tbl1,"a"
|
|
|
|
MMUTableL1:
|
|
|
|
.set SECT, MMUTableL2 /* 0x0000_0000 - 0x3FFF_FFFF */
|
|
.8byte SECT + 0x3 /* 1GB DDR */
|
|
|
|
.rept 0x3 /* 0x4000_0000 - 0xFFFF_FFFF */
|
|
.set SECT, SECT + 0x1000 /*1GB DDR, 1GB PL, 2GB other devices n memory */
|
|
.8byte SECT + 0x3
|
|
.endr
|
|
|
|
.set SECT,0x100000000
|
|
.rept 0xC /* 0x0001_0000_0000 - 0x0003_FFFF_FFFF */
|
|
.8byte SECT + reserved /* 12GB Reserved */
|
|
.set SECT, SECT + 0x40000000
|
|
.endr
|
|
|
|
.rept 0x10 /* 0x0004_0000_0000 - 0x0007_FFFF_FFFF */
|
|
.8byte SECT + Device /* 8GB PL, 8GB PCIe */
|
|
.set SECT, SECT + 0x40000000
|
|
.endr
|
|
|
|
|
|
#ifdef XPAR_PSU_DDR_1_S_AXI_BASEADDR
|
|
.set DDR_1_START, XPAR_PSU_DDR_1_S_AXI_BASEADDR
|
|
.set DDR_1_END, XPAR_PSU_DDR_1_S_AXI_HIGHADDR
|
|
.set DDR_1_SIZE, (DDR_1_END - DDR_1_START)+1
|
|
#if defined(DDR_1_SIZE) && DDR_1_SIZE > 0x800000000
|
|
/* If DDR size is larger than 32GB, truncate to 32GB */
|
|
.set DDR_1_REG, 0x20
|
|
#else
|
|
.set DDR_1_REG, DDR_1_SIZE/0x40000000
|
|
#endif
|
|
#else
|
|
.set DDR_1_REG, 0
|
|
#endif
|
|
|
|
.set UNDEF_1_REG, 0x20 - DDR_1_REG
|
|
|
|
.rept DDR_1_REG /* DDR based on size in hdf*/
|
|
.8byte SECT + Memory
|
|
.set SECT, SECT+0x40000000
|
|
.endr
|
|
|
|
.rept UNDEF_1_REG /* reserved for region where ddr is absent */
|
|
.8byte SECT + reserved
|
|
.set SECT, SECT+0x40000000
|
|
.endr
|
|
|
|
.rept 0x1C0 /* 0x0010_0000_0000 - 0x007F_FFFF_FFFF */
|
|
.8byte SECT + Device /* 448 GB PL */
|
|
.set SECT, SECT + 0x40000000
|
|
.endr
|
|
|
|
|
|
.rept 0x100 /* 0x0080_0000_0000 - 0x00BF_FFFF_FFFF */
|
|
.8byte SECT + Device /* 256GB PCIe */
|
|
.set SECT, SECT + 0x40000000
|
|
.endr
|
|
|
|
|
|
.rept 0x100 /* 0x00C0_0000_0000 - 0x00FF_FFFF_FFFF */
|
|
.8byte SECT + reserved /* 256GB reserved */
|
|
.set SECT, SECT + 0x40000000
|
|
.endr
|
|
|
|
|
|
.section .mmu_tbl2,"a"
|
|
|
|
MMUTableL2:
|
|
|
|
.set SECT, 0
|
|
|
|
#ifdef XPAR_PSU_DDR_0_S_AXI_BASEADDR
|
|
.set DDR_0_START, XPAR_PSU_DDR_0_S_AXI_BASEADDR
|
|
.set DDR_0_END, XPAR_PSU_DDR_0_S_AXI_HIGHADDR
|
|
.set DDR_0_SIZE, (DDR_0_END - DDR_0_START)+1
|
|
#if defined(DDR_0_SIZE) && DDR_0_SIZE > 0x80000000
|
|
/* If DDR size is larger than 2GB, truncate to 2GB */
|
|
.set DDR_0_REG, 0x400
|
|
#else
|
|
.set DDR_0_REG, DDR_0_SIZE/0x200000
|
|
#endif
|
|
#else
|
|
.set DDR_0_REG, 0
|
|
#endif
|
|
|
|
.set UNDEF_0_REG, 0x400 - DDR_0_REG
|
|
|
|
.rept DDR_0_REG /* DDR based on size in hdf*/
|
|
.8byte SECT + Memory
|
|
.set SECT, SECT+0x200000
|
|
.endr
|
|
|
|
.rept UNDEF_0_REG /* reserved for region where ddr is absent */
|
|
.8byte SECT + reserved
|
|
.set SECT, SECT+0x200000
|
|
.endr
|
|
|
|
.rept 0x0200 /* 0x8000_0000 - 0xBFFF_FFFF */
|
|
.8byte SECT + Device /* 1GB lower PL */
|
|
.set SECT, SECT+0x200000
|
|
.endr
|
|
|
|
.rept 0x0100 /* 0xC000_0000 - 0xDFFF_FFFF */
|
|
.8byte SECT + Device /* 512MB QSPI */
|
|
.set SECT, SECT+0x200000
|
|
.endr
|
|
|
|
.rept 0x080 /* 0xE000_0000 - 0xEFFF_FFFF */
|
|
.8byte SECT + Device /* 256MB lower PCIe */
|
|
.set SECT, SECT+0x200000
|
|
.endr
|
|
|
|
#ifdef TARGET_versal
|
|
/* Versal: LPD/PMC peripherals at 0xF0000000 - 0xF7FFFFFF (includes QSPI @ 0xF1030000) */
|
|
.rept 0x040 /* 0xF000_0000 - 0xF7FF_FFFF */
|
|
.8byte SECT + Device /* 128MB LPD peripherals (QSPI, I2C, etc) */
|
|
.set SECT, SECT+0x200000
|
|
.endr
|
|
#else
|
|
/* ZynqMP: This region is reserved */
|
|
.rept 0x040 /* 0xF000_0000 - 0xF7FF_FFFF */
|
|
.8byte SECT + reserved /* 128MB Reserved */
|
|
.set SECT, SECT+0x200000
|
|
.endr
|
|
#endif
|
|
|
|
.rept 0x8 /* 0xF800_0000 - 0xF8FF_FFFF */
|
|
.8byte SECT + Device /* 16MB coresight */
|
|
.set SECT, SECT+0x200000
|
|
.endr
|
|
|
|
/* 1MB RPU LLP is marked for 2MB region as the minimum block size in
|
|
translation table is 2MB and adjacent 63MB reserved region is
|
|
converted to 62MB */
|
|
|
|
.rept 0x1 /* 0xF900_0000 - 0xF91F_FFFF */
|
|
.8byte SECT + Device /* 2MB RPU low latency port */
|
|
.set SECT, SECT+0x200000
|
|
.endr
|
|
|
|
.rept 0x1F /* 0xF920_0000 - 0xFCFF_FFFF */
|
|
.8byte SECT + reserved /* 62MB Reserved */
|
|
.set SECT, SECT+0x200000
|
|
.endr
|
|
|
|
.rept 0x8 /* 0xFD00_0000 - 0xFDFF_FFFF */
|
|
.8byte SECT + Device /* 16MB FPS */
|
|
.set SECT, SECT+0x200000
|
|
.endr
|
|
|
|
.rept 0xE /* 0xFE00_0000 - 0xFFBF_FFFF */
|
|
.8byte SECT + Device /* 28MB LPS */
|
|
.set SECT, SECT+0x200000
|
|
.endr
|
|
|
|
/* 0xFFC0_0000 - 0xFFDF_FFFF */
|
|
.8byte SECT + Device /*2MB PMU/CSU */
|
|
|
|
.set SECT, SECT+0x200000 /* 0xFFE0_0000 - 0xFFFF_FFFF*/
|
|
.8byte SECT + Memory /* 2MB OCM/TCM */
|
|
|
|
#else /* TARGET_nxp_ls1028a */
|
|
/* ============================================================================
|
|
* LS1028A translation tables (4KB granule, 40-bit VA/PA, T0SZ=24).
|
|
* Memory map:
|
|
* 0x00000000-0x17FFFFFF Device (CCSR: DCFG 0x1E00000, RST 0x1E60000,
|
|
* SP805 0xC000000, FlexSPI ctrl 0x20C0000, ...)
|
|
* 0x18000000-0x181FFFFF Normal (OCRAM: wolfBoot .data/.bss/stack)
|
|
* 0x18200000-0x1FFFFFFF Device
|
|
* 0x20000000-0x3FFFFFFF Normal (FlexSPI NOR: wolfBoot XIP code, executable)
|
|
* 0x40000000-0x7FFFFFFF Device
|
|
* 0x80000000-0xFFFFFFFF Normal (DDR, 2GB) Outer-Shareable Cacheable
|
|
* 0x100000000-0x7FFFFFFFF Device (incl. ENETC ECAM 0x1F0000000 + BAR
|
|
* 0x1F8000000 at the 7-8GB block)
|
|
* DDR is Normal Outer-Shareable Cacheable (MemoryOSH) for coherency with the
|
|
* ENETC coherent bus-master DMA (SICAR=0x27276767), which snoops from outside
|
|
* the CPU cluster. NOR/OCRAM are Normal for XIP execution and cacheable data.
|
|
* ========================================================================== */
|
|
.section .mmu_tbl0,"a"
|
|
MMUTableL0:
|
|
.set SECT, MMUTableL1
|
|
.8byte SECT + 0x3 /* 0x00_0000_0000 - 0x7F_FFFF_FFFF -> L1 */
|
|
.8byte 0x0 /* 0x80_0000_0000 - 0xFF_FFFF_FFFF -> invalid */
|
|
|
|
.section .mmu_tbl1,"a"
|
|
MMUTableL1:
|
|
.set SECT, MMUTableL2
|
|
.8byte SECT + 0x3 /* 0x0000_0000 - 0x3FFF_FFFF -> L2 (mixed) */
|
|
.set SECT, 0x40000000
|
|
.8byte SECT + Device /* 0x4000_0000 - 0x7FFF_FFFF 1GB Device */
|
|
.set SECT, 0x80000000
|
|
.8byte SECT + MemoryOSH /* 0x8000_0000 - 0xBFFF_FFFF 1GB DDR (OSH for DMA) */
|
|
.set SECT, 0xC0000000
|
|
.8byte SECT + MemoryOSH /* 0xC000_0000 - 0xFFFF_FFFF 1GB DDR (OSH for DMA) */
|
|
.set SECT, 0x100000000
|
|
.rept 0x1FC /* 0x1_0000_0000 - 0x7F_FFFF_FFFF Device (508GB) */
|
|
.8byte SECT + Device /* incl. ENETC ECAM/BAR at the 7-8GB block */
|
|
.set SECT, SECT + 0x40000000
|
|
.endr
|
|
|
|
.section .mmu_tbl2,"a"
|
|
MMUTableL2:
|
|
.set SECT, 0x0
|
|
.rept 0xC0 /* 0x0000_0000 - 0x17FF_FFFF Device (384MB CCSR) */
|
|
.8byte SECT + Device
|
|
.set SECT, SECT + 0x200000
|
|
.endr
|
|
.8byte SECT + Memory /* 0x1800_0000 - 0x181F_FFFF OCRAM */
|
|
.set SECT, SECT + 0x200000
|
|
.rept 0x3F /* 0x1820_0000 - 0x1FFF_FFFF Device */
|
|
.8byte SECT + Device
|
|
.set SECT, SECT + 0x200000
|
|
.endr
|
|
.rept 0x100 /* 0x2000_0000 - 0x3FFF_FFFF NOR (512MB, exec) */
|
|
.8byte SECT + Memory
|
|
.set SECT, SECT + 0x200000
|
|
.endr
|
|
#endif /* TARGET_nxp_ls1028a */
|
|
|
|
|
|
/*
|
|
* FPUContextSize is the size of the array where floating point registers are
|
|
* stored when required. The default size corresponds to the case when there is no
|
|
* nested interrupt. If there are nested interrupts in application which are using
|
|
* floating point operation, the size of FPUContextSize need to be increased as per
|
|
* requirement
|
|
*/
|
|
|
|
.set FPUContextSize, 528
|
|
|
|
.macro saveregister
|
|
stp X0,X1, [sp,#-0x10]!
|
|
stp X2,X3, [sp,#-0x10]!
|
|
stp X4,X5, [sp,#-0x10]!
|
|
stp X6,X7, [sp,#-0x10]!
|
|
stp X8,X9, [sp,#-0x10]!
|
|
stp X10,X11, [sp,#-0x10]!
|
|
stp X12,X13, [sp,#-0x10]!
|
|
stp X14,X15, [sp,#-0x10]!
|
|
stp X16,X17, [sp,#-0x10]!
|
|
stp X18,X19, [sp,#-0x10]!
|
|
stp X29,X30, [sp,#-0x10]!
|
|
.endm
|
|
|
|
.macro restoreregister
|
|
ldp X29,X30, [sp], #0x10
|
|
ldp X18,X19, [sp], #0x10
|
|
ldp X16,X17, [sp], #0x10
|
|
ldp X14,X15, [sp], #0x10
|
|
ldp X12,X13, [sp], #0x10
|
|
ldp X10,X11, [sp], #0x10
|
|
ldp X8,X9, [sp], #0x10
|
|
ldp X6,X7, [sp], #0x10
|
|
ldp X4,X5, [sp], #0x10
|
|
ldp X2,X3, [sp], #0x10
|
|
ldp X0,X1, [sp], #0x10
|
|
.endm
|
|
|
|
.macro savefloatregister
|
|
|
|
/* Load the floating point context array address from FPUContextBase */
|
|
ldr x1,=FPUContextBase
|
|
ldr x0, [x1]
|
|
|
|
/* Save all the floating point register to the array */
|
|
stp q0,q1, [x0], #0x20
|
|
stp q2,q3, [x0], #0x20
|
|
stp q4,q5, [x0], #0x20
|
|
stp q6,q7, [x0], #0x20
|
|
stp q8,q9, [x0], #0x20
|
|
stp q10,q11, [x0], #0x20
|
|
stp q12,q13, [x0], #0x20
|
|
stp q14,q15, [x0], #0x20
|
|
stp q16,q17, [x0], #0x20
|
|
stp q18,q19, [x0], #0x20
|
|
stp q20,q21, [x0], #0x20
|
|
stp q22,q23, [x0], #0x20
|
|
stp q24,q25, [x0], #0x20
|
|
stp q26,q27, [x0], #0x20
|
|
stp q28,q29, [x0], #0x20
|
|
stp q30,q31, [x0], #0x20
|
|
mrs x2, FPCR
|
|
mrs x3, FPSR
|
|
stp x2, x3, [x0], #0x10
|
|
|
|
/* Save current address of floating point context array to FPUContextBase */
|
|
str x0, [x1]
|
|
.endm
|
|
|
|
.macro restorefloatregister
|
|
|
|
/* Restore the address of floating point context array from FPUContextBase */
|
|
ldr x1,=FPUContextBase
|
|
ldr x0, [x1]
|
|
|
|
/* Restore all the floating point register from the array */
|
|
ldp x2, x3, [x0,#-0x10]!
|
|
msr FPCR, x2
|
|
msr FPSR, x3
|
|
ldp q30,q31, [x0,#-0x20]!
|
|
ldp q28,q29, [x0,#-0x20]!
|
|
ldp q26,q27, [x0,#-0x20]!
|
|
ldp q24,q25, [x0,#-0x20]!
|
|
ldp q22,q23, [x0,#-0x20]!
|
|
ldp q20,q21, [x0,#-0x20]!
|
|
ldp q18,q19, [x0,#-0x20]!
|
|
ldp q16,q17, [x0,#-0x20]!
|
|
ldp q14,q15, [x0,#-0x20]!
|
|
ldp q12,q13, [x0,#-0x20]!
|
|
ldp q10,q11, [x0,#-0x20]!
|
|
ldp q8,q9, [x0,#-0x20]!
|
|
ldp q6,q7, [x0,#-0x20]!
|
|
ldp q4,q5, [x0,#-0x20]!
|
|
ldp q2,q3, [x0,#-0x20]!
|
|
ldp q0,q1, [x0,#-0x20]!
|
|
|
|
/* Save current address of floating point context array to FPUContextBase */
|
|
str x0, [x1]
|
|
.endm
|
|
|
|
.macro exception_return
|
|
eret
|
|
#ifdef TARGET_versal
|
|
dsb nsh
|
|
isb
|
|
#endif
|
|
.endm
|
|
|
|
|
|
.section .vectors, "a"
|
|
|
|
_vector_table:
|
|
.set VBAR, _vector_table
|
|
.org VBAR
|
|
|
|
/*
|
|
* if application is built for XEN GUEST as EL1 Non-secure following image
|
|
* header is required by XEN.
|
|
*/
|
|
#if defined(HYP_GUEST) && HYP_GUEST == 1
|
|
/* Valid Image header */
|
|
/* HW reset vector */
|
|
ldr x16, =_boot
|
|
br x16
|
|
#ifdef TARGET_versal
|
|
dsb nsh
|
|
isb
|
|
#endif
|
|
/* text offset. */
|
|
.dword 0
|
|
/* image size. */
|
|
.dword 0
|
|
/* flags. */
|
|
.dword 8
|
|
/* RES0 */
|
|
.dword 0
|
|
.dword 0
|
|
.dword 0
|
|
|
|
/* magic */
|
|
.dword 0x644d5241
|
|
/* RES0 */
|
|
.dword 0
|
|
/* End of Image header. */
|
|
#endif
|
|
b _boot
|
|
|
|
.org (VBAR + 0x200)
|
|
b SynchronousInterruptHandler
|
|
|
|
.org (VBAR + 0x280)
|
|
b IRQInterruptHandler
|
|
|
|
.org (VBAR + 0x300)
|
|
b FIQInterruptHandler
|
|
|
|
.org (VBAR + 0x380)
|
|
b SErrorInterruptHandler
|
|
|
|
|
|
SynchronousInterruptHandler:
|
|
saveregister
|
|
|
|
/* Check if the Synchronous abort is occurred due to floating point access. */
|
|
#if defined(EL3_SECURE) && EL3_SECURE == 1
|
|
mrs x0, ESR_EL3
|
|
#elif defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
|
|
mrs x0, ESR_EL2
|
|
#else
|
|
mrs x0, ESR_EL1
|
|
#endif
|
|
and x0, x0, #(0x3F << 26)
|
|
mov x1, #(0x7 << 26)
|
|
cmp x0, x1
|
|
/* If exception is not due to floating point access go to synchronous handler */
|
|
bne synchronoushandler
|
|
|
|
/*
|
|
* If excpetion occurred due to floating point access, Enable the floating point
|
|
* access i.e. do not trap floating point instruction
|
|
*/
|
|
#if defined(EL3_SECURE) && EL3_SECURE == 1
|
|
mrs x1,CPTR_EL3
|
|
bic x1, x1, #(0x1<<10)
|
|
msr CPTR_EL3, x1
|
|
#elif defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
|
|
mrs x1,CPTR_EL2
|
|
orr x1, x1, #(0x3 << 20) /* Clear TFP bit to enable FP/SIMD */
|
|
msr CPTR_EL2, x1
|
|
#else
|
|
mrs x1,CPACR_EL1
|
|
orr x1, x1, #(0x1<<20)
|
|
msr CPACR_EL1, x1
|
|
#endif
|
|
isb
|
|
|
|
/* If the floating point access was previously enabled, store FPU context
|
|
* registers(storefloat).
|
|
*/
|
|
ldr x0, =FPUStatus
|
|
ldrb w1,[x0]
|
|
cbnz w1, storefloat
|
|
/*
|
|
* If the floating point access was not enabled previously, save the status of
|
|
* floating point accessibility i.e. enabled and store floating point context
|
|
* array address(FPUContext) to FPUContextBase.
|
|
*/
|
|
mov w1, #0x1
|
|
strb w1, [x0]
|
|
ldr x0, =FPUContext
|
|
ldr x1, =FPUContextBase
|
|
str x0,[x1]
|
|
b restorecontext
|
|
storefloat:
|
|
savefloatregister
|
|
b restorecontext
|
|
synchronoushandler:
|
|
bl SynchronousInterrupt
|
|
restorecontext:
|
|
restoreregister
|
|
exception_return
|
|
|
|
IRQInterruptHandler:
|
|
|
|
saveregister
|
|
/* Save the status of SPSR, ELR and CPTR to stack */
|
|
#if defined(EL3_SECURE) && EL3_SECURE == 1
|
|
mrs x0, CPTR_EL3
|
|
mrs x1, ELR_EL3
|
|
mrs x2, SPSR_EL3
|
|
#elif defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
|
|
mrs x0, CPTR_EL2
|
|
mrs x1, ELR_EL2
|
|
mrs x2, SPSR_EL2
|
|
#else
|
|
mrs x0, CPACR_EL1
|
|
mrs x1, ELR_EL1
|
|
mrs x2, SPSR_EL1
|
|
#endif
|
|
stp x0, x1, [sp,#-0x10]!
|
|
str x2, [sp,#-0x10]!
|
|
|
|
/* Trap floating point access */
|
|
#if defined(EL3_SECURE) && EL3_SECURE == 1
|
|
mrs x1,CPTR_EL3
|
|
orr x1, x1, #(0x1<<10)
|
|
msr CPTR_EL3, x1
|
|
#elif defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
|
|
mrs x1,CPTR_EL2
|
|
bic x1, x1, #(0x3<<20) /* Set TFP bits to trap FP/SIMD */
|
|
msr CPTR_EL2, x1
|
|
#else
|
|
mrs x1,CPACR_EL1
|
|
bic x1, x1, #(0x1<<20)
|
|
msr CPACR_EL1, x1
|
|
#endif
|
|
isb
|
|
|
|
bl IRQInterrupt
|
|
/*
|
|
* If floating point access is enabled during interrupt handling,
|
|
* restore floating point registers.
|
|
*/
|
|
|
|
#if defined(EL3_SECURE) && EL3_SECURE == 1
|
|
mrs x0, CPTR_EL3
|
|
ands x0, x0, #(0x1<<10)
|
|
bne RestorePrevState
|
|
#elif defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
|
|
mrs x0, CPTR_EL2
|
|
ands x0, x0, #(0x3<<20)
|
|
bne RestorePrevState
|
|
#else
|
|
mrs x0,CPACR_EL1
|
|
ands x0, x0, #(0x1<<20)
|
|
beq RestorePrevState
|
|
#endif
|
|
|
|
restorefloatregister
|
|
|
|
/* Restore the status of SPSR, ELR and CPTR from stack */
|
|
RestorePrevState:
|
|
ldr x2,[sp],0x10
|
|
ldp x0, x1, [sp],0x10
|
|
#if defined(EL3_SECURE) && EL3_SECURE == 1
|
|
msr CPTR_EL3, x0
|
|
msr ELR_EL3, x1
|
|
msr SPSR_EL3, x2
|
|
#elif defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
|
|
msr CPTR_EL2, x0
|
|
msr ELR_EL2, x1
|
|
msr SPSR_EL2, x2
|
|
#else
|
|
msr CPACR_EL1, x0
|
|
msr ELR_EL1, x1
|
|
msr SPSR_EL1, x2
|
|
#endif
|
|
restoreregister
|
|
exception_return
|
|
|
|
FIQInterruptHandler:
|
|
|
|
saveregister
|
|
/* Save the status of SPSR, ELR and CPTR to stack */
|
|
#if defined(EL3_SECURE) && EL3_SECURE == 1
|
|
mrs x0, CPTR_EL3
|
|
mrs x1, ELR_EL3
|
|
mrs x2, SPSR_EL3
|
|
#elif defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
|
|
mrs x0, CPTR_EL2
|
|
mrs x1, ELR_EL2
|
|
mrs x2, SPSR_EL2
|
|
#else
|
|
mrs x0, CPACR_EL1
|
|
mrs x1, ELR_EL1
|
|
mrs x2, SPSR_EL1
|
|
#endif
|
|
stp x0, x1, [sp,#-0x10]!
|
|
str x2, [sp,#-0x10]!
|
|
|
|
/* Trap floating point access */
|
|
#if defined(EL3_SECURE) && EL3_SECURE == 1
|
|
mrs x1,CPTR_EL3
|
|
orr x1, x1, #(0x1<<10)
|
|
msr CPTR_EL3, x1
|
|
#elif defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
|
|
mrs x1,CPTR_EL2
|
|
bic x1, x1, #(0x3<<20)
|
|
msr CPTR_EL2, x1
|
|
#else
|
|
mrs x1,CPACR_EL1
|
|
bic x1, x1, #(0x1<<20)
|
|
msr CPACR_EL1, x1
|
|
#endif
|
|
isb
|
|
bl FIQInterrupt
|
|
/*
|
|
* If floating point access is enabled during interrupt handling,
|
|
* restore floating point registers.
|
|
*/
|
|
|
|
#if defined(EL3_SECURE) && EL3_SECURE == 1
|
|
mrs x0, CPTR_EL3
|
|
ands x0, x0, #(0x1<<10)
|
|
bne RestorePrevStatefiq
|
|
#elif defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
|
|
mrs x0, CPTR_EL2
|
|
ands x0, x0, #(0x3<<20)
|
|
bne RestorePrevStatefiq
|
|
#else
|
|
mrs x0,CPACR_EL1
|
|
ands x0, x0, #(0x1<<20)
|
|
beq RestorePrevStatefiq
|
|
#endif
|
|
|
|
restorefloatregister
|
|
|
|
/* Restore the status of SPSR, ELR and CPTR from stack */
|
|
RestorePrevStatefiq:
|
|
ldr x2,[sp],0x10
|
|
ldp x0, x1, [sp],0x10
|
|
#if defined(EL3_SECURE) && EL3_SECURE == 1
|
|
msr CPTR_EL3, x0
|
|
msr ELR_EL3, x1
|
|
msr SPSR_EL3, x2
|
|
#elif defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
|
|
msr CPTR_EL2, x0
|
|
msr ELR_EL2, x1
|
|
msr SPSR_EL2, x2
|
|
#else
|
|
msr CPACR_EL1, x0
|
|
msr ELR_EL1, x1
|
|
msr SPSR_EL1, x2
|
|
#endif
|
|
restoreregister
|
|
exception_return
|
|
|
|
SErrorInterruptHandler:
|
|
|
|
saveregister
|
|
bl SErrorInterrupt
|
|
restoreregister
|
|
exception_return
|
|
|
|
|
|
.align 8
|
|
/* Array to store floating point registers */
|
|
FPUContext:
|
|
.skip FPUContextSize
|
|
/* Stores address for floating point context array */
|
|
FPUContextBase:
|
|
.skip 8
|
|
FPUStatus:
|
|
.skip 1
|
|
|
|
.align 8
|
|
#elif defined(USE_SIMPLE_STARTUP)
|
|
.section ".boot"
|
|
|
|
.global _vector_table
|
|
_vector_table:
|
|
#if defined(TARGET_cm4)
|
|
/* ARM64 Linux kernel image header (Documentation/arm64/booting.rst). The RPi
|
|
* VideoCore firmware (start4.elf) only enters a 64-bit kernel that carries
|
|
* this header (branch at off 0, "ARM\x64" magic at off 56); it then runs the
|
|
* image in place at its 2MB-aligned load address (0x200000) rather than
|
|
* relocating to 0x80000. code0 branches to the real entry. */
|
|
b _cm4_entry
|
|
.long 0 /* code1 */
|
|
.quad 0 /* text_offset */
|
|
.quad _end - _vector_table /* image_size */
|
|
.quad 0xA /* flags: LE, 4KB pages, load anywhere */
|
|
.quad 0 /* res2 */
|
|
.quad 0 /* res3 */
|
|
.quad 0 /* res4 */
|
|
.ascii "ARM\x64" /* magic 0x644d5241 at offset 56 */
|
|
.long 0 /* res5 */
|
|
_cm4_entry:
|
|
#endif
|
|
mov x21, x0 // read ATAG/FDT address
|
|
|
|
#if defined(TARGET_cm4)
|
|
/* Load-address self-check: wolfBoot is linked absolutely at 0x200000 and is
|
|
* NOT position independent (the ldr =_vector_table below loads the absolute
|
|
* stack top). Compare the runtime address (PC-relative adr) with the linked
|
|
* address (from the literal pool); a mismatch means the firmware placed the
|
|
* image at a different 2MB base, so halt loudly instead of executing from the
|
|
* wrong VMA. The generated config.txt pins kernel_address=0x200000. */
|
|
adr x2, _vector_table
|
|
ldr x3, =_vector_table
|
|
cmp x2, x3
|
|
b.eq 5f
|
|
6: wfi
|
|
b 6b
|
|
5:
|
|
#endif
|
|
|
|
4: ldr x1, =_vector_table // get start of .text in x1
|
|
// Read current EL
|
|
mrs x0, CurrentEL
|
|
and x0, x0, #0x0C
|
|
|
|
// EL == 3?
|
|
cmp x0, #12
|
|
bne 2f
|
|
3: mrs x2, scr_el3
|
|
orr x2, x2, 0x0F // scr_el3 |= NS|IRQ|FIQ|EA
|
|
msr scr_el3, x2
|
|
|
|
msr cptr_el3, xzr // enable FP/SIMD
|
|
|
|
// EL == 1?
|
|
2: cmp x0, #4
|
|
beq 1f
|
|
|
|
// EL == 2?
|
|
mov x2, #3 << 20
|
|
msr cptr_el2, x2 /* Enable FP/SIMD */
|
|
#if defined(TARGET_cm4) && defined(DEBUG) && defined(DEBUG_UART)
|
|
ldr x2, =cm4_vectors /* install fault-dump exception vectors (debug) */
|
|
msr VBAR_EL2, x2
|
|
isb /* ensure new vector base is in effect */
|
|
#endif
|
|
b 0f
|
|
|
|
1: mov x0, #3 << 20
|
|
msr cpacr_el1, x0 // Enable FP/SIMD for EL1
|
|
msr sp_el1, x1
|
|
|
|
/* Suspend slave CPUs */
|
|
0: mrs x3, mpidr_el1 // read MPIDR_EL1
|
|
and x3, x3, #3 // CPUID = MPIDR_EL1 & 0x03
|
|
cbz x3, 8f // if 0, branch forward
|
|
7: wfi // infinite sleep
|
|
b 7b
|
|
|
|
8:
|
|
#ifdef TARGET_tegra234
|
|
/* Use the reserved in-DRAM stack from hal/tegra234.ld. The generic path
|
|
* sets sp to the image base, which on this target is the BL33 load address
|
|
* - growing down from there runs into memory wolfBoot does not own. */
|
|
ldr x1, =END_STACK
|
|
#endif
|
|
mov sp, x1 // set stack pointer
|
|
#ifdef TARGET_tegra234
|
|
/* Stash the handoff x0 (DTB pointer, saved in x21 at reset) in a .data
|
|
* global so C can read it after boot_entry_C clears BSS. */
|
|
adrp x2, boot_handoff_x0
|
|
add x2, x2, :lo12:boot_handoff_x0
|
|
str x21, [x2]
|
|
#endif
|
|
bl boot_entry_C // boot_entry_C never returns
|
|
b 7b // go to sleep anyhow in case.
|
|
|
|
#if defined(TARGET_cm4) && defined(DEBUG) && defined(DEBUG_UART)
|
|
/* Minimal EL2 exception vectors for CM4 bring-up debug. On any exception
|
|
* (data/instruction abort, etc.) dump ESR/ELR/FAR over UART instead of the
|
|
* silent hang the simple startup would otherwise produce. */
|
|
.macro CM4_VEC label
|
|
.align 7
|
|
\label:
|
|
b cm4_fault_common
|
|
.endm
|
|
|
|
.align 11
|
|
.global cm4_vectors
|
|
cm4_vectors:
|
|
CM4_VEC cm4_v0 /* Current EL, SP0: Sync */
|
|
CM4_VEC cm4_v1 /* IRQ */
|
|
CM4_VEC cm4_v2 /* FIQ */
|
|
CM4_VEC cm4_v3 /* SError */
|
|
CM4_VEC cm4_v4 /* Current EL, SPx: Sync */
|
|
CM4_VEC cm4_v5 /* IRQ */
|
|
CM4_VEC cm4_v6 /* FIQ */
|
|
CM4_VEC cm4_v7 /* SError */
|
|
CM4_VEC cm4_v8 /* Lower EL, AArch64: Sync */
|
|
CM4_VEC cm4_v9 /* IRQ */
|
|
CM4_VEC cm4_v10 /* FIQ */
|
|
CM4_VEC cm4_v11 /* SError */
|
|
CM4_VEC cm4_v12 /* Lower EL, AArch32: Sync */
|
|
CM4_VEC cm4_v13 /* IRQ */
|
|
CM4_VEC cm4_v14 /* FIQ */
|
|
CM4_VEC cm4_v15 /* SError */
|
|
|
|
cm4_fault_common:
|
|
/* Switch to a dedicated fault stack before calling the C handler: the
|
|
* exception may have been taken with SP unset (the window before 'mov sp,x1'
|
|
* or on a secondary core that never sets SP) or with SP overflowed, and
|
|
* cm4_fault_handler needs a valid stack for its wolfBoot_printf calls. */
|
|
ldr x3, =cm4_fault_stack_top
|
|
mov sp, x3
|
|
mrs x0, esr_el2
|
|
mrs x1, elr_el2
|
|
mrs x2, far_el2
|
|
bl cm4_fault_handler /* void cm4_fault_handler(esr, elr, far) */
|
|
9: wfi
|
|
b 9b
|
|
|
|
.section ".bss"
|
|
.balign 16
|
|
cm4_fault_stack:
|
|
.skip 1024
|
|
cm4_fault_stack_top:
|
|
.section ".boot"
|
|
#endif /* TARGET_cm4 && DEBUG && DEBUG_UART */
|
|
#if 0
|
|
.section ".boot"
|
|
.global _vector_table
|
|
_vector_table:
|
|
bl boot_entry_C // boot_entry_C never returns
|
|
#endif
|
|
#endif /* !USE_BUILTIN_STARTUP */
|
|
|
|
|
|
/*
|
|
* void flush_dcache_range(start, end)
|
|
*
|
|
* clean & invalidate data cache in the range
|
|
*
|
|
* x0: start address
|
|
* x1: end address
|
|
*/
|
|
.global flush_dcache_range
|
|
flush_dcache_range:
|
|
mrs x3, ctr_el0
|
|
lsr x3, x3, #16
|
|
and x3, x3, #0xf
|
|
mov x2, #4
|
|
lsl x2, x2, x3 /* cache line size */
|
|
|
|
/* x2 <- minimal cache line size in cache system */
|
|
sub x3, x2, #1
|
|
bic x0, x0, x3
|
|
1: dc civac, x0 /* clean & invalidate data or unified cache */
|
|
add x0, x0, x2
|
|
cmp x0, x1
|
|
b.lo 1b
|
|
dsb sy
|
|
ret
|
|
|
|
|
|
/*
|
|
* void zynqmp_dcache_disable(void)
|
|
*
|
|
* Clean+invalidate the entire data cache to PoC by set/way, then disable the
|
|
* D-cache (SCTLR_EL3.C=0) and drop the core out of the coherency domain
|
|
* (CPUECTLR_EL1.SMPEN=0). Used to TEST whether the QSPI body-load failure is
|
|
* purely a coherency artifact: with no cache lines to allocate into and the
|
|
* core out of the snoop domain, the coherent QSPIDMA writes must land in DDR.
|
|
* Clobbers x0-x11. Modeled on the ARMv8 set/way routine used elsewhere here.
|
|
*/
|
|
.global zynqmp_dcache_disable
|
|
zynqmp_dcache_disable:
|
|
mrs x0, clidr_el1
|
|
and x3, x0, #0x07000000
|
|
lsr x3, x3, #23 /* x3 = LoC * 2 */
|
|
cbz x3, .Lzdd_done
|
|
mov x10, #0
|
|
.Lzdd_level:
|
|
add x2, x10, x10, lsr #1
|
|
lsr x1, x0, x2
|
|
and x1, x1, #7
|
|
cmp x1, #2
|
|
b.lt .Lzdd_skip
|
|
msr csselr_el1, x10
|
|
isb
|
|
mrs x1, ccsidr_el1
|
|
and x2, x1, #7
|
|
add x2, x2, #4 /* x2 = log2(line len) */
|
|
mov x4, #0x3ff
|
|
and x4, x4, x1, lsr #3 /* x4 = max way */
|
|
clz w5, w4
|
|
mov x7, #0x7fff
|
|
and x7, x7, x1, lsr #13 /* x7 = max set */
|
|
.Lzdd_set:
|
|
mov x9, x4
|
|
.Lzdd_way:
|
|
lsl x6, x9, x5
|
|
orr x11, x10, x6
|
|
lsl x6, x7, x2
|
|
orr x11, x11, x6
|
|
dc cisw, x11
|
|
subs x9, x9, #1
|
|
b.ge .Lzdd_way
|
|
subs x7, x7, #1
|
|
b.ge .Lzdd_set
|
|
.Lzdd_skip:
|
|
add x10, x10, #2
|
|
cmp x3, x10
|
|
b.gt .Lzdd_level
|
|
.Lzdd_done:
|
|
mov x10, #0
|
|
msr csselr_el1, x10
|
|
dsb sy
|
|
isb
|
|
/* disable D-cache (SCTLR_EL3.C, bit 2) */
|
|
mrs x1, sctlr_el3
|
|
bic x1, x1, #(1 << 2)
|
|
msr sctlr_el3, x1
|
|
isb
|
|
/* leave the SMP/coherency domain (CPUECTLR_EL1.SMPEN, bit 6) */
|
|
mrs x1, S3_1_C15_C2_1
|
|
bic x1, x1, #(1 << 6)
|
|
msr S3_1_C15_C2_1, x1
|
|
dsb sy
|
|
isb
|
|
ret
|
|
|
|
|
|
/* Initialize GIC 400 (GICv2) */
|
|
.global gicv2_init_secure
|
|
gicv2_init_secure:
|
|
ldr x0, =GICD_BASE
|
|
mov w9, #0x3 /* EnableGrp0 | EnableGrp1 */
|
|
str w9, [x0, GICD_CTLR] /* Secure GICD_CTLR */
|
|
ldr w9, [x0, GICD_TYPER]
|
|
and w10, w9, #0x1f /* ITLinesNumber */
|
|
cbz w10, 1f /* No SPIs */
|
|
add x11, x0, GICD_IGROUPRn
|
|
mov w9, #~0 /* Config SPIs as Grp1 */
|
|
str w9, [x11], #0x4
|
|
0: str w9, [x11], #0x4
|
|
sub w10, w10, #0x1
|
|
cbnz w10, 0b
|
|
|
|
ldr x1, =GICC_BASE /* GICC_CTLR */
|
|
mov w0, #3 /* EnableGrp0 | EnableGrp1 */
|
|
str w0, [x1]
|
|
|
|
mov w0, #1 << 7 /* Allow NS access to GICC_PMR */
|
|
str w0, [x1, #4] /* GICC_PMR */
|
|
1:
|
|
ret
|
|
|
|
#if defined(BOOT_EL1) && defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
|
|
/*
|
|
* Transition from EL2 to EL1 and jump to application
|
|
*
|
|
* Parameters:
|
|
* x0: entry_point - Address to jump to in EL1
|
|
* x1: dts_addr - Device tree address (passed in x0 to application)
|
|
*
|
|
* This function configures the necessary system registers for EL1 operation
|
|
* and performs an exception return (ERET) to drop from EL2 to EL1.
|
|
*
|
|
* Based on ARM Architecture Reference Manual and U-Boot implementation.
|
|
*/
|
|
.global el2_to_el1_boot
|
|
el2_to_el1_boot:
|
|
/* Save parameters - x0=entry_point, x1=dts_addr */
|
|
mov x19, x0 /* Save entry_point in x19 */
|
|
mov x20, x1 /* Save dts_addr in x20 */
|
|
|
|
/* 1. Configure timer access for EL1 */
|
|
mrs x0, CNTHCTL_EL2
|
|
orr x0, x0, #3 /* EL1PCEN | EL1PCTEN - enable EL1 timer access */
|
|
msr CNTHCTL_EL2, x0
|
|
msr CNTVOFF_EL2, xzr /* Clear virtual timer offset */
|
|
|
|
/* 2. Configure virtual processor ID */
|
|
mrs x0, MIDR_EL1
|
|
msr VPIDR_EL2, x0
|
|
mrs x0, MPIDR_EL1
|
|
msr VMPIDR_EL2, x0
|
|
|
|
/* 3. Disable coprocessor traps to EL2 */
|
|
mov x0, #0x33ff /* CPTR_EL2: RES1 bits, no traps */
|
|
msr CPTR_EL2, x0
|
|
msr HSTR_EL2, xzr /* No traps to EL2 on system registers */
|
|
mov x0, #(3 << 20) /* CPACR_EL1: Full FP/SIMD access */
|
|
msr CPACR_EL1, x0
|
|
|
|
/* 4. Initialize SCTLR_EL1 with safe defaults (RES1 bits, MMU/cache off) */
|
|
/* RES1 bits: 29,28,23,22,20,11 = 0x30d00800 */
|
|
movz x0, #0x800
|
|
movk x0, #0x30d0, lsl #16
|
|
msr SCTLR_EL1, x0
|
|
|
|
/* 5. Migrate stack pointer and vector base to EL1 */
|
|
/* SP_EL1 must be 16-byte aligned per ARM spec */
|
|
mov x0, sp
|
|
bic x0, x0, #0xF /* Ensure 16-byte alignment */
|
|
msr SP_EL1, x0
|
|
mrs x0, VBAR_EL2
|
|
msr VBAR_EL1, x0
|
|
dsb sy /* Ensure SP_EL1 and VBAR_EL1 writes complete */
|
|
isb /* Ensure writes take effect */
|
|
|
|
/* 6. Configure HCR_EL2 - EL1 is AArch64, no hypervisor calls */
|
|
/* Check if PAuth (Pointer Authentication) is supported */
|
|
mrs x0, ID_AA64ISAR1_EL1 /* Read ISA feature register */
|
|
mov x1, #(0xF << 28) /* GPI mask */
|
|
orr x1, x1, #(0xF << 24) /* GPA mask */
|
|
orr x1, x1, #(0xF << 8) /* API mask */
|
|
orr x1, x1, #(0xF << 4) /* APA mask */
|
|
tst x0, x1 /* Test if PAuth supported (Z=1 if not supported) */
|
|
mov x0, #(1 << 31) /* RW: EL1 is AArch64 */
|
|
orr x0, x0, #(1 << 29) /* HCD: Disable HVC instruction */
|
|
mov x1, x0 /* Copy base value */
|
|
orr x1, x1, #(1 << 41) /* API: Trap PAuth instructions */
|
|
orr x1, x1, #(1 << 40) /* APK: Trap PAuth key access */
|
|
csel x0, x0, x1, eq /* If PAuth not supported (eq), use x0 (base), else x1 (with traps) */
|
|
msr HCR_EL2, x0
|
|
dsb sy /* Ensure HCR_EL2 write completes */
|
|
isb /* Ensure HCR_EL2 takes effect */
|
|
|
|
/* 7. Set up SPSR_EL2 for return to EL1h with all interrupts masked */
|
|
/* M[3:0] = 0101 = EL1h (EL1 with SP_EL1) - NOT 0100 which is EL1t! */
|
|
/* M[4] = 0 = AArch64 mode (bit 4 must be 0 for AArch64, 1 for AArch32) */
|
|
/* DAIF = 0xF = all interrupts masked */
|
|
/* Value: 0x3C5 = (0xF << 6) | 0x5 */
|
|
movz x0, #0x3C5 /* DAIF=0xF (bits 9:6), M[3:0]=0x5 (EL1h) */
|
|
msr SPSR_EL2, x0
|
|
dsb sy /* Ensure SPSR_EL2 write completes */
|
|
isb /* Ensure SPSR_EL2 takes effect */
|
|
|
|
/* 8. Set exception return address and DTB pointer, then ERET */
|
|
/* Critical: All register writes must complete before eret */
|
|
msr ELR_EL2, x19 /* Entry point in ELR_EL2 */
|
|
mov x0, x20 /* DTB address in x0 (first arg) */
|
|
mov x1, xzr /* Zero remaining argument registers */
|
|
mov x2, xzr
|
|
mov x3, xzr
|
|
dsb sy /* Ensure all writes complete */
|
|
isb /* Ensure all effects are visible */
|
|
eret /* Exception return to EL1 */
|
|
|
|
/* Should never reach here */
|
|
b .
|
|
#endif /* BOOT_EL1 && EL2_HYPERVISOR */
|
|
|
|
|
|
/*
|
|
* Clean entire D-cache to the Point of Coherency (PoC), invalidate the
|
|
* I-cache to the Point of Unification (PoU), and disable MMU + I/D-cache
|
|
* at EL2. Returns normally to the caller.
|
|
*
|
|
* Terminology (ARM ARM B2.8):
|
|
* PoC - Point of Coherency: the point at which all observers (CPUs,
|
|
* DMA masters, etc.) see the same memory. Cleaning to PoC
|
|
* guarantees the image bytes we memcpy'd are visible to the
|
|
* next stage's first uncached instruction fetches.
|
|
* PoU - Point of Unification: the point at which instruction and data
|
|
* caches converge. Invalidating I-cache to PoU ensures stale
|
|
* fetches are discarded before we hand off.
|
|
*
|
|
* wolfBoot's startup (line ~347 above) enables MMU+I+D cache at EL2 for
|
|
* its own use. Any payload we hand off to (Linux kernel, hypervisor,
|
|
* bare-metal RTOS, a later bootloader stage) expects to enter without
|
|
* inheriting wolfBoot's translation tables, and the ARM64 Linux boot
|
|
* protocol (Documentation/arch/arm64/booting.rst) explicitly REQUIRES
|
|
* MMU off, D-cache off, and the loaded image cleaned to PoC. This
|
|
* helper performs that teardown and returns; the caller then performs
|
|
* the actual jump with whatever ABI the payload expects.
|
|
*
|
|
* Safe to return because wolfBoot's .text is identity-mapped (VA=PA)
|
|
* at EL2, so instruction fetch keeps working after SCTLR_EL2.M is
|
|
* cleared.
|
|
*
|
|
* AAPCS64: clobbers x0-x7, x9-x11; x30 (LR) is preserved because the
|
|
* set/way loop body does not touch it.
|
|
*/
|
|
#if defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
|
|
.global el2_flush_and_disable_mmu
|
|
el2_flush_and_disable_mmu:
|
|
/* ---- 1. Clean & invalidate entire data cache to PoC by set/way ----
|
|
* Standard ARMv8 routine, adapted from arm-trusted-firmware /
|
|
* U-Boot / Linux. Iterates every (level, set, way) triple and
|
|
* issues `dc cisw` on it. Terminates at the Level of Coherency
|
|
* (LoC) read from CLIDR_EL1. */
|
|
mrs x0, clidr_el1
|
|
and x3, x0, #0x07000000 /* x3 = LoC (level of coherency) */
|
|
lsr x3, x3, #23 /* x3 = LoC * 2 */
|
|
cbz x3, .Ldcache_done
|
|
mov x10, #0 /* x10 = current cache level << 1 */
|
|
|
|
.Ldcache_level_loop:
|
|
add x2, x10, x10, lsr #1 /* x2 = level * 3 */
|
|
lsr x1, x0, x2 /* x1 = ctype field for this level */
|
|
and x1, x1, #7
|
|
cmp x1, #2
|
|
b.lt .Ldcache_skip_level /* No data cache at this level */
|
|
msr csselr_el1, x10 /* Select cache level (instruction = 0) */
|
|
isb
|
|
mrs x1, ccsidr_el1
|
|
and x2, x1, #7 /* x2 = log2(line length) - 4 */
|
|
add x2, x2, #4 /* x2 = log2(line length) */
|
|
mov x4, #0x3ff
|
|
and x4, x4, x1, lsr #3 /* x4 = max way number */
|
|
clz w5, w4 /* x5 = bit position of way size */
|
|
mov x7, #0x7fff
|
|
and x7, x7, x1, lsr #13 /* x7 = max set number */
|
|
|
|
.Ldcache_set_loop:
|
|
mov x9, x4 /* x9 = current way */
|
|
.Ldcache_way_loop:
|
|
lsl x6, x9, x5
|
|
orr x11, x10, x6 /* level | way */
|
|
lsl x6, x7, x2
|
|
orr x11, x11, x6 /* level | way | set */
|
|
dc cisw, x11 /* clean & invalidate by set/way */
|
|
subs x9, x9, #1
|
|
b.ge .Ldcache_way_loop
|
|
subs x7, x7, #1
|
|
b.ge .Ldcache_set_loop
|
|
|
|
.Ldcache_skip_level:
|
|
add x10, x10, #2
|
|
cmp x3, x10
|
|
b.gt .Ldcache_level_loop
|
|
|
|
.Ldcache_done:
|
|
mov x10, #0
|
|
msr csselr_el1, x10
|
|
dsb sy
|
|
isb
|
|
|
|
/* ---- 2. Invalidate entire I-cache to PoU ----
|
|
* `ic iallu` invalidates all instruction cache to the Point of
|
|
* Unification for the local PE. */
|
|
ic iallu
|
|
dsb ish
|
|
isb
|
|
|
|
/* ---- 3. Disable MMU + I-cache + D-cache at EL2 ----
|
|
* SCTLR_EL2.M (bit 0) = MMU enable
|
|
* SCTLR_EL2.C (bit 2) = D-cache enable
|
|
* SCTLR_EL2.I (bit 12) = I-cache enable
|
|
*
|
|
* ARM ARM (B2.7.2) requires `dsb sy` before `isb` when modifying
|
|
* SCTLR_ELx.M so the system register write is observable before the
|
|
* pipeline is re-synchronized. Matches the MMU-enable sequence used
|
|
* earlier in this file.
|
|
*/
|
|
mrs x0, SCTLR_EL2
|
|
bic x0, x0, #(1 << 0) /* M */
|
|
bic x0, x0, #(1 << 2) /* C */
|
|
bic x0, x0, #(1 << 12) /* I */
|
|
msr SCTLR_EL2, x0
|
|
dsb sy
|
|
isb
|
|
|
|
ret
|
|
#endif /* EL2_HYPERVISOR */
|
|
|
|
#if defined(WOLFBOOT_ZYNQMP_FSBL)
|
|
/*
|
|
* void el3_jump_to_entry(uintptr_t entry)
|
|
*
|
|
* Generic AArch64 EL3 exit primitive: cleans & invalidates the entire D-cache
|
|
* to the PoC, invalidates the I-cache, disables the EL3 MMU + I/D-caches, then
|
|
* branches to the given entry point at EL3 with the MMU off. Not ZynqMP-
|
|
* specific -- reusable by any AArch64 EL3-FSBL target that hands off to a
|
|
* next-stage image entered at EL3 (here BL31, mirroring the stock ZynqMP FSBL's
|
|
* XFsbl_Exit()). Any handoff parameters are the caller's responsibility (the
|
|
* ZynqMP caller publishes them via PMU_GLOBAL scratch in hal/zynqmp_atf.c
|
|
* beforehand), so no GP-register arguments are used here.
|
|
*
|
|
* x0: entry. The set/way loop below clobbers x0-x11, so the entry point is
|
|
* preserved in x19 (callee-saved, untouched by the loop). Does not return.
|
|
*/
|
|
.global el3_jump_to_entry
|
|
el3_jump_to_entry:
|
|
mov x19, x0 /* preserve entry point across the loop */
|
|
|
|
/* ---- 1. Clean & invalidate entire data cache to PoC by set/way ---- */
|
|
mrs x0, clidr_el1
|
|
and x3, x0, #0x07000000 /* x3 = LoC (level of coherency) */
|
|
lsr x3, x3, #23 /* x3 = LoC * 2 */
|
|
cbz x3, .Latf_dcache_done
|
|
mov x10, #0 /* x10 = current cache level << 1 */
|
|
|
|
.Latf_dcache_level_loop:
|
|
add x2, x10, x10, lsr #1 /* x2 = level * 3 */
|
|
lsr x1, x0, x2 /* x1 = ctype field for this level */
|
|
and x1, x1, #7
|
|
cmp x1, #2
|
|
b.lt .Latf_dcache_skip_level /* No data cache at this level */
|
|
msr csselr_el1, x10 /* Select cache level (instruction = 0) */
|
|
isb
|
|
mrs x1, ccsidr_el1
|
|
and x2, x1, #7 /* x2 = log2(line length) - 4 */
|
|
add x2, x2, #4 /* x2 = log2(line length) */
|
|
mov x4, #0x3ff
|
|
and x4, x4, x1, lsr #3 /* x4 = max way number */
|
|
clz w5, w4 /* x5 = bit position of way size */
|
|
mov x7, #0x7fff
|
|
and x7, x7, x1, lsr #13 /* x7 = max set number */
|
|
|
|
.Latf_dcache_set_loop:
|
|
mov x9, x4 /* x9 = current way */
|
|
.Latf_dcache_way_loop:
|
|
lsl x6, x9, x5
|
|
orr x11, x10, x6 /* level | way */
|
|
lsl x6, x7, x2
|
|
orr x11, x11, x6 /* level | way | set */
|
|
dc cisw, x11 /* clean & invalidate by set/way */
|
|
subs x9, x9, #1
|
|
b.ge .Latf_dcache_way_loop
|
|
subs x7, x7, #1
|
|
b.ge .Latf_dcache_set_loop
|
|
|
|
.Latf_dcache_skip_level:
|
|
add x10, x10, #2
|
|
cmp x3, x10
|
|
b.gt .Latf_dcache_level_loop
|
|
|
|
.Latf_dcache_done:
|
|
mov x10, #0
|
|
msr csselr_el1, x10
|
|
dsb sy
|
|
isb
|
|
|
|
/* ---- 2. Invalidate entire I-cache to PoU ---- */
|
|
ic iallu
|
|
dsb ish
|
|
isb
|
|
|
|
/* ---- 3. Disable MMU + I-cache + D-cache at EL3 ---- */
|
|
mrs x0, SCTLR_EL3
|
|
bic x0, x0, #(1 << 0) /* M */
|
|
bic x0, x0, #(1 << 2) /* C */
|
|
bic x0, x0, #(1 << 12) /* I */
|
|
msr SCTLR_EL3, x0
|
|
dsb sy
|
|
isb
|
|
|
|
/* ---- 4. Branch to BL31 (does not return) ---- */
|
|
br x19
|
|
#endif /* WOLFBOOT_ZYNQMP_FSBL */
|
|
|
|
.end
|