Fixes for getting the "test-app" booting

pull/679/head
David Garske 2026-01-15 16:28:04 -08:00 committed by Daniele Lacamera
parent 0fac0c5fe2
commit dad2888450
14 changed files with 539 additions and 113 deletions

View File

@ -27,6 +27,10 @@ DEBUG?=1
DEBUG_SYMBOLS=1
DEBUG_UART=1
# Boot Benchmarking (optional):
# Enables timing of boot operations (flash read, integrity, signature).
BOOT_BENCHMARK?=1
VTOR?=1
CORTEX_M0?=0
NO_ASM?=0

View File

@ -1981,7 +1981,7 @@ qemu-system-aarch64 -machine xlnx-zcu102 -cpu cortex-a53 -serial stdio -display
## Versal Gen 1 VMK180
AMD Versal Prime VM1802 ACAP - Dual ARM Cortex-A72 (VMK180 Evaluation Board)
AMD Versal Prime Series VMK180 Evaluation Kit - Versal Prime XCVM1802-2MSEVSVA2197 Adaptive SoC - Dual ARM Cortex-A72
wolfBoot replaces U-Boot in the Versal boot flow:
```
@ -2080,6 +2080,29 @@ Flash `BOOT.BIN` to QSPI flash using one of the following methods:
sf write ${loadaddr} 0 ${filesize}
```
### QSPI Flash
VMK180 uses dual parallel MT25QU01GBBB flash (128MB each, 256MB total). The QSPI driver supports:
- DMA mode (default) or IO polling mode (`GQSPI_MODE_IO`)
- Quad SPI (4-bit) for faster reads
- 4-byte addressing for full flash access
- Hardware striping for dual parallel operation
- 75MHz default clock (configurable via `GQSPI_CLK_DIV`)
### Building and Signing Test Application
```sh
# Build and sign the test application
make test-app/image.bin
make test-app/image_v1_signed.bin
```
The signed test application will be at `test-app/image_v1_signed.bin`.
### Flashing Test Application
After flashing `BOOT.BIN` to QSPI offset 0x0, flash the signed test app to the boot partition at offset `0x800000` using your preferred method.
### Example Boot Output
```
@ -2088,11 +2111,30 @@ wolfBoot Secure Boot - AMD Versal
========================================
Current EL: 2
Timer Freq: 99999904 Hz
ext_flash_read: STUB
ext_flash_read: STUB
Versions: Boot 0, Update 0
No valid image found!
wolfBoot: PANIC!
QSPI: Lower ID: 20 BB 21
QSPI: Upper ID: 20 BB 21
QSPI: 75MHz, Quad mode, DMA
Versions: Boot 1, Update 0
Trying Boot partition at 0x800000
Loading header 512 bytes from 0x800000 to 0xFFFFE00
Loading image 664 bytes from 0x800200 to 0x10000000...done
Boot partition: 0xFFFFE00 (sz 664, ver 0x1, type 0x601)
Checking integrity...done
Verifying signature...done
Successfully selected image in part: 0
Firmware Valid
Loading elf at 0x10000000
Invalid elf, falling back to raw binary
Loading DTB (size 24894) from 0x1000 to RAM at 0x1000
Booting at 0x10000000
===========================================
wolfBoot Test Application - AMD Versal
===========================================
Application running successfully!
Entering idle loop...
```

View File

@ -272,6 +272,21 @@ void hal_delay_us(uint32_t us)
;
}
/**
* Get current time in microseconds (for benchmarking)
*/
uint64_t hal_get_timer_us(void)
{
uint64_t cntpct = timer_get_count();
uint64_t cntfrq = timer_get_freq();
if (cntfrq == 0)
cntfrq = TIMER_CLK_FREQ;
/* Convert to microseconds: (count * 1000000) / freq */
return (cntpct * 1000000ULL) / cntfrq;
}
/* ============================================================================
* QSPI Flash Driver (GQSPI)
@ -473,7 +488,9 @@ static void flush_dcache_range(uintptr_t start, uintptr_t end)
__asm__ volatile("dsb sy" : : : "memory");
}
/* Wait for DMA completion */
/* Wait for DMA completion
* Returns: 0 on success, -1 on timeout
*/
static int qspi_dma_wait(void)
{
uint32_t timeout = GQSPIDMA_TIMEOUT_TRIES;
@ -483,6 +500,8 @@ static int qspi_dma_wait(void)
if (timeout == 0) {
QSPI_DEBUG_PRINTF("QSPI: DMA timeout\n");
/* Clear any pending interrupts */
GQSPIDMA_ISR = GQSPIDMA_ISR_ALL_MASK;
return -1;
}
@ -782,10 +801,12 @@ static int qspi_transfer_qread_dma(QspiDev_t *dev, const uint8_t *cmd, uint32_t
/* DMA RX Phase */
if (ret == 0 && rxLen > 0) {
uint32_t remaining = rxLen;
uint32_t remaining;
uint32_t xferSz;
/* Check alignment - DMA requires cache-line aligned buffer */
/* Check alignment - DMA requires cache-line aligned buffer.
* If unaligned or not a multiple of 4 bytes, use temp buffer.
* CRITICAL: GenFIFO transfer size must match DMA size! */
if (((uintptr_t)rxData & (GQSPI_DMA_ALIGN - 1)) || (rxLen & 3)) {
/* Use temp buffer for unaligned data */
dmaPtr = dma_tmpbuf;
@ -799,6 +820,9 @@ static int qspi_transfer_qread_dma(QspiDev_t *dev, const uint8_t *cmd, uint32_t
dmaLen = rxLen;
}
/* GenFIFO must request the same number of bytes as DMA expects */
remaining = dmaLen;
/* Setup DMA destination */
GQSPIDMA_DST = ((uintptr_t)dmaPtr & 0xFFFFFFFFUL);
GQSPIDMA_DST_MSB = ((uintptr_t)dmaPtr >> 32);
@ -839,7 +863,7 @@ static int qspi_transfer_qread_dma(QspiDev_t *dev, const uint8_t *cmd, uint32_t
/* Invalidate cache after DMA */
flush_dcache_range((uintptr_t)dmaPtr, (uintptr_t)dmaPtr + dmaLen);
/* Copy from temp buffer if needed */
/* Copy from temp buffer if needed (only copy requested bytes) */
if (ret == 0 && useTemp) {
memcpy(rxData, dmaPtr, rxLen);
}
@ -1224,11 +1248,12 @@ static void qspi_init(void)
GQSPI_ISR = GQSPI_IXR_ALL_MASK;
dsb();
/* Preserve PLM's CFG but switch to IO mode for our transfers
/* Preserve PLM's CFG but set IO mode for initial commands (ID read, etc.)
* PLM: 0xA0080010 = DMA mode | manual start | WP_HOLD | CLK_POL
* Key: Keep manual start mode (bit 29) and clock settings */
* Key: Keep manual start mode (bit 29) and clock settings
* Note: qspi_transfer_qread_dma() will switch to DMA mode for reads */
cfg = (cfg & ~GQSPI_CFG_MODE_EN_MASK); /* Clear mode bits */
cfg |= GQSPI_CFG_MODE_EN_IO; /* Set IO mode */
cfg |= GQSPI_CFG_MODE_EN_IO; /* Set IO mode for init */
GQSPI_CFG = cfg;
dsb();
@ -1237,6 +1262,18 @@ static void qspi_init(void)
GQSPI_RX_THRESH = 1;
GQSPI_GF_THRESH = 16;
#ifndef GQSPI_MODE_IO
/* Initialize DMA controller - this was missing compared to zynq.c!
* Without this, DMA transfers can hang or timeout because the DMA
* controller is in an undefined state after PLM handoff.
*/
GQSPIDMA_CTRL = GQSPIDMA_CTRL_DEF;
GQSPIDMA_CTRL2 = GQSPIDMA_CTRL2_DEF;
GQSPIDMA_ISR = GQSPIDMA_ISR_ALL_MASK; /* Clear all pending interrupts */
GQSPIDMA_IER = GQSPIDMA_ISR_ALL_MASK; /* Enable all interrupts */
dsb();
#endif
QSPI_DEBUG_PRINTF("QSPI: After config - CFG=0x%08x\n", GQSPI_CFG);
/* Configure device for single flash (lower) first */
@ -1353,9 +1390,35 @@ void hal_prepare_boot(void)
}
#endif
/* Memory barriers before jumping to application */
dsb();
isb();
/* Clean and invalidate caches for the loaded application.
* The application was written to RAM via D-cache, but the CPU will
* fetch instructions via I-cache from main memory. We must:
* 1. Clean D-cache (flush dirty data to memory)
* 2. Invalidate I-cache (ensure fresh instruction fetch)
*/
/* Clean entire D-cache to Point of Coherency */
__asm__ volatile("dsb sy");
/* Clean D-cache for application region (0x10000000, 1MB should be enough) */
{
uintptr_t addr;
uintptr_t end = 0x10000000 + (1 * 1024 * 1024);
for (addr = 0x10000000; addr < end; addr += 64) {
/* DC CVAC - Clean data cache line by VA to PoC */
__asm__ volatile("dc cvac, %0" : : "r"(addr));
}
}
/* Data synchronization barrier - ensure clean completes */
__asm__ volatile("dsb sy");
/* Invalidate instruction cache to ensure fresh code is fetched */
__asm__ volatile("ic iallu");
/* Ensure cache invalidation completes before jumping */
__asm__ volatile("dsb sy");
__asm__ volatile("isb");
}
#ifdef MMU
@ -1504,9 +1567,14 @@ int ext_flash_read(uintptr_t address, uint8_t *data, int len)
return -1;
}
QSPI_DEBUG_PRINTF("ext_flash_read: addr=0x%lx len=%d\n",
(unsigned long)address, len);
if (qspiDev.stripe) {
/* For dual parallel the address is divided by 2 */
addr /= 2;
QSPI_DEBUG_PRINTF(" stripe mode: flash_addr=0x%lx\n",
(unsigned long)addr);
}
/* Use Quad Read command (0x6C) with 4-byte address */
@ -1523,7 +1591,22 @@ int ext_flash_read(uintptr_t address, uint8_t *data, int len)
ret = qspi_transfer_qread_dma(&qspiDev, cmd, 5, data, len, GQSPI_DUMMY_READ);
#endif
return ret;
/* On DMA timeout, fill buffer with 0xFF to simulate unwritten flash.
* This handles reads to partition trailer areas that haven't been written.
* wolfBoot will see 0xFF (not magic) and handle appropriately. */
if (ret != 0) {
memset(data, 0xFF, len);
}
QSPI_DEBUG_PRINTF("ext_flash_read: ret=%d data[0-7]=%02x %02x %02x %02x %02x %02x %02x %02x\n",
ret,
len > 0 ? data[0] : 0, len > 1 ? data[1] : 0,
len > 2 ? data[2] : 0, len > 3 ? data[3] : 0,
len > 4 ? data[4] : 0, len > 5 ? data[5] : 0,
len > 6 ? data[6] : 0, len > 7 ? data[7] : 0);
/* Return bytes read on success (like zynq.c) */
return (ret == 0) ? len : ret;
}
int ext_flash_erase(uintptr_t address, int len)

View File

@ -61,6 +61,21 @@
#endif /* USE_BUILTIN_STARTUP */
/* ============================================================================
* Versal-specific Boot Defaults
* ============================================================================
* SKIP_GIC_INIT: Versal uses GICv3 (not GICv2 like ZynqMP).
* BL31 handles GIC initialization, so skip gicv2_init_secure().
* BOOT_EL1: wolfBoot runs at EL2, but applications (Linux, test-app)
* expect EL1. Transition from EL2 to EL1 before jumping to app.
*/
#ifndef SKIP_GIC_INIT
#define SKIP_GIC_INIT 1
#endif
#ifndef BOOT_EL1
#define BOOT_EL1 1
#endif
/* ============================================================================
* Memory Map

View File

@ -53,8 +53,8 @@ void hal_deinit();
void hal_init(void);
#ifdef WOLFBOOT_UPDATE_DISK
/* Timer functions (platform-specific) */
/* Timer functions (platform-specific, used for benchmarking) */
#if defined(WOLFBOOT_UPDATE_DISK) || defined(BOOT_BENCHMARK)
uint64_t hal_get_timer_us(void);
#endif

View File

@ -604,6 +604,15 @@ endif
ifeq ($(NO_QNX),1)
CFLAGS+=-D"NO_QNX"
endif
ifeq ($(SKIP_GIC_INIT),1)
CFLAGS+=-D"SKIP_GIC_INIT"
endif
ifeq ($(BOOT_EL1),1)
CFLAGS+=-D"BOOT_EL1"
endif
ifeq ($(BOOT_BENCHMARK),1)
CFLAGS+=-D"BOOT_BENCHMARK"
endif
ifeq ($(ALLOW_DOWNGRADE),1)
CFLAGS+= -D"ALLOW_DOWNGRADE"

View File

@ -37,6 +37,16 @@ extern unsigned int _end_data;
extern void main(void);
extern void gicv2_init_secure(void);
/* SKIP_GIC_INIT - Skip GIC initialization before booting app
* This is needed for:
* - Versal: Uses GICv3, not GICv2. BL31 handles GIC setup.
* - Systems where another bootloader stage handles GIC init
* NO_QNX also implies SKIP_GIC_INIT for backwards compatibility
*/
#if defined(NO_QNX) && !defined(SKIP_GIC_INIT)
#define SKIP_GIC_INIT
#endif
unsigned int current_el(void)
{
unsigned long el;
@ -44,6 +54,97 @@ unsigned int current_el(void)
return (unsigned int)((el >> 2) & 0x3U);
}
#if defined(BOOT_EL1) && defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
/**
* @brief Transition from EL2 to EL1 and jump 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.
*
* @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();
}
#endif /* BOOT_EL1 && EL2_HYPERVISOR */
void boot_entry_C(void)
{
register unsigned int *dst, *src;
@ -101,6 +202,32 @@ void RAMFUNCTION do_boot(const uint32_t *app_offset)
hal_dts_fixup((uint32_t*)dts_offset);
#endif
#ifndef SKIP_GIC_INIT
/* Initialize GICv2 for Kernel (ZynqMP and similar platforms)
* Skip this for:
* - Versal (uses GICv3, handled by BL31)
* - Platforms where BL31 or another stage handles GIC
*/
gicv2_init_secure();
#endif
#if defined(BOOT_EL1) && defined(EL2_HYPERVISOR) && EL2_HYPERVISOR == 1
/* Transition from EL2 to EL1 before jumping to application.
* This is needed when:
* - Application expects to run at EL1 (e.g., Linux kernel)
* - wolfBoot runs at EL2 (hypervisor mode)
*/
{
#ifdef MMU
uintptr_t dts = (uintptr_t)dts_offset;
#else
uintptr_t dts = 0;
#endif
el2_to_el1_boot((uintptr_t)app_offset, dts);
}
#else
/* Stay at current EL (EL2 or EL3) and jump directly to application */
/* Set application address via x4 */
asm volatile("mov x4, %0" : : "r"(app_offset));
@ -111,11 +238,6 @@ void RAMFUNCTION do_boot(const uint32_t *app_offset)
asm volatile("mov x5, xzr");
#endif
#ifndef NO_QNX
/* Initialize GICv2 for Kernel */
gicv2_init_secure();
#endif
/* Zero registers x1, x2, x3 */
asm volatile("mov x3, xzr");
asm volatile("mov x2, xzr");
@ -126,6 +248,7 @@ void RAMFUNCTION do_boot(const uint32_t *app_offset)
/* Unconditionally jump to app_entry at x4 */
asm volatile("br x4");
#endif /* BOOT_EL1 */
}
#ifdef RAM_CODE

View File

@ -29,6 +29,7 @@
#include "printf.h"
#include "wolfboot/wolfboot.h"
#include <string.h>
#ifdef WOLFBOOT_TPM
#include "tpm.h"
#endif
@ -42,6 +43,19 @@ extern int wolfBoot_get_dts_size(void *dts_addr);
extern uint32_t kernel_load_addr;
extern uint32_t dts_load_addr;
#ifdef BOOT_BENCHMARK
/* Timing variable for benchmarking - placed at function scope */
static uint64_t _boot_bench_start;
#define BENCHMARK_START() _boot_bench_start = hal_get_timer_us()
#define BENCHMARK_END(msg) do { \
uint64_t _elapsed_ms = (hal_get_timer_us() - _boot_bench_start) / 1000;\
wolfBoot_printf(msg " (%lu ms)\n", (unsigned long)_elapsed_ms); \
} while(0)
#else
#define BENCHMARK_START() do {} while(0)
#define BENCHMARK_END(msg) wolfBoot_printf(msg "\n")
#endif
#if ((defined(EXT_FLASH) && defined(NO_XIP)) || \
(defined(EXT_ENCRYPTED) && defined(MMU))) && \
!defined(WOLFBOOT_NO_RAMBOOT)
@ -82,8 +96,9 @@ int wolfBoot_ramboot(struct wolfBoot_image *img, uint8_t *src, uint8_t *dst)
img_size = wolfBoot_image_size((uint8_t*)dst);
/* Read the entire image into RAM */
wolfBoot_printf("Loading image %d bytes from %p to %p\n",
wolfBoot_printf("Loading image %d bytes from %p to %p...",
img_size, src + IMAGE_HEADER_SIZE, dst + IMAGE_HEADER_SIZE);
BENCHMARK_START();
#if defined(EXT_FLASH) && defined(NO_XIP)
ret = ext_flash_read((uintptr_t)src + IMAGE_HEADER_SIZE,
dst + IMAGE_HEADER_SIZE, img_size);
@ -94,6 +109,7 @@ int wolfBoot_ramboot(struct wolfBoot_image *img, uint8_t *src, uint8_t *dst)
#else
memcpy(dst + IMAGE_HEADER_SIZE, src + IMAGE_HEADER_SIZE, img_size);
#endif
BENCHMARK_END("done");
/* mark image as no longer external */
img->not_ext = 1;
@ -168,12 +184,31 @@ void RAMFUNCTION wolfBoot_start(void)
#else
ret = wolfBoot_open_image(&os_image, active);
#endif
if ( (ret < 0) ||
((ret = wolfBoot_verify_integrity(&os_image) < 0)) ||
((ret = wolfBoot_verify_authenticity(&os_image)) < 0)) {
if (ret < 0) {
goto backup_on_failure;
}
} else {
/* Verify image integrity (hash check) */
wolfBoot_printf("Checking integrity...");
BENCHMARK_START();
ret = wolfBoot_verify_integrity(&os_image);
if (ret < 0) {
wolfBoot_printf("FAILED\n");
goto backup_on_failure;
}
BENCHMARK_END("done");
/* Verify image authenticity (signature check) */
wolfBoot_printf("Verifying signature...");
BENCHMARK_START();
ret = wolfBoot_verify_authenticity(&os_image);
if (ret < 0) {
wolfBoot_printf("FAILED\n");
goto backup_on_failure;
}
BENCHMARK_END("done");
{
/* Success - integrity and signature valid */
#if !defined(WOLFBOOT_NO_LOAD_ADDRESS) && defined(WOLFBOOT_LOAD_ADDRESS)
load_address = (uint32_t*)WOLFBOOT_LOAD_ADDRESS;

View File

@ -1,42 +1,42 @@
/* Memory region - address comes from WOLFBOOT_LOAD_ADDRESS, size from WOLFBOOT_TEST_APP_SIZE */
MEMORY
{
DDR_MEM(rx) : ORIGIN = 0xa0000, LENGTH = 0x80000000 - 0xa0000
FLASH (rx) : ORIGIN = @WOLFBOOT_LOAD_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@
}
ENTRY(main);
/* Use _start if boot_arm64_start.S is linked, otherwise fall back to main */
ENTRY(_start);
SECTIONS
{
.text :
{
_start_text = .;
KEEP(*(.boot*))
KEEP(*(.text.startup)) /* Startup code (_start) if boot_arm64_start.S is used */
KEEP(*(.boot*)) /* Legacy boot section for compatibility */
*(.text*)
*(.rodata*)
*(.note.*)
. = ALIGN(4);
_end_text = .;
} > DDR_MEM
.edidx :
{
. = ALIGN(4);
*(.ARM.exidx*)
} > DDR_MEM
} > FLASH
.edidx :
{
. = ALIGN(4);
*(.ARM.exidx*)
} > DDR_MEM
} > FLASH
.data :
{
PROVIDE(_stored_data = .); /* For boot_arm64_start.S data copying */
_start_data = .;
KEEP(*(.data*))
. = ALIGN(4);
KEEP(*(.ramcode))
. = ALIGN(4);
_end_data = .;
} > DDR_MEM
} > FLASH
.bss (NOLOAD) :
{
@ -48,9 +48,19 @@ SECTIONS
_end_bss = .;
__bss_end__ = .;
_end = .;
} > DDR_MEM
} > FLASH
.stack (NOLOAD) :
{
. = ALIGN(16);
. = . + 0x4000; /* 16KB stack */
_stack = .;
} > FLASH
. = ALIGN(4);
}
END_STACK = _start_text;
/* Stack pointer - use proper stack section if available, otherwise fall back to text start */
PROVIDE(__stack = _stack);
PROVIDE(END_STACK = _start_text); /* Legacy compatibility for platforms that don't use stack section */

View File

@ -133,6 +133,10 @@ ifeq ($(ARCH),RISCV)
APP_OBJS+=startup_riscv.o vector_riscv.o
endif
ifeq ($(ARCH),AARCH64)
APP_OBJS:=boot_arm64_start.o $(APP_OBJS)
endif
ifeq ($(ARCH),RISCV64)
APP_OBJS+=startup_riscv.o vector_riscv.o
endif
@ -507,6 +511,15 @@ ifeq ($(TARGET),nxp_ls1028a)
LSCRIPT_TEMPLATE:=AARCH64-ls1028a.ld
endif
ifeq ($(TARGET),versal)
LSCRIPT_TEMPLATE:=AARCH64.ld
LDFLAGS+=-nostdlib
endif
ifeq ($(TARGET),zynq)
LSCRIPT_TEMPLATE:=AARCH64.ld
endif
ifeq ($(TARGET),pic32ck)
APP_OBJS+=../hal/pic32c.o
endif

View File

@ -24,21 +24,35 @@
#include <stdint.h>
#include "wolfboot/wolfboot.h"
/* UART registers (same as in hal/versal.h) */
/* UART registers for PL011 UART (Versal uses PL011, NOT Cadence UART)
* Register layout from hal/versal.h:
* - Data Register (DR): offset 0x00
* - Flag Register (FR): offset 0x18
* - Control Register (CR): offset 0x30
*/
#define VERSAL_UART0_BASE 0xFF000000UL
#define UART_SR_OFFSET 0x2C
#define UART_FIFO_OFFSET 0x30
#define UART_SR_TXFULL (1UL << 4)
#define UART_SR_TXEMPTY (1UL << 3)
#define UART_DR_OFFSET 0x00 /* Data Register (TX/RX) */
#define UART_FR_OFFSET 0x18 /* Flag Register */
#define UART_FR_TXFF (1UL << 5) /* TX FIFO full */
#define UART_FR_TXFE (1UL << 7) /* TX FIFO empty */
#define UART_SR (*((volatile uint32_t*)(VERSAL_UART0_BASE + UART_SR_OFFSET)))
#define UART_FIFO (*((volatile uint32_t*)(VERSAL_UART0_BASE + UART_FIFO_OFFSET)))
#define UART_DR (*((volatile uint32_t*)(VERSAL_UART0_BASE + UART_DR_OFFSET)))
#define UART_FR (*((volatile uint32_t*)(VERSAL_UART0_BASE + UART_FR_OFFSET)))
/* Get current exception level */
static uint32_t get_current_el(void)
{
uint64_t current_el;
__asm__ volatile("mrs %0, CurrentEL" : "=r" (current_el));
return (uint32_t)((current_el >> 2) & 0x3);
}
static void uart_tx(uint8_t c)
{
while (UART_SR & UART_SR_TXFULL)
/* Wait while TX FIFO is full */
while (UART_FR & UART_FR_TXFF)
;
UART_FIFO = c;
UART_DR = c;
}
static void uart_print(const char *s)
@ -52,17 +66,24 @@ static void uart_print(const char *s)
void main(void)
{
uint32_t el = get_current_el();
uart_print("\n\n");
uart_print("===========================================\n");
uart_print(" wolfBoot Test Application - AMD Versal\n");
uart_print("===========================================\n\n");
/* Print current exception level */
uart_print("Current EL: ");
uart_tx('0' + el);
uart_print("\n");
uart_print("Application running successfully!\n");
uart_print("\nEntering idle loop...\n");
/* Wait for transmit to complete */
while (!(UART_SR & UART_SR_TXEMPTY))
/* Wait for transmit to complete (TX FIFO empty) */
while (!(UART_FR & UART_FR_TXFE))
;
/* Idle loop */

View File

@ -0,0 +1,67 @@
/* boot_arm64_start.S
*
* AArch64 (64-bit ARM) boot startup code for test applications
*
* Copyright (C) 2025 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
*/
.section .text.startup, "ax"
.global _start
.type _start, @function
_start:
/* Set up stack pointer */
ldr x0, =__stack
mov sp, x0
/* Copy data section from flash to RAM if needed */
ldr x0, =_stored_data
ldr x1, =_start_data
ldr x2, =_end_data
cmp x1, x2
b.ge 2f
cmp x0, x1
b.eq 2f /* Skip if data already in place */
1:
ldr x3, [x0], #8
str x3, [x1], #8
cmp x1, x2
b.lt 1b
2:
/* Clear BSS */
ldr x0, =__bss_start__
ldr x1, =__bss_end__
cmp x0, x1
b.ge 4f
3:
str xzr, [x0], #8
cmp x0, x1
b.lt 3b
4:
/* Jump to main - never returns */
bl main
/* If main returns, loop forever */
5:
wfi
b 5b
.size _start, . - _start

View File

@ -1,57 +0,0 @@
/* versal.ld - Test application linker script for AMD Versal
*
* Copyright (C) 2025 wolfSSL Inc.
*
* This application is loaded by wolfBoot to WOLFBOOT_LOAD_ADDRESS
*/
OUTPUT_FORMAT("elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_start)
/* Memory regions - application loaded by wolfBoot */
MEMORY
{
/* Application loaded at WOLFBOOT_LOAD_ADDRESS */
RAM (rwx) : ORIGIN = 0x10000000, LENGTH = 64M
}
SECTIONS
{
.text : {
_start_text = .;
KEEP(*(.text._start))
*(.text*)
*(.rodata*)
. = ALIGN(8);
_end_text = .;
} > RAM
.data : {
. = ALIGN(8);
_start_data = .;
*(.data*)
. = ALIGN(8);
_end_data = .;
} > RAM
.bss (NOLOAD) : {
. = ALIGN(8);
__bss_start__ = .;
*(.bss*)
*(COMMON)
. = ALIGN(8);
__bss_end__ = .;
} > RAM
.stack (NOLOAD) : {
. = ALIGN(16);
. = . + 0x4000;
_stack = .;
} > RAM
_end = .;
}
PROVIDE(__stack = _stack);

View File

@ -2,7 +2,8 @@
# Build, flash QSPI, and boot VMK180 - all in one script
#
# Usage:
# ./build_flash_qspi.sh # Full build, flash, and boot
# ./build_flash_qspi.sh # Full build, flash, and boot wolfBoot
# ./build_flash_qspi.sh --test-app # Full build + flash test app to boot partition
# ./build_flash_qspi.sh --boot-sdcard # Test SD card boot mode only
# ./build_flash_qspi.sh --boot-qspi # Test QSPI boot mode only
#
@ -234,9 +235,11 @@ test_boot() {
}
# Check for test modes
FLASH_TEST_APP=false
case "${1:-}" in
test-boot|--boot-sdcard) test_boot boot_sdcard "boot-sdcard" ;;
--boot-qspi) test_boot boot_qspi "boot-qspi" ;;
--test-app) FLASH_TEST_APP=true ;;
esac
# Build wolfBoot
@ -244,6 +247,20 @@ log_info "Building wolfBoot..."
cp config/examples/versal_vmk180.config .config
make clean && make
# Build test app if requested
if [ "$FLASH_TEST_APP" = "true" ]; then
log_info "Building and signing test application..."
make test-app/image.bin
make test-app/image_v1_signed.bin
testapp_size=$(stat -c%s "test-app/image_v1_signed.bin")
log_info "Test app size: $testapp_size bytes"
# Copy test app to TFTP directory
cp test-app/image_v1_signed.bin "${TFTP_DIR}/"
log_ok "Test app copied to TFTP: ${TFTP_DIR}/image_v1_signed.bin"
fi
# Generate BOOT.BIN
log_info "Generating BOOT.BIN..."
@ -275,6 +292,14 @@ 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"
if [ "$FLASH_TEST_APP" = "true" ]; then
testapp_size=$(stat -c%s "${TFTP_DIR}/image_v1_signed.bin")
testapp_size_hex=$(printf "0x%x" $testapp_size)
log_info "Test app size: $testapp_size bytes"
fi
# Flash QSPI via U-Boot TFTP
log_info "Flashing QSPI..."
boot_sdcard
@ -283,6 +308,8 @@ expect <<EXPECT_EOF
set timeout 90
set pty "$UART_PTY"
set filesize_hex "$filesize_hex"
set testapp_size_hex "$testapp_size_hex"
set flash_test_app "$FLASH_TEST_APP"
set board_ip "$BOARD_IP"
set server_ip "$SERVER_IP"
@ -421,11 +448,45 @@ expect {
}
set timeout 90
puts "QSPI flash and verification complete!"
puts "BOOT.BIN flash and verification complete!"
# Flash test app if requested
if { \$flash_test_app eq "true" } {
puts ""
puts "=== Flashing test app to boot partition at 0x800000 ==="
puts "Downloading test app via TFTP..."
send "tftpboot 0x10000000 image_v1_signed.bin\r"
expect {
"Bytes transferred" { puts "TFTP download successful" }
"Error" { puts "TFTP download failed"; exit 1 }
timeout { puts "TFTP timeout"; exit 1 }
}
expect "Versal>"
puts "Erasing boot partition at 0x800000 (128KB sector)..."
send "sf erase 0x800000 0x20000\r"
expect {
"Versal>" { puts "Erase complete" }
timeout { puts "Erase timeout"; exit 1 }
}
puts "Writing test app to 0x800000..."
send "sf write 0x10000000 0x800000 \$testapp_size_hex\r"
expect {
"Versal>" { puts "Write complete" }
timeout { puts "Write timeout"; exit 1 }
}
puts "Test app flashed to boot partition!"
}
puts ""
puts "All flash operations complete!"
close
EXPECT_EOF
log_ok "Flash and verification complete!"
log_ok "Flash operations complete!"
# Restart continuous UART logging after expect exits
log_info "Restarting continuous UART logging..."