mirror of https://github.com/wolfSSL/wolfBoot.git
Versal port cleanups
parent
dad2888450
commit
ee3c313a66
|
|
@ -572,6 +572,12 @@ jobs:
|
|||
arch: aarch64
|
||||
config-file: ./config/examples/zynqmp.config
|
||||
|
||||
versal_vmk180_test:
|
||||
uses: ./.github/workflows/test-build-aarch64.yml
|
||||
with:
|
||||
arch: aarch64
|
||||
config-file: ./config/examples/versal_vmk180.config
|
||||
|
||||
sim_wolfhsm_test:
|
||||
uses: ./.github/workflows/test-build.yml
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -2041,7 +2041,7 @@ make
|
|||
If you don't already have prebuilt firmware, clone the Xilinx prebuilt firmware repository:
|
||||
|
||||
```sh
|
||||
git clone --branch xlnx_rel_v2024.1 https://github.com/Xilinx/soc-prebuilt-firmware.git
|
||||
git clone --branch xlnx_rel_v2024.2 https://github.com/Xilinx/soc-prebuilt-firmware.git
|
||||
export PREBUILT_DIR=$(pwd)/../soc-prebuilt-firmware/vmk180-versal
|
||||
```
|
||||
|
||||
|
|
@ -2066,7 +2066,7 @@ The BIF file (`boot_wolfboot.bif`) references files using relative paths in the
|
|||
|
||||
### Flashing QSPI
|
||||
|
||||
Flash `BOOT.BIN` to QSPI flash using one of the following methods:
|
||||
Flash `BOOT.BIN` to QSPI flash using your preferred method. For example:
|
||||
|
||||
- **Vitis**: Use the Hardware Manager to program the QSPI flash via JTAG. Load `BOOT.BIN` and program to QSPI32 flash memory.
|
||||
|
||||
|
|
@ -2093,15 +2093,33 @@ VMK180 uses dual parallel MT25QU01GBBB flash (128MB each, 256MB total). The QSPI
|
|||
|
||||
```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
|
||||
**Test Application Details:**
|
||||
- Uses generic `boot_arm64_start.S` startup code (shared with other AArch64 platforms)
|
||||
- Uses generic `AARCH64.ld` linker script with `@WOLFBOOT_LOAD_ADDRESS@` placeholder
|
||||
- Displays current exception level (EL) and firmware version
|
||||
- Entry point: `_start` (in `boot_arm64_start.S`) which sets up stack, clears BSS, and calls `main()`
|
||||
|
||||
After flashing `BOOT.BIN` to QSPI offset 0x0, flash the signed test app to the boot partition at offset `0x800000` using your preferred method.
|
||||
### Firmware Update Testing
|
||||
|
||||
wolfBoot supports firmware updates using the UPDATE partition. The bootloader automatically selects the image with the higher version number from either the BOOT or UPDATE partition.
|
||||
|
||||
**Partition Layout:**
|
||||
- BOOT partition: `0x800000`
|
||||
- UPDATE partition: `0x3400000`
|
||||
- For RAM-based boot (Versal), images are loaded to `WOLFBOOT_LOAD_ADDRESS` (`0x10000000`)
|
||||
|
||||
**Update Behavior:**
|
||||
- wolfBoot checks both BOOT and UPDATE partitions on boot
|
||||
- Selects the partition with the higher version number
|
||||
- Falls back to the other partition if verification fails
|
||||
- The test application displays the firmware version it was signed with
|
||||
|
||||
To test firmware updates, build and sign the test application with different version numbers, then flash them to the appropriate partitions using your preferred method.
|
||||
|
||||
### Example Boot Output
|
||||
|
||||
|
|
@ -2131,7 +2149,8 @@ Booting at 0x10000000
|
|||
===========================================
|
||||
wolfBoot Test Application - AMD Versal
|
||||
===========================================
|
||||
|
||||
Current EL: 1
|
||||
Firmware Version: 2 (0x00000002)
|
||||
Application running successfully!
|
||||
|
||||
Entering idle loop...
|
||||
|
|
|
|||
627
hal/versal.c
627
hal/versal.c
|
|
@ -338,7 +338,8 @@ static int qspi_initialized = 0;
|
|||
|
||||
/* Forward declarations */
|
||||
static int qspi_transfer(QspiDev_t *dev, const uint8_t *txData, uint32_t txLen,
|
||||
uint8_t *rxData, uint32_t rxLen, uint32_t dummyClocks);
|
||||
uint8_t *rxData, uint32_t rxLen, uint32_t dummyClocks,
|
||||
const uint8_t *writeData, uint32_t writeLen);
|
||||
static int qspi_wait_ready(QspiDev_t *dev);
|
||||
|
||||
/* Wait for GenFIFO empty (all entries processed) with timeout */
|
||||
|
|
@ -573,13 +574,25 @@ static int qspi_fifo_rx(uint8_t *data, uint32_t len)
|
|||
|
||||
/* Core QSPI transfer function using GenFIFO */
|
||||
static int qspi_transfer(QspiDev_t *dev, const uint8_t *txData, uint32_t txLen,
|
||||
uint8_t *rxData, uint32_t rxLen, uint32_t dummyClocks)
|
||||
uint8_t *rxData, uint32_t rxLen, uint32_t dummyClocks,
|
||||
const uint8_t *writeData, uint32_t writeLen)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t entry;
|
||||
uint32_t i;
|
||||
uint32_t chunkLen;
|
||||
uint32_t txEntry, chunkEntry;
|
||||
const uint8_t *writePtr;
|
||||
uint32_t remaining, offset, xferSz;
|
||||
uint32_t rxEntry;
|
||||
|
||||
/* Enable GQSPI controller */
|
||||
/* Set DMA mode only for Quad reads (when dummyClocks > 0) and not in IO mode */
|
||||
if (dummyClocks > 0 && rxLen > 0) {
|
||||
#ifndef GQSPI_MODE_IO
|
||||
GQSPI_CFG = (GQSPI_CFG & ~GQSPI_CFG_MODE_EN_MASK) | GQSPI_CFG_MODE_EN_DMA;
|
||||
#endif
|
||||
}
|
||||
GQSPI_EN = 1;
|
||||
dsb();
|
||||
|
||||
|
|
@ -605,45 +618,173 @@ static int qspi_transfer(QspiDev_t *dev, const uint8_t *txData, uint32_t txLen,
|
|||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
|
||||
/* Dummy clocks phase (for fast read commands) */
|
||||
/* Dummy clocks phase (for fast read commands)
|
||||
* Use QSPI mode if dummy clocks are present (indicates Quad Read) */
|
||||
if (ret == 0 && dummyClocks > 0) {
|
||||
ret = qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(dummyClocks));
|
||||
uint32_t dummyEntry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_QSPI_MODE |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
GQSPI_GEN_FIFO_IMM(dummyClocks);
|
||||
ret = qspi_gen_fifo_push(dummyEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
}
|
||||
|
||||
/* === RX Phase === */
|
||||
if (dev->stripe) {
|
||||
/* Striped mode: IMM(1) reads 1 byte from each flash = 2 bytes total */
|
||||
for (i = 0; i < rxLen && ret == 0; i += 2) {
|
||||
uint32_t rxEntry = entry | GQSPI_GEN_FIFO_RX |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
GQSPI_GEN_FIFO_STRIPE |
|
||||
GQSPI_GEN_FIFO_IMM(1);
|
||||
/* === TX Write Data Phase === */
|
||||
if (ret == 0 && writeLen > 0 && writeData != NULL) {
|
||||
txEntry = entry | GQSPI_GEN_FIFO_TX | GQSPI_GEN_FIFO_DATA_XFER |
|
||||
(dev->stripe & GQSPI_GEN_FIFO_STRIPE);
|
||||
writePtr = writeData;
|
||||
chunkLen = writeLen;
|
||||
|
||||
ret = qspi_gen_fifo_push(rxEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
if (ret == 0) {
|
||||
/* Read 2 bytes (one from each flash, interleaved) */
|
||||
ret = qspi_fifo_rx(&rxData[i], 2);
|
||||
}
|
||||
while (chunkLen > 0 && ret == 0) {
|
||||
uint32_t chunk = (chunkLen > 255) ? 255 : chunkLen;
|
||||
chunkEntry = txEntry | GQSPI_GEN_FIFO_IMM(chunk);
|
||||
|
||||
ret = qspi_gen_fifo_push(chunkEntry);
|
||||
if (ret != 0) break;
|
||||
|
||||
/* Start GenFIFO processing so it drains TX FIFO as we fill it */
|
||||
GQSPI_CFG |= GQSPI_CFG_START_GEN_FIFO;
|
||||
dsb();
|
||||
|
||||
/* Push data to TX FIFO */
|
||||
ret = qspi_fifo_tx(writePtr, chunk);
|
||||
if (ret != 0) break;
|
||||
|
||||
/* Wait for GenFIFO to complete */
|
||||
ret = qspi_wait_genfifo_empty();
|
||||
|
||||
writePtr += chunk;
|
||||
chunkLen -= chunk;
|
||||
}
|
||||
} else {
|
||||
/* Single flash: read 1 byte at a time */
|
||||
for (i = 0; i < rxLen && ret == 0; i++) {
|
||||
uint32_t rxEntry = entry | GQSPI_GEN_FIFO_RX |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
GQSPI_GEN_FIFO_IMM(1);
|
||||
}
|
||||
|
||||
ret = qspi_gen_fifo_push(rxEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
/* === RX Phase === */
|
||||
if (ret == 0 && rxLen > 0) {
|
||||
/* Use QSPI mode for RX if dummy clocks were used (Quad Read) */
|
||||
if (dummyClocks > 0) {
|
||||
rxEntry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_QSPI_MODE |
|
||||
GQSPI_GEN_FIFO_RX |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
(dev->stripe & GQSPI_GEN_FIFO_STRIPE);
|
||||
|
||||
#ifndef GQSPI_MODE_IO
|
||||
/* DMA mode: Use DMA for RX phase */
|
||||
if ((GQSPI_CFG & GQSPI_CFG_MODE_EN_MASK) == GQSPI_CFG_MODE_EN_DMA) {
|
||||
uint8_t *dmaPtr;
|
||||
uint32_t dmaLen;
|
||||
int useTemp = 0;
|
||||
|
||||
/* 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;
|
||||
dmaLen = (rxLen + GQSPI_DMA_ALIGN - 1) & ~(GQSPI_DMA_ALIGN - 1);
|
||||
if (dmaLen > sizeof(dma_tmpbuf)) {
|
||||
dmaLen = sizeof(dma_tmpbuf);
|
||||
}
|
||||
useTemp = 1;
|
||||
} else {
|
||||
dmaPtr = rxData;
|
||||
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);
|
||||
GQSPIDMA_SIZE = dmaLen;
|
||||
|
||||
/* Enable DMA done interrupt */
|
||||
GQSPIDMA_IER = GQSPIDMA_ISR_DONE;
|
||||
|
||||
/* Flush dcache for DMA coherency */
|
||||
flush_dcache_range((uintptr_t)dmaPtr, (uintptr_t)dmaPtr + dmaLen);
|
||||
|
||||
/* Push all GenFIFO entries first (use EXP mode for large transfers) */
|
||||
while (ret == 0 && remaining > 0) {
|
||||
xferSz = qspi_calc_exp(remaining, &rxEntry);
|
||||
ret = qspi_gen_fifo_push(rxEntry);
|
||||
remaining -= xferSz;
|
||||
}
|
||||
|
||||
/* Trigger GenFIFO */
|
||||
if (ret == 0) {
|
||||
GQSPI_CFG |= GQSPI_CFG_START_GEN_FIFO;
|
||||
dsb();
|
||||
}
|
||||
|
||||
/* Wait for DMA completion */
|
||||
if (ret == 0) {
|
||||
ret = qspi_dma_wait();
|
||||
}
|
||||
|
||||
/* Invalidate cache after DMA */
|
||||
flush_dcache_range((uintptr_t)dmaPtr, (uintptr_t)dmaPtr + dmaLen);
|
||||
|
||||
/* Copy from temp buffer if needed (only copy requested bytes) */
|
||||
if (ret == 0 && useTemp) {
|
||||
memcpy(rxData, dmaPtr, rxLen);
|
||||
}
|
||||
} else {
|
||||
/* IO mode: Use FIFO polling (fallback when DMA mode not enabled) */
|
||||
remaining = rxLen;
|
||||
offset = 0;
|
||||
while (ret == 0 && remaining > 0) {
|
||||
xferSz = qspi_calc_exp(remaining, &rxEntry);
|
||||
ret = qspi_gen_fifo_push(rxEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = qspi_fifo_rx(&rxData[offset], xferSz);
|
||||
}
|
||||
offset += xferSz;
|
||||
remaining -= xferSz;
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = qspi_fifo_rx(&rxData[i], 1);
|
||||
#else /* GQSPI_MODE_IO */
|
||||
/* IO mode: Use FIFO polling */
|
||||
remaining = rxLen;
|
||||
offset = 0;
|
||||
while (ret == 0 && remaining > 0) {
|
||||
xferSz = qspi_calc_exp(remaining, &rxEntry);
|
||||
ret = qspi_gen_fifo_push(rxEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = qspi_fifo_rx(&rxData[offset], xferSz);
|
||||
}
|
||||
offset += xferSz;
|
||||
remaining -= xferSz;
|
||||
}
|
||||
#endif /* !GQSPI_MODE_IO */
|
||||
} else {
|
||||
/* SPI mode for simple reads */
|
||||
rxEntry = entry | GQSPI_GEN_FIFO_RX |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
(dev->stripe & GQSPI_GEN_FIFO_STRIPE) |
|
||||
GQSPI_GEN_FIFO_IMM(1);
|
||||
uint32_t readSz = dev->stripe ? 2 : 1;
|
||||
|
||||
for (i = 0; i < rxLen && ret == 0; i += readSz) {
|
||||
ret = qspi_gen_fifo_push(rxEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = qspi_fifo_rx(&rxData[i], readSz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -654,301 +795,12 @@ static int qspi_transfer(QspiDev_t *dev, const uint8_t *txData, uint32_t txLen,
|
|||
qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(1));
|
||||
qspi_gen_fifo_start_and_wait();
|
||||
|
||||
/* Disable controller */
|
||||
GQSPI_EN = 0;
|
||||
dsb();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* QSPI Read transfer - uses Quad mode (4-bit) for data phase
|
||||
* Command and address are sent in SPI mode, data received in QSPI mode */
|
||||
static int qspi_transfer_qread(QspiDev_t *dev, const uint8_t *cmd, uint32_t cmdLen,
|
||||
uint8_t *rxData, uint32_t rxLen, uint32_t dummyClocks)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t entry, rxEntry;
|
||||
uint32_t i;
|
||||
|
||||
/* Enable GQSPI controller */
|
||||
GQSPI_EN = 1;
|
||||
dsb();
|
||||
|
||||
/* Base entry for command phase: bus + CS + SPI mode */
|
||||
entry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_GEN_FIFO_MODE_SPI;
|
||||
|
||||
/* CS assertion */
|
||||
ret = qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(1));
|
||||
|
||||
/* TX Phase - send command + address bytes in SPI mode */
|
||||
for (i = 0; i < cmdLen && ret == 0; i++) {
|
||||
uint32_t txEntry = entry | GQSPI_GEN_FIFO_TX |
|
||||
GQSPI_GEN_FIFO_IMM(cmd[i]);
|
||||
ret = qspi_gen_fifo_push(txEntry);
|
||||
}
|
||||
|
||||
/* Trigger and wait for TX to complete */
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
|
||||
/* Dummy clocks phase (required for Fast/Quad Read)
|
||||
* Send dummy clocks: DATA_XFER with no TX or RX, IMM = clock count */
|
||||
if (ret == 0 && dummyClocks > 0) {
|
||||
uint32_t dummyEntry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_QSPI_MODE |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
GQSPI_GEN_FIFO_IMM(dummyClocks);
|
||||
ret = qspi_gen_fifo_push(dummyEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
}
|
||||
|
||||
/* RX Phase - receive data in QSPI mode (4-bit)
|
||||
* Use EXP mode for large transfers (pattern from zynq.c) */
|
||||
rxEntry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_QSPI_MODE |
|
||||
GQSPI_GEN_FIFO_RX |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
(dev->stripe & GQSPI_GEN_FIFO_STRIPE);
|
||||
|
||||
{
|
||||
uint32_t remaining = rxLen;
|
||||
uint32_t offset = 0;
|
||||
uint32_t xferSz;
|
||||
|
||||
while (ret == 0 && remaining > 0) {
|
||||
xferSz = qspi_calc_exp(remaining, &rxEntry);
|
||||
ret = qspi_gen_fifo_push(rxEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = qspi_fifo_rx(&rxData[offset], xferSz);
|
||||
}
|
||||
offset += xferSz;
|
||||
remaining -= xferSz;
|
||||
}
|
||||
}
|
||||
|
||||
/* CS Deassert */
|
||||
entry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) | GQSPI_GEN_FIFO_MODE_SPI;
|
||||
qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(1));
|
||||
qspi_gen_fifo_start_and_wait();
|
||||
|
||||
/* Disable controller */
|
||||
GQSPI_EN = 0;
|
||||
dsb();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Switch back to IO mode if DMA was used and disable controller */
|
||||
#ifndef GQSPI_MODE_IO
|
||||
/* DMA-enabled QSPI Read transfer
|
||||
* Uses DMA for RX phase for better performance on large reads */
|
||||
static int qspi_transfer_qread_dma(QspiDev_t *dev, const uint8_t *cmd, uint32_t cmdLen,
|
||||
uint8_t *rxData, uint32_t rxLen, uint32_t dummyClocks)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t entry, rxEntry;
|
||||
uint32_t i;
|
||||
uint8_t *dmaPtr;
|
||||
uint32_t dmaLen;
|
||||
int useTemp = 0;
|
||||
|
||||
/* Enable GQSPI controller in DMA mode */
|
||||
GQSPI_CFG = (GQSPI_CFG & ~GQSPI_CFG_MODE_EN_MASK) | GQSPI_CFG_MODE_EN_DMA;
|
||||
GQSPI_EN = 1;
|
||||
dsb();
|
||||
|
||||
/* Base entry for command phase: bus + CS + SPI mode */
|
||||
entry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_GEN_FIFO_MODE_SPI;
|
||||
|
||||
/* CS assertion */
|
||||
ret = qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(1));
|
||||
|
||||
/* TX Phase - send command + address bytes in SPI mode */
|
||||
for (i = 0; i < cmdLen && ret == 0; i++) {
|
||||
uint32_t txEntry = entry | GQSPI_GEN_FIFO_TX |
|
||||
GQSPI_GEN_FIFO_IMM(cmd[i]);
|
||||
ret = qspi_gen_fifo_push(txEntry);
|
||||
if ((GQSPI_CFG & GQSPI_CFG_MODE_EN_MASK) == GQSPI_CFG_MODE_EN_DMA) {
|
||||
GQSPI_CFG = (GQSPI_CFG & ~GQSPI_CFG_MODE_EN_MASK) | GQSPI_CFG_MODE_EN_IO;
|
||||
}
|
||||
|
||||
/* Trigger and wait for TX to complete */
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
|
||||
/* Dummy clocks phase */
|
||||
if (ret == 0 && dummyClocks > 0) {
|
||||
uint32_t dummyEntry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_QSPI_MODE |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
GQSPI_GEN_FIFO_IMM(dummyClocks);
|
||||
ret = qspi_gen_fifo_push(dummyEntry);
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
}
|
||||
|
||||
/* DMA RX Phase */
|
||||
if (ret == 0 && rxLen > 0) {
|
||||
uint32_t remaining;
|
||||
uint32_t xferSz;
|
||||
|
||||
/* 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;
|
||||
dmaLen = (rxLen + GQSPI_DMA_ALIGN - 1) & ~(GQSPI_DMA_ALIGN - 1);
|
||||
if (dmaLen > sizeof(dma_tmpbuf)) {
|
||||
dmaLen = sizeof(dma_tmpbuf);
|
||||
}
|
||||
useTemp = 1;
|
||||
} else {
|
||||
dmaPtr = rxData;
|
||||
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);
|
||||
GQSPIDMA_SIZE = dmaLen;
|
||||
|
||||
/* Enable DMA done interrupt */
|
||||
GQSPIDMA_IER = GQSPIDMA_ISR_DONE;
|
||||
|
||||
/* Flush dcache for DMA coherency */
|
||||
flush_dcache_range((uintptr_t)dmaPtr, (uintptr_t)dmaPtr + dmaLen);
|
||||
|
||||
/* Setup GenFIFO for RX with DMA - use EXP mode for large transfers */
|
||||
rxEntry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_QSPI_MODE |
|
||||
GQSPI_GEN_FIFO_RX |
|
||||
GQSPI_GEN_FIFO_DATA_XFER |
|
||||
(dev->stripe & GQSPI_GEN_FIFO_STRIPE);
|
||||
|
||||
/* Use qspi_calc_exp for large transfers (pattern from zynq.c) */
|
||||
while (ret == 0 && remaining > 0) {
|
||||
xferSz = qspi_calc_exp(remaining, &rxEntry);
|
||||
ret = qspi_gen_fifo_push(rxEntry);
|
||||
remaining -= xferSz;
|
||||
}
|
||||
|
||||
/* Trigger GenFIFO */
|
||||
if (ret == 0) {
|
||||
GQSPI_CFG |= GQSPI_CFG_START_GEN_FIFO;
|
||||
dsb();
|
||||
}
|
||||
|
||||
/* Wait for DMA completion */
|
||||
if (ret == 0) {
|
||||
ret = qspi_dma_wait();
|
||||
}
|
||||
|
||||
/* Invalidate cache after DMA */
|
||||
flush_dcache_range((uintptr_t)dmaPtr, (uintptr_t)dmaPtr + dmaLen);
|
||||
|
||||
/* Copy from temp buffer if needed (only copy requested bytes) */
|
||||
if (ret == 0 && useTemp) {
|
||||
memcpy(rxData, dmaPtr, rxLen);
|
||||
}
|
||||
}
|
||||
|
||||
/* CS Deassert */
|
||||
entry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) | GQSPI_GEN_FIFO_MODE_SPI;
|
||||
qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(1));
|
||||
qspi_gen_fifo_start_and_wait();
|
||||
|
||||
/* Switch back to IO mode and disable controller */
|
||||
GQSPI_CFG = (GQSPI_CFG & ~GQSPI_CFG_MODE_EN_MASK) | GQSPI_CFG_MODE_EN_IO;
|
||||
GQSPI_EN = 0;
|
||||
dsb();
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* !GQSPI_MODE_IO */
|
||||
|
||||
/* Write page data to flash (for page programming) */
|
||||
static int qspi_write_page(QspiDev_t *dev, const uint8_t *cmd, uint32_t cmdLen,
|
||||
const uint8_t *data, uint32_t dataLen)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t entry;
|
||||
uint32_t i;
|
||||
|
||||
/* Enable GQSPI controller */
|
||||
GQSPI_EN = 1;
|
||||
dsb();
|
||||
|
||||
/* Base entry: bus + CS + SPI mode (page program uses SPI mode) */
|
||||
entry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) |
|
||||
(dev->cs & GQSPI_GEN_FIFO_CS_MASK) |
|
||||
GQSPI_GEN_FIFO_MODE_SPI;
|
||||
|
||||
/* CS assertion */
|
||||
ret = qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(1));
|
||||
|
||||
/* TX Phase - send command bytes (includes address) via immediate mode */
|
||||
for (i = 0; i < cmdLen && ret == 0; i++) {
|
||||
uint32_t txEntry = entry | GQSPI_GEN_FIFO_TX |
|
||||
GQSPI_GEN_FIFO_IMM(cmd[i]);
|
||||
ret = qspi_gen_fifo_push(txEntry);
|
||||
}
|
||||
|
||||
/* Trigger and wait for command to complete */
|
||||
if (ret == 0) {
|
||||
ret = qspi_gen_fifo_start_and_wait();
|
||||
}
|
||||
|
||||
/* TX Phase - send data via TX FIFO (not immediate mode) */
|
||||
if (ret == 0 && dataLen > 0) {
|
||||
uint32_t txEntry = entry | GQSPI_GEN_FIFO_TX | GQSPI_GEN_FIFO_DATA_XFER |
|
||||
(dev->stripe & GQSPI_GEN_FIFO_STRIPE);
|
||||
|
||||
while (dataLen > 0 && ret == 0) {
|
||||
uint32_t chunkLen = (dataLen > 255) ? 255 : dataLen;
|
||||
uint32_t chunkEntry = txEntry | GQSPI_GEN_FIFO_IMM(chunkLen);
|
||||
|
||||
ret = qspi_gen_fifo_push(chunkEntry);
|
||||
if (ret != 0) break;
|
||||
|
||||
/* Start GenFIFO processing so it drains TX FIFO as we fill it */
|
||||
GQSPI_CFG |= GQSPI_CFG_START_GEN_FIFO;
|
||||
dsb();
|
||||
|
||||
/* Push data to TX FIFO */
|
||||
ret = qspi_fifo_tx(data, chunkLen);
|
||||
if (ret != 0) break;
|
||||
|
||||
/* Wait for GenFIFO to complete */
|
||||
ret = qspi_wait_genfifo_empty();
|
||||
|
||||
data += chunkLen;
|
||||
dataLen -= chunkLen;
|
||||
}
|
||||
}
|
||||
|
||||
/* CS Deassert */
|
||||
entry = (dev->bus & GQSPI_GEN_FIFO_BUS_MASK) | GQSPI_GEN_FIFO_MODE_SPI;
|
||||
qspi_gen_fifo_push(entry | GQSPI_GEN_FIFO_IMM(1));
|
||||
qspi_gen_fifo_start_and_wait();
|
||||
|
||||
/* Disable controller */
|
||||
#endif
|
||||
GQSPI_EN = 0;
|
||||
dsb();
|
||||
|
||||
|
|
@ -962,7 +814,7 @@ static int qspi_read_id(QspiDev_t *dev, uint8_t *id, uint32_t len)
|
|||
int ret;
|
||||
|
||||
cmd[0] = FLASH_CMD_READ_ID;
|
||||
ret = qspi_transfer(dev, cmd, 1, id, len, 0);
|
||||
ret = qspi_transfer(dev, cmd, 1, id, len, 0, NULL, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -983,13 +835,13 @@ static int qspi_read_status(QspiDev_t *dev, uint8_t *status)
|
|||
tmpDev.cs = GQSPI_GEN_FIFO_CS_LOWER;
|
||||
tmpDev.stripe = 0;
|
||||
cmd[0] = FLASH_CMD_READ_STATUS;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, &data[0], 1, 0);
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, &data[0], 1, 0, NULL, 0);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* Read from upper chip */
|
||||
tmpDev.bus = GQSPI_GEN_FIFO_BUS_UP;
|
||||
tmpDev.cs = GQSPI_GEN_FIFO_CS_UPPER;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, &data[1], 1, 0);
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, &data[1], 1, 0, NULL, 0);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* AND the status from both chips */
|
||||
|
|
@ -998,7 +850,7 @@ static int qspi_read_status(QspiDev_t *dev, uint8_t *status)
|
|||
}
|
||||
|
||||
cmd[0] = FLASH_CMD_READ_STATUS;
|
||||
ret = qspi_transfer(dev, cmd, 1, data, 1, 0);
|
||||
ret = qspi_transfer(dev, cmd, 1, data, 1, 0, NULL, 0);
|
||||
if (ret == 0) {
|
||||
*status = data[0];
|
||||
}
|
||||
|
|
@ -1021,13 +873,13 @@ static int qspi_read_flag_status(QspiDev_t *dev, uint8_t *status)
|
|||
tmpDev.cs = GQSPI_GEN_FIFO_CS_LOWER;
|
||||
tmpDev.stripe = 0;
|
||||
cmd[0] = FLASH_CMD_READ_FLAG_STATUS;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, &data[0], 1, 0);
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, &data[0], 1, 0, NULL, 0);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* Read from upper chip */
|
||||
tmpDev.bus = GQSPI_GEN_FIFO_BUS_UP;
|
||||
tmpDev.cs = GQSPI_GEN_FIFO_CS_UPPER;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, &data[1], 1, 0);
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, &data[1], 1, 0, NULL, 0);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* AND the status from both chips */
|
||||
|
|
@ -1036,7 +888,7 @@ static int qspi_read_flag_status(QspiDev_t *dev, uint8_t *status)
|
|||
}
|
||||
|
||||
cmd[0] = FLASH_CMD_READ_FLAG_STATUS;
|
||||
ret = qspi_transfer(dev, cmd, 1, data, 1, 0);
|
||||
ret = qspi_transfer(dev, cmd, 1, data, 1, 0, NULL, 0);
|
||||
if (ret == 0) {
|
||||
*status = data[0];
|
||||
}
|
||||
|
|
@ -1078,16 +930,16 @@ static int qspi_write_enable(QspiDev_t *dev)
|
|||
tmpDev.bus = GQSPI_GEN_FIFO_BUS_LOW;
|
||||
tmpDev.cs = GQSPI_GEN_FIFO_CS_LOWER;
|
||||
tmpDev.stripe = 0;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, NULL, 0, 0);
|
||||
ret = qspi_transfer(&tmpDev, cmd, sizeof(cmd), NULL, 0, 0, NULL, 0);
|
||||
if (ret != 0) return ret;
|
||||
|
||||
/* Send to upper chip */
|
||||
tmpDev.bus = GQSPI_GEN_FIFO_BUS_UP;
|
||||
tmpDev.cs = GQSPI_GEN_FIFO_CS_UPPER;
|
||||
ret = qspi_transfer(&tmpDev, cmd, 1, NULL, 0, 0);
|
||||
ret = qspi_transfer(&tmpDev, cmd, sizeof(cmd), NULL, 0, 0, NULL, 0);
|
||||
if (ret != 0) return ret;
|
||||
} else {
|
||||
ret = qspi_transfer(dev, cmd, 1, NULL, 0, 0);
|
||||
ret = qspi_transfer(dev, cmd, sizeof(cmd), NULL, 0, 0, NULL, 0);
|
||||
if (ret != 0) return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1108,7 +960,7 @@ static int qspi_write_disable(QspiDev_t *dev)
|
|||
uint8_t cmd[1];
|
||||
|
||||
cmd[0] = FLASH_CMD_WRITE_DISABLE;
|
||||
return qspi_transfer(dev, cmd, 1, NULL, 0, 0);
|
||||
return qspi_transfer(dev, cmd, sizeof(cmd), NULL, 0, 0, NULL, 0);
|
||||
}
|
||||
|
||||
#if GQPI_USE_4BYTE_ADDR == 1
|
||||
|
|
@ -1123,7 +975,7 @@ static int qspi_enter_4byte_addr(QspiDev_t *dev)
|
|||
if (ret != 0) return ret;
|
||||
|
||||
cmd[0] = FLASH_CMD_ENTER_4B_MODE;
|
||||
ret = qspi_transfer(dev, cmd, 1, NULL, 0, 0);
|
||||
ret = qspi_transfer(dev, cmd, sizeof(cmd), NULL, 0, 0, NULL, 0);
|
||||
QSPI_DEBUG_PRINTF("QSPI: Enter 4-byte mode: ret=%d\n", ret);
|
||||
|
||||
if (ret == 0) {
|
||||
|
|
@ -1143,7 +995,7 @@ static int qspi_exit_4byte_addr(QspiDev_t *dev)
|
|||
if (ret != 0) return ret;
|
||||
|
||||
cmd[0] = FLASH_CMD_EXIT_4B_MODE;
|
||||
ret = qspi_transfer(dev, cmd, 1, NULL, 0, 0);
|
||||
ret = qspi_transfer(dev, cmd, sizeof(cmd), NULL, 0, 0, NULL, 0);
|
||||
QSPI_DEBUG_PRINTF("QSPI: Exit 4-byte mode: ret=%d\n", ret);
|
||||
|
||||
if (ret == 0) {
|
||||
|
|
@ -1158,19 +1010,22 @@ static int qspi_exit_4byte_addr(QspiDev_t *dev)
|
|||
#ifndef TEST_EXT_ADDRESS
|
||||
#define TEST_EXT_ADDRESS 0x2800000 /* 40MB */
|
||||
#endif
|
||||
#ifndef TEST_EXT_SIZE
|
||||
#define TEST_EXT_SIZE (FLASH_PAGE_SIZE * 4)
|
||||
#endif
|
||||
|
||||
static int test_ext_flash(QspiDev_t* dev)
|
||||
{
|
||||
int ret;
|
||||
uint32_t i;
|
||||
uint8_t pageData[FLASH_PAGE_SIZE * 4];
|
||||
uint8_t pageData[TEST_EXT_SIZE];
|
||||
|
||||
(void)dev;
|
||||
wolfBoot_printf("Testing ext flash at 0x%x...\n", TEST_EXT_ADDRESS);
|
||||
|
||||
#ifndef TEST_FLASH_READONLY
|
||||
/* Erase sector */
|
||||
ret = ext_flash_erase(TEST_EXT_ADDRESS, FLASH_SECTOR_SIZE);
|
||||
ret = ext_flash_erase(TEST_EXT_ADDRESS, WOLFBOOT_SECTOR_SIZE);
|
||||
wolfBoot_printf("Erase Sector: Ret %d\n", ret);
|
||||
|
||||
/* Write Pages */
|
||||
|
|
@ -1185,7 +1040,7 @@ static int test_ext_flash(QspiDev_t* dev)
|
|||
memset(pageData, 0, sizeof(pageData));
|
||||
ret = ext_flash_read(TEST_EXT_ADDRESS, pageData, sizeof(pageData));
|
||||
wolfBoot_printf("Read Page: Ret %d\n", ret);
|
||||
if (ret != 0) {
|
||||
if (ret < 0) {
|
||||
wolfBoot_printf("Flash read failed!\n");
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1228,8 +1083,6 @@ static void qspi_init(void)
|
|||
|
||||
/* Read initial state left by PLM */
|
||||
cfg = GQSPI_CFG;
|
||||
QSPI_DEBUG_PRINTF("QSPI: PLM state - CFG=0x%08x ISR=0x%08x\n",
|
||||
cfg, GQSPI_ISR);
|
||||
|
||||
/* Disable controller during reconfiguration */
|
||||
GQSPI_EN = 0;
|
||||
|
|
@ -1251,7 +1104,7 @@ static void qspi_init(void)
|
|||
/* 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
|
||||
* Note: qspi_transfer_qread_dma() will switch to DMA mode for reads */
|
||||
* Note: ext_flash_read() will switch to DMA mode for reads if not in IO mode */
|
||||
cfg = (cfg & ~GQSPI_CFG_MODE_EN_MASK); /* Clear mode bits */
|
||||
cfg |= GQSPI_CFG_MODE_EN_IO; /* Set IO mode for init */
|
||||
GQSPI_CFG = cfg;
|
||||
|
|
@ -1274,8 +1127,6 @@ static void qspi_init(void)
|
|||
dsb();
|
||||
#endif
|
||||
|
||||
QSPI_DEBUG_PRINTF("QSPI: After config - CFG=0x%08x\n", GQSPI_CFG);
|
||||
|
||||
/* Configure device for single flash (lower) first */
|
||||
qspiDev.mode = GQSPI_GEN_FIFO_MODE_SPI;
|
||||
qspiDev.bus = GQSPI_GEN_FIFO_BUS_LOW;
|
||||
|
|
@ -1343,9 +1194,6 @@ static void qspi_init(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
#endif /* EXT_FLASH */
|
||||
|
||||
|
||||
/* ============================================================================
|
||||
* HAL Public Interface
|
||||
* ============================================================================
|
||||
|
|
@ -1353,24 +1201,29 @@ static void qspi_init(void)
|
|||
|
||||
void hal_init(void)
|
||||
{
|
||||
#if defined(__WOLFBOOT) && defined(DEBUG_UART)
|
||||
const char *banner = "\n"
|
||||
"========================================\n"
|
||||
"wolfBoot Secure Boot - AMD Versal\n"
|
||||
"========================================\n";
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_UART
|
||||
uart_init();
|
||||
#endif
|
||||
|
||||
#ifdef __WOLFBOOT
|
||||
wolfBoot_printf("%s", banner);
|
||||
#endif
|
||||
wolfBoot_printf("Current EL: %d\n", current_el());
|
||||
wolfBoot_printf("Timer Freq: %lu Hz\n", (unsigned long)timer_get_freq());
|
||||
#endif /* DEBUG_UART */
|
||||
|
||||
#ifdef EXT_FLASH
|
||||
qspi_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* EXT_FLASH */
|
||||
|
||||
void hal_prepare_boot(void)
|
||||
{
|
||||
#if defined(EXT_FLASH) && GQPI_USE_4BYTE_ADDR == 1
|
||||
|
|
@ -1448,14 +1301,30 @@ void* hal_get_dts_update_address(void)
|
|||
}
|
||||
#endif /* MMU */
|
||||
|
||||
#ifdef WOLFBOOT_DUALBOOT
|
||||
/**
|
||||
* Get the primary (boot) partition address in flash
|
||||
* Returns the flash address where the boot partition starts
|
||||
*/
|
||||
void* hal_get_primary_address(void)
|
||||
{
|
||||
return (void*)WOLFBOOT_PARTITION_BOOT_ADDRESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the update partition address in flash
|
||||
* Returns the flash address where the update partition starts
|
||||
*/
|
||||
void* hal_get_update_address(void)
|
||||
{
|
||||
return (void*)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
|
||||
}
|
||||
#endif /* WOLFBOOT_DUALBOOT */
|
||||
|
||||
/* ============================================================================
|
||||
* Flash Functions (STUBS)
|
||||
* ============================================================================
|
||||
* These are placeholder implementations.
|
||||
* Real implementation will depend on boot media:
|
||||
* - OSPI flash (VERSAL_OSPI_BASE)
|
||||
* - SD/eMMC via SDHCI (VERSAL_SD0_BASE / VERSAL_SD1_BASE)
|
||||
* There is no "internal flash" on the Versal, so these are stubs.
|
||||
*/
|
||||
|
||||
void RAMFUNCTION hal_flash_unlock(void)
|
||||
|
|
@ -1473,10 +1342,6 @@ int RAMFUNCTION hal_flash_write(uintptr_t address, const uint8_t *data, int len)
|
|||
(void)address;
|
||||
(void)data;
|
||||
(void)len;
|
||||
|
||||
/* Stub - flash write not implemented */
|
||||
wolfBoot_printf("hal_flash_write: STUB (addr=0x%lx, len=%d)\n",
|
||||
(unsigned long)address, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -1484,10 +1349,6 @@ int RAMFUNCTION hal_flash_erase(uintptr_t address, int len)
|
|||
{
|
||||
(void)address;
|
||||
(void)len;
|
||||
|
||||
/* Stub - flash erase not implemented */
|
||||
wolfBoot_printf("hal_flash_erase: STUB (addr=0x%lx, len=%d)\n",
|
||||
(unsigned long)address, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -1515,11 +1376,15 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len)
|
|||
uint8_t cmd[5];
|
||||
uint32_t xferSz, page, pages;
|
||||
uintptr_t addr;
|
||||
const uint8_t *pageData;
|
||||
|
||||
if (!qspi_initialized) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
QSPI_DEBUG_PRINTF("ext_flash_write: addr=0x%lx, len=%d\n",
|
||||
(unsigned long)address, len);
|
||||
|
||||
/* Write by page */
|
||||
pages = ((len + (FLASH_PAGE_SIZE - 1)) / FLASH_PAGE_SIZE);
|
||||
for (page = 0; page < pages && ret == 0; page++) {
|
||||
|
|
@ -1543,9 +1408,9 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len)
|
|||
cmd[3] = (addr >> 8) & 0xFF;
|
||||
cmd[4] = addr & 0xFF;
|
||||
|
||||
/* Send command + data - hardware handles striping */
|
||||
ret = qspi_write_page(&qspiDev, cmd, 5,
|
||||
data + (page * FLASH_PAGE_SIZE), xferSz);
|
||||
pageData = data + (page * FLASH_PAGE_SIZE);
|
||||
ret = qspi_transfer(&qspiDev, cmd, sizeof(cmd), NULL, 0, 0, pageData, xferSz);
|
||||
|
||||
QSPI_DEBUG_PRINTF("Flash Page %d Write: Ret %d\n", page, ret);
|
||||
if (ret != 0) break;
|
||||
|
||||
|
|
@ -1560,7 +1425,7 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len)
|
|||
int ext_flash_read(uintptr_t address, uint8_t *data, int len)
|
||||
{
|
||||
uint8_t cmd[5];
|
||||
int ret;
|
||||
int ret = 0;
|
||||
uintptr_t addr = address;
|
||||
|
||||
if (!qspi_initialized) {
|
||||
|
|
@ -1573,8 +1438,6 @@ int ext_flash_read(uintptr_t address, uint8_t *data, int 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 */
|
||||
|
|
@ -1584,28 +1447,14 @@ int ext_flash_read(uintptr_t address, uint8_t *data, int len)
|
|||
cmd[3] = (addr >> 8) & 0xFF;
|
||||
cmd[4] = addr & 0xFF;
|
||||
|
||||
/* Hardware handles striping via GQSPI_GEN_FIFO_STRIPE flag */
|
||||
#ifdef GQSPI_MODE_IO
|
||||
ret = qspi_transfer_qread(&qspiDev, cmd, 5, data, len, GQSPI_DUMMY_READ);
|
||||
#else
|
||||
ret = qspi_transfer_qread_dma(&qspiDev, cmd, 5, data, len, GQSPI_DUMMY_READ);
|
||||
#endif
|
||||
ret = qspi_transfer(&qspiDev, cmd, sizeof(cmd), data, len, GQSPI_DUMMY_READ, NULL, 0);
|
||||
|
||||
/* 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. */
|
||||
/* On error, fill buffer with 0xFF to simulate unwritten flash */
|
||||
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) */
|
||||
QSPI_DEBUG_PRINTF("ext_flash_read: ret=%d\n", ret);
|
||||
return (ret == 0) ? len : ret;
|
||||
}
|
||||
|
||||
|
|
@ -1619,6 +1468,9 @@ int ext_flash_erase(uintptr_t address, int len)
|
|||
return -1;
|
||||
}
|
||||
|
||||
QSPI_DEBUG_PRINTF("ext_flash_erase: addr=0x%lx, len=%d\n",
|
||||
(unsigned long)address, len);
|
||||
|
||||
while (len > 0 && ret == 0) {
|
||||
addr = address;
|
||||
if (qspiDev.stripe) {
|
||||
|
|
@ -1635,9 +1487,10 @@ int ext_flash_erase(uintptr_t address, int len)
|
|||
cmd[2] = (addr >> 16) & 0xFF;
|
||||
cmd[3] = (addr >> 8) & 0xFF;
|
||||
cmd[4] = addr & 0xFF;
|
||||
ret = qspi_transfer(&qspiDev, cmd, sizeof(cmd), NULL, 0, 0, NULL, 0);
|
||||
|
||||
ret = qspi_transfer(&qspiDev, cmd, 5, NULL, 0, 0);
|
||||
QSPI_DEBUG_PRINTF("ext_flash_erase: addr=0x%lx\n", (unsigned long)address);
|
||||
QSPI_DEBUG_PRINTF(" Flash Erase: Ret %d, Address 0x%x\n",
|
||||
ret, address);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = qspi_wait_ready(&qspiDev);
|
||||
|
|
|
|||
|
|
@ -58,6 +58,23 @@ void hal_init(void);
|
|||
uint64_t hal_get_timer_us(void);
|
||||
#endif
|
||||
|
||||
/* Boot benchmarking macros
|
||||
* Usage: Declare BENCHMARK_DECLARE() at function scope,
|
||||
* then use BENCHMARK_START() and BENCHMARK_END(msg) to measure time.
|
||||
*/
|
||||
#ifdef BOOT_BENCHMARK
|
||||
#define BENCHMARK_DECLARE() 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)\r\n", (unsigned long)_elapsed_ms); \
|
||||
} while(0)
|
||||
#else
|
||||
#define BENCHMARK_DECLARE() do {} while(0)
|
||||
#define BENCHMARK_START() do {} while(0)
|
||||
#define BENCHMARK_END(msg) wolfBoot_printf(msg "\r\n")
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_64BIT
|
||||
typedef uintptr_t haladdr_t; /* 64-bit platforms */
|
||||
int hal_flash_write(uintptr_t address, const uint8_t *data, int len);
|
||||
|
|
|
|||
|
|
@ -1291,11 +1291,12 @@ int wolfBoot_dualboot_candidate(void)
|
|||
}
|
||||
#else
|
||||
|
||||
static int wolfBoot_current_firmware_version()
|
||||
static int wolfBoot_current_firmware_version(void)
|
||||
{
|
||||
return wolfBoot_get_blob_version(hal_get_primary_address());
|
||||
}
|
||||
static int wolfBoot_update_firmware_version() {
|
||||
static int wolfBoot_update_firmware_version(void)
|
||||
{
|
||||
return wolfBoot_get_blob_version(hal_get_update_address());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
uint32_t dts_size = 0;
|
||||
#endif
|
||||
char part_name[4] = {'P', ':', 'X', '\0'};
|
||||
uint64_t start_us, elapsed_ms;
|
||||
BENCHMARK_DECLARE();
|
||||
|
||||
#ifdef DISK_ENCRYPT
|
||||
/* Initialize encryption - this sets up the cipher with key from storage */
|
||||
|
|
@ -400,7 +400,7 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
|
||||
/* Read the image into RAM */
|
||||
wolfBoot_printf("Loading image from disk...");
|
||||
start_us = hal_get_timer_us();
|
||||
BENCHMARK_START();
|
||||
load_off = 0;
|
||||
do {
|
||||
ret = disk_part_read(BOOT_DISK, cur_part, load_off,
|
||||
|
|
@ -416,13 +416,12 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
selected ^= 1;
|
||||
continue;
|
||||
}
|
||||
elapsed_ms = (hal_get_timer_us() - start_us) / 1000;
|
||||
wolfBoot_printf("done. (%lu ms)\r\n", (unsigned long)elapsed_ms);
|
||||
BENCHMARK_END("done");
|
||||
|
||||
#ifdef DISK_ENCRYPT
|
||||
/* Decrypt the image in RAM */
|
||||
wolfBoot_printf("Decrypting image...");
|
||||
start_us = hal_get_timer_us();
|
||||
BENCHMARK_START();
|
||||
ret = decrypt_image((uint8_t*)load_address,
|
||||
os_image.fw_size + IMAGE_HEADER_SIZE);
|
||||
if (ret != 0) {
|
||||
|
|
@ -430,8 +429,7 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
selected ^= 1;
|
||||
continue;
|
||||
}
|
||||
elapsed_ms = (hal_get_timer_us() - start_us) / 1000;
|
||||
wolfBoot_printf("done. (%lu ms)\r\n", (unsigned long)elapsed_ms);
|
||||
BENCHMARK_END("done");
|
||||
#endif
|
||||
|
||||
memset(&os_image, 0, sizeof(os_image));
|
||||
|
|
@ -443,25 +441,23 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
}
|
||||
|
||||
wolfBoot_printf("Checking image integrity...");
|
||||
start_us = hal_get_timer_us();
|
||||
BENCHMARK_START();
|
||||
if (wolfBoot_verify_integrity(&os_image) != 0) {
|
||||
wolfBoot_printf("Error validating integrity for %s\r\n", part_name);
|
||||
selected ^= 1;
|
||||
continue;
|
||||
}
|
||||
elapsed_ms = (hal_get_timer_us() - start_us) / 1000;
|
||||
wolfBoot_printf("done. (%lu ms)\r\n", (unsigned long)elapsed_ms);
|
||||
BENCHMARK_END("done");
|
||||
|
||||
wolfBoot_printf("Verifying image signature...");
|
||||
start_us = hal_get_timer_us();
|
||||
BENCHMARK_START();
|
||||
if (wolfBoot_verify_authenticity(&os_image) != 0) {
|
||||
wolfBoot_printf("Error validating authenticity for %s\r\n",
|
||||
part_name);
|
||||
selected ^= 1;
|
||||
continue;
|
||||
} else {
|
||||
elapsed_ms = (hal_get_timer_us() - start_us) / 1000;
|
||||
wolfBoot_printf("done. (%lu ms)\r\n", (unsigned long)elapsed_ms);
|
||||
BENCHMARK_END("done");
|
||||
failures = 0;
|
||||
break; /* Success case */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,19 +43,6 @@ 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)
|
||||
|
|
@ -71,6 +58,7 @@ int wolfBoot_ramboot(struct wolfBoot_image *img, uint8_t *src, uint8_t *dst)
|
|||
{
|
||||
int ret;
|
||||
uint32_t img_size;
|
||||
BENCHMARK_DECLARE();
|
||||
|
||||
/* read header into RAM */
|
||||
wolfBoot_printf("Loading header %d bytes from %p to %p\n",
|
||||
|
|
@ -122,6 +110,7 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
{
|
||||
int active = -1, ret = 0;
|
||||
struct wolfBoot_image os_image;
|
||||
BENCHMARK_DECLARE();
|
||||
#ifdef WOLFBOOT_UBOOT_LEGACY
|
||||
uint8_t *image_ptr;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = 256K
|
||||
DRAM (rwx) : ORIGIN = 0x80001000 , LENGTH = 0xBFFFFFFF
|
||||
OCRAM (rwx) : ORIGIN = 0x18020100, LENGTH = 128K
|
||||
}
|
||||
|
||||
ENTRY(main);
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
_start_text = .;
|
||||
KEEP(*(.boot*))
|
||||
*(.text*)
|
||||
*(.rodata*)
|
||||
*(.note.*)
|
||||
. = ALIGN(4);
|
||||
_end_text = .;
|
||||
} > OCRAM
|
||||
|
||||
.edidx :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.ARM.exidx*)
|
||||
} > OCRAM
|
||||
|
||||
PROVIDE(_stored_data = .);
|
||||
|
||||
.data :
|
||||
{
|
||||
_start_data = .;
|
||||
KEEP(*(.data*))
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.ramcode))
|
||||
. = ALIGN(4);
|
||||
_end_data = .;
|
||||
} > OCRAM
|
||||
|
||||
.bss (NOLOAD) :
|
||||
{
|
||||
_start_bss = .;
|
||||
__bss_start__ = .;
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_end_bss = .;
|
||||
__bss_end__ = .;
|
||||
_end = .;
|
||||
} > OCRAM
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
||||
END_STACK = _start_text;
|
||||
|
|
@ -135,6 +135,9 @@ endif
|
|||
|
||||
ifeq ($(ARCH),AARCH64)
|
||||
APP_OBJS:=boot_arm64_start.o $(APP_OBJS)
|
||||
# Prevent inclusion of standard C runtime startup files that conflict with boot_arm64_start.S
|
||||
# Use -Wl, prefix to pass flags directly to linker when GCC is used as linker driver
|
||||
LDFLAGS+=-nostartfiles -nostdlib -nodefaultlibs -Wl,--entry=_start
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),RISCV64)
|
||||
|
|
@ -288,6 +291,9 @@ endif
|
|||
|
||||
ifeq ($(EXT_FLASH),1)
|
||||
CFLAGS+=-D"EXT_FLASH=1" -D"PART_UPDATE_EXT=1"
|
||||
ifeq ($(NO_XIP),1)
|
||||
CFLAGS+=-D"PART_BOOT_EXT=1"
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(SPI_FLASH),1)
|
||||
|
|
@ -507,13 +513,12 @@ ifeq ($(TARGET),x86_fsp_qemu)
|
|||
LDFLAGS=
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),nxp_ls1028a)
|
||||
LSCRIPT_TEMPLATE:=AARCH64-ls1028a.ld
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),versal)
|
||||
LSCRIPT_TEMPLATE:=AARCH64.ld
|
||||
LDFLAGS+=-nostdlib
|
||||
# Enable DEBUG_UART for test-app to use wolfBoot_printf and hal functions
|
||||
DEBUG_UART:=1
|
||||
CFLAGS+=-DDEBUG_UART
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),zynq)
|
||||
|
|
|
|||
|
|
@ -22,74 +22,37 @@
|
|||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hal.h"
|
||||
#include "hal/versal.h"
|
||||
#include "wolfboot/wolfboot.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_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_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)
|
||||
{
|
||||
/* Wait while TX FIFO is full */
|
||||
while (UART_FR & UART_FR_TXFF)
|
||||
;
|
||||
UART_DR = c;
|
||||
}
|
||||
|
||||
static void uart_print(const char *s)
|
||||
{
|
||||
while (*s) {
|
||||
if (*s == '\n')
|
||||
uart_tx('\r');
|
||||
uart_tx((uint8_t)*s++);
|
||||
}
|
||||
}
|
||||
#include "printf.h"
|
||||
|
||||
void main(void)
|
||||
{
|
||||
uint32_t el = get_current_el();
|
||||
uint32_t boot_version, update_version;
|
||||
|
||||
uart_print("\n\n");
|
||||
uart_print("===========================================\n");
|
||||
uart_print(" wolfBoot Test Application - AMD Versal\n");
|
||||
uart_print("===========================================\n\n");
|
||||
/* Initialize HAL (UART, etc.) */
|
||||
hal_init();
|
||||
|
||||
/* Print current exception level */
|
||||
uart_print("Current EL: ");
|
||||
uart_tx('0' + el);
|
||||
uart_print("\n");
|
||||
/* Get versions from both partitions */
|
||||
boot_version = wolfBoot_get_image_version(PART_BOOT);
|
||||
update_version = wolfBoot_get_image_version(PART_UPDATE);
|
||||
|
||||
uart_print("Application running successfully!\n");
|
||||
wolfBoot_printf("\n\n");
|
||||
wolfBoot_printf("===========================================\n");
|
||||
wolfBoot_printf(" wolfBoot Test Application - AMD Versal\n");
|
||||
wolfBoot_printf("===========================================\n\n");
|
||||
|
||||
uart_print("\nEntering idle loop...\n");
|
||||
/* 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);
|
||||
|
||||
/* Wait for transmit to complete (TX FIFO empty) */
|
||||
while (!(UART_FR & UART_FR_TXFE))
|
||||
;
|
||||
wolfBoot_printf("Application running successfully!\n");
|
||||
wolfBoot_printf("\nEntering idle loop...\n");
|
||||
|
||||
/* Idle loop */
|
||||
while (1) {
|
||||
__asm__ volatile("wfi");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -65,3 +65,16 @@ _start:
|
|||
b 5b
|
||||
|
||||
.size _start, . - _start
|
||||
|
||||
/* Provide _exit stub for bare-metal builds (required by some standard library code) */
|
||||
.section .text, "ax"
|
||||
.global _exit
|
||||
.type _exit, @function
|
||||
|
||||
_exit:
|
||||
/* Loop forever - bare-metal applications don't exit */
|
||||
6:
|
||||
wfi
|
||||
b 6b
|
||||
|
||||
.size _exit, . - _exit
|
||||
|
|
|
|||
|
|
@ -2,15 +2,16 @@
|
|||
# Build, flash QSPI, and boot VMK180 - all in one script
|
||||
#
|
||||
# Usage:
|
||||
# ./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
|
||||
# ./versal_test.sh # Full build, flash, and boot wolfBoot
|
||||
# ./versal_test.sh --test-app # Full build + flash test app to boot partition
|
||||
# ./versal_test.sh --test-update # Full build + flash test app v2 to update partition
|
||||
# ./versal_test.sh --boot-sdcard # Test SD card boot mode only
|
||||
# ./versal_test.sh --boot-qspi # Test QSPI boot mode only
|
||||
#
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
WOLFBOOT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
|
||||
WOLFBOOT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
cd "$WOLFBOOT_ROOT"
|
||||
|
||||
# Config
|
||||
|
|
@ -19,7 +20,7 @@ UART_BAUD="${UART_BAUD:-115200}"
|
|||
SERVER_IP="${SERVER_IP:-10.0.4.24}"
|
||||
BOARD_IP="${BOARD_IP:-10.0.4.90}"
|
||||
TFTP_DIR="${TFTP_DIR:-/srv/tftp}"
|
||||
VITIS_PATH="${VITIS_PATH:-/opt/Xilinx/Vitis/2024.1}"
|
||||
VITIS_PATH="${VITIS_PATH:-/opt/Xilinx/Vitis/2024.2}"
|
||||
RELAY_PORT="${RELAY_PORT:-/dev/ttyACM2}"
|
||||
UART_LOG="${UART_LOG:-${WOLFBOOT_ROOT}/uart_log.txt}"
|
||||
|
||||
|
|
@ -37,6 +38,89 @@ for cmd in expect socat; do
|
|||
command -v "$cmd" &>/dev/null || { log_error "$cmd not found - install with: sudo apt install $cmd"; exit 1; }
|
||||
done
|
||||
|
||||
# Load configuration from .config file
|
||||
# Parses Makefile-style .config and sets global variables
|
||||
# Handles both KEY=VALUE and KEY?=VALUE syntax (for ?=, only sets if not already set)
|
||||
# Since .config uses Makefile syntax, we parse it directly rather than using make
|
||||
load_config() {
|
||||
local config_file="${1:-.config}"
|
||||
|
||||
[ ! -f "$config_file" ] && { log_error "Config file not found: $config_file"; return 1; }
|
||||
|
||||
# Extract variables from .config file
|
||||
# Pattern: KEY?=VALUE or KEY=VALUE (ignores comments and blank lines)
|
||||
while IFS= read -r line; do
|
||||
# Skip comments and blank lines
|
||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
[[ -z "${line// }" ]] && continue
|
||||
|
||||
# Extract key, conditional flag, and value
|
||||
# Match: optional whitespace, key, optional ?, =, optional whitespace, value
|
||||
if [[ "$line" =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)[[:space:]]*(\?)?=[[:space:]]*(.*)$ ]]; then
|
||||
local key="${BASH_REMATCH[1]}"
|
||||
local conditional="${BASH_REMATCH[2]}" # "?" if present
|
||||
local value="${BASH_REMATCH[3]}"
|
||||
|
||||
# Remove surrounding quotes if present
|
||||
value="${value#\"}"
|
||||
value="${value%\"}"
|
||||
|
||||
# Strip trailing whitespace from value
|
||||
value="${value%"${value##*[![:space:]]}"}"
|
||||
|
||||
# For ?= syntax, only set if variable is not already set
|
||||
if [ -n "$conditional" ]; then
|
||||
# Check if variable is already set using indirect reference
|
||||
# ${!key} expands to the value of the variable named by $key
|
||||
if [ -z "${!key:-}" ]; then
|
||||
# Variable not set, assign it using declare
|
||||
declare -g "${key}=${value}"
|
||||
fi
|
||||
else
|
||||
# Always set for = syntax
|
||||
declare -g "${key}=${value}"
|
||||
fi
|
||||
fi
|
||||
done < <(grep -E '^[[:space:]]*[A-Za-z_][A-Za-z0-9_]*[[:space:]]*(\?)?=' "$config_file" 2>/dev/null || true)
|
||||
|
||||
# Export all config variables as globals
|
||||
export IMAGE_HEADER_SIZE SIGN HASH SECONDARY_SIGN_OPTIONS SECONDARY_PRIVATE_KEY
|
||||
|
||||
# Calculate IMAGE_SIGNATURE_SIZE based on SIGN algorithm
|
||||
case "${SIGN:-}" in
|
||||
ECC256) IMAGE_SIGNATURE_SIZE=64 ;;
|
||||
ECC384) IMAGE_SIGNATURE_SIZE=96 ;;
|
||||
ECC521) IMAGE_SIGNATURE_SIZE=132 ;;
|
||||
ED25519) IMAGE_SIGNATURE_SIZE=64 ;;
|
||||
ED448) IMAGE_SIGNATURE_SIZE=114 ;;
|
||||
RSA2048) IMAGE_SIGNATURE_SIZE=256 ;;
|
||||
RSA3072) IMAGE_SIGNATURE_SIZE=384 ;;
|
||||
RSA4096) IMAGE_SIGNATURE_SIZE=512 ;;
|
||||
*) IMAGE_SIGNATURE_SIZE=96 ;; # Default to ECC384
|
||||
esac
|
||||
export IMAGE_SIGNATURE_SIZE
|
||||
|
||||
# Build SIGN_OPTIONS from SIGN and HASH
|
||||
SIGN_OPTIONS=""
|
||||
case "${SIGN:-}" in
|
||||
ECC256) SIGN_OPTIONS="--ecc256" ;;
|
||||
ECC384) SIGN_OPTIONS="--ecc384" ;;
|
||||
ECC521) SIGN_OPTIONS="--ecc521" ;;
|
||||
ED25519) SIGN_OPTIONS="--ed25519" ;;
|
||||
ED448) SIGN_OPTIONS="--ed448" ;;
|
||||
RSA2048) SIGN_OPTIONS="--rsa2048" ;;
|
||||
RSA3072) SIGN_OPTIONS="--rsa3072" ;;
|
||||
RSA4096) SIGN_OPTIONS="--rsa4096" ;;
|
||||
esac
|
||||
|
||||
case "${HASH:-}" in
|
||||
SHA256) SIGN_OPTIONS="$SIGN_OPTIONS --sha256" ;;
|
||||
SHA384) SIGN_OPTIONS="$SIGN_OPTIONS --sha384" ;;
|
||||
SHA3) SIGN_OPTIONS="$SIGN_OPTIONS --sha3" ;;
|
||||
esac
|
||||
export SIGN_OPTIONS
|
||||
}
|
||||
|
||||
# Initialize UART capture variables
|
||||
UART_PIDS=()
|
||||
UART_PTY=""
|
||||
|
|
@ -236,10 +320,12 @@ test_boot() {
|
|||
|
||||
# Check for test modes
|
||||
FLASH_TEST_APP=false
|
||||
FLASH_UPDATE_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 ;;
|
||||
--test-update) FLASH_TEST_APP=true; FLASH_UPDATE_APP=true ;;
|
||||
esac
|
||||
|
||||
# Build wolfBoot
|
||||
|
|
@ -249,16 +335,63 @@ 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
|
||||
if [ "$FLASH_UPDATE_APP" = "true" ]; then
|
||||
log_info "Building and signing test application version 2..."
|
||||
make test-app/image.bin
|
||||
|
||||
testapp_size=$(stat -c%s "test-app/image_v1_signed.bin")
|
||||
log_info "Test app size: $testapp_size bytes"
|
||||
# Sign as version 2 for update testing
|
||||
# Load all config values from .config file
|
||||
load_config .config
|
||||
|
||||
# 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"
|
||||
IMAGE_TRAILER_SIZE=0 # Usually 0 unless delta updates are used
|
||||
PRIVATE_KEY="${PRIVATE_KEY:-wolfboot_signing_private_key.der}"
|
||||
|
||||
BOOT_IMG="test-app/image.bin"
|
||||
|
||||
# Build sign command with environment variables
|
||||
# The sign tool needs IMAGE_HEADER_SIZE and IMAGE_SIGNATURE_SIZE as environment variables
|
||||
export IMAGE_HEADER_SIZE IMAGE_SIGNATURE_SIZE IMAGE_TRAILER_SIZE
|
||||
|
||||
log_info "Signing test app as version 2..."
|
||||
log_info " IMAGE_HEADER_SIZE=$IMAGE_HEADER_SIZE"
|
||||
log_info " IMAGE_SIGNATURE_SIZE=$IMAGE_SIGNATURE_SIZE"
|
||||
log_info " SIGN=$SIGN"
|
||||
log_info " HASH=$HASH"
|
||||
log_info " SIGN_OPTIONS=$SIGN_OPTIONS"
|
||||
log_info " PRIVATE_KEY=$PRIVATE_KEY"
|
||||
|
||||
# Sign the image as version 2
|
||||
if [ "$SIGN" != "NONE" ] && [ -n "$SECONDARY_PRIVATE_KEY" ]; then
|
||||
./tools/keytools/sign $SIGN_OPTIONS $SECONDARY_SIGN_OPTIONS "$BOOT_IMG" "$PRIVATE_KEY" "$SECONDARY_PRIVATE_KEY" 2 || {
|
||||
log_error "Signing failed with secondary key"
|
||||
exit 1
|
||||
}
|
||||
elif [ "$SIGN" != "NONE" ]; then
|
||||
./tools/keytools/sign $SIGN_OPTIONS "$BOOT_IMG" "$PRIVATE_KEY" 2 || {
|
||||
log_error "Signing failed"
|
||||
exit 1
|
||||
}
|
||||
else
|
||||
./tools/keytools/sign $SIGN_OPTIONS "$BOOT_IMG" 2 || {
|
||||
log_error "Signing failed (SIGN=NONE)"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
testapp_size=$(stat -c%s "test-app/image_v2_signed.bin")
|
||||
log_info "Test app v2 size: $testapp_size bytes"
|
||||
cp test-app/image_v2_signed.bin "${TFTP_DIR}/"
|
||||
log_ok "Test app v2 copied to TFTP: ${TFTP_DIR}/image_v2_signed.bin"
|
||||
else
|
||||
log_info "Building and signing test application version 1..."
|
||||
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"
|
||||
cp test-app/image_v1_signed.bin "${TFTP_DIR}/"
|
||||
log_ok "Test app copied to TFTP: ${TFTP_DIR}/image_v1_signed.bin"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Generate BOOT.BIN
|
||||
|
|
@ -271,7 +404,7 @@ export PREBUILT_DIR="${WOLFBOOT_ROOT}/../soc-prebuilt-firmware/vmk180-versal"
|
|||
log_info "Copying prebuilt firmware files..."
|
||||
[ ! -d "${PREBUILT_DIR}" ] && {
|
||||
log_error "Prebuilt firmware directory not found: ${PREBUILT_DIR}"
|
||||
log_info "Clone with: git clone --branch xlnx_rel_v2024.1 https://github.com/Xilinx/soc-prebuilt-firmware.git"
|
||||
log_info "Clone with: git clone --branch xlnx_rel_v2024.2 https://github.com/Xilinx/soc-prebuilt-firmware.git"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
|
@ -283,7 +416,7 @@ cp "${PREBUILT_DIR}/system-default.dtb" .
|
|||
|
||||
# Generate BOOT.BIN from wolfBoot root directory
|
||||
source "${VITIS_PATH}/settings64.sh"
|
||||
bootgen -arch versal -image ./tools/scripts/vmk180/boot_wolfboot.bif -w -o BOOT.BIN
|
||||
bootgen -arch versal -image ./tools/scripts/versal_boot.bif -w -o BOOT.BIN
|
||||
|
||||
# Copy BOOT.BIN to TFTP directory
|
||||
cp BOOT.BIN "${TFTP_DIR}/"
|
||||
|
|
@ -295,9 +428,15 @@ 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"
|
||||
if [ "$FLASH_UPDATE_APP" = "true" ]; then
|
||||
testapp_size=$(stat -c%s "${TFTP_DIR}/image_v2_signed.bin")
|
||||
testapp_size_hex=$(printf "0x%x" $testapp_size)
|
||||
log_info "Test app v2 size: $testapp_size bytes"
|
||||
else
|
||||
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
|
||||
fi
|
||||
|
||||
# Flash QSPI via U-Boot TFTP
|
||||
|
|
@ -310,6 +449,7 @@ set pty "$UART_PTY"
|
|||
set filesize_hex "$filesize_hex"
|
||||
set testapp_size_hex "$testapp_size_hex"
|
||||
set flash_test_app "$FLASH_TEST_APP"
|
||||
set flash_update_app "$FLASH_UPDATE_APP"
|
||||
set board_ip "$BOARD_IP"
|
||||
set server_ip "$SERVER_IP"
|
||||
|
||||
|
|
@ -452,33 +592,70 @@ 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 ==="
|
||||
if { \$flash_update_app eq "true" } {
|
||||
puts ""
|
||||
puts "=== Flashing test app v2 to UPDATE partition at 0x3400000 ==="
|
||||
|
||||
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 }
|
||||
puts "Downloading test app v2 via TFTP..."
|
||||
send "tftpboot 0x10000000 image_v2_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 UPDATE partition at 0x3400000 (128KB sector)..."
|
||||
send "sf erase 0x3400000 0x20000\r"
|
||||
expect {
|
||||
"Versal>" { puts "Erase complete" }
|
||||
timeout { puts "Erase timeout"; exit 1 }
|
||||
}
|
||||
|
||||
puts "Writing test app v2 to 0x3400000..."
|
||||
send "sf write 0x10000000 0x3400000 \$testapp_size_hex\r"
|
||||
expect {
|
||||
"Versal>" { puts "Write complete" }
|
||||
timeout { puts "Write timeout"; exit 1 }
|
||||
}
|
||||
|
||||
puts "Test app v2 flashed to UPDATE partition!"
|
||||
} else {
|
||||
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 "Erasing update partition at 0x3400000 (128KB sector)..."
|
||||
send "sf erase 0x3400000 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!"
|
||||
}
|
||||
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 ""
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
# build_flash_qspi.sh
|
||||
|
||||
All-in-one script for building wolfBoot, generating BOOT.BIN, flashing QSPI, and booting VMK180.
|
||||
|
||||
wolfBoot replaces U-Boot in the Versal boot flow:
|
||||
|
||||
```
|
||||
PLM (PMC) -> PSM -> BL31 (EL3) -> wolfBoot (EL2) -> Linux (EL1)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Full build, flash, and boot from QSPI
|
||||
./build_flash_qspi.sh
|
||||
|
||||
# Test boot mode switching only (no build/flash)
|
||||
./build_flash_qspi.sh --boot-sdcard # Test SD card boot mode
|
||||
./build_flash_qspi.sh --boot-qspi # Test QSPI boot mode
|
||||
```
|
||||
|
||||
## What It Does
|
||||
|
||||
1. **Builds wolfBoot**: Compiles wolfBoot from source
|
||||
2. **Generates BOOT.BIN**:
|
||||
- Copies prebuilt firmware files from `../soc-prebuilt-firmware/vmk180-versal/` to wolfBoot root
|
||||
- Runs bootgen to create BOOT.BIN
|
||||
- Copies BOOT.BIN to TFTP directory
|
||||
3. **Flashes QSPI**:
|
||||
- Sets board to SD card boot mode
|
||||
- Captures UART output via PTY bridge
|
||||
- Interrupts U-Boot autoboot
|
||||
- Configures network (TFTP server/client)
|
||||
- Downloads BOOT.BIN via TFTP
|
||||
- Erases and programs QSPI flash
|
||||
- Verifies flash contents
|
||||
4. **Boots from QSPI**:
|
||||
- Switches boot mode to QSPI
|
||||
- Captures UART output for 30 seconds
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Prebuilt firmware**: Clone `soc-prebuilt-firmware` repository:
|
||||
```bash
|
||||
git clone --branch xlnx_rel_v2024.1 https://github.com/Xilinx/soc-prebuilt-firmware.git
|
||||
```
|
||||
Place it as a sibling directory to wolfBoot (i.e., `../soc-prebuilt-firmware/`)
|
||||
|
||||
- **TFTP server**: Install and configure:
|
||||
```bash
|
||||
sudo apt install tftpd-hpa
|
||||
sudo mkdir -p /srv/tftp
|
||||
sudo chmod 777 /srv/tftp
|
||||
```
|
||||
|
||||
- **Required tools**: `expect` and `socat`
|
||||
```bash
|
||||
sudo apt install expect socat
|
||||
```
|
||||
|
||||
- **Vitis 2024.1 or 2024.2**: Required for bootgen
|
||||
```bash
|
||||
export VITIS_PATH=/opt/Xilinx/Vitis/2024.1
|
||||
```
|
||||
|
||||
- **Relay board**: Connected to `/dev/ttyACM2` (configurable via `RELAY_PORT`)
|
||||
|
||||
- **UART connection**: VMK180 UART0 connected (default: `/dev/ttyUSB2`)
|
||||
|
||||
- **ARM Toolchain**: `aarch64-none-elf-gcc`
|
||||
|
||||
## Configuration
|
||||
|
||||
Environment variables can be customized:
|
||||
|
||||
```bash
|
||||
export UART_PORT=/dev/ttyUSB2 # VMK180 UART port
|
||||
export UART_BAUD=115200 # UART baud rate
|
||||
export SERVER_IP=10.0.4.24 # TFTP server IP (host PC)
|
||||
export BOARD_IP=10.0.4.90 # VMK180 IP address
|
||||
export TFTP_DIR=/srv/tftp # TFTP directory
|
||||
export VITIS_PATH=/opt/Xilinx/Vitis/2024.1 # Vitis installation
|
||||
export RELAY_PORT=/dev/ttyACM2 # Relay board serial port
|
||||
export UART_LOG=./uart_log.txt # UART log file
|
||||
```
|
||||
|
||||
## UART Capture
|
||||
|
||||
The script automatically captures UART output:
|
||||
- Creates a PTY bridge using `socat` for reliable capture
|
||||
- Logs all output to `uart_log.txt` (default)
|
||||
- Continues capturing after flash completes
|
||||
- Press Ctrl+C to stop early (capture continues in background)
|
||||
|
||||
View live output:
|
||||
```bash
|
||||
tail -f uart_log.txt
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Prebuilt firmware not found
|
||||
|
||||
Ensure `soc-prebuilt-firmware` is cloned as a sibling directory to wolfBoot:
|
||||
```bash
|
||||
cd ..
|
||||
git clone --branch xlnx_rel_v2024.1 https://github.com/Xilinx/soc-prebuilt-firmware.git
|
||||
```
|
||||
|
||||
### TFTP timeout
|
||||
|
||||
- Check network connection between host PC and VMK180
|
||||
- Verify IP addresses match (`SERVER_IP` and `BOARD_IP`)
|
||||
- Ensure TFTP server is running: `sudo systemctl status tftpd-hpa`
|
||||
|
||||
### UART capture fails
|
||||
|
||||
- Verify UART port permissions: `sudo chmod 666 /dev/ttyUSB*`
|
||||
- Check UART port is correct: `ls -la /dev/ttyUSB*`
|
||||
- Ensure no other process is using the port
|
||||
|
||||
### Relay control fails
|
||||
|
||||
- Check relay board connection and port (`RELAY_PORT`)
|
||||
- Verify relay board is powered and connected
|
||||
- Check serial port permissions: `sudo chmod 666 /dev/ttyACM*`
|
||||
|
||||
### Boot hangs or wolfBoot doesn't start
|
||||
|
||||
- Verify BL31 is correctly jumping to 0x8000000
|
||||
- Check that wolfBoot entry point matches: `aarch64-none-elf-readelf -h wolfboot.elf`
|
||||
- Check UART output for error messages
|
||||
- Verify prebuilt firmware files are correct for your board revision
|
||||
Loading…
Reference in New Issue