Fixes for EL2 -> EL1 on boot

pull/679/head
David Garske 2026-01-16 15:27:45 -08:00 committed by Daniele Lacamera
parent ee3c313a66
commit ae63f60efc
6 changed files with 134 additions and 93 deletions

View File

@ -1208,14 +1208,12 @@ void hal_init(void)
"========================================\n";
#endif
#ifdef DEBUG_UART
uart_init();
#ifdef __WOLFBOOT
wolfBoot_printf("%s", banner);
#endif
wolfBoot_printf("Current EL: %d\n", current_el());
#endif /* DEBUG_UART */
#endif
#ifdef EXT_FLASH
qspi_init();

View File

@ -59,6 +59,14 @@
/* ARM Errata */
#define CONFIG_ARM_ERRATA_855873 1
/* BL31-applied errata/CVEs (preserve when modifying CPUACTLR):
* - Erratum 859971
* - Erratum 1319367
* - CVE-2017-5715 (Spectre V2)
* - CVE-2018-3639 (SSB)
* - CVE-2022-23960
*/
#endif /* USE_BUILTIN_STARTUP */
/* ============================================================================
@ -519,7 +527,9 @@
#define GQSPI_DUMMY_READ 8 /* Dummy clocks for Fast/Quad Read */
#endif
#ifndef XALIGNED
#define XALIGNED(x) __attribute__((aligned(x)))
#endif
/* ============================================================================

View File

@ -25,6 +25,11 @@
#include "loader.h"
#include "wolfboot/wolfboot.h"
/* Include platform-specific header for EL configuration defines */
#ifdef TARGET_versal
#include "hal/versal.h"
#endif
/* Linker exported variables */
extern unsigned int __bss_start__;
extern unsigned int __bss_end__;
@ -47,12 +52,15 @@ extern void gicv2_init_secure(void);
#define SKIP_GIC_INIT
#endif
#ifndef TARGET_versal
/* current_el() is defined in hal/versal.h for Versal */
unsigned int current_el(void)
{
unsigned long el;
asm volatile("mrs %0, CurrentEL" : "=r" (el) : : "cc");
return (unsigned int)((el >> 2) & 0x3U);
}
#endif
#if defined(BOOT_EL1) && defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
/**
@ -66,83 +74,7 @@ unsigned int current_el(void)
* @param entry_point Address to jump to in EL1
* @param dts_addr Device tree address (passed in x0 to application)
*/
static void RAMFUNCTION el2_to_el1_boot(uintptr_t entry_point, uintptr_t dts_addr)
{
/* 1. Configure timer access for EL1 */
asm volatile(
"mrs x0, cnthctl_el2\n\t"
"orr x0, x0, #3\n\t" /* EL1PCEN | EL1PCTEN - enable EL1 timer access */
"msr cnthctl_el2, x0\n\t"
"msr cntvoff_el2, xzr" /* Clear virtual timer offset */
::: "x0"
);
/* 2. Configure virtual processor ID */
asm volatile(
"mrs x0, midr_el1\n\t"
"msr vpidr_el2, x0\n\t"
"mrs x0, mpidr_el1\n\t"
"msr vmpidr_el2, x0"
::: "x0"
);
/* 3. Disable coprocessor traps to EL2 */
asm volatile(
"mov x0, #0x33ff\n\t" /* CPTR_EL2: RES1 bits, no traps */
"msr cptr_el2, x0\n\t"
"msr hstr_el2, xzr\n\t" /* No traps to EL2 on system registers */
"mov x0, #(3 << 20)\n\t" /* CPACR_EL1: Full FP/SIMD access */
"msr cpacr_el1, x0"
::: "x0"
);
/* 4. Initialize SCTLR_EL1 with safe defaults (RES1 bits, MMU/cache off) */
asm volatile(
"ldr x0, =0x30d00800\n\t" /* RES1 bits: 29,28,23,22,20,11 */
"msr sctlr_el1, x0"
::: "x0"
);
/* 5. Migrate stack pointer and vector base to EL1 */
asm volatile(
"mov x0, sp\n\t"
"msr sp_el1, x0\n\t"
"mrs x0, vbar_el2\n\t"
"msr vbar_el1, x0"
::: "x0"
);
/* 6. Configure HCR_EL2 - EL1 is AArch64, no hypervisor calls */
asm volatile(
"mov x0, #(1 << 31)\n\t" /* RW: EL1 is AArch64 */
"orr x0, x0, #(1 << 29)\n\t" /* HCD: Disable HVC instruction */
"msr hcr_el2, x0"
::: "x0"
);
/* 7. Set up SPSR_EL2 for return to EL1h with all interrupts masked */
asm volatile(
"mov x0, #0x3c4\n\t" /* DAIF masked (0xF<<6) + M[4:0]=0b00100 (EL1h) */
"msr spsr_el2, x0"
::: "x0"
);
/* 8. Set exception return address and DTB pointer, then ERET */
asm volatile(
"msr elr_el2, %0\n\t" /* Entry point in ELR_EL2 */
"mov x0, %1\n\t" /* DTB address in x0 (first arg) */
"mov x1, xzr\n\t" /* Zero remaining argument registers */
"mov x2, xzr\n\t"
"mov x3, xzr\n\t"
"eret"
:
: "r"(entry_point), "r"(dts_addr)
: "x0", "x1", "x2", "x3"
);
/* Should never reach here */
__builtin_unreachable();
}
extern void el2_to_el1_boot(uintptr_t entry_point, uintptr_t dts_addr);
#endif /* BOOT_EL1 && EL2_HYPERVISOR */
void boot_entry_C(void)

View File

@ -198,8 +198,11 @@ InitEL3:
orr w1, w1, #(1 << 1) /* Set IRQ bit (IRQs routed to EL3) */
msr SCR_EL3, x1
/* Configure cpu auxiliary control register EL1 */
ldr x0,=0x80CA000 /* L1 Data prefetch control - 5, Enable device split throttle, 2 independent data prefetch streams */
/* Configure CPUACTLR_EL1 - read-modify-write to preserve BL31 workarounds:
* 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
@ -1231,4 +1234,101 @@ gicv2_init_secure:
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 */
.end

View File

@ -32,7 +32,6 @@ void main(void)
{
uint32_t boot_version, update_version;
/* Initialize HAL (UART, etc.) */
hal_init();
/* Get versions from both partitions */
@ -44,9 +43,11 @@ void main(void)
wolfBoot_printf(" wolfBoot Test Application - AMD Versal\n");
wolfBoot_printf("===========================================\n\n");
wolfBoot_printf("Current EL: %d\n", current_el());
/* Print firmware versions */
wolfBoot_printf("Boot Partition Version: %d (0x%08x)\n", boot_version, boot_version);
wolfBoot_printf("Update Partition Version: %d (0x%08x)\n", update_version, update_version);
wolfBoot_printf("BOOT: Version: %d (0x%08x)\n", boot_version, boot_version);
wolfBoot_printf("UPDATE: Version: %d (0x%08x)\n", update_version, update_version);
wolfBoot_printf("Application running successfully!\n");
wolfBoot_printf("\nEntering idle loop...\n");

View File

@ -331,7 +331,10 @@ esac
# Build wolfBoot
log_info "Building wolfBoot..."
cp config/examples/versal_vmk180.config .config
make clean && make
make clean
make || { log_error "Failed to build wolfBoot"; exit 1; }
[ ! -f "wolfboot.elf" ] && { log_error "wolfboot.elf not found after build"; exit 1; }
# Build test app if requested
if [ "$FLASH_TEST_APP" = "true" ]; then
@ -396,6 +399,7 @@ fi
# Generate BOOT.BIN
log_info "Generating BOOT.BIN..."
[ ! -f "wolfboot.elf" ] && { log_error "wolfboot.elf not found - cannot generate BOOT.BIN"; exit 1; }
# Set PREBUILT_DIR (relative to wolfBoot root)
export PREBUILT_DIR="${WOLFBOOT_ROOT}/../soc-prebuilt-firmware/vmk180-versal"
@ -414,16 +418,12 @@ cp "${PREBUILT_DIR}/psmfw.elf" .
cp "${PREBUILT_DIR}/bl31.elf" .
cp "${PREBUILT_DIR}/system-default.dtb" .
# Generate BOOT.BIN from wolfBoot root directory
source "${VITIS_PATH}/settings64.sh"
bootgen -arch versal -image ./tools/scripts/versal_boot.bif -w -o BOOT.BIN
# Copy BOOT.BIN to TFTP directory
cp BOOT.BIN "${TFTP_DIR}/"
rm -f BOOT.BIN
bootgen -arch versal -image ./tools/scripts/versal_boot.bif -w -o BOOT.BIN || { log_error "bootgen failed"; exit 1; }
cp BOOT.BIN "${TFTP_DIR}/" || { log_error "Failed to copy BOOT.BIN to TFTP directory"; exit 1; }
filesize=$(stat -c%s "${TFTP_DIR}/BOOT.BIN")
filesize_hex=$(printf "0x%x" $filesize)
log_info "BOOT.BIN size: $filesize bytes"
# Get test app size if flashing it
testapp_size_hex="0x0"