mirror of https://github.com/wolfSSL/wolfBoot.git
Versal SDCard support
parent
39085635c6
commit
9697f6a4f8
21
arch.mk
21
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
234
docs/Targets.md
234
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
|
||||
**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
|
||||
|
||||
|
|
|
|||
180
hal/versal.c
180
hal/versal.c
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -467,6 +467,7 @@ InitEL1:
|
|||
|
||||
/* Assembly startup error handler */
|
||||
error:
|
||||
wfi
|
||||
b error
|
||||
|
||||
|
||||
|
|
|
|||
194
src/disk.c
194
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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
58
src/sdhci.c
58
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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
;;
|
||||
"")
|
||||
|
|
|
|||
Loading…
Reference in New Issue