diff --git a/arch.mk b/arch.mk index fb34653813..3d68736b26 100644 --- a/arch.mk +++ b/arch.mk @@ -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 diff --git a/config/examples/versal_vmk180_sdcard.config b/config/examples/versal_vmk180_sdcard.config new file mode 100644 index 0000000000..0520270030 --- /dev/null +++ b/config/examples/versal_vmk180_sdcard.config @@ -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 diff --git a/docs/Targets.md b/docs/Targets.md index 798132f84c..6299721749 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -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 - -For debugging with OCRAM (OCM), set `WOLFBOOT_ORIGIN=0xFFFC0000` in the config file. Versal Gen 1 OCM is 256KB at `0xFFFC0000 - 0xFFFFFFFF`. - -### Building wolfBoot +**QSPI Flash** -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 - -wolfBoot can boot a signed Linux kernel on the Versal VMK180. This replaces U-Boot entirely for a secure boot chain. +**Booting PetaLinux (QSPI)** -#### 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 -``` -This creates `fitImage_v1_signed.bin`. +tftp ${loadaddr} fitImage_v1_signed.bin +sf probe 0 +sf erase 0x800000 +${filesize} +sf write ${loadaddr} 0x800000 ${filesize} +``` -#### 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`: - -```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: +**Automated 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 < 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 */ diff --git a/hal/versal.h b/hal/versal.h index 6fcaa9f623..54b09254e4 100644 --- a/hal/versal.h +++ b/hal/versal.h @@ -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 diff --git a/src/boot_aarch64.c b/src/boot_aarch64.c index 9b0ad62876..e7224827dc 100644 --- a/src/boot_aarch64.c +++ b/src/boot_aarch64.c @@ -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) { } \ No newline at end of file +/* ============================================================================ + * 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 */ diff --git a/src/boot_aarch64_start.S b/src/boot_aarch64_start.S index 6f48ed5dab..b664d1a568 100644 --- a/src/boot_aarch64_start.S +++ b/src/boot_aarch64_start.S @@ -467,6 +467,7 @@ InitEL1: /* Assembly startup error handler */ error: + wfi b error diff --git a/src/disk.c b/src/disk.c index 692e865e4c..d9c1eec3a0 100644 --- a/src/disk.c +++ b/src/disk.c @@ -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); + /* 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; + } - 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; + /* Parse and validate GPT header */ + if (gpt_parse_header((uint8_t*)sector, &ptable) != 0) { + wolfBoot_printf("Invalid GPT header\r\n"); + return -1; } - r = disk_read(drv, address, ptable.array_sz, entry_buf); - if (r < 0) { + wolfBoot_printf("Valid GPT partition table\r\n"); + wolfBoot_printf("Max number of partitions: %d\r\n", ptable.n_part); + + 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; } - /* 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; - - 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("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; + 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; } } diff --git a/src/sdhci.c b/src/sdhci.c index 82cb39ed63..5e62a12958 100644 --- a/src/sdhci.c +++ b/src/sdhci.c @@ -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 */ - diff --git a/src/update_disk.c b/src/update_disk.c index 0055e365c9..03095ba50e 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -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(); diff --git a/test-app/app_versal.c b/test-app/app_versal.c index c7bee8e1a8..81731f3f1e 100644 --- a/test-app/app_versal.c +++ b/test-app/app_versal.c @@ -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"); diff --git a/tools/scripts/versal_test.sh b/tools/scripts/versal_test.sh index fcde227c36..f4acd63d3a 100755 --- a/tools/scripts/versal_test.sh +++ b/tools/scripts/versal_test.sh @@ -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" </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 ;; "")