mirror of https://github.com/wolfSSL/wolfBoot.git
Fixes for getting the "test-app" booting
parent
0fac0c5fe2
commit
dad2888450
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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...
|
||||
```
|
||||
|
||||
|
||||
|
|
|
|||
105
hal/versal.c
105
hal/versal.c
|
|
@ -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)
|
||||
|
|
|
|||
15
hal/versal.h
15
hal/versal.h
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
@ -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..."
|
||||
|
|
|
|||
Loading…
Reference in New Issue