From ee3c313a66c9a5ae4e70a99ea681a30dd9bcc190 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 16 Jan 2026 11:03:03 -0800 Subject: [PATCH] Versal port cleanups --- .github/workflows/test-configs.yml | 6 + docs/Targets.md | 31 +- hal/versal.c | 627 +++++++----------- include/hal.h | 17 + src/libwolfboot.c | 5 +- src/update_disk.c | 22 +- src/update_ram.c | 15 +- test-app/AARCH64-ls1028a.ld | 55 -- test-app/Makefile | 13 +- test-app/app_versal.c | 75 +-- test-app/boot_arm64_start.S | 13 + .../boot_wolfboot.bif => versal_boot.bif} | 0 .../build_flash_qspi.sh => versal_test.sh} | 265 ++++++-- tools/scripts/vmk180/README.md | 133 ---- 14 files changed, 564 insertions(+), 713 deletions(-) delete mode 100644 test-app/AARCH64-ls1028a.ld rename tools/scripts/{vmk180/boot_wolfboot.bif => versal_boot.bif} (100%) rename tools/scripts/{vmk180/build_flash_qspi.sh => versal_test.sh} (58%) delete mode 100644 tools/scripts/vmk180/README.md diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index d173cdec..01374c1f 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -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: diff --git a/docs/Targets.md b/docs/Targets.md index d0f25b2d..2037e959 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -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... diff --git a/hal/versal.c b/hal/versal.c index ee5b03c6..7c4f0fa1 100644 --- a/hal/versal.c +++ b/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); diff --git a/include/hal.h b/include/hal.h index 92f04ca0..3b1ff17e 100644 --- a/include/hal.h +++ b/include/hal.h @@ -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); diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 5daf23bf..8f5ab880 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -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()); } diff --git a/src/update_disk.c b/src/update_disk.c index 614eedf0..0055e365 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -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 */ } diff --git a/src/update_ram.c b/src/update_ram.c index 8f8f00a9..82193aaf 100644 --- a/src/update_ram.c +++ b/src/update_ram.c @@ -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 diff --git a/test-app/AARCH64-ls1028a.ld b/test-app/AARCH64-ls1028a.ld deleted file mode 100644 index c96342bb..00000000 --- a/test-app/AARCH64-ls1028a.ld +++ /dev/null @@ -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; diff --git a/test-app/Makefile b/test-app/Makefile index 47acba02..c2ac52a4 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -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) diff --git a/test-app/app_versal.c b/test-app/app_versal.c index 2bac3b75..935dfa26 100644 --- a/test-app/app_versal.c +++ b/test-app/app_versal.c @@ -22,74 +22,37 @@ */ #include + +#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"); } } - - diff --git a/test-app/boot_arm64_start.S b/test-app/boot_arm64_start.S index 5f71f25c..7f7ca7e1 100644 --- a/test-app/boot_arm64_start.S +++ b/test-app/boot_arm64_start.S @@ -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 diff --git a/tools/scripts/vmk180/boot_wolfboot.bif b/tools/scripts/versal_boot.bif similarity index 100% rename from tools/scripts/vmk180/boot_wolfboot.bif rename to tools/scripts/versal_boot.bif diff --git a/tools/scripts/vmk180/build_flash_qspi.sh b/tools/scripts/versal_test.sh similarity index 58% rename from tools/scripts/vmk180/build_flash_qspi.sh rename to tools/scripts/versal_test.sh index 2e947118..4d63b4fb 100755 --- a/tools/scripts/vmk180/build_flash_qspi.sh +++ b/tools/scripts/versal_test.sh @@ -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 "" diff --git a/tools/scripts/vmk180/README.md b/tools/scripts/vmk180/README.md deleted file mode 100644 index 1ef9a288..00000000 --- a/tools/scripts/vmk180/README.md +++ /dev/null @@ -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