Versal SDCard support

pull/681/head
David Garske 2026-02-02 14:50:27 -08:00 committed by Daniele Lacamera
parent 39085635c6
commit 9697f6a4f8
12 changed files with 900 additions and 274 deletions

21
arch.mk
View File

@ -87,6 +87,12 @@ ifeq ($(ARCH),AARCH64)
CFLAGS+=-DWOLFBOOT_DUALBOOT
# Support detection and skip of U-Boot legacy header
CFLAGS+=-DWOLFBOOT_UBOOT_LEGACY
# PLM owns RVBAR on Versal in JTAG boot; skip RVBAR writes
CFLAGS+=-DSKIP_RVBAR=1
# Disable SDMA for multi-block transfers - use PIO instead.
# The Versal Arasan SDHCI controller does not restart SDMA after
# boundary crossings via SRS22/SRS23 writes (Cadence-specific behavior).
CFLAGS_EXTRA+=-DSDHCI_SDMA_DISABLED
endif
ifeq ($(TARGET),nxp_ls1028a)
@ -1520,7 +1526,20 @@ BOOT_IMG?=test-app/image.bin
ifeq ($(ARCH),AARCH64)
CFLAGS+=-DMMU -DWOLFBOOT_FDT -DWOLFBOOT_DUALBOOT
OBJS+=src/fdt.o
UPDATE_OBJS:=src/update_ram.o
ifneq ($(filter 1,$(DISK_SDCARD) $(DISK_EMMC)),)
# Disk-based boot (SD card or eMMC)
CFLAGS+=-DWOLFBOOT_UPDATE_DISK
ifeq ($(MAX_DISKS),)
MAX_DISKS=1
endif
CFLAGS+=-DMAX_DISKS=$(MAX_DISKS)
UPDATE_OBJS:=src/update_disk.o
OBJS+=src/gpt.o
OBJS+=src/disk.o
else
# RAM-based boot from external flash (default)
UPDATE_OBJS:=src/update_ram.o
endif
else
ifeq ($(DUALBANK_SWAP),1)
CFLAGS+=-DWOLFBOOT_DUALBOOT

View File

@ -0,0 +1,113 @@
# wolfBoot configuration for AMD Versal VMK180 - SD Card Boot
# Versal Prime VM1802 ACAP - Dual ARM Cortex-A72
#
# This configuration enables SD card boot for the Versal:
# PLM -> PSM -> BL31 (EL3) -> wolfBoot (EL2) -> Linux (EL1)
#
# wolfBoot loads firmware images from MBR partitions on SD card.
# Uses the generic SDHCI driver with SD1 controller (external SD slot).
ARCH?=AARCH64
TARGET?=versal
WOLFBOOT_VERSION?=1
# ECC-384 with SHA-384 (good balance of security and performance)
SIGN?=ECC384
HASH?=SHA384
IMAGE_HEADER_SIZE?=512
# Debug options
DEBUG?=1
DEBUG_SYMBOLS=1
DEBUG_UART=1
# SD card support - use SDHCI driver
DISK_SDCARD?=1
DISK_EMMC?=0
# Disable QSPI flash when using SD card
EXT_FLASH?=0
NO_XIP=1
# ELF loading support
ELF?=1
# Boot Benchmarking (optional)
BOOT_BENCHMARK?=1
# General options
VTOR?=1
CORTEX_M0?=0
NO_ASM?=0
ALLOW_DOWNGRADE?=0
NVM_FLASH_WRITEONCE?=0
V?=0
SPMATH?=1
RAM_CODE?=0
DUALBANK_SWAP?=0
PKA?=0
WOLFTPM?=0
# Toolchain
USE_GCC=1
CROSS_COMPILE=aarch64-none-elf-
# ============================================================================
# Partition Layout - MBR (required by Versal boot ROM)
# ============================================================================
# SD Card partition layout (MBR):
# Partition 1: boot (128MB, FAT32 LBA, bootable) - BOOT.BIN
# Partition 2: OFP_A (200MB, Linux) - Primary signed image
# Partition 3: OFP_B (200MB, Linux) - Update signed image
# Partition 4: rootfs (remainder) - Linux root filesystem
#
# Use partition numbers instead of flash addresses
# These are 0-based indices into the parsed partition array:
# part[0]=boot, part[1]=OFP_A, part[2]=OFP_B, part[3]=rootfs
WOLFBOOT_NO_PARTITIONS=1
CFLAGS_EXTRA+=-DBOOT_PART_A=1
CFLAGS_EXTRA+=-DBOOT_PART_B=2
# Disk read chunk size (512KB)
CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x80000
# Linux rootfs is on partition 4 (default is /dev/mmcblk0p2 for QSPI boot)
CFLAGS_EXTRA+=-DLINUX_BOOTARGS_ROOT=\"/dev/mmcblk0p4\"
# ============================================================================
# Boot Memory Layout
# ============================================================================
# wolfBoot runs from DDR at 0x8000000 (same address as U-Boot)
WOLFBOOT_ORIGIN=0x8000000
# Load Partition to RAM Address (Linux kernel loads here)
WOLFBOOT_LOAD_ADDRESS?=0x10000000
# DTS (Device Tree) load address
# Must be in DDR low (0x0-0x7FFFFFFF) - matches QSPI config and FIT ITS load address
WOLFBOOT_LOAD_DTS_ADDRESS?=0x1000
# ============================================================================
# Required for test-app (even with WOLFBOOT_NO_PARTITIONS=1)
# ============================================================================
WOLFBOOT_PARTITION_BOOT_ADDRESS=0x80200000
WOLFBOOT_PARTITION_SIZE=0x4000000
WOLFBOOT_SECTOR_SIZE=0x1000
# ============================================================================
# UART Configuration - UART1 for APU console (matches VMK180 board)
# ============================================================================
CFLAGS_EXTRA+=-DDEBUG_UART_NUM=0
# ============================================================================
# Optional Debug Options (uncomment to enable)
# ============================================================================
# SDHCI driver debug logs
#CFLAGS_EXTRA+=-DDEBUG_SDHCI
# Disk layer debug logs
#CFLAGS_EXTRA+=-DDEBUG_DISK
# GPT partition debug logs
#CFLAGS_EXTRA+=-DDEBUG_GPT
# Disk read/write test at boot
#CFLAGS_EXTRA+=-DDISK_TEST

View File

@ -1982,25 +1982,33 @@ qemu-system-aarch64 -machine xlnx-zcu102 -cpu cortex-a53 -serial stdio -display
## Versal Gen 1 VMK180
AMD Versal Prime Series VMK180 Evaluation Kit - Versal Prime XCVM1802-2MSEVSVA2197 Adaptive SoC - Dual ARM Cortex-A72
AMD Versal Prime Series VMK180 Evaluation Kit - Versal Prime XCVM1802-2MSEVSVA2197 Adaptive SoC - Dual ARM Cortex-A72.
wolfBoot replaces U-Boot in the Versal boot flow:
```
PLM -> PSM -> BL31 (EL3) -> wolfBoot (EL2) -> Linux (EL1)
```
wolfBoot runs from DDR at address `0x8000000` at EL2 (non-secure). All clock, MIO, and DDR initialization is handled by PLM/PSM before wolfBoot starts.
wolfBoot runs from DDR at `0x8000000` (EL2, non-secure). All clock, MIO, and DDR initialization is handled by PLM/PSM before wolfBoot starts.
See example configuration file at `config/examples/versal_vmk180.config`.
This target supports **two boot paths**:
- **QSPI boot** (primary, production-style): `config/examples/versal_vmk180.config`
- **SD card boot** (MBR, A/B images): `config/examples/versal_vmk180_sdcard.config`
### Prerequisites
1. **Xilinx Vitis 2024.1 or 2024.2** (required for bootgen - 2025.1 or later has QSPI boot issues)
1. **Xilinx Vitis 2024.1 or newer**
Note: If using QSPI there are bootgen issues with 2025.1+, so recommend 2024.1 or 2024.2
- Set `VITIS_PATH` environment variable: `export VITIS_PATH=/opt/Xilinx/Vitis/2024.1`
2. **Toolchain**: `aarch64-none-elf-gcc`
2. **Toolchain**
- ARM GCC toolchain: `aarch64-none-elf-gcc`
### Common Notes
- Debugging with OCRAM (OCM): set `WOLFBOOT_ORIGIN=0xFFFC0000` (OCM is 256KB at `0xFFFC0000 - 0xFFFFFFFF`).
- Test application uses generic `boot_arm64_start.S` and `AARCH64.ld` and prints EL + version.
- Entry point: `_start` (in `boot_arm64_start.S`) which sets up stack, clears BSS, and calls `main()`
### Configuration Options
@ -2014,8 +2022,11 @@ Key configuration options in `config/examples/versal_vmk180.config`:
- `EXT_FLASH=1` - External flash support
- `ELF=1` - ELF loading support
### Memory Layout
### QSPI Boot (default)
Use `config/examples/versal_vmk180.config`.
**QSPI layout**
| Partition | Size | Address | Description |
|-------------|--------|---------|-------------|
| Bootloader | - | 0x8000000 | wolfBoot in DDR (loaded by BL31) |
@ -2023,51 +2034,41 @@ Key configuration options in `config/examples/versal_vmk180.config`:
| Update | 44MB | 0x3400000 | Update partition in QSPI |
| Swap | - | 0x6000000 | Swap area in QSPI |
### Debugging
**QSPI Flash**
For debugging with OCRAM (OCM), set `WOLFBOOT_ORIGIN=0xFFFC0000` in the config file. Versal Gen 1 OCM is 256KB at `0xFFFC0000 - 0xFFFFFFFF`.
### Building wolfBoot
Build wolfBoot from the wolfBoot root directory:
VMK180 uses dual parallel MT25QU01GBBB flash (128MB each, 256MB total). The QSPI driver supports:
- DMA mode (default) or IO polling mode (`GQSPI_MODE_IO`)
- Quad SPI (4-bit) for faster reads
- 4-byte addressing for full flash access
- Hardware striping for dual parallel operation
- 75MHz default clock (configurable via `GQSPI_CLK_DIV`)
**Build wolfBoot**
```sh
cp config/examples/versal_vmk180.config .config
make clean
make
```
### Building BOOT.BIN
If you don't already have prebuilt firmware, clone the Xilinx prebuilt firmware repository:
**Build BOOT.BIN**
```sh
git clone --branch xlnx_rel_v2024.2 https://github.com/Xilinx/soc-prebuilt-firmware.git
export PREBUILT_DIR=$(pwd)/../soc-prebuilt-firmware/vmk180-versal
```
Copy the required files into wolfboot root directory:
```sh
cp ${PREBUILT_DIR}/project_1.pdi .
cp ${PREBUILT_DIR}/plm.elf .
cp ${PREBUILT_DIR}/psmfw.elf .
cp ${PREBUILT_DIR}/bl31.elf .
cp ${PREBUILT_DIR}/system-default.dtb .
```
Source the Vitis environment and generate BOOT.BIN using bootgen:
```sh
source ${VITIS_PATH}/settings64.sh
bootgen -arch versal -image ./tools/scripts/vmk180/boot_wolfboot.bif -w -o BOOT.BIN
```
The BIF file (`boot_wolfboot.bif`) references files using relative paths in the same directory. After successful generation, `BOOT.BIN` will be created in `tools/scripts/vmk180/`.
The BIF file (`boot_wolfboot.bif`) references files using relative paths in the same directory.
### Flashing QSPI
**Flash QSPI**
Flash `BOOT.BIN` to QSPI flash using your preferred method. For example:
Flash `BOOT.BIN` to QSPI flash using your preferred method:
- **Vitis**: Use the Hardware Manager to program the QSPI flash via JTAG. Load `BOOT.BIN` and program to QSPI32 flash memory.
@ -2081,48 +2082,23 @@ Flash `BOOT.BIN` to QSPI flash using your preferred method. For example:
sf write ${loadaddr} 0 ${filesize}
```
### QSPI Flash
VMK180 uses dual parallel MT25QU01GBBB flash (128MB each, 256MB total). The QSPI driver supports:
- DMA mode (default) or IO polling mode (`GQSPI_MODE_IO`)
- Quad SPI (4-bit) for faster reads
- 4-byte addressing for full flash access
- Hardware striping for dual parallel operation
- 75MHz default clock (configurable via `GQSPI_CLK_DIV`)
### Building and Signing Test Application
```sh
# Build and sign the test application
make test-app/image_v1_signed.bin
```
The signed test application will be at `test-app/image_v1_signed.bin`.
**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()`
### Firmware Update Testing
**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:**
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.
To test firmware updates, build and sign the test application with different version numbers, then flash them to the appropriate partitions.
### Example Boot Output
**Example Boot Output**
```
========================================
@ -2157,65 +2133,37 @@ Application running successfully!
Entering idle loop...
```
### Booting PetaLinux
**Booting PetaLinux (QSPI)**
wolfBoot can boot a signed Linux kernel on the Versal VMK180. This replaces U-Boot entirely for a secure boot chain.
#### Prerequisites
wolfBoot can boot a signed Linux kernel on the Versal VMK180, replacing U-Boot entirely for a secure boot chain.
Prerequisites:
1. **PetaLinux 2024.2** (or compatible version) built for VMK180
2. **Pre-built Linux images** from your PetaLinux build:
- `Image` - Uncompressed Linux kernel (ARM64)
- `system-default.dtb` - Device tree blob for VMK180
- `bl31.elf` - ARM Trusted Firmware
- `plm.elf` - Platform Loader & Manager
- `psmfw.elf` - PSM firmware
3. **SD card** with root filesystem (PetaLinux rootfs.ext4 written to partition 2)
#### Boot Flow
```
PLM -> PSM -> BL31 (EL3) -> wolfBoot (EL2) -> Linux (EL1)
```
wolfBoot:
1. Loads the signed FIT image from QSPI flash
2. Verifies the cryptographic signature (ECC384/SHA384)
3. Parses the FIT image to extract kernel and DTB
4. Applies DTB fixups (bootargs for root filesystem)
5. Transitions from EL2 to EL1 and jumps to the kernel
#### Creating the FIT Image
wolfBoot uses a FIT (Flattened Image Tree) image containing the kernel and device tree. Create the FIT image using the provided ITS file:
```sh
# Copy Linux images to wolfBoot root directory
cp /path/to/petalinux/images/linux/Image .
cp /path/to/petalinux/images/linux/system-default.dtb .
# Create FIT image using mkimage
mkimage -f hal/versal.its fitImage
```
The ITS file (`hal/versal.its`) specifies:
wolfBoot uses a FIT (Flattened Image Tree) image containing the kernel and device tree. The ITS file (`hal/versal.its`) specifies:
- Kernel load address: `0x00200000`
- DTB load address: `0x00001000`
- SHA256 hashes for integrity
#### Signing the FIT Image
Sign the FIT image with wolfBoot tools:
Create and sign the FIT image, then flash to QSPI:
```sh
# Sign with ECC384 (default for Versal config)
cp /path/to/petalinux/images/linux/Image .
cp /path/to/petalinux/images/linux/system-default.dtb .
mkimage -f hal/versal.its fitImage
./tools/keytools/sign --ecc384 --sha384 fitImage wolfboot_signing_private_key.der 1
tftp ${loadaddr} fitImage_v1_signed.bin
sf probe 0
sf erase 0x800000 +${filesize}
sf write ${loadaddr} 0x800000 ${filesize}
```
This creates `fitImage_v1_signed.bin`.
#### DTB Fixup for Root Filesystem
**DTB Fixup for Root Filesystem**
wolfBoot automatically modifies the device tree to set the kernel command line (`bootargs`). The default configuration mounts the root filesystem from SD card partition 2:
@ -2230,31 +2178,14 @@ To customize the root device, add to your config:
CFLAGS_EXTRA+=-DLINUX_BOOTARGS_ROOT=\"/dev/mmcblk0p4\"
```
#### Flashing to QSPI
Flash the signed FIT image to the boot partition at `0x800000`:
**Automated Testing**
```sh
# From U-Boot (via SD card boot)
tftp ${loadaddr} fitImage_v1_signed.bin
sf probe 0
sf erase 0x800000 +${filesize}
sf write ${loadaddr} 0x800000 ${filesize}
```
#### Automated Testing
The test script supports Linux boot testing:
```sh
# Set path to PetaLinux images
export LINUX_IMAGES_DIR=/path/to/petalinux/images/linux
# Build wolfBoot, create signed FIT, flash to QSPI, and boot
./tools/scripts/versal_test.sh --linux
```
#### Example Linux Boot Output
**Example Linux Boot Output**
```
========================================
@ -2294,7 +2225,7 @@ PetaLinux 2024.2 xilinx-vmk180 ttyAMA0
xilinx-vmk180 login:
```
#### Boot Performance
**Boot Performance**
Typical boot timing with ECC384/SHA384 signing:
@ -2305,6 +2236,69 @@ Typical boot timing with ECC384/SHA384 signing:
| ECC384 signature verify | ~3ms |
| **Total wolfBoot overhead** | **~870ms** |
---
### SD Card Boot (MBR + A/B)
Use `config/examples/versal_vmk180_sdcard.config`. This uses the Arasan SDHCI controller and an **MBR** partitioned SD card.
**Partition layout**
| Partition | Name | Size | Type | Contents |
|-----------|------|------|------|----------|
| 1 | boot | 128MB | FAT32 LBA (0x0c), bootable | BOOT.BIN (PLM + PSM + BL31 + wolfBoot) |
| 2 | OFP_A | 200MB | Linux (0x83) | Primary signed firmware image |
| 3 | OFP_B | 200MB | Linux (0x83) | Update signed firmware image |
| 4 | rootfs | remainder | Linux (0x83) | Linux root filesystem |
**Build wolfBoot + sign test images**
```sh
cp config/examples/versal_vmk180_sdcard.config .config
make clean
make
make test-app/image.bin
./tools/keytools/sign --ecc384 --sha384 test-app/image.bin wolfboot_signing_private_key.der 1
./tools/keytools/sign --ecc384 --sha384 test-app/image.bin wolfboot_signing_private_key.der 2
```
**Create SD image**
```sh
dd if=/dev/zero of=sdcard.img bs=1M count=1024
sfdisk sdcard.img <<EOF
label: dos
unit: sectors
1 : start=2048, size=128M, type=c, bootable
2 : size=200M, type=83
3 : size=200M, type=83
4 : type=83
EOF
SECTOR2=$(sfdisk -d sdcard.img | awk '/sdcard.img2/ {for (i=1;i<=NF;i++) if ($i ~ /start=/) {gsub(/start=|,/, "", $i); print $i}}')
SECTOR3=$(sfdisk -d sdcard.img | awk '/sdcard.img3/ {for (i=1;i<=NF;i++) if ($i ~ /start=/) {gsub(/start=|,/, "", $i); print $i}}')
dd if=test-app/image_v1_signed.bin of=sdcard.img bs=512 seek=$SECTOR2 conv=notrunc
dd if=test-app/image_v2_signed.bin of=sdcard.img bs=512 seek=$SECTOR3 conv=notrunc
```
**Provision SD card**
```sh
sudo dd if=sdcard.img of=/dev/sdX bs=4M status=progress conv=fsync
sync
sudo mkfs.vfat -F 32 -n BOOT /dev/sdX1
sudo mount /dev/sdX1 /mnt
sudo cp BOOT.BIN /mnt/
sudo umount /mnt
sudo fdisk -l /dev/sdX
```
**Boot Mode**
| Boot Mode | MODE Pins 3:0 | Mode SW1[4:1] |
| --------- | ------------- | -------------- |
| JTAG | 0 0 0 0 | on, on, on, on |
| QSPI32 | 0 0 1 0 | on, on, off,on |
| SD1 | 1 1 1 0 | off,off,off,on |
## Cypress PSoC-6

View File

@ -1144,6 +1144,8 @@ static void qspi_init(void)
#endif
}
#endif /* EXT_FLASH */
/* ============================================================================
* HAL Public Interface
* ============================================================================
@ -1151,17 +1153,16 @@ 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
uart_init();
#ifdef __WOLFBOOT
wolfBoot_printf("%s", banner);
#if defined(DEBUG_UART) && defined(__WOLFBOOT)
wolfBoot_printf(
"\n========================================\n"
"wolfBoot Secure Boot - AMD Versal\n"
#ifndef WOLFBOOT_REPRODUCIBLE_BUILD
"Build: " __DATE__ " " __TIME__ "\n"
#endif
"========================================\n");
wolfBoot_printf("Current EL: %d\n", current_el());
#endif
@ -1170,8 +1171,6 @@ void hal_init(void)
#endif
}
#endif /* EXT_FLASH */
void hal_prepare_boot(void)
{
#if defined(EXT_FLASH) && GQPI_USE_4BYTE_ADDR == 1
@ -1297,7 +1296,7 @@ int hal_dts_fixup(void* dts_addr)
#endif /* __WOLFBOOT */
#endif /* MMU */
#ifdef WOLFBOOT_DUALBOOT
#if defined(WOLFBOOT_DUALBOOT) && !defined(WOLFBOOT_NO_PARTITIONS)
/**
* Get the primary (boot) partition address in flash
* Returns the flash address where the boot partition starts
@ -1315,7 +1314,7 @@ void* hal_get_update_address(void)
{
return (void*)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
}
#endif /* WOLFBOOT_DUALBOOT */
#endif /* WOLFBOOT_DUALBOOT && !WOLFBOOT_NO_PARTITIONS */
/* ============================================================================
* Flash Functions (STUBS)
@ -1524,5 +1523,158 @@ int ext_flash_erase(uintptr_t address, int len)
#endif /* EXT_FLASH */
#endif /* TARGET_versal */
/* ============================================================================
* SD Card Support (SDHCI)
* ============================================================================
* The Versal uses an Arasan SDHCI controller with standard register layout,
* unlike PolarFire which uses a Cadence SD4HC controller. The generic SDHCI
* driver (src/sdhci.c) expects Cadence register offsets (HRS at 0x000,
* SRS at 0x200), so we translate in the HAL register access functions.
*
* SD1 at 0xF1050000 is the external SD card slot on VMK180.
* PLM already initializes the SD controller, so platform init is minimal.
* Initial implementation uses polling mode (no GIC setup required).
*/
#if defined(DISK_SDCARD) || defined(DISK_EMMC)
#include "sdhci.h"
/* Use SD1 for external SD card slot on VMK180 */
#define VERSAL_SDHCI_BASE VERSAL_SD1_BASE /* 0xF1050000 */
/* ============================================================================
* Register Translation: Cadence SD4HC -> Standard SDHCI (Arasan)
* ============================================================================
* The generic SDHCI driver (src/sdhci.c) uses Cadence SD4HC register offsets:
* - HRS registers at 0x000-0x01F (Cadence-specific: reset, PHY, eMMC mode)
* - SRS registers at 0x200-0x2FF (standard SDHCI mapped at offset +0x200)
*
* Versal uses the Arasan SDHCI controller with standard register layout:
* - Standard SDHCI registers at 0x000-0x0FF (no 0x200 offset)
*
* Translation:
* - SRS offsets (>= 0x200): subtract 0x200 to get standard offset
* - HRS00 (0x000): map SWR bit to standard Software Reset All (SRA)
* - HRS01, HRS04, HRS06: Cadence-specific, not applicable on Versal
*/
#define CADENCE_SRS_OFFSET 0x200
/* Standard SDHCI Software Reset is in the Clock/Timeout/Reset register */
#define STD_SDHCI_RESET_REG 0x2C /* Clock Control / Timeout / SW Reset */
#define STD_SDHCI_SRA (1U << 24) /* Software Reset for All */
/* Handle reads from Cadence HRS registers (0x000-0x1FF) */
static uint32_t versal_sdhci_hrs_read(uint32_t hrs_offset)
{
volatile uint8_t *base = (volatile uint8_t *)VERSAL_SDHCI_BASE;
switch (hrs_offset) {
case 0x000: /* HRS00 - Software Reset */
{
/* Map standard SRA (bit 24 of 0x2C) to Cadence SWR (bit 0) */
uint32_t val = *((volatile uint32_t *)(base + STD_SDHCI_RESET_REG));
return (val & STD_SDHCI_SRA) ? 1U : 0U;
}
case 0x010: /* HRS04 - PHY access (Cadence-specific) */
/* Return ACK set to prevent wait loops from hanging */
return (1U << 26); /* SDHCI_HRS04_UIS_ACK */
default:
/* HRS01 (debounce), HRS02, HRS06 (eMMC mode) - not applicable */
return 0;
}
}
/* Handle writes to Cadence HRS registers (0x000-0x1FF) */
static void versal_sdhci_hrs_write(uint32_t hrs_offset, uint32_t val)
{
volatile uint8_t *base = (volatile uint8_t *)VERSAL_SDHCI_BASE;
switch (hrs_offset) {
case 0x000: /* HRS00 - Software Reset */
if (val & 1U) { /* SWR bit -> standard SRA */
uint32_t reg = *((volatile uint32_t *)(base + STD_SDHCI_RESET_REG));
reg |= STD_SDHCI_SRA;
*((volatile uint32_t *)(base + STD_SDHCI_RESET_REG)) = reg;
}
break;
default:
/* HRS01, HRS04, HRS06 - not applicable on Versal, ignore */
break;
}
}
/* Register access functions for generic SDHCI driver.
* Translates Cadence SD4HC register offsets to standard Arasan SDHCI layout. */
uint32_t sdhci_reg_read(uint32_t offset)
{
volatile uint8_t *base = (volatile uint8_t *)VERSAL_SDHCI_BASE;
/* Cadence SRS registers (0x200+) -> standard SDHCI (subtract 0x200) */
if (offset >= CADENCE_SRS_OFFSET) {
return *((volatile uint32_t *)(base + offset - CADENCE_SRS_OFFSET));
}
/* Cadence HRS registers (0x000-0x1FF) -> translate to standard equivalents */
return versal_sdhci_hrs_read(offset);
}
void sdhci_reg_write(uint32_t offset, uint32_t val)
{
volatile uint8_t *base = (volatile uint8_t *)VERSAL_SDHCI_BASE;
/* Cadence SRS registers (0x200+) -> standard SDHCI (subtract 0x200) */
if (offset >= CADENCE_SRS_OFFSET) {
*((volatile uint32_t *)(base + offset - CADENCE_SRS_OFFSET)) = val;
return;
}
/* Cadence HRS registers (0x000-0x1FF) -> translate to standard equivalents */
versal_sdhci_hrs_write(offset, val);
}
/* Platform initialization - called from sdhci_init()
* PLM already initializes the SD controller on Versal when booting from SD card,
* so we don't need to configure clocks/reset (CRL registers are protected at EL2).
* We verify the SDHCI controller is accessible via standard register reads. */
void sdhci_platform_init(void)
{
#ifdef DEBUG_SDHCI
volatile uint8_t *base = (volatile uint8_t *)VERSAL_SDHCI_BASE;
uint32_t val;
wolfBoot_printf("sdhci_platform_init: SD1 at 0x%x\n",
(unsigned int)VERSAL_SDHCI_BASE);
/* Read standard SDHCI registers to verify controller access */
val = *((volatile uint32_t *)(base + 0x24)); /* Present State */
wolfBoot_printf(" Present State: 0x%x\n", (unsigned int)val);
val = *((volatile uint32_t *)(base + 0x40)); /* Capabilities */
wolfBoot_printf(" Capabilities: 0x%x\n", (unsigned int)val);
(void)val;
#endif
/* PLM already configured SD1 - no clock/reset setup needed */
}
/* Platform interrupt setup - called from sdhci_init()
* Using polling mode for simplicity - no GIC setup needed */
void sdhci_platform_irq_init(void)
{
/* Polling mode: no interrupt setup required
* GIC interrupt support can be added later if needed */
#ifdef DEBUG_SDHCI
wolfBoot_printf("sdhci_platform_irq_init: Using polling mode\n");
#endif
}
/* Platform bus mode selection - called from sdhci_init() */
void sdhci_platform_set_bus_mode(int is_emmc)
{
(void)is_emmc;
#ifdef DEBUG_SDHCI
wolfBoot_printf("sdhci_platform_set_bus_mode: is_emmc=%d\n", is_emmc);
#endif
/* Nothing additional needed for Versal - mode is set in generic driver */
}
#endif /* DISK_SDCARD || DISK_EMMC */
#endif /* TARGET_versal */

View File

@ -279,12 +279,21 @@
#define CRL_UART0_REF_CTRL (*((volatile uint32_t*)(VERSAL_CRL_BASE + 0x0128)))
#define CRL_UART1_REF_CTRL (*((volatile uint32_t*)(VERSAL_CRL_BASE + 0x012C)))
/* SD/SDIO Reference Clock Control */
#define CRL_SDIO0_REF_CTRL (*((volatile uint32_t*)(VERSAL_CRL_BASE + 0x0104)))
#define CRL_SDIO1_REF_CTRL (*((volatile uint32_t*)(VERSAL_CRL_BASE + 0x0108)))
/* UART Reset Control - from lpd_data.cdo line 258 */
#define CRL_RST_UART0 (*((volatile uint32_t*)(VERSAL_CRL_BASE + 0x0318)))
#define CRL_RST_UART1 (*((volatile uint32_t*)(VERSAL_CRL_BASE + 0x031C)))
#define CRL_RST_UART0_BIT (1UL << 0)
#define CRL_RST_UART1_BIT (1UL << 0) /* Each UART has its own register */
/* SD/SDIO Reset Control */
#define CRL_RST_SDIO0 (*((volatile uint32_t*)(VERSAL_CRL_BASE + 0x0324)))
#define CRL_RST_SDIO1 (*((volatile uint32_t*)(VERSAL_CRL_BASE + 0x0328)))
#define CRL_RST_SDIO_BIT (1UL << 0)
/* Backward compatibility alias */
#define CRL_RST_UART CRL_RST_UART0

View File

@ -199,8 +199,45 @@ void RAMFUNCTION arch_reboot(void)
}
#endif
/* Exception handler stubs - bootloader does not handle interrupts */
void SynchronousInterrupt(void) { }
void IRQInterrupt(void) { }
void FIQInterrupt(void) { }
void SErrorInterrupt(void) { }
/* ============================================================================
* Exception Handlers for EL2 (optional DEBUG_HARDFAULT)
* ============================================================================
*/
#if defined(DEBUG_HARDFAULT) && defined(DEBUG_UART) && defined(EL2_HYPERVISOR)
#define READ_SYSREG(_out, _reg) __asm__ volatile("mrs %0, " #_reg : "=r"(_out))
static void print_exception_info(const char *type)
{
uint64_t esr, elr, far;
READ_SYSREG(esr, ESR_EL2);
READ_SYSREG(elr, ELR_EL2);
READ_SYSREG(far, FAR_EL2);
wolfBoot_printf("\n\n*** %s EXCEPTION ***\n", type);
wolfBoot_printf("ESR_EL2: 0x%08x%08x\n", (uint32_t)(esr >> 32), (uint32_t)esr);
wolfBoot_printf("ELR_EL2: 0x%08x%08x\n", (uint32_t)(elr >> 32), (uint32_t)elr);
wolfBoot_printf("FAR_EL2: 0x%08x%08x\n", (uint32_t)(far >> 32), (uint32_t)far);
wolfBoot_printf("*** SYSTEM HALTED ***\n");
}
static void hardfault_halt(const char *type)
{
print_exception_info(type);
while (1) { __asm__ volatile("wfi"); }
}
void SynchronousInterrupt(void) { hardfault_halt("SYNCHRONOUS"); }
void IRQInterrupt(void) { hardfault_halt("IRQ"); }
void FIQInterrupt(void) { hardfault_halt("FIQ"); }
void SErrorInterrupt(void) { hardfault_halt("SERROR"); }
#else
/* Simple stubs when debug not enabled */
void SynchronousInterrupt(void) { while (1) { __asm__ volatile("wfi"); } }
void IRQInterrupt(void) { while (1) { __asm__ volatile("wfi"); } }
void FIQInterrupt(void) { while (1) { __asm__ volatile("wfi"); } }
void SErrorInterrupt(void) { while (1) { __asm__ volatile("wfi"); } }
#endif /* DEBUG_HARDFAULT && DEBUG_UART && EL2_HYPERVISOR */

View File

@ -467,6 +467,7 @@ InitEL1:
/* Assembly startup error handler */
error:
wfi
b error

View File

@ -21,11 +21,15 @@
*/
/**
* @file disk.c
* @brief GPT disk driver implementation.
* @brief Disk driver with GPT and MBR partition table support.
*
* This file contains the GPT disk driver that uses disk I/O operations.
* It uses the generic GPT parsing functions from src/gpt.c for partition
* table parsing.
* This file contains the disk driver that uses disk I/O operations.
* It supports both GPT and MBR partition tables:
* - GPT: Uses protective MBR + GPT header (via src/gpt.c)
* - MBR: Falls back to standard MBR partition entries
*
* MBR fallback is needed for platforms like Versal where the boot ROM
* requires MBR but wolfBoot needs to read data partitions.
*/
#ifndef _WOLFBOOT_DISK_C_
#define _WOLFBOOT_DISK_C_
@ -55,13 +59,62 @@ static struct disk_drive Drives[MAX_DISKS] = {0};
* @return The number of partitions found and initialized on success, or -1 if
* the drive cannot be opened or no valid GPT partition table is found.
*/
/**
* @brief Parse MBR partition table entries.
*
* Reads up to 4 primary MBR partition entries and populates the drive's
* partition array. Start/end are stored as byte offsets (LBA * sector size).
*
* @param[in,out] drive Pointer to the disk_drive structure to populate.
* @param[in] mbr_sector The 512-byte MBR sector data.
* @return The number of partitions found, or -1 on error.
*/
static int disk_open_mbr(struct disk_drive *drive, const uint8_t *mbr_sector)
{
uint32_t i;
const struct gpt_mbr_part_entry *pte;
for (i = 0; i < 4; i++) {
pte = (const struct gpt_mbr_part_entry *)(mbr_sector +
GPT_MBR_ENTRY_START + (i * sizeof(struct gpt_mbr_part_entry)));
/* Skip empty entries (type 0) and extended partition types */
if (pte->ptype == 0x00 || pte->ptype == 0x05 || pte->ptype == 0x0F ||
pte->ptype == 0x85) {
continue;
}
if (pte->lba_first == 0 || pte->lba_size == 0) {
continue;
}
{
uint32_t n = drive->n_parts;
uint64_t start_bytes = (uint64_t)pte->lba_first * GPT_SECTOR_SIZE;
uint64_t end_bytes = start_bytes +
((uint64_t)pte->lba_size * GPT_SECTOR_SIZE) - 1;
drive->part[n].drv = drive->drv;
drive->part[n].start = start_bytes;
drive->part[n].end = end_bytes;
memset(drive->part[n].name, 0, sizeof(drive->part[n].name));
drive->n_parts++;
wolfBoot_printf(" MBR part %u: type=0x%02x, start=0x%x, "
"size=%uMB\r\n", i + 1, pte->ptype,
(uint32_t)start_bytes,
(uint32_t)(pte->lba_size / 2048));
}
}
return drive->n_parts;
}
int disk_open(int drv)
{
int r;
uint32_t i;
uint32_t n_parts = 0;
uint32_t gpt_lba = 0;
struct guid_ptable ptable;
uint8_t sector[GPT_SECTOR_SIZE] XALIGNED(4);
if ((drv < 0) || (drv > MAX_DISKS)) {
@ -78,84 +131,83 @@ int disk_open(int drv)
return -1;
}
/* Check for protective MBR and get GPT header location */
if (gpt_check_mbr_protective((uint8_t*)sector, &gpt_lba) != 0) {
wolfBoot_printf("Cannot find valid partition table entry for GPT\r\n");
return -1;
}
wolfBoot_printf("Found GPT PTE at sector %u\r\n", gpt_lba);
wolfBoot_printf("Found valid boot signature in MBR\r\n");
Drives[drv].is_open = 1;
Drives[drv].drv = drv;
Drives[drv].n_parts = 0;
/* Read GPT header */
r = disk_read(drv, GPT_SECTOR_SIZE * gpt_lba, GPT_SECTOR_SIZE, sector);
if (r < 0) {
wolfBoot_printf("Disk read failed\r\n");
return -1;
}
/* Try GPT first: check for protective MBR with type 0xEE */
if (gpt_check_mbr_protective((uint8_t*)sector, &gpt_lba) == 0) {
struct guid_ptable ptable;
/* Parse and validate GPT header */
if (gpt_parse_header((uint8_t*)sector, &ptable) != 0) {
wolfBoot_printf("Invalid partition table\r\n");
return -1;
}
wolfBoot_printf("Found GPT PTE at sector %u\r\n", gpt_lba);
wolfBoot_printf("Valid GPT partition table\r\n");
wolfBoot_printf("Current LBA: 0x%llx \r\n", ptable.main_lba);
wolfBoot_printf("Backup LBA: 0x%llx \r\n", ptable.backup_lba);
wolfBoot_printf("Max number of partitions: %d\r\n", ptable.n_part);
n_parts = ptable.n_part;
if (ptable.n_part > MAX_PARTITIONS) {
n_parts = MAX_PARTITIONS;
wolfBoot_printf("Software limited: only allowing up to %d partitions "
"per disk.\r\n", n_parts);
}
wolfBoot_printf("Disk size: %d\r\n",
(1 + ptable.last_usable - ptable.first_usable) * GPT_SECTOR_SIZE);
/* Read and parse partition entries */
for (i = 0; i < n_parts; i++) {
struct gpt_part_info part_info;
uint64_t address = (ptable.start_array * GPT_SECTOR_SIZE) +
(i * ptable.array_sz);
uint8_t entry_buf[GPT_PART_ENTRY_SIZE] XALIGNED(4); /* Max partition entry size */
if (ptable.array_sz > sizeof(entry_buf)) {
wolfBoot_printf("Partition entry size too large\r\n");
break;
}
r = disk_read(drv, address, ptable.array_sz, entry_buf);
/* Read GPT header */
r = disk_read(drv, GPT_SECTOR_SIZE * gpt_lba, GPT_SECTOR_SIZE, sector);
if (r < 0) {
wolfBoot_printf("Disk read failed\r\n");
return -1;
}
/* Parse partition entry using generic function */
if (gpt_parse_partition((uint8_t*)entry_buf, ptable.array_sz, &part_info) == 0) {
uint64_t size;
uint32_t part_count;
/* Parse and validate GPT header */
if (gpt_parse_header((uint8_t*)sector, &ptable) != 0) {
wolfBoot_printf("Invalid GPT header\r\n");
return -1;
}
size = part_info.end - part_info.start + 1;
part_count = Drives[drv].n_parts;
Drives[drv].n_parts++;
Drives[drv].part[part_count].drv = drv;
Drives[drv].part[part_count].start = part_info.start;
Drives[drv].part[part_count].end = part_info.end;
memcpy(&Drives[drv].part[part_count].name, part_info.name,
sizeof(part_info.name));
wolfBoot_printf("Valid GPT partition table\r\n");
wolfBoot_printf("Max number of partitions: %d\r\n", ptable.n_part);
wolfBoot_printf("disk%d.p%u ", drv, part_count);
wolfBoot_printf("(%x_%xh", (uint32_t)(size >> 32), (uint32_t)size);
wolfBoot_printf("@ %x_%x)\r\n",
(uint32_t)(part_info.start >> 32),
(uint32_t)(part_info.start));
} else {
/* Empty partition entry - end of used entries */
break;
n_parts = ptable.n_part;
if (n_parts > MAX_PARTITIONS)
n_parts = MAX_PARTITIONS;
/* Read and parse GPT partition entries */
for (i = 0; i < n_parts; i++) {
struct gpt_part_info part_info;
uint64_t address = (ptable.start_array * GPT_SECTOR_SIZE) +
(i * ptable.array_sz);
uint8_t entry_buf[GPT_PART_ENTRY_SIZE] XALIGNED(4);
if (ptable.array_sz > sizeof(entry_buf))
break;
r = disk_read(drv, address, ptable.array_sz, entry_buf);
if (r < 0)
return -1;
if (gpt_parse_partition((uint8_t*)entry_buf, ptable.array_sz,
&part_info) == 0) {
uint64_t size = part_info.end - part_info.start + 1;
uint32_t pc = Drives[drv].n_parts;
Drives[drv].n_parts++;
Drives[drv].part[pc].drv = drv;
Drives[drv].part[pc].start = part_info.start;
Drives[drv].part[pc].end = part_info.end;
memcpy(&Drives[drv].part[pc].name, part_info.name,
sizeof(part_info.name));
wolfBoot_printf(" GPT part %u: %x_%xh @ %x_%x\r\n", pc,
(uint32_t)(size >> 32), (uint32_t)size,
(uint32_t)(part_info.start >> 32),
(uint32_t)(part_info.start));
} else {
break; /* End of used entries */
}
}
} else {
const uint16_t *boot_sig = (const uint16_t *)(sector +
GPT_MBR_BOOTSIG_OFFSET);
/* Check MBR boot signature (0xAA55) */
if (*boot_sig != GPT_MBR_BOOTSIG_VALUE) {
wolfBoot_printf("No valid partition table found\r\n");
return -1;
}
wolfBoot_printf("Found MBR partition table\r\n");
if (disk_open_mbr(&Drives[drv], sector) < 0) {
wolfBoot_printf("Failed to parse MBR\r\n");
return -1;
}
}

View File

@ -108,18 +108,28 @@ void sdhci_irq_handler(void)
{
uint32_t status = SDHCI_REG(SDHCI_SRS12);
/* Check for DMA interrupt */
/* Check for DMA interrupt (SDMA boundary crossing) */
if (status & SDHCI_SRS12_DMAINT) {
/* Read updated DMA address - engine will increment block */
uint32_t* addr = (uint32_t*)(uintptr_t)((((uint64_t)SDHCI_REG(SDHCI_SRS23)) << 32) |
SDHCI_REG(SDHCI_SRS22));
/* Set new DMA address for next boundary */
SDHCI_REG_SET(SDHCI_SRS22, (uint32_t)(uintptr_t)addr);
SDHCI_REG_SET(SDHCI_SRS23, (uint32_t)(((uint64_t)(uintptr_t)addr) >> 32));
/* triggers next DMA block on write of top bit */
/* Read the next DMA address saved by the controller */
uint32_t addr_lo = SDHCI_REG(SDHCI_SRS22);
uint32_t addr_hi = SDHCI_REG(SDHCI_SRS23);
/* Clear DMA interrupt status before restarting */
SDHCI_REG_SET(SDHCI_SRS12, SDHCI_SRS12_DMAINT);
#if defined(__riscv)
asm volatile("fence rw, rw" ::: "memory");
#elif defined(__aarch64__)
asm volatile("dsb sy" ::: "memory");
#endif
/* Write SDMA address to resume transfer.
* Per SDHCI v4 spec: write high 32 bits first, then low 32 bits.
* Writing the low address (SRS22 / offset 0x058) triggers the
* DMA engine to resume. */
SDHCI_REG_SET(SDHCI_SRS23, addr_hi);
SDHCI_REG_SET(SDHCI_SRS22, addr_lo);
g_mmc_irq_status |= SDHCI_IRQ_FLAG_DMAINT;
SDHCI_REG_SET(SDHCI_SRS12, SDHCI_SRS12_DMAINT); /* Clear interrupt */
}
/* Check for transfer complete */
@ -183,10 +193,21 @@ static void sdhci_disable_sdma_interrupts(void)
SDHCI_REG_SET(SDHCI_SRS14, reg);
}
/* Wait for SDHCI interrupt with timeout */
/* Wait for SDHCI interrupt with timeout.
* Supports both hardware interrupt and polling modes:
* - Interrupt mode: g_mmc_irq_pending set by sdhci_irq_handler() via platform ISR
* - Polling mode: directly reads SRS12 status register and calls handler */
static int sdhci_wait_irq(uint32_t expected_flags, uint32_t timeout)
{
while (timeout-- > 0) {
/* Poll SRS12 directly for platforms without interrupt routing.
* In interrupt mode this is redundant (bits already cleared by ISR). */
uint32_t status = SDHCI_REG(SDHCI_SRS12);
if (status & (SDHCI_SRS12_TC | SDHCI_SRS12_CC | SDHCI_SRS12_DMAINT |
SDHCI_SRS12_EDT | SDHCI_SRS12_EINT)) {
sdhci_irq_handler();
}
if (g_mmc_irq_pending) {
g_mmc_irq_pending = 0;
@ -1152,18 +1173,20 @@ static int sdhci_transfer(int dir, uint32_t cmd_index, uint32_t block_addr,
else if (is_multi_block) {
cmd_reg |= SDHCI_SRS03_MSBS; /* enable multi-block select */
#ifndef SDHCI_SDMA_DISABLED
if (sz >= SDHCI_DMA_THRESHOLD) { /* use DMA for large transfers */
cmd_reg |= SDHCI_SRS03_DMAE; /* enable DMA */
bcr_reg = (block_count << SDHCI_SRS01_BCCT_SHIFT) |
SDHCI_DMA_BUFF_BOUNDARY | SDHCI_BLOCK_SIZE;
/* SDMA mode */
/* SDMA mode with Host Version 4 enable.
* HV4E is required for SDMA to use the 64-bit address registers
* (SRS22/SRS23) instead of the legacy 32-bit register (SRS00).
* A64S is cleared to use 32-bit DMA addressing. */
sdhci_reg_or(SDHCI_SRS10, SDHCI_SRS10_DMA_SDMA);
if (dir == SDHCI_DIR_WRITE) {
sdhci_reg_or(SDHCI_SRS15, SDHCI_SRS15_HV4E);
sdhci_reg_and(SDHCI_SRS16, ~SDHCI_SRS16_A64S);
}
sdhci_reg_or(SDHCI_SRS15, SDHCI_SRS15_HV4E);
sdhci_reg_and(SDHCI_SRS16, ~SDHCI_SRS16_A64S);
/* Set SDMA address */
SDHCI_REG_SET(SDHCI_SRS22, (uint32_t)(uintptr_t)buf);
SDHCI_REG_SET(SDHCI_SRS23, (uint32_t)(((uint64_t)(uintptr_t)buf) >> 32));
@ -1171,7 +1194,9 @@ static int sdhci_transfer(int dir, uint32_t cmd_index, uint32_t block_addr,
/* Enable SDMA interrupts */
sdhci_enable_sdma_interrupts();
}
else {
else
#endif /* !SDHCI_SDMA_DISABLED */
{
bcr_reg = (block_count << SDHCI_SRS01_BCCT_SHIFT) |
SDHCI_BLOCK_SIZE;
}
@ -1666,4 +1691,3 @@ void disk_close(int drv)
}
#endif /* DISK_SDCARD || DISK_EMMC */

View File

@ -207,27 +207,6 @@ static int decrypt_header(const uint8_t *src, uint8_t *dst)
return 0;
}
/**
* @brief Decrypt an image in RAM.
*
* This function decrypts the full image (header + firmware) using the
* configured encryption algorithm. The decryption is done in-place.
*
* @param data Pointer to the encrypted image data.
* @param size Size of the image (header + firmware).
*
* @return 0 if successful, -1 on failure.
*/
static int decrypt_image(uint8_t *data, uint32_t size)
{
/* Reset IV to start of image (block 0) */
disk_crypto_set_iv(0);
/* Decrypt entire image - CTR mode handles counter increment internally */
crypto_decrypt(data, data, size);
return 0;
}
#endif /* DISK_ENCRYPT */
extern int wolfBoot_get_dts_size(void *dts_addr);
@ -261,6 +240,7 @@ void RAMFUNCTION wolfBoot_start(void)
uint32_t *load_address;
int failures = 0;
uint32_t load_off;
const uint8_t *hdr_ptr = NULL;
#ifdef MMU
uint8_t *dts_addr = NULL;
uint32_t dts_size = 0;
@ -365,6 +345,7 @@ void RAMFUNCTION wolfBoot_start(void)
continue;
}
hdr_ptr = p_hdr;
#ifdef DISK_ENCRYPT
/* Decrypt header to parse image size */
if (decrypt_header(p_hdr, dec_hdr) != 0) {
@ -372,12 +353,10 @@ void RAMFUNCTION wolfBoot_start(void)
selected ^= 1;
continue;
}
memset(&os_image, 0, sizeof(os_image));
ret = wolfBoot_open_image_address(&os_image, (void*)dec_hdr);
#else
memset(&os_image, 0, sizeof(os_image));
ret = wolfBoot_open_image_address(&os_image, (void*)p_hdr);
hdr_ptr = dec_hdr;
#endif
memset(&os_image, 0, sizeof(os_image));
ret = wolfBoot_open_image_address(&os_image, (void*)hdr_ptr);
if (ret < 0) {
wolfBoot_printf("Error parsing loaded image\r\n");
selected ^= 1;
@ -398,17 +377,18 @@ void RAMFUNCTION wolfBoot_start(void)
part_name);
#endif
/* Read the image into RAM */
/* Read the payload into RAM (skip header) */
wolfBoot_printf("Loading image from disk...");
BENCHMARK_START();
load_off = 0;
do {
ret = disk_part_read(BOOT_DISK, cur_part, load_off,
DISK_BLOCK_SIZE, ((uint8_t *)load_address) + load_off);
ret = disk_part_read(BOOT_DISK, cur_part,
IMAGE_HEADER_SIZE + load_off, DISK_BLOCK_SIZE,
((uint8_t *)load_address) + load_off);
if (ret < 0)
break;
load_off += ret;
} while (load_off < os_image.fw_size + IMAGE_HEADER_SIZE);
} while (load_off < os_image.fw_size);
if (ret < 0) {
wolfBoot_printf("Error reading image from disk: p%d\r\n",
@ -419,26 +399,27 @@ void RAMFUNCTION wolfBoot_start(void)
BENCHMARK_END("done");
#ifdef DISK_ENCRYPT
/* Decrypt the image in RAM */
/* Decrypt the payload in RAM */
wolfBoot_printf("Decrypting image...");
BENCHMARK_START();
ret = decrypt_image((uint8_t*)load_address,
os_image.fw_size + IMAGE_HEADER_SIZE);
if (ret != 0) {
wolfBoot_printf("Error decrypting image\r\n");
selected ^= 1;
continue;
if ((IMAGE_HEADER_SIZE % ENCRYPT_BLOCK_SIZE) != 0) {
wolfBoot_printf("Encrypted disk images require aligned header size\r\n");
wolfBoot_panic();
}
disk_crypto_set_iv(IMAGE_HEADER_SIZE / ENCRYPT_BLOCK_SIZE);
crypto_decrypt((uint8_t*)load_address, (uint8_t*)load_address,
os_image.fw_size);
BENCHMARK_END("done");
#endif
memset(&os_image, 0, sizeof(os_image));
ret = wolfBoot_open_image_address(&os_image, (void*)load_address);
ret = wolfBoot_open_image_address(&os_image, (void*)hdr_ptr);
if (ret < 0) {
wolfBoot_printf("Error parsing loaded image\r\n");
selected ^= 1;
continue;
}
os_image.fw_base = (uint8_t*)load_address;
wolfBoot_printf("Checking image integrity...");
BENCHMARK_START();

View File

@ -30,14 +30,12 @@
void main(void)
{
#ifdef WOLFBOOT_FIXED_PARTITIONS
uint32_t boot_version, update_version;
#endif
hal_init();
/* Get versions from both partitions */
boot_version = wolfBoot_get_image_version(PART_BOOT);
update_version = wolfBoot_get_image_version(PART_UPDATE);
wolfBoot_printf("\n\n");
wolfBoot_printf("===========================================\n");
wolfBoot_printf(" wolfBoot Test Application - AMD Versal\n");
@ -45,9 +43,17 @@ void main(void)
wolfBoot_printf("Current EL: %d\n", current_el());
#ifdef WOLFBOOT_FIXED_PARTITIONS
/* Get versions from both partitions (only available with fixed partitions) */
boot_version = wolfBoot_get_image_version(PART_BOOT);
update_version = wolfBoot_get_image_version(PART_UPDATE);
/* Print firmware versions */
wolfBoot_printf("BOOT: Version: %d (0x%08x)\n", boot_version, boot_version);
wolfBoot_printf("UPDATE: Version: %d (0x%08x)\n", update_version, update_version);
#else
wolfBoot_printf("Boot mode: Disk-based (GPT/MBR partitions)\n");
#endif
wolfBoot_printf("Application running successfully!\n");
wolfBoot_printf("\nEntering idle loop...\n");

View File

@ -176,6 +176,83 @@ stop_uart_capture() {
cleanup() { [ "$KEEP_UART_CAPTURE" = "false" ] && { log_info "Cleaning up..."; stop_uart_capture; kill_existing_uart_processes; }; }
trap cleanup EXIT INT TERM
# SD card image configuration
SDCARD_IMG="${SDCARD_IMG:-${WOLFBOOT_ROOT}/sdcard.img}"
SDCARD_SIZE_MB="${SDCARD_SIZE_MB:-1024}"
SDCARD_BOOT_SIZE_MB="${SDCARD_BOOT_SIZE_MB:-128}"
SDCARD_OFP_SIZE_MB="${SDCARD_OFP_SIZE_MB:-200}"
# Helper: Create SD card image with MBR partitions
# Layout:
# Partition 1: boot (128MB, FAT32 LBA, bootable) - BOOT.BIN
# Partition 2: OFP_A (200MB, Linux) - Primary signed FIT image
# Partition 3: OFP_B (200MB, Linux) - Update signed FIT image
# Partition 4: rootfs (remainder) - Linux root filesystem
# Note: Versal boot ROM requires MBR (does not support GPT)
create_sdcard_image() {
local img="$1" size_mb="${2:-$SDCARD_SIZE_MB}"
log_info "Creating ${size_mb}MB SD card image: $img"
dd if=/dev/zero of="$img" bs=1M count="$size_mb" status=progress 2>/dev/null || { log_error "Failed to create image"; return 1; }
log_info "Creating MBR partition table..."
sfdisk "$img" <<EOF
label: dos
unit: sectors
1 : start=2048, size=${SDCARD_BOOT_SIZE_MB}M, type=c, bootable
2 : size=${SDCARD_OFP_SIZE_MB}M, type=83
3 : size=${SDCARD_OFP_SIZE_MB}M, type=83
4 : type=83
EOF
if [ $? -ne 0 ]; then
log_error "Failed to create MBR partitions"
return 1
fi
log_ok "MBR partition table created"
fdisk -l "$img"
}
# Helper: Get partition offset in bytes from MBR
get_partition_offset() {
local img="$1" part="$2"
local sector
# Parse sfdisk dump for partition start sector
sector=$(sfdisk -d "$img" 2>/dev/null | grep "^${img}${part}" | sed 's/.*start=\s*\([0-9]*\).*/\1/')
if [ -z "$sector" ]; then
# Fallback: parse fdisk output
sector=$(fdisk -l "$img" 2>/dev/null | grep "^${img}${part}" | awk '{print $2}')
[ "$sector" = "*" ] && sector=$(fdisk -l "$img" 2>/dev/null | grep "^${img}${part}" | awk '{print $3}')
fi
if [ -z "$sector" ] || [ "$sector" -eq 0 ] 2>/dev/null; then
log_error "Failed to read partition $part offset"
echo 0
return
fi
echo $((sector * 512))
}
# Helper: Write file to partition in SD card image
write_to_partition() {
local img="$1" part="$2" file="$3"
local offset_bytes=$(get_partition_offset "$img" "$part")
local offset_blocks=$((offset_bytes / 512))
if [ -z "$offset_blocks" ] || [ "$offset_blocks" -eq 0 ]; then
log_error "Failed to get partition $part offset"
return 1
fi
log_info "Writing $file to partition $part (offset: ${offset_bytes} bytes, sector: ${offset_blocks})"
dd if="$file" of="$img" bs=512 seek="$offset_blocks" conv=notrunc status=progress 2>/dev/null || {
log_error "Failed to write $file to partition $part"
return 1
}
log_ok "Written $(stat -c%s "$file") bytes to partition $part"
}
# Helper: Check Linux images directory
check_linux_images() {
local required_files="$1"
@ -298,8 +375,10 @@ Options:
(none) Full build, flash, and boot wolfBoot
--test-app Full build + flash test app to boot partition
--test-update Full build + flash test app v2 to update partition
--linux Build wolfBoot + signed Linux FIT image and boot
--linux Build wolfBoot + signed Linux FIT image and boot (QSPI)
--linux-sdcard Build wolfBoot + signed Linux FIT image for SD card boot
--linux-uboot Build BOOT.BIN with U-Boot and flash Linux FIT image
--sdcard Build wolfBoot with SD card config and create SD card image
--boot-sdcard Test SD card boot mode only (no build/flash)
--boot-qspi Test QSPI boot mode only (no build/flash)
--skipuart Skip UART capture (use with --boot-sdcard/--boot-qspi)
@ -311,19 +390,23 @@ Environment Variables:
BOARD_IP Board IP address (default: 10.0.4.90)
TFTP_DIR TFTP directory path (default: /srv/tftp)
VITIS_PATH Xilinx Vitis installation path (default: /opt/Xilinx/Vitis/2024.2)
LINUX_IMAGES_DIR Path to PetaLinux images directory (for --linux and --linux-uboot)
LINUX_IMAGES_DIR Path to PetaLinux images directory (for --linux, --linux-sdcard, --linux-uboot)
SDCARD_IMG SD card image output path (default: sdcard.img)
SDCARD_SIZE_MB SD card image size in MB (default: 512)
Examples:
$0 --boot-sdcard --skipuart # Reset to SD boot without UART capture
$0 --boot-qspi --skipuart # Reset to QSPI boot without UART capture
$0 --sdcard # Build with SD card config and create SD card image
LINUX_IMAGES_DIR=/path/to/images/linux $0 --linux-sdcard # PetaLinux SD card boot
EOF
}
# Check for --skipuart flag before starting UART capture
# Check for --skipuart flag or modes that don't need UART before starting UART capture
SKIP_UART=false
for arg in "$@"; do
case "$arg" in
--skipuart) SKIP_UART=true ;;
--skipuart|--sdcard|--linux-sdcard) SKIP_UART=true ;;
esac
done
@ -429,6 +512,161 @@ case "${1:-}" in
log_ok "Signed FIT size: $(stat -c%s fitImage_v1_signed.bin) bytes"
flash_and_boot "BOOT.BIN:0x0 fitImage_v1_signed.bin:0x800000" 90 "wolfBoot + Linux boot"
exit 0
;;
--linux-sdcard)
log_info "=== Linux SD Card Boot Mode ==="
check_linux_images "plm.elf psmfw.elf bl31.elf Image system-default.dtb" "--linux-sdcard"
command -v mkimage &>/dev/null || { log_error "mkimage not found - install with: sudo apt install u-boot-tools"; exit 1; }
log_info "Copying Linux boot files..."
for f in plm.elf psmfw.elf bl31.elf Image system-default.dtb; do cp "${LINUX_IMAGES_DIR}/${f}" .; done
copy_pdi
# Build wolfBoot with SD card configuration
log_info "Building wolfBoot with SD card config..."
cp config/examples/versal_vmk180_sdcard.config .config
make clean && make || { log_error "Failed to build wolfBoot"; exit 1; }
[ ! -f "wolfboot.elf" ] && { log_error "wolfboot.elf not found"; exit 1; }
load_config .config
# Create FIT image from Linux kernel + DTB
log_info "Creating FIT image..."
mkimage -f ./hal/versal.its fitImage || { log_error "mkimage failed"; exit 1; }
log_ok "FIT image created: fitImage ($(stat -c%s fitImage) bytes)"
# Sign FIT image
log_info "Signing FIT image..."
export IMAGE_HEADER_SIZE IMAGE_SIGNATURE_SIZE
PRIVATE_KEY="${PRIVATE_KEY:-wolfboot_signing_private_key.der}"
./tools/keytools/sign $SIGN_OPTIONS fitImage "$PRIVATE_KEY" 1 || { log_error "Signing v1 failed"; exit 1; }
./tools/keytools/sign $SIGN_OPTIONS fitImage "$PRIVATE_KEY" 2 || { log_error "Signing v2 failed"; exit 1; }
log_ok "Signed FIT images: fitImage_v1_signed.bin, fitImage_v2_signed.bin"
# Create SD card image with MBR partitions
create_sdcard_image "$SDCARD_IMG" "$SDCARD_SIZE_MB" || exit 1
# Write signed FIT images to partitions (OFP_A=2, OFP_B=3)
log_info "Writing signed FIT images to SD card partitions..."
write_to_partition "$SDCARD_IMG" 2 fitImage_v1_signed.bin || exit 1
write_to_partition "$SDCARD_IMG" 3 fitImage_v2_signed.bin || exit 1
# Write rootfs to partition 4 if available
ROOTFS_IMG=""
if [ -f "${LINUX_IMAGES_DIR}/rootfs.ext4" ]; then
ROOTFS_IMG="${LINUX_IMAGES_DIR}/rootfs.ext4"
elif [ -f "${LINUX_IMAGES_DIR}/rootfs.cpio.gz" ]; then
ROOTFS_IMG="${LINUX_IMAGES_DIR}/rootfs.cpio.gz"
fi
if [ -n "$ROOTFS_IMG" ]; then
log_info "Writing rootfs to partition 4..."
write_to_partition "$SDCARD_IMG" 4 "$ROOTFS_IMG" || exit 1
log_ok "rootfs written ($(stat -c%s "$ROOTFS_IMG") bytes)"
else
log_info "No rootfs found in $LINUX_IMAGES_DIR (looked for rootfs.ext4, rootfs.cpio.gz)"
log_info "You can write rootfs to partition 4 manually"
fi
log_ok "SD card image created: $SDCARD_IMG"
# Generate BOOT.BIN
log_info ""
log_info "Generating BOOT.BIN with wolfBoot..."
source "${VITIS_PATH}/settings64.sh" 2>/dev/null || true
if command -v bootgen &>/dev/null; then
rm -f BOOT.BIN
bootgen -arch versal -image ./tools/scripts/versal_boot.bif -w -o BOOT.BIN || log_error "bootgen failed"
[ -f BOOT.BIN ] && {
log_ok "BOOT.BIN size: $(stat -c%s BOOT.BIN) bytes"
cp BOOT.BIN "${TFTP_DIR}/" 2>/dev/null && log_ok "BOOT.BIN copied to TFTP"
}
else
log_error "bootgen not found - source Vitis settings or set VITIS_PATH"
fi
log_info ""
log_info "SD Card Partition Layout:"
log_info " Partition 1 (boot): FAT32 - BOOT.BIN (PLM + PSM + BL31 + wolfBoot)"
log_info " Partition 2 (OFP_A): Signed Linux FIT image v1 (primary)"
log_info " Partition 3 (OFP_B): Signed Linux FIT image v2 (update)"
log_info " Partition 4 (rootfs): Linux root filesystem"
log_info ""
log_info "Provision SD card:"
log_info " sudo ./tools/scripts/versal_sdcard_provision.sh /dev/sdX"
log_info ""
log_info "Or manually:"
log_info " sudo dd if=$SDCARD_IMG of=/dev/sdX bs=4M status=progress conv=fsync"
log_info " sync"
log_info " sudo mkfs.vfat -F 32 -n BOOT /dev/sdX1"
log_info " sudo mount /dev/sdX1 /mnt && sudo cp BOOT.BIN /mnt/ && sudo umount /mnt"
exit 0
;;
--sdcard)
log_info "=== SD Card Boot Mode ==="
# Build wolfBoot with SD card configuration
log_info "Building wolfBoot with SD card config..."
cp config/examples/versal_vmk180_sdcard.config .config
make clean && make || { log_error "Failed to build wolfBoot"; exit 1; }
[ ! -f "wolfboot.elf" ] && { log_error "wolfboot.elf not found"; exit 1; }
load_config .config
# Build and sign test application
log_info "Building and signing test application..."
make test-app/image.bin || { log_error "Failed to build test app"; exit 1; }
export IMAGE_HEADER_SIZE IMAGE_SIGNATURE_SIZE
PRIVATE_KEY="${PRIVATE_KEY:-wolfboot_signing_private_key.der}"
./tools/keytools/sign $SIGN_OPTIONS test-app/image.bin "$PRIVATE_KEY" 1 || { log_error "Signing v1 failed"; exit 1; }
./tools/keytools/sign $SIGN_OPTIONS test-app/image.bin "$PRIVATE_KEY" 2 || { log_error "Signing v2 failed"; exit 1; }
log_ok "Signed test applications: image_v1_signed.bin, image_v2_signed.bin"
# Create SD card image with GPT partitions
create_sdcard_image "$SDCARD_IMG" "$SDCARD_SIZE_MB" || exit 1
# Write signed images to partitions (OFP_A=2, OFP_B=3)
log_info "Writing signed images to SD card partitions..."
write_to_partition "$SDCARD_IMG" 2 test-app/image_v1_signed.bin || exit 1
write_to_partition "$SDCARD_IMG" 3 test-app/image_v2_signed.bin || exit 1
log_ok "SD card image created: $SDCARD_IMG"
# Generate BOOT.BIN if prebuilt files are available
export PREBUILT_DIR="${WOLFBOOT_ROOT}/../soc-prebuilt-firmware/vmk180-versal"
if [ -d "${PREBUILT_DIR}" ]; then
log_info ""
log_info "Generating BOOT.BIN with wolfBoot..."
for f in project_1.pdi plm.elf psmfw.elf bl31.elf system-default.dtb; do
[ -f "${PREBUILT_DIR}/${f}" ] && cp "${PREBUILT_DIR}/${f}" .
done
source "${VITIS_PATH}/settings64.sh" 2>/dev/null || true
if command -v bootgen &>/dev/null; then
rm -f BOOT.BIN
bootgen -arch versal -image ./tools/scripts/versal_boot.bif -w -o BOOT.BIN || log_error "bootgen failed"
[ -f BOOT.BIN ] && {
log_ok "BOOT.BIN size: $(stat -c%s BOOT.BIN) bytes"
cp BOOT.BIN "${TFTP_DIR}/" 2>/dev/null && log_ok "BOOT.BIN copied to TFTP"
}
fi
fi
log_info ""
log_info "SD Card Partition Layout:"
log_info " Partition 1 (boot): FAT32 - BOOT.BIN goes here"
log_info " Partition 2 (OFP_A): Primary signed FIT image (written)"
log_info " Partition 3 (OFP_B): Update signed FIT image (written)"
log_info " Partition 4 (rootfs): Linux root filesystem"
log_info ""
log_info "To write to physical SD card (replace /dev/sdX):"
log_info " sudo dd if=$SDCARD_IMG of=/dev/sdX bs=4M status=progress conv=fsync"
log_info " sync"
log_info ""
log_info "Then format partition 1 as FAT32 and copy BOOT.BIN:"
log_info " sudo mkfs.vfat -F 32 -n BOOT /dev/sdX1"
log_info " sudo mount /dev/sdX1 /mnt"
log_info " sudo cp BOOT.BIN /mnt/"
log_info " sudo umount /mnt"
exit 0
;;
"")