From 7fa33fd6d71442615162b2ad80d0a07ad7b8f9d7 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 29 Jan 2026 13:46:55 -0800 Subject: [PATCH 1/2] Add MPFS250 QSPI support --- arch.mk | 22 +- config/examples/polarfire_mpfs250.config | 6 +- config/examples/polarfire_mpfs250_qspi.config | 99 +++ docs/Targets.md | 40 + hal/mpfs250.c | 553 ++++++++++++- hal/mpfs250.h | 168 +++- hal/riscv.h | 33 +- src/boot_riscv.c | 2 +- src/boot_riscv_start.S | 11 +- src/elf.c | 12 +- src/vector_riscv.S | 6 +- test-app/RISCV64-mpfs250.ld | 17 +- test-app/startup_riscv.c | 12 +- test-app/vector_riscv.S | 53 ++ tools/scripts/mpfs_program.sh | 746 ++++++++++++++++++ 15 files changed, 1727 insertions(+), 53 deletions(-) create mode 100644 config/examples/polarfire_mpfs250_qspi.config create mode 100755 tools/scripts/mpfs_program.sh diff --git a/arch.mk b/arch.mk index 7c1c04cc20..04ec64f24b 100644 --- a/arch.mk +++ b/arch.mk @@ -576,10 +576,19 @@ endif ifeq ($(ARCH),RISCV64) CROSS_COMPILE?=riscv64-unknown-elf- CFLAGS+=-DMMU -DWOLFBOOT_DUALBOOT - CFLAGS+=-DWOLFBOOT_UPDATE_DISK -DMAX_DISKS=1 - UPDATE_OBJS:=src/update_disk.o - OBJS += src/gpt.o - OBJS += src/disk.o + + # If SD card or eMMC is enabled use update_disk loader with GPT support + ifneq ($(filter 1,$(DISK_SDCARD) $(DISK_EMMC)),) + CFLAGS+=-DWOLFBOOT_UPDATE_DISK -DMAX_DISKS=1 + UPDATE_OBJS:=src/update_disk.o + OBJS += src/gpt.o + OBJS += src/disk.o + else + # Use RAM-based update path for non-memory-mapped flash (SC SPI) + # Images are loaded into RAM before execution + UPDATE_OBJS?=src/update_ram.o + endif + ARCH_FLAGS=-march=rv64imafd -mabi=lp64d -mcmodel=medany CFLAGS+=-fno-builtin-printf -DUSE_M_TIME -g -nostartfiles -DARCH_RISCV -DARCH_RISCV64 CFLAGS+=$(ARCH_FLAGS) @@ -1440,6 +1449,11 @@ endif CFLAGS+=-DARCH_FLASH_OFFSET=$(ARCH_FLASH_OFFSET) BOOT_IMG?=test-app/image.bin +# When ELF loading is enabled, sign the ELF file (not the flat binary) +ifeq ($(ELF),1) + BOOT_IMG=test-app/image.elf +endif + ## Update mechanism ifeq ($(ARCH),AARCH64) CFLAGS+=-DMMU -DWOLFBOOT_FDT -DWOLFBOOT_DUALBOOT diff --git a/config/examples/polarfire_mpfs250.config b/config/examples/polarfire_mpfs250.config index c14fa4b2d2..4b3ec64ff9 100644 --- a/config/examples/polarfire_mpfs250.config +++ b/config/examples/polarfire_mpfs250.config @@ -26,6 +26,10 @@ DEBUG?=0 DEBUG_SYMBOLS?=1 DEBUG_UART?=1 VTOR?=1 + +# Flash Configuration +# EXT_FLASH=0: Use eMMC/SD card for firmware storage (default) +# EXT_FLASH=1: Use QSPI flash (Micron MT25QL01G 128MB) EXT_FLASH?=0 SPI_FLASH?=0 NO_XIP?=1 @@ -44,8 +48,6 @@ ELF?=1 # Use RISC-V assembly version of ECDSA and SHA NO_ASM?=0 -# Optional: Use smaller SHA512 -#CFLAGS_EXTRA+=-DUSE_SLOW_SHA512 # SDCard or eMMC support via SDHCI driver DISK_SDCARD?=1 diff --git a/config/examples/polarfire_mpfs250_qspi.config b/config/examples/polarfire_mpfs250_qspi.config new file mode 100644 index 0000000000..7799b401d7 --- /dev/null +++ b/config/examples/polarfire_mpfs250_qspi.config @@ -0,0 +1,99 @@ +ARCH?=RISCV64 +TARGET?=mpfs250 + +# ECC P384 + SHA384 +SIGN?=ECC384 +HASH?=SHA384 +IMAGE_HEADER_SIZE=512 + +# ML-DSA 87 + SHA256 +#SIGN=ML_DSA +#HASH=SHA256 +#ML_DSA_LEVEL=5 +#IMAGE_SIGNATURE_SIZE=4627 +#IMAGE_HEADER_SIZE=12288 + +WOLFBOOT_VERSION?=1 +ARMORED?=0 +DEBUG?=0 +DEBUG_SYMBOLS?=1 +DEBUG_UART?=1 +VTOR?=1 + +NO_XIP?=1 + +NVM_FLASH_WRITEONCE?=0 +UART_FLASH?=0 +V?=0 +NO_MPU?=1 +RAM_CODE?=0 +SPMATH?=1 +DUALBANK_SWAP?=0 +PKA?=0 +ENCRYPT=0 +WOLFTPM?=0 +ELF?=1 +#DEBUG_ELF?=1 + +# Use RISC-V assembly version of ECDSA and SHA +NO_ASM?=0 + +# QSPI Flash Configuration +# Using Micron MT25QL01GBBB (128MB, 64KB sectors) +EXT_FLASH?=1 +SPI_FLASH?=0 + +# SPI Flash Controller Selection: +# MPFS_SC_SPI: Use System Controller SPI services (fabric-connected flash) +# This is how HSS accesses the design flash via SCB mailbox. +# DEFAULT: Use MSS QSPI Controller directly (0x21000000) +# For external flash connected to MSS QSPI pins. +CFLAGS_EXTRA+=-DMPFS_SC_SPI + + +# Enable SD card temporarily (wolfBoot still loads from SD, apps from QSPI) +# For pure QSPI boot, HSS would need to load wolfBoot from QSPI +DISK_SDCARD?=0 +DISK_EMMC?=0 + +# DDR Address for wolfBoot to start from +# Comes from hal/mpfs.yaml +WOLFBOOT_ORIGIN?=0x80000000 + +# DDR Address where application image will be loaded from flash +# Used by update_ram.c for non-XIP boot +# Must be well above wolfBoot's memory region +WOLFBOOT_LOAD_ADDRESS?=0x8E000000 + +# Flash geometry (64 KB sector) +WOLFBOOT_SECTOR_SIZE?=0x10000 + +# Partition layout for 128MB QSPI flash +# HSS Boot Info: 0x00000000 - 0x00000400 (1KB) +# wolfBoot partition: 0x00000400 - 0x0001FFFF (127KB) +# Boot partition: 0x00020000 - 0x01FFFFFF (~32MB) +# Update partition: 0x02000000 - 0x03FFFFFF (32MB) +# Swap partition: 0x04000000 - 0x0400FFFF (64KB) +# Remaining: 0x04010000 - 0x07FFFFFF (~64MB available) +WOLFBOOT_PARTITION_SIZE?=0x1FE0000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x20000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x2000000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x4000000 + +# DTS (Device Tree) +WOLFBOOT_LOAD_DTS_ADDRESS?=0x8A000000 +WOLFBOOT_DTS_BOOT_ADDRESS?=0x6000000 # DTS at 96MB (after swap) +WOLFBOOT_DTS_UPDATE_ADDRESS?=0x6010000 # DTS update at 96MB + 64KB + +# Speed up reads from flash by using larger blocks +CFLAGS_EXTRA+=-DWOLFBOOT_SHA_BLOCK_SIZE=4096 + +# Optional Encryption +#CUSTOM_ENCRYPT_KEY=1 +#ENCRYPT=1 +#ENCRYPT_WITH_AES256=1 +#OBJS_EXTRA=src/my_custom_encrypt_key.o + +# Optional QSPI debugging +# Uncomment for verbose QSPI debug output +#CFLAGS_EXTRA+=-DDEBUG_QSPI diff --git a/docs/Targets.md b/docs/Targets.md index bfcc448b09..197df16e72 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -859,6 +859,46 @@ sudo dd if=wolfboot.bin of=/dev/sdc1 bs=512 && sudo cmp wolfboot.bin /dev/sdc1 Note: +### PolarFire SoC QSPI + +PolarFire QSPI can be accessed in two ways. The selection is made at build time and affects how wolfBoot +talks to the flash: + +```text + +-------------------+ +----------------------+ + | U54 cores | | U54 cores | + | (wolfBoot) | | (wolfBoot) | + +---------+---------+ +----------+-----------+ + | | + | direct QSPI controller | SCB mailbox service + | (MSS QSPI @ 0x2100_0000) | (System Controller) + v v + +-------------------+ +----------------------+ + | MSS QSPI IP | | System Controller | + +---------+---------+ +----------+-----------+ + | | + v v + External QSPI flash Fabric-connected flash +``` + +Build options: + +- MSS QSPI controller (direct, read/write/erase) + - `EXT_FLASH=1` + - Do not set `MPFS_SC_SPI` + - Example config: `config/examples/polarfire_mpfs250_qspi.config` with `CFLAGS_EXTRA` line removed. + +- System Controller SPI services (fabric flash via SCB mailbox, read-only) + - `EXT_FLASH=1` + - `CFLAGS_EXTRA+=-DMPFS_SC_SPI` + - Example config: `config/examples/polarfire_mpfs250_qspi.config` as-is. + +Notes: +- For QSPI-based boot flows, disable SD/eMMC in the config (`DISK_SDCARD=0`, `DISK_EMMC=0`) unless you + explicitly want wolfBoot to load from disk and the application from QSPI. +- The MSS QSPI path expects external flash on the MSS QSPI pins; the System Controller path matches + HSS access to the design flash via the SCB mailbox. + ### PolarFire testing This section describes how to build the test-application, create a custom uSD with required partitions and copying signed test-application to uSD partitions. diff --git a/hal/mpfs250.c b/hal/mpfs250.c index 50511fe42e..2792787426 100644 --- a/hal/mpfs250.c +++ b/hal/mpfs250.c @@ -60,6 +60,10 @@ void hal_init(void) LIBWOLFBOOT_VERSION_STRING,__DATE__, __TIME__); #endif #endif + +#ifdef EXT_FLASH + qspi_init(); +#endif } /* ============================================================================ @@ -290,26 +294,549 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) } #ifdef EXT_FLASH -/* External flash support +/* ========================================================================== + * QSPI Flash Controller Implementation + * + * Two modes supported (selected at build time): + * - MPFS_SC_SPI=1: System Controller SPI services (read-only) + * - MPFS_SC_SPI=0: MSS QSPI Controller direct access (read/write/erase) + * ========================================================================== */ + +#ifndef MPFS_SC_SPI +/* ========================================================================== + * MSS QSPI Controller Functions (direct peripheral access) + * ========================================================================== */ + +/* Send Release from Deep Power-Down / Wake up command */ +static void qspi_flash_wakeup(void) +{ + uint8_t cmd = 0xAB; /* Release from Deep Power-Down */ + qspi_transfer_block(QSPI_MODE_WRITE, &cmd, 1, NULL, 0, 0); + /* Flash needs tRES1 (3us typ) to wake up - add small delay */ + for (volatile int i = 0; i < 10000; i++); +} +#endif /* !MPFS_SC_SPI */ + +/* QSPI Controller Initialization + * + * PolarFire SoC supports two SPI flash access methods: + * + * MPFS_SC_SPI=1 (default): System Controller SPI services + * - Uses SCB mailbox (MSS_SYS_spi_copy) for fabric-connected flash + * - This is how HSS loads images from design flash + * + * MPFS_SC_SPI=0: MSS QSPI Controller direct access + * - Uses QSPI peripheral at 0x21000000 + * - For external flash connected to MSS QSPI pins + */ +void qspi_init(void) +{ +#ifdef MPFS_SC_SPI + wolfBoot_printf("QSPI: Using System Controller SPI services\n"); +#else + uint8_t id[3]; + + wolfBoot_printf("QSPI: Using MSS QSPI Controller (0x%x)\n", QSPI_BASE); + + /* Enable QSPI peripheral clock */ + SYSREG_SUBBLK_CLOCK_CR |= SYSREG_SUBBLK_CLOCK_CR_QSPI; + + /* Small delay after clock enable */ + for (volatile int i = 0; i < 100; i++); + + /* Release MSS QSPI from reset */ + SYSREG_SOFT_RESET_CR &= ~SYSREG_SOFT_RESET_CR_QSPI; + + /* Small delay to ensure reset is released */ + for (volatile int i = 0; i < 1000; i++); + + /* Disable controller before configuration */ + QSPI_CONTROL = 0; + + /* Disable all interrupts */ + QSPI_IEN = 0; + + /* Configure QSPI Control Register: + * - Clock divider for ~5MHz (conservative) + * - CPOL=1 (clock idle high) for SPI Mode 3 + * - Sample on SCK edge + * - Enable controller + */ + QSPI_CONTROL = + (QSPI_CLK_DIV_30 << QSPI_CTRL_CLKRATE_OFFSET) | + QSPI_CTRL_CLKIDLE | + QSPI_CTRL_SAMPLE_SCK | + QSPI_CTRL_EN; + + /* Wait for controller to be ready */ + while (!(QSPI_STATUS & QSPI_STATUS_READY)); + + /* Wake up flash from deep power-down (if applicable) */ + qspi_flash_wakeup(); + + /* Read and display JEDEC ID for verification */ + if (qspi_read_id(id) == 0) { + wolfBoot_printf("QSPI: Flash ID = 0x%02x 0x%02x 0x%02x\n", + id[0], id[1], id[2]); + } + + /* Enter 4-byte addressing mode for >16MB flash */ + qspi_enter_4byte_mode(); +#endif /* MPFS_SC_SPI */ +} + +#ifndef MPFS_SC_SPI +/* QSPI Block Transfer Function + * Modeled after Microchip's MSS_QSPI_polled_transfer_block reference driver. + * + * read_mode: 0=write (QSPI_MODE_WRITE), 1=read (QSPI_MODE_READ) + * cmd: Command buffer (opcode + address bytes) + * cmd_len: Length of command (opcode + address, NOT including opcode separately) + * data: Data buffer for read/write + * data_len: Length of data phase + * dummy_cycles: Number of idle cycles between command and data phase + */ +int qspi_transfer_block(uint8_t read_mode, const uint8_t *cmd, uint32_t cmd_len, + uint8_t *data, uint32_t data_len, uint8_t dummy_cycles) +{ + uint32_t total_bytes = cmd_len + data_len; + uint32_t frames; + uint32_t i; + uint32_t timeout; + + /* Wait for controller to be ready before starting */ + timeout = 100000; + while (!(QSPI_STATUS & QSPI_STATUS_READY) && --timeout); + if (timeout == 0) { + wolfBoot_printf("QSPI: Timeout waiting for READY\n"); + return -1; + } + + /* Configure FRAMES register: + * - Total bytes: command + data (idle cycles handled by hardware) + * - Command bytes: number of TX-only bytes before data phase + * - Idle cycles: inserted by hardware between command and data + * - FWORD: set for word-aligned transfers (per Microchip reference) + */ + frames = ((total_bytes & 0xFFFF) << QSPI_FRAMES_TOTALBYTES_OFFSET) | + ((cmd_len & 0x1FF) << QSPI_FRAMES_CMDBYTES_OFFSET) | + ((dummy_cycles & 0xF) << QSPI_FRAMES_IDLE_OFFSET) | + (1u << QSPI_FRAMES_FWORD_OFFSET); + + QSPI_FRAMES = frames; + +#ifdef DEBUG_QSPI + wolfBoot_printf("QSPI: cmd[0]=0x%x, cmd_len=%d, data_len=%d, frames=0x%x\n", + cmd[0], cmd_len, data_len, frames); +#endif + + /* Send command bytes (opcode + address) */ + for (i = 0; i < cmd_len; i++) { + timeout = 100000; + while ((QSPI_STATUS & QSPI_STATUS_TXFULL) && --timeout); + if (timeout == 0) { + wolfBoot_printf("QSPI: TX FIFO full timeout\n"); + return -2; + } + QSPI_TX_DATA = cmd[i]; + } + +#ifdef DEBUG_QSPI + wolfBoot_printf("QSPI: Cmd sent, status=0x%x, read_mode=%d\n", + QSPI_STATUS, read_mode); +#endif + + if (read_mode) { + /* Wait for command TX to complete before reading response */ + timeout = 100000; + while (!(QSPI_STATUS & QSPI_STATUS_TXDONE) && --timeout); + if (timeout == 0) { + wolfBoot_printf("QSPI: TX done timeout (read), status=0x%x\n", + QSPI_STATUS); + return -5; + } + + /* Read mode: receive data after command + idle cycles */ + for (i = 0; i < data_len; i++) { + timeout = 1000000; + while (!(QSPI_STATUS & QSPI_STATUS_RXAVAIL) && --timeout); + if (timeout == 0) { + wolfBoot_printf("QSPI: RX timeout at byte %d, status=0x%x\n", + i, QSPI_STATUS); + return -3; + } + data[i] = QSPI_RX_DATA; + } + /* Wait for receive complete */ + timeout = 100000; + while (!(QSPI_STATUS & QSPI_STATUS_RXDONE) && --timeout); + } else { + /* Write mode: send data bytes */ + if (data && data_len > 0) { + for (i = 0; i < data_len; i++) { + timeout = 100000; + while ((QSPI_STATUS & QSPI_STATUS_TXFULL) && --timeout); + if (timeout == 0) { + wolfBoot_printf("QSPI: TX data timeout\n"); + return -4; + } + QSPI_TX_DATA = data[i]; + } + } + /* Wait for transmit complete */ + timeout = 100000; + while (!(QSPI_STATUS & QSPI_STATUS_TXDONE) && --timeout); + } + + return 0; +} + +/* Read JEDEC ID from flash */ +int qspi_read_id(uint8_t *id_buf) +{ + uint8_t cmd = QSPI_CMD_READ_ID_OPCODE; + return qspi_transfer_block(QSPI_MODE_READ, &cmd, 1, id_buf, 3, 0); +} + +/* Send Write Enable command */ +int qspi_write_enable(void) +{ + uint8_t cmd = QSPI_CMD_WRITE_ENABLE_OPCODE; + return qspi_transfer_block(QSPI_MODE_WRITE, &cmd, 1, NULL, 0, 0); +} + +/* Wait for flash to be ready (poll status register) */ +int qspi_wait_ready(uint32_t timeout_ms) +{ + uint8_t cmd = QSPI_CMD_READ_STATUS_OPCODE; + uint8_t status; + uint32_t count = 0; + uint32_t max_count = timeout_ms * 1000; /* Rough timing */ + + do { + qspi_transfer_block(QSPI_MODE_READ, &cmd, 1, &status, 1, 0); + if (!(status & 0x01)) { /* Bit 0 = WIP (Write In Progress) */ + return 0; /* Ready */ + } + count++; + } while (count < max_count); + + return -1; /* Timeout */ +} + +/* Enter 4-byte addressing mode (required for >32MB flash) */ +int qspi_enter_4byte_mode(void) +{ + uint8_t cmd = QSPI_CMD_ENTER_4BYTE_MODE; + return qspi_transfer_block(QSPI_MODE_WRITE, &cmd, 1, NULL, 0, 0); +} + +/* Read from QSPI flash (4-byte addressing) */ +static int qspi_flash_read(uint32_t address, uint8_t *data, uint32_t len) +{ + uint8_t cmd[5]; + + /* Build 4-byte read command */ + cmd[0] = QSPI_CMD_4BYTE_READ_OPCODE; + cmd[1] = (address >> 24) & 0xFF; + cmd[2] = (address >> 16) & 0xFF; + cmd[3] = (address >> 8) & 0xFF; + cmd[4] = address & 0xFF; + + return qspi_transfer_block(QSPI_MODE_READ, cmd, 5, data, len, 0); +} + +/* Write to QSPI flash - single page (max 256 bytes) */ +static int qspi_flash_write_page(uint32_t address, const uint8_t *data, uint32_t len) +{ + uint8_t cmd[5]; + int ret; + + /* Ensure page alignment and length */ + if (len > FLASH_PAGE_SIZE) { + len = FLASH_PAGE_SIZE; + } + + /* Enable write */ + ret = qspi_write_enable(); + if (ret != 0) { + return ret; + } + + /* Build 4-byte page program command */ + cmd[0] = QSPI_CMD_4BYTE_PAGE_PROG_OPCODE; + cmd[1] = (address >> 24) & 0xFF; + cmd[2] = (address >> 16) & 0xFF; + cmd[3] = (address >> 8) & 0xFF; + cmd[4] = address & 0xFF; + + /* Send command + data */ + ret = qspi_transfer_block(QSPI_MODE_WRITE, cmd, 5, (uint8_t *)data, len, 0); + if (ret != 0) { + return ret; + } + + /* Wait for write to complete */ + return qspi_wait_ready(1000); /* 1 second timeout */ +} + +/* Erase 64KB sector */ +static int qspi_flash_sector_erase(uint32_t address) +{ + uint8_t cmd[5]; + int ret; + + /* Enable write */ + ret = qspi_write_enable(); + if (ret != 0) { + return ret; + } + + /* Build 4-byte sector erase command */ + cmd[0] = QSPI_CMD_4BYTE_SECTOR_ERASE; + cmd[1] = (address >> 24) & 0xFF; + cmd[2] = (address >> 16) & 0xFF; + cmd[3] = (address >> 8) & 0xFF; + cmd[4] = address & 0xFF; + + ret = qspi_transfer_block(QSPI_MODE_WRITE, cmd, 5, NULL, 0, 0); + if (ret != 0) { + return ret; + } + + /* Wait for erase to complete (64KB erase can take several seconds) */ + return qspi_wait_ready(10000); /* 10 second timeout */ +} +#endif /* !MPFS_SC_SPI */ + +#ifdef MPFS_SC_SPI +/* ========================================================================== + * System Controller SPI Flash Services + * + * The SPI flash on PolarFire SoC is accessed through System Controller + * services via the SCB mailbox, NOT through direct QSPI peripheral access. + * This matches how HSS accesses the flash using MSS_SYS_spi_copy(). + * ========================================================================== */ + +/** + * sc_spi_copy - Copy data from SPI flash to memory using System Controller * - * Note: These are intentional stubs. PolarFire SoC MPFS250 uses eMMC/SD card - * for firmware storage and updates, not external SPI/QSPI flash. The EXT_FLASH - * define may be set in some configurations, but actual storage operations are - * handled by the SDHCI driver and disk partition layer. + * @dest_addr: Destination address in MSS memory (where data will be copied) + * @flash_addr: Source address in SPI flash + * @len: Number of bytes to copy + * + * Returns: 0 on success, negative error code on failure */ +static int sc_spi_copy(uint64_t dest_addr, uint32_t flash_addr, uint32_t len) +{ + uint32_t cmd; + uint32_t status; + int timeout; + uint8_t mb_data[20]; /* 17 bytes needed, aligned to 20 */ + + /* Check if System Controller is busy */ + if (SCBCTRL_REG(SERVICES_SR_OFFSET) & SERVICES_SR_BUSY_MASK) { + wolfBoot_printf("SC: Busy\n"); + return -1; + } + + /* Prepare mailbox data for SPI_COPY service (17 bytes): + * Bytes 0-7: destination address (64-bit) + * Bytes 8-11: source SPI flash address (32-bit) + * Bytes 12-15: byte count (32-bit) + * Byte 16: options (clock speed: 1=40MHz, 2=20MHz, 3=13.33MHz) + */ + mb_data[0] = (dest_addr >> 0) & 0xFF; + mb_data[1] = (dest_addr >> 8) & 0xFF; + mb_data[2] = (dest_addr >> 16) & 0xFF; + mb_data[3] = (dest_addr >> 24) & 0xFF; + mb_data[4] = (dest_addr >> 32) & 0xFF; + mb_data[5] = (dest_addr >> 40) & 0xFF; + mb_data[6] = (dest_addr >> 48) & 0xFF; + mb_data[7] = (dest_addr >> 56) & 0xFF; + mb_data[8] = (flash_addr >> 0) & 0xFF; + mb_data[9] = (flash_addr >> 8) & 0xFF; + mb_data[10] = (flash_addr >> 16) & 0xFF; + mb_data[11] = (flash_addr >> 24) & 0xFF; + mb_data[12] = (len >> 0) & 0xFF; + mb_data[13] = (len >> 8) & 0xFF; + mb_data[14] = (len >> 16) & 0xFF; + mb_data[15] = (len >> 24) & 0xFF; + mb_data[16] = SPI_COPY_OPT_13MHZ; /* Use 13.33MHz (safest) */ + + /* Write mailbox data (word-aligned writes) */ + SCBMBOX_REG(0) = *(uint32_t*)&mb_data[0]; + SCBMBOX_REG(4) = *(uint32_t*)&mb_data[4]; + SCBMBOX_REG(8) = *(uint32_t*)&mb_data[8]; + SCBMBOX_REG(12) = *(uint32_t*)&mb_data[12]; + SCBMBOX_REG(16) = mb_data[16]; /* Last byte (options) */ + + /* Build and send service command: + * Bits 0-6: opcode (0x50 for SPI_COPY) + * Bits 7-15: mailbox offset (0) + * Bit 0 of CR: REQ (request bit) + */ + cmd = ((SYS_SERV_CMD_SPI_COPY & 0x7F) << SERVICES_CR_COMMAND_SHIFT) | + SERVICES_CR_REQ_MASK; + SCBCTRL_REG(SERVICES_CR_OFFSET) = cmd; + + /* Wait for REQ bit to clear (command accepted) */ + timeout = 100000; + while ((SCBCTRL_REG(SERVICES_CR_OFFSET) & SERVICES_CR_REQ_MASK) && --timeout); + if (timeout == 0) { + wolfBoot_printf("SC: REQ timeout\n"); + return -2; + } + + /* Wait for BUSY bit to clear (command completed) */ + timeout = 10000000; /* Long timeout for large transfers */ + while ((SCBCTRL_REG(SERVICES_SR_OFFSET) & SERVICES_SR_BUSY_MASK) && --timeout); + if (timeout == 0) { + wolfBoot_printf("SC: BUSY timeout\n"); + return -3; + } + + /* Check status (upper 16 bits of status register) */ + status = (SCBCTRL_REG(SERVICES_SR_OFFSET) >> SERVICES_SR_STATUS_SHIFT) & 0xFFFF; + if (status != 0) { + wolfBoot_printf("SC: Error 0x%x\n", status); + return -4; + } + + return 0; +} +#endif /* MPFS_SC_SPI */ + +/* ========================================================================== + * External Flash API Implementation + * ========================================================================== */ +void ext_flash_lock(void) +{ + /* Optional: Could implement write protection here */ +} + +void ext_flash_unlock(void) +{ +} + +int ext_flash_write(uintptr_t address, const uint8_t *data, int len) +{ +#ifdef MPFS_SC_SPI + /* System Controller SPI does not support write operations */ + (void)address; + (void)data; + (void)len; + wolfBoot_printf("SC SPI: Write not supported\n"); + return -1; +#else + uint32_t page_offset; + uint32_t chunk_len; + int ret; + + #ifdef DEBUG_QSPI + wolfBoot_printf("QSPI: Write 0x%x, len %d\n", (uint32_t)address, len); + #endif + + /* Write data page by page */ + while (len > 0) { + /* Calculate bytes to write in this page */ + page_offset = address & (FLASH_PAGE_SIZE - 1); + chunk_len = FLASH_PAGE_SIZE - page_offset; + if (chunk_len > (uint32_t)len) { + chunk_len = len; + } + + /* Write page */ + ret = qspi_flash_write_page(address, data, chunk_len); + if (ret != 0) { + return ret; + } + + /* Update pointers */ + address += chunk_len; + data += chunk_len; + len -= chunk_len; + } + + return 0; +#endif /* MPFS_SC_SPI */ +} + +int ext_flash_read(uintptr_t address, uint8_t *data, int len) +{ +#ifdef MPFS_SC_SPI + /* Use System Controller SPI copy service (like HSS does) */ + int ret; + #ifdef DEBUG_QSPI + wolfBoot_printf("SC SPI: Read 0x%x -> 0x%lx, len %d\n", + (uint32_t)address, (unsigned long)data, len); + #endif + ret = sc_spi_copy((uint64_t)(uintptr_t)data, (uint32_t)address, (uint32_t)len); + /* Return length on success (expected by wolfBoot), negative on error */ + return (ret == 0) ? len : ret; +#else + /* Use MSS QSPI Controller direct access */ + #ifdef DEBUG_QSPI + wolfBoot_printf("QSPI: Read 0x%x -> 0x%lx, len %d\n", + (uint32_t)address, (unsigned long)data, len); + #endif + return qspi_flash_read((uint32_t)address, data, (uint32_t)len); +#endif +} + +int ext_flash_erase(uintptr_t address, int len) +{ +#ifdef MPFS_SC_SPI + /* System Controller SPI does not support erase operations */ + (void)address; + (void)len; + wolfBoot_printf("SC SPI: Erase not supported\n"); + return -1; +#else + uint32_t sector_addr; + uint32_t end_addr; + int ret; + + #ifdef DEBUG_QSPI + wolfBoot_printf("QSPI: Erase 0x%x, len %d\n", (uint32_t)address, len); + #endif + + /* Align to sector boundaries */ + sector_addr = address & ~(FLASH_SECTOR_SIZE - 1); + end_addr = address + len; + + /* Erase sectors */ + while (sector_addr < end_addr) { + #ifdef DEBUG_QSPI + wolfBoot_printf("QSPI: Erasing sector at 0x%08X\n", sector_addr); + #endif + + ret = qspi_flash_sector_erase(sector_addr); + if (ret != 0) { + wolfBoot_printf("QSPI: Erase failed\n"); + return ret; + } + + sector_addr += FLASH_SECTOR_SIZE; + } + + return 0; +#endif /* MPFS_SC_SPI */ +} + +#else /* !EXT_FLASH */ + +/* Stubs for when QSPI is disabled */ void ext_flash_lock(void) { - /* Stub: not used - eMMC/SD controller handles access control */ } void ext_flash_unlock(void) { - /* Stub: not used - eMMC/SD controller handles access control */ } int ext_flash_write(uintptr_t address, const uint8_t *data, int len) { - /* Stub: not used - updates written via SDHCI/GPT partitions */ (void)address; (void)data; (void)len; @@ -318,7 +845,6 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len) int ext_flash_read(uintptr_t address, uint8_t *data, int len) { - /* Stub: not used - firmware loaded via SDHCI/GPT partitions */ (void)address; (void)data; (void)len; @@ -327,17 +853,24 @@ int ext_flash_read(uintptr_t address, uint8_t *data, int len) int ext_flash_erase(uintptr_t address, int len) { - /* Stub: not used - eMMC/SD uses block-level operations */ (void)address; (void)len; return 0; } + #endif /* EXT_FLASH */ #if defined(MMU) && !defined(WOLFBOOT_NO_PARTITIONS) void* hal_get_dts_address(void) { +#if defined(EXT_FLASH) && defined(NO_XIP) + /* Flash is not memory-mapped when using NO_XIP with external flash + * (e.g. SC SPI). DTS must be loaded via ext_flash_read, not direct + * dereference. Return NULL so the caller skips the direct-access path. */ + return NULL; +#else return (void*)WOLFBOOT_DTS_BOOT_ADDRESS; +#endif } #endif diff --git a/hal/mpfs250.h b/hal/mpfs250.h index 36c3bda239..2eacc52f5e 100644 --- a/hal/mpfs250.h +++ b/hal/mpfs250.h @@ -36,7 +36,13 @@ /* Write "0xDEAD" to cause a full MSS reset*/ #define SYSREG_MSS_RESET_CR (*((volatile uint32_t*)(SYSREG_BASE + 0x18))) -/* Peripheral Soft Reset Control Register */ +/* Peripheral Subblock Clock Control Register (offset 0x84) */ +#define SYSREG_SUBBLK_CLOCK_CR (*((volatile uint32_t*)(SYSREG_BASE + 0x84))) +#define SYSREG_SUBBLK_CLOCK_CR_ENVM (1U << 0) +#define SYSREG_SUBBLK_CLOCK_CR_MMC (1U << 3) +#define SYSREG_SUBBLK_CLOCK_CR_QSPI (1U << 19) + +/* Peripheral Soft Reset Control Register (offset 0x88) */ #define SYSREG_SOFT_RESET_CR (*((volatile uint32_t*)(SYSREG_BASE + 0x88))) #define SYSREG_SOFT_RESET_CR_ENVM (1U << 0) #define SYSREG_SOFT_RESET_CR_MMC (1U << 3) @@ -155,6 +161,12 @@ /* System Service command opcodes */ #define SYS_SERV_CMD_SERIAL_NUMBER 0x00u +#define SYS_SERV_CMD_SPI_COPY 0x50u + +/* SPI Copy service options (clock speed) */ +#define SPI_COPY_OPT_40MHZ 1u +#define SPI_COPY_OPT_20MHZ 2u +#define SPI_COPY_OPT_13MHZ 3u /* Device serial number size in bytes */ #define DEVICE_SERIAL_NUMBER_SIZE 16 @@ -209,5 +221,159 @@ #define PLIC_CONTEXT_U54_4_S 8 +#ifdef EXT_FLASH +/* ========================================================================== + * QSPI Flash Controller Definitions + * + * PolarFire SoC supports two methods for SPI flash access: + * + * 1. System Controller SPI (MPFS_SC_SPI=1, default): + * - Uses SCB mailbox services (MSS_SYS_spi_copy) + * - For fabric-connected flash (design flash) + * - This is how HSS loads images from SPI flash + * - Read-only (writes/erases not supported via SC services) + * + * 2. MSS QSPI Controller (MPFS_SC_SPI=0): + * - Direct peripheral access at 0x21000000 + * - For external flash connected to MSS QSPI pins + * - Supports read, write, and erase operations + * + * ========================================================================== */ + +/* QSPI Controller Base Address (for MSS QSPI mode) */ +#ifndef QSPI_BASE +#define QSPI_BASE 0x21000000u /* MSS QSPI Controller */ +#endif + +/* QSPI Register Offsets */ +#define QSPI_CONTROL (*(volatile uint32_t *)(QSPI_BASE + 0x00)) +#define QSPI_FRAMES (*(volatile uint32_t *)(QSPI_BASE + 0x04)) +#define QSPI_IEN (*(volatile uint32_t *)(QSPI_BASE + 0x0C)) +#define QSPI_STATUS (*(volatile uint32_t *)(QSPI_BASE + 0x10)) +#define QSPI_DIRECT (*(volatile uint32_t *)(QSPI_BASE + 0x14)) +#define QSPI_ADDRUP (*(volatile uint32_t *)(QSPI_BASE + 0x18)) +#define QSPI_RX_DATA (*(volatile uint8_t *)(QSPI_BASE + 0x40)) +#define QSPI_TX_DATA (*(volatile uint8_t *)(QSPI_BASE + 0x44)) +#define QSPI_X4_RX_DATA (*(volatile uint32_t *)(QSPI_BASE + 0x48)) +#define QSPI_X4_TX_DATA (*(volatile uint32_t *)(QSPI_BASE + 0x4C)) +#define QSPI_FRAMESUP (*(volatile uint32_t *)(QSPI_BASE + 0x50)) + +/* QSPI Control Register Bits */ +#define QSPI_CTRL_EN_OFFSET 0 +#define QSPI_CTRL_XIP_OFFSET 2 +#define QSPI_CTRL_XIPADDR_OFFSET 3 +#define QSPI_CTRL_CLKIDLE_OFFSET 10 +#define QSPI_CTRL_SAMPLE_OFFSET 11 +#define QSPI_CTRL_QMODE0_OFFSET 13 +#define QSPI_CTRL_QMODE12_OFFSET 14 +#define QSPI_CTRL_FLAGSX4_OFFSET 16 +#define QSPI_CTRL_CLKRATE_OFFSET 24 + +#define QSPI_CTRL_EN (1u << QSPI_CTRL_EN_OFFSET) +#define QSPI_CTRL_XIP (1u << QSPI_CTRL_XIP_OFFSET) +#define QSPI_CTRL_CLKIDLE (1u << QSPI_CTRL_CLKIDLE_OFFSET) +#define QSPI_CTRL_SAMPLE_MASK (0x3u << QSPI_CTRL_SAMPLE_OFFSET) +#define QSPI_CTRL_SAMPLE_SCK (0x0u << QSPI_CTRL_SAMPLE_OFFSET) +#define QSPI_CTRL_SAMPLE_HCLKF (0x1u << QSPI_CTRL_SAMPLE_OFFSET) +#define QSPI_CTRL_SAMPLE_HCLKR (0x2u << QSPI_CTRL_SAMPLE_OFFSET) +#define QSPI_CTRL_QMODE0 (1u << QSPI_CTRL_QMODE0_OFFSET) +#define QSPI_CTRL_QMODE12_MASK (0x3u << QSPI_CTRL_QMODE12_OFFSET) +#define QSPI_CTRL_CLKRATE_MASK (0xFu << QSPI_CTRL_CLKRATE_OFFSET) + +/* QSPI Frames Register Bits */ +#define QSPI_FRAMES_TOTALBYTES_OFFSET 0 +#define QSPI_FRAMES_CMDBYTES_OFFSET 16 +#define QSPI_FRAMES_QSPI_OFFSET 25 +#define QSPI_FRAMES_IDLE_OFFSET 26 +#define QSPI_FRAMES_FBYTE_OFFSET 30 +#define QSPI_FRAMES_FWORD_OFFSET 31 + +#define QSPI_FRAMES_TOTALBYTES_MASK (0xFFFFu << QSPI_FRAMES_TOTALBYTES_OFFSET) +#define QSPI_FRAMES_CMDBYTES_MASK (0x1FFu << QSPI_FRAMES_CMDBYTES_OFFSET) +#define QSPI_FRAMES_QSPI (1u << QSPI_FRAMES_QSPI_OFFSET) +#define QSPI_FRAMES_IDLE_MASK (0xFu << QSPI_FRAMES_IDLE_OFFSET) + +/* QSPI Status Register Bits */ +#define QSPI_STATUS_TXDONE (1u << 0) +#define QSPI_STATUS_RXDONE (1u << 1) +#define QSPI_STATUS_RXAVAIL (1u << 2) +#define QSPI_STATUS_TXAVAIL (1u << 3) +#define QSPI_STATUS_RXEMPTY (1u << 4) +#define QSPI_STATUS_TXFULL (1u << 5) +#define QSPI_STATUS_READY (1u << 7) +#define QSPI_STATUS_FLAGSX4 (1u << 8) + +/* QSPI Clock Configuration */ +#define QSPI_CLK_DIV_2 0x01u +#define QSPI_CLK_DIV_4 0x02u +#define QSPI_CLK_DIV_6 0x03u +#define QSPI_CLK_DIV_8 0x04u +#define QSPI_CLK_DIV_10 0x05u +#define QSPI_CLK_DIV_12 0x06u +#define QSPI_CLK_DIV_30 0x0Fu /* Conservative: ~5MHz from 150MHz APB */ + +/* QSPI SPI Modes */ +#define QSPI_SPI_MODE0 0 /* CPOL=0, CPHA=0 */ +#define QSPI_SPI_MODE3 1 /* CPOL=1, CPHA=1 */ + +/* QSPI IO Formats */ +#define QSPI_IO_FORMAT_NORMAL 0 /* 1-bit SPI */ +#define QSPI_IO_FORMAT_DUAL_EX0 1 /* 2-bit with extended mode 0 */ +#define QSPI_IO_FORMAT_QUAD_EX0 2 /* 4-bit with extended mode 0 */ +#define QSPI_IO_FORMAT_DUAL_EX1 3 /* 2-bit with extended mode 1 */ +#define QSPI_IO_FORMAT_QUAD_EX1 4 /* 4-bit with extended mode 1 */ +#define QSPI_IO_FORMAT_DUAL_FULL 5 /* Full 2-bit mode */ +#define QSPI_IO_FORMAT_QUAD_FULL 6 /* Full 4-bit mode */ + +/* Micron MT25QL01G Flash Commands */ +#define QSPI_CMD_READ_ID_OPCODE 0x9Fu /* JEDEC ID Read */ +#define QSPI_CMD_MIO_READ_ID_OPCODE 0xAFu /* Multiple IO Read ID */ +#define QSPI_CMD_READ_STATUS_OPCODE 0x05u /* Read Status Register */ +#define QSPI_CMD_WRITE_ENABLE_OPCODE 0x06u /* Write Enable */ +#define QSPI_CMD_WRITE_DISABLE_OPCODE 0x04u /* Write Disable */ +#define QSPI_CMD_4BYTE_READ_OPCODE 0x13u /* 4-byte address read */ +#define QSPI_CMD_4BYTE_FAST_READ_OPCODE 0x0Cu /* 4-byte fast read */ +#define QSPI_CMD_4BYTE_QUAD_READ_OPCODE 0xECu /* 4-byte quad I/O read */ +#define QSPI_CMD_4BYTE_PAGE_PROG_OPCODE 0x12u /* 4-byte page program */ +#define QSPI_CMD_4BYTE_SECTOR_ERASE 0xDCu /* 4-byte 64KB sector erase */ +#define QSPI_CMD_ENTER_4BYTE_MODE 0xB7u /* Enter 4-byte address mode */ +#define QSPI_CMD_EXIT_4BYTE_MODE 0xE9u /* Exit 4-byte address mode */ + +/* Flash Geometry - Micron MT25QL01GBBB (128MB) */ +#ifndef FLASH_DEVICE_SIZE +#define FLASH_DEVICE_SIZE (128 * 1024 * 1024) /* 128MB (1Gb) */ +#endif + +#ifndef FLASH_PAGE_SIZE +#define FLASH_PAGE_SIZE 256 /* 256 bytes */ +#endif + +#ifndef FLASH_SECTOR_SIZE +#define FLASH_SECTOR_SIZE (64 * 1024) /* 64KB sectors */ +#endif + +/* QSPI Transfer Modes */ +#define QSPI_MODE_WRITE 0 +#define QSPI_MODE_READ 1 + +/* Function declarations for QSPI (when EXT_FLASH enabled) */ +#ifndef __ASSEMBLER__ +void qspi_init(void); + +#ifndef MPFS_SC_SPI +/* MSS QSPI Controller functions (only when not using SC SPI) */ +int qspi_transfer_block(uint8_t read_mode, const uint8_t *cmd, uint32_t cmd_len, + uint8_t *data, uint32_t data_len, uint8_t dummy_cycles); +int qspi_read_id(uint8_t *id_buf); +int qspi_write_enable(void); +int qspi_wait_ready(uint32_t timeout_ms); +int qspi_enter_4byte_mode(void); +#endif /* !MPFS_SC_SPI */ + +#endif /* __ASSEMBLER__ */ + +#endif /* EXT_FLASH */ + + #endif /* MPFS250_DEF_INCLUDED */ diff --git a/hal/riscv.h b/hal/riscv.h index 4195645ce5..c9fe6edad7 100644 --- a/hal/riscv.h +++ b/hal/riscv.h @@ -23,20 +23,23 @@ #define RISCV_H -/* TODO: Add support for machine mode wolfBoot */ -#if 1 -#define WOLFBOOT_RISCV_SMODE /* supervisor mode */ -#else -#define WOLFBOOT_RISCV_MMODE /* machine mode */ -#endif +/* ============================================================================ + * RISC-V Privilege Mode Selection + * + * - Machine mode (direct boot from eNVM) : WOLFBOOT_RISCV_MMODE + * - Supervisor mode (running under HSS/SBI) : default + * + * ============================================================================ */ -/* Initial stack pointer address (stack grows downward from here) */ + /* Initial stack pointer address (stack grows downward from here) */ #ifndef WOLFBOOT_STACK_TOP -#ifdef WOLFBOOT_RISCV_SMODE -#define WOLFBOOT_STACK_TOP 0x80200000 -#else -#define WOLFBOOT_STACK_TOP 0x80000000 -#endif + #ifdef WOLFBOOT_RISCV_MMODE + /* M-mode: Stack at end of L2 Scratchpad (256KB) */ + #define WOLFBOOT_STACK_TOP 0x0A040000 + #else + /* S-mode: Stack in DDR */ + #define WOLFBOOT_STACK_TOP 0x80200000 + #endif #endif /* ============================================================================ @@ -75,6 +78,12 @@ #define CSR_MIMPID 0xF13 /* Implementation ID */ #define CSR_MHARTID 0xF14 /* Hardware thread ID */ +#ifdef WOLFBOOT_RISCV_MMODE +#define MODE_PREFIX(__suffix) m##__suffix +#else +#define MODE_PREFIX(__suffix) s##__suffix +#endif + /* ============================================================================ * CSR Access Macros * ============================================================================ */ diff --git a/src/boot_riscv.c b/src/boot_riscv.c index a25e7aefbf..e472626e33 100644 --- a/src/boot_riscv.c +++ b/src/boot_riscv.c @@ -301,7 +301,7 @@ void do_boot(const uint32_t *app_offset) #if __riscv_xlen == 64 #ifdef MMU asm volatile( - #ifdef WOLFBOOT_RISCV_SMODE + #ifndef WOLFBOOT_RISCV_MMODE "csrw satp, zero\n" "sfence.vma\n" #endif diff --git a/src/boot_riscv_start.S b/src/boot_riscv_start.S index 4ed11d5efc..78ba50d51e 100644 --- a/src/boot_riscv_start.S +++ b/src/boot_riscv_start.S @@ -25,12 +25,7 @@ #include "hal/mpfs250.h" #endif -#ifdef WOLFBOOT_RISCV_SMODE -#define MODE_PREFIX(__suffix) s##__suffix -#else -#define MODE_PREFIX(__suffix) m##__suffix -#endif - +/* MODE_PREFIX is now defined in hal/riscv.h */ /* ============================================================================ * RISC-V Boot Entry Point @@ -100,7 +95,7 @@ _reset: * M-mode: MSIE (Software) + MEIE (External) * S-mode: SSIE (Software) + SEIE (External) */ -#ifdef WOLFBOOT_RISCV_SMODE +#ifndef WOLFBOOT_RISCV_MMODE li t0, (SIE_SSIE | SIE_SEIE) #else li t0, (MIE_MSIE | MIE_MEIE) @@ -115,7 +110,7 @@ _reset: * M-mode: mstatus.MIE (bit 3) * S-mode: sstatus.SIE (bit 1) */ -#ifdef WOLFBOOT_RISCV_SMODE +#ifndef WOLFBOOT_RISCV_MMODE li t0, SSTATUS_SIE #else li t0, MSTATUS_MIE diff --git a/src/elf.c b/src/elf.c index eb33793dc8..0efe5a91c1 100644 --- a/src/elf.c +++ b/src/elf.c @@ -136,11 +136,14 @@ int elf_load_image_mmu(uint8_t *image, uintptr_t *pentry, elf_mmu_map_cb mmu_cb) } } - /* confirm the entry won't clobber any of the headers */ - if ((uint8_t*)vaddr + file_size < image || + /* Confirm the entry won't clobber remaining unread program headers. + * Only protect headers [i+1, entry_count) not yet parsed. + * Use memmove for safe in-place ELF loading (e.g., RAM boot). */ + if (i + 1 >= entry_count || /* last header, nothing left to protect */ + (uint8_t*)vaddr + file_size <= (entry_off + ((i + 1) * entry_size)) || (uint8_t*)vaddr > (entry_off + entry_count * entry_size)) { - memcpy((void*)vaddr, image + offset, file_size); + memmove((void*)vaddr, image + offset, file_size); if (mem_size > file_size) { memset((void*)(uintptr_t)(vaddr + file_size), 0, mem_size - file_size); @@ -148,6 +151,9 @@ int elf_load_image_mmu(uint8_t *image, uintptr_t *pentry, elf_mmu_map_cb mmu_cb) #ifdef ARCH_PPC flush_cache(paddr, mem_size); #endif + #ifdef __riscv_zifencei + asm volatile("fence.i" ::: "memory"); + #endif } #ifdef DEBUG_ELF else { diff --git a/src/vector_riscv.S b/src/vector_riscv.S index a79f89a900..70c83e930d 100644 --- a/src/vector_riscv.S +++ b/src/vector_riscv.S @@ -65,7 +65,7 @@ STORE x29, 29 * REGBYTES(sp) STORE x30, 30 * REGBYTES(sp) STORE x31, 31 * REGBYTES(sp) -#ifdef WOLFBOOT_RISCV_SMODE +#ifndef WOLFBOOT_RISCV_MMODE csrr a0, scause csrr a1, sepc csrr a2, stval @@ -76,7 +76,7 @@ #endif mv a3, sp jal handle_trap -#ifdef WOLFBOOT_RISCV_SMODE +#ifndef WOLFBOOT_RISCV_MMODE csrw sepc, a0 #else csrw mepc, a0 @@ -116,7 +116,7 @@ LOAD x31, 31 * REGBYTES(sp) LOAD x2, 2 * REGBYTES(sp) addi sp, sp, 32 * REGBYTES -#ifdef WOLFBOOT_RISCV_SMODE +#ifndef WOLFBOOT_RISCV_MMODE sret #else mret diff --git a/test-app/RISCV64-mpfs250.ld b/test-app/RISCV64-mpfs250.ld index 1b3eb7d99f..ff55622aef 100644 --- a/test-app/RISCV64-mpfs250.ld +++ b/test-app/RISCV64-mpfs250.ld @@ -13,9 +13,9 @@ ENTRY( _reset ) /* Memory areas */ MEMORY { - /* TODO: Configure actual memory regions based on PolarFire SoC memory map */ - /* Application typically runs from DDR or LSRAM */ - IRAM (rx) :ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@ + /* For RAM boot (NO_XIP), use WOLFBOOT_LOAD_ADDRESS for execution + * For XIP boot, use WOLFBOOT_TEST_APP_ADDRESS (partition + header) */ + IRAM (rx) :ORIGIN = @WOLFBOOT_LOAD_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@ DDR (rwx) :ORIGIN = 0x80000000, LENGTH = 1M LSRAM (rwx) :ORIGIN = 0x08000000, LENGTH = 128K } @@ -52,7 +52,7 @@ SECTIONS /* used by the startup to initialize data */ _stored_data = LOADADDR(.data); - /* Initialized data sections transported to RAM */ + /* Initialized data sections - keep in same region as code for RAM boot */ .data : { . = ALIGN(8); @@ -64,7 +64,10 @@ SECTIONS . = ALIGN(8); _end_data = .; /* define a global symbol at data end */ - } >LSRAM AT> IRAM + } >IRAM + + /* use from libwolfboot.c */ + _stored_data = _start_data; /* Uninitialized data section */ /* Mark as NOLOAD so it's not included in binary (zero-initialized at runtime) */ @@ -81,7 +84,7 @@ SECTIONS . = ALIGN(8); _end_bss = .; /* define a global symbol at bss end */ __bss_end__ = _end_bss; - } >LSRAM + } >IRAM /* User_heap_stack section, used to check that there is enough RAM left */ /* Mark as NOLOAD so it's not included in binary (zero-initialized at runtime) */ @@ -96,7 +99,7 @@ SECTIONS . = ALIGN(8); PROVIDE ( END_STACK = . ); PROVIDE ( _end_stack = . ); - } >LSRAM + } >IRAM /* Global pointer for RISC-V */ . = ALIGN(8); diff --git a/test-app/startup_riscv.c b/test-app/startup_riscv.c index f200a0bc7a..27b4a13f89 100644 --- a/test-app/startup_riscv.c +++ b/test-app/startup_riscv.c @@ -20,6 +20,7 @@ */ #include +#include "hal/riscv.h" extern void trap_entry(void); extern void trap_exit(void); @@ -42,7 +43,11 @@ void __attribute__((naked,section(".init"))) _reset(void) { asm volatile("la sp, _end_stack"); /* Set up vectored interrupt, with IV starting at offset 0x100 */ +#ifndef WOLFBOOT_RISCV_MMODE + asm volatile("csrw stvec, %0":: "r"((uint8_t *)(&_start_vector) + 1)); +#else asm volatile("csrw mtvec, %0":: "r"((uint8_t *)(&_start_vector) + 1)); +#endif src = (uint32_t *) &_stored_data; dst = (uint32_t *) &_start_data; @@ -74,8 +79,11 @@ void do_boot(const uint32_t *app_offset) static uint32_t synctrap_cause = 0; void __attribute__((naked)) isr_synctrap(void) { - asm volatile("csrr %0,mcause" : "=r"(synctrap_cause)); - //asm volatile("ebreak"); +#ifndef WOLFBOOT_RISCV_MMODE + asm volatile("csrr %0, scause" : "=r"(synctrap_cause)); +#else + asm volatile("csrr %0, mcause" : "=r"(synctrap_cause)); +#endif } void isr_empty(void) diff --git a/test-app/vector_riscv.S b/test-app/vector_riscv.S index 548925d20d..b4df227151 100644 --- a/test-app/vector_riscv.S +++ b/test-app/vector_riscv.S @@ -20,6 +20,57 @@ * */ +#include "hal/riscv.h" + +#if __riscv_xlen == 64 + +.macro trap_entry + addi sp, sp, -128 + sd x1, 0(sp) + sd x5, 8(sp) + sd x6, 16(sp) + sd x7, 24(sp) + sd x10, 32(sp) + sd x11, 40(sp) + sd x12, 48(sp) + sd x13, 56(sp) + sd x14, 64(sp) + sd x15, 72(sp) + sd x16, 80(sp) + sd x17, 88(sp) + sd x28, 96(sp) + sd x29, 104(sp) + sd x30, 112(sp) + sd x31, 120(sp) +.endm + +.macro trap_exit + ld x1, 0(sp) + ld x5, 8(sp) + ld x6, 16(sp) + ld x7, 24(sp) + ld x10, 32(sp) + ld x11, 40(sp) + ld x12, 48(sp) + ld x13, 56(sp) + ld x14, 64(sp) + ld x15, 72(sp) + ld x16, 80(sp) + ld x17, 88(sp) + ld x28, 96(sp) + ld x29, 104(sp) + ld x30, 112(sp) + ld x31, 120(sp) + addi sp, sp, 128 +#ifndef WOLFBOOT_RISCV_MMODE + sret +#else + mret +#endif +.endm + +#else /* __riscv_xlen == 32 */ + .macro trap_entry addi sp, sp, -64 sw x1, 0(sp) @@ -62,6 +113,8 @@ mret .endm +#endif /* __riscv_xlen */ + .section .isr_vector .align 8 diff --git a/tools/scripts/mpfs_program.sh b/tools/scripts/mpfs_program.sh new file mode 100755 index 0000000000..31fd8870e2 --- /dev/null +++ b/tools/scripts/mpfs_program.sh @@ -0,0 +1,746 @@ +#!/bin/bash +# +# wolfBoot PolarFire SoC Programming Script +# Automates building, flashing, and verifying wolfBoot on MPFS target +# + +set -e + +# Configuration +HSS_TTY="${HSS_TTY:-/dev/ttyUSB0}" +WOLFBOOT_TTY="${WOLFBOOT_TTY:-/dev/ttyUSB1}" +BLOCK_DEV="${BLOCK_DEV:-/dev/sda}" +BAUD_RATE="${BAUD_RATE:-115200}" +TIMEOUT_HSS="${TIMEOUT_HSS:-30}" +TIMEOUT_BLOCK="${TIMEOUT_BLOCK:-15}" +TIMEOUT_WOLFBOOT="${TIMEOUT_WOLFBOOT:-30}" +WOLFBOOT_BIN="${WOLFBOOT_BIN:-wolfboot.bin}" +CONFIG_FILE="${CONFIG_FILE:-./config/examples/polarfire_mpfs250.config}" +STORAGE_MODE="${STORAGE_MODE:-}" # Can be "emmc" or "sdcard" +RESET_METHOD="${RESET_METHOD:-hss}" # "hss" (HSS console reset) or "flashpro" (FlashPro6) + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Logging functions +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Check for required tools +check_dependencies() { + local missing=0 + for tool in dtc hss-payload-generator lsblk stty; do + if ! command -v "$tool" &>/dev/null; then + log_error "Required tool '$tool' not found" + missing=1 + fi + done + + # Check for FlashPro6 tool if using flashpro reset method + if [[ "$RESET_METHOD" == "flashpro" ]]; then + if ! command -v fpgenprog &>/dev/null; then + log_error "FlashPro6 tool 'fpgenprog' not found (required for flashpro reset method)" + missing=1 + fi + fi + + if [[ $missing -eq 1 ]]; then + exit 1 + fi +} + +# Reset control functions +reset_target_hss() { + log_info "Resetting target via HSS console..." + + # Configure serial port + stty -F "$HSS_TTY" "$BAUD_RATE" cs8 -cstopb -parenb -echo raw -icanon min 1 time 0 + + # Open file descriptor for serial port + exec 3<>"$HSS_TTY" + + # Send Ctrl+C to interrupt any running command + printf "\003" >&3 + sleep 0.3 + + # Clear any pending output + while read -r -t 0.1 -u 3 line 2>/dev/null; do + : # discard + done + + # Send reset command + log_info "Sending 'reset' command to HSS..." + echo "reset" >&3 + sleep 0.5 + + # Read response + while read -r -t 0.5 -u 3 line 2>/dev/null; do + echo "$line" + done + + # Close file descriptor + exec 3>&- + + log_success "Reset command sent via HSS" + sleep 2 # Give device time to reset +} + +reset_target_flashpro() { + log_info "Resetting target via FlashPro6..." + + # Use fpgenprog to reset the target + # The exact command may vary based on FlashPro6 configuration + if fpgenprog -n -a "RESET" 2>&1 | tee /tmp/flashpro_reset.log; then + log_success "Target reset via FlashPro6" + else + log_warn "FlashPro6 reset may have failed (check /tmp/flashpro_reset.log)" + fi + + sleep 2 # Give device time to reset +} + +reset_target() { + case "$RESET_METHOD" in + hss) + reset_target_hss + ;; + flashpro) + reset_target_flashpro + ;; + *) + log_error "Unknown reset method: $RESET_METHOD (use 'hss' or 'flashpro')" + return 1 + ;; + esac +} + +# Build wolfBoot +build_wolfboot() { + log_info "Building wolfBoot..." + + log_info "Copying config: $CONFIG_FILE -> .config" + cp "$CONFIG_FILE" .config + + log_info "Running make clean..." + make clean + + # Build with storage-specific flags if specified + local make_opts="" + if [[ "$STORAGE_MODE" == "emmc" ]]; then + make_opts="DISK_EMMC=1" + log_info "Building for eMMC storage..." + elif [[ "$STORAGE_MODE" == "sdcard" ]]; then + make_opts="DISK_SDCARD=1" + log_info "Building for SD card storage..." + fi + + log_info "Building wolfboot.elf..." + make $make_opts wolfboot.elf + + log_info "Size of wolfboot.elf:" + size wolfboot.elf + + log_info "Compiling device tree..." + dtc -I dts -O dtb hal/mpfs.dts -o hal/mpfs.dtb + + log_info "Generating HSS payload..." + hss-payload-generator -vvv -c ./hal/mpfs.yaml "$WOLFBOOT_BIN" + + log_success "Build completed successfully!" +} + +# Wait for HSS CLI prompt and enter usbdmsc mode +enter_usbdmsc_mode() { + log_info "Waiting for HSS CLI prompt on $HSS_TTY..." + + # Configure serial port + stty -F "$HSS_TTY" "$BAUD_RATE" cs8 -cstopb -parenb -echo raw -icanon min 1 time 0 + + # Open file descriptor for serial port + exec 3<>"$HSS_TTY" + + local buffer="" + local elapsed=0 + local found_prompt=0 + local found_cli=0 + + # Wait for "Press a key to enter CLI, ESC to skip" + log_info "Waiting for HSS boot prompt..." + while [[ $elapsed -lt $TIMEOUT_HSS ]]; do + # Read available data with timeout + if read -r -t 1 -u 3 line 2>/dev/null; then + echo "$line" + buffer+="$line" + if [[ "$buffer" == *"Press a key to enter CLI"* ]]; then + found_prompt=1 + break + fi + fi + elapsed=$((elapsed + 1)) + done + + if [[ $found_prompt -eq 0 ]]; then + exec 3>&- + log_error "Timeout waiting for HSS CLI prompt" + return 1 + fi + + # Send a key to enter CLI + sleep 0.5 + echo -e "\r" >&3 + + # Wait for the >> prompt + buffer="" + elapsed=0 + log_info "Waiting for HSS command prompt..." + while [[ $elapsed -lt 10 ]]; do + if read -r -t 1 -u 3 line 2>/dev/null; then + echo "$line" + buffer+="$line" + if [[ "$buffer" == *">>"* ]]; then + found_cli=1 + break + fi + fi + elapsed=$((elapsed + 1)) + done + + if [[ $found_cli -eq 0 ]]; then + exec 3>&- + log_error "Timeout waiting for HSS command prompt" + return 1 + fi + + # IMPORTANT: Always select SD card storage for USBDMSC mode + # (We're flashing wolfboot.bin to the SD card boot partition) + # Don't use QSPI for USBDMSC unless that's the actual target + local usbdmsc_storage="${USBDMSC_STORAGE:-sdcard}" + + if [[ -n "$STORAGE_MODE" ]] && [[ "$STORAGE_MODE" != "qspi" ]]; then + # If user specified a storage mode (and it's not qspi), use that + usbdmsc_storage="$STORAGE_MODE" + fi + + log_info "Setting storage to '$usbdmsc_storage' for USBDMSC mode..." + echo "$usbdmsc_storage" >&3 + sleep 1 + + # Read response + while read -r -t 1 -u 3 line 2>/dev/null; do + echo "$line" + done + + # Send usbdmsc command + log_info "Sending usbdmsc command..." + echo "usbdmsc" >&3 + sleep 2 + + # Read any remaining output + while read -r -t 1 -u 3 line 2>/dev/null; do + echo "$line" + done + + # Close file descriptor + exec 3>&- + + log_success "USBDMSC mode activated on $usbdmsc_storage" +} + +# Unmount any mounted partitions on the block device +unmount_block_device() { + log_info "Checking for mounted partitions on $BLOCK_DEV..." + + # Find all mounted partitions on this device + local mounted_parts + mounted_parts=$(mount | grep "^${BLOCK_DEV}" | awk '{print $1}' || true) + + if [[ -n "$mounted_parts" ]]; then + for part in $mounted_parts; do + log_info "Unmounting $part..." + if sudo umount "$part" 2>/dev/null; then + log_success "Unmounted $part" + else + log_warn "Failed to unmount $part (may already be unmounted)" + fi + done + # Give system time to fully release the device + sleep 1 + else + log_info "No mounted partitions found on $BLOCK_DEV" + fi +} + +# Wait for block device to become available +wait_for_block_device() { + local partition="${BLOCK_DEV}1" + local elapsed=0 + + log_info "Waiting for $partition to become available..." + + while [[ $elapsed -lt $TIMEOUT_BLOCK ]]; do + if lsblk "$partition" &>/dev/null; then + # Wait a bit more for the device to be fully ready + sleep 1 + if lsblk "$partition" &>/dev/null; then + log_success "Block device $partition is available" + lsblk "$partition" + return 0 + fi + fi + sleep 1 + elapsed=$((elapsed + 1)) + echo -n "." + done + + echo "" + log_error "Timeout waiting for $partition" + return 1 +} + +# Flash and verify wolfboot.bin +flash_and_verify() { + local partition="${BLOCK_DEV}1" + + log_info "Flashing $WOLFBOOT_BIN to $partition..." + + # Flash the image + sudo dd if="$WOLFBOOT_BIN" of="$partition" bs=512 status=progress + sync + + log_info "Verifying flash..." + + # Verify - cmp should report "EOF on wolfboot.bin" if successful + local cmp_output + cmp_output=$(sudo cmp "$WOLFBOOT_BIN" "$partition" 2>&1) || true + + if echo "$cmp_output" | grep -q "EOF on $WOLFBOOT_BIN"; then + log_success "Verification successful: $cmp_output" + return 0 + elif [[ -z "$cmp_output" ]]; then + # No output from cmp means files are identical up to the size of the smaller one + log_success "Verification successful (files match)" + return 0 + else + log_error "Verification failed: $cmp_output" + return 1 + fi +} + +# Capture wolfBoot output +capture_wolfboot_output() { + local output_file="${1:-wolfboot_output_$(date +%Y%m%d_%H%M%S).log}" + + log_info "Capturing wolfBoot output from $WOLFBOOT_TTY to $output_file..." + log_info "Press Ctrl+C to stop capture" + + # Configure serial port + stty -F "$WOLFBOOT_TTY" "$BAUD_RATE" cs8 -cstopb -parenb -echo raw + + # Use timeout with cat to capture output, or just cat if user wants manual stop + if [[ -n "$TIMEOUT_WOLFBOOT" ]] && [[ "$TIMEOUT_WOLFBOOT" -gt 0 ]]; then + timeout "$TIMEOUT_WOLFBOOT" cat "$WOLFBOOT_TTY" | tee "$output_file" || true + else + cat "$WOLFBOOT_TTY" | tee "$output_file" + fi + + log_success "Output captured to $output_file" +} + +# Capture wolfBoot output using timeout and cat +capture_wolfboot_output_timed() { + local output_file="${1:-wolfboot_output_$(date +%Y%m%d_%H%M%S).log}" + + log_info "Capturing wolfBoot output from $WOLFBOOT_TTY..." + log_info "Capture will run for $TIMEOUT_WOLFBOOT seconds" + + # Configure serial port + stty -F "$WOLFBOOT_TTY" "$BAUD_RATE" cs8 -cstopb -parenb -echo raw + + # Use timeout with cat to capture output + timeout "$TIMEOUT_WOLFBOOT" cat "$WOLFBOOT_TTY" 2>/dev/null | tee "$output_file" || true + + echo "" + log_success "Output captured to $output_file" +} + +# Set storage mode and boot (for final boot after flashing) +# Starts wolfBoot capture immediately before sending boot command to avoid missing output +set_storage_and_boot() { + local output_file="${1:-wolfboot_output_$(date +%Y%m%d_%H%M%S).log}" + + if [[ -z "$STORAGE_MODE" ]]; then + log_info "No storage mode specified, letting device boot normally" + return 0 + fi + + log_info "Waiting for HSS CLI prompt to set storage mode..." + + # Configure serial port immediately so we don't miss early boot output + stty -F "$HSS_TTY" "$BAUD_RATE" cs8 -cstopb -parenb -echo raw -icanon min 1 time 0 + + # Open file descriptor for serial port + exec 3<>"$HSS_TTY" + + # Clear any stale data from before power cycle + while read -r -t 0.1 -u 3 line 2>/dev/null; do + : # discard + done + + local buffer="" + local elapsed=0 + local found_prompt=0 + local found_cli=0 + + # Wait for "Press a key to enter CLI, ESC to skip" + # Use longer timeout for post-flash boot (device may need to initialize) + local boot_timeout=$((TIMEOUT_HSS * 2)) + log_info "Waiting for HSS boot prompt (timeout: ${boot_timeout}s)..." + while [[ $elapsed -lt $boot_timeout ]]; do + # Read available data with timeout + if read -r -t 1 -u 3 line 2>/dev/null; then + echo "$line" + buffer+="$line" + if [[ "$buffer" == *"Press a key to enter CLI"* ]]; then + found_prompt=1 + break + fi + else + # Show progress every 5 seconds + if [[ $((elapsed % 5)) -eq 0 ]] && [[ $elapsed -gt 0 ]]; then + echo -n "." + fi + fi + elapsed=$((elapsed + 1)) + done + + if [[ $found_prompt -eq 0 ]]; then + exec 3>&- + echo "" + log_error "Timeout waiting for HSS CLI prompt after ${boot_timeout}s" + log_error "Device may still be booting or HSS output not detected" + return 1 + fi + + # Send a key to enter CLI + sleep 0.5 + echo -e "\r" >&3 + + # Wait for the >> prompt + buffer="" + elapsed=0 + log_info "Waiting for HSS command prompt..." + while [[ $elapsed -lt 10 ]]; do + if read -r -t 1 -u 3 line 2>/dev/null; then + echo "$line" + buffer+="$line" + if [[ "$buffer" == *">>"* ]]; then + found_cli=1 + break + fi + fi + elapsed=$((elapsed + 1)) + done + + if [[ $found_cli -eq 0 ]]; then + exec 3>&- + log_error "Timeout waiting for HSS command prompt" + return 1 + fi + + # Send storage mode command + log_info "Setting storage mode to: $STORAGE_MODE" + echo "$STORAGE_MODE" >&3 + sleep 1 + + # Read response + while read -r -t 1 -u 3 line 2>/dev/null; do + echo "$line" + done + + # Configure wolfBoot serial port BEFORE sending boot command + stty -F "$WOLFBOOT_TTY" "$BAUD_RATE" cs8 -cstopb -parenb -echo raw + + # Start wolfBoot capture in background BEFORE sending boot command + # This ensures we don't miss any early boot output + log_info "Starting wolfBoot capture (output: $output_file)..." + timeout "$TIMEOUT_WOLFBOOT" cat "$WOLFBOOT_TTY" 2>/dev/null | tee "$output_file" & + local capture_pid=$! + + # Give capture process a moment to start + sleep 0.2 + + # Send boot command to boot from the selected storage + log_info "Sending boot command..." + echo "boot" >&3 + + # Close HSS file descriptor immediately to avoid delays + exec 3>&- + + log_success "Boot command sent, capturing wolfBoot output for ${TIMEOUT_WOLFBOOT}s..." + + # Wait for capture to complete + wait $capture_pid || true + + echo "" + log_success "Output captured to $output_file" +} + +# Show usage +usage() { + cat << EOF +Usage: $(basename "$0") [OPTIONS] [COMMAND] + +wolfBoot PolarFire SoC Programming Script + +Commands: + all Run full workflow (build, flash, capture) + build Build wolfBoot only + flash Flash and verify only (assumes device in USBDMSC mode) + capture Capture wolfBoot output only + reset Reset the target only + +Options: + -h, --help Show this help message + -c, --config FILE Config file (default: $CONFIG_FILE) + -d, --device DEV Block device (default: $BLOCK_DEV) + -H, --hss-tty TTY HSS serial port (default: $HSS_TTY) + -W, --wolfboot-tty TTY wolfBoot serial port (default: $WOLFBOOT_TTY) + -r, --reset-method TYPE Reset method: 'hss' or 'flashpro' (default: $RESET_METHOD) + -o, --output FILE Output file for captured log + -t, --timeout SEC Timeout for wolfBoot capture (default: $TIMEOUT_WOLFBOOT) + -s, --storage MODE Storage mode for final boot: 'emmc' or 'sdcard' (default: none) + -u, --usbdmsc-storage Storage for USBDMSC flashing: 'emmc' or 'sdcard' (default: sdcard) + --skip-build Skip the build step in 'all' command + +Environment Variables: + HSS_TTY HSS serial port + WOLFBOOT_TTY wolfBoot serial port + BLOCK_DEV Block device for flashing + RESET_METHOD Reset method: 'hss' or 'flashpro' (default: hss) + BAUD_RATE Serial baud rate (default: 115200) + TIMEOUT_HSS Timeout for HSS prompt (default: 30s) + TIMEOUT_BLOCK Timeout for block device (default: 15s) + TIMEOUT_WOLFBOOT Timeout for capture (default: 30s) + STORAGE_MODE Storage mode for final boot: 'emmc' or 'sdcard' + USBDMSC_STORAGE Storage for USBDMSC flashing: 'emmc' or 'sdcard' (default: sdcard) + +Examples: + # Flash wolfBoot to SD card, boot from SD card + $(basename "$0") -s sdcard all + + # Flash wolfBoot to eMMC, boot from eMMC + $(basename "$0") -u emmc -s emmc all + + # Flash to SD (default), but boot from eMMC + $(basename "$0") -s emmc all + + # Build only + $(basename "$0") build + + # Flash existing build (skip build step) + $(basename "$0") --skip-build all + + # Capture boot output only + $(basename "$0") -o boot.log capture + + # Reset target using FlashPro6 instead of HSS + $(basename "$0") -r flashpro reset + +Notes: + - USBDMSC storage (-u) is for flashing wolfboot.bin (default: sdcard) + - Boot storage (-s) is what HSS will boot from after flashing + - If QSPI flash doesn't respond (JEDEC ID 000000), check IOMUX/FPGA routing + +EOF +} + +# Main workflow +run_all() { + local skip_build="${1:-0}" + local output_file="${2:-}" + + log_info "Starting full wolfBoot programming workflow..." + echo "" + + # Step 1: Build (if not skipped) + if [[ "$skip_build" -eq 0 ]]; then + log_info "=== Step 1: Building wolfBoot ===" + build_wolfboot + echo "" + else + log_info "=== Step 1: Skipping build ===" + if [[ ! -f "$WOLFBOOT_BIN" ]]; then + log_error "$WOLFBOOT_BIN not found. Cannot skip build." + exit 1 + fi + echo "" + fi + + # Step 2: Reset target + log_info "=== Step 2: Resetting target ===" + reset_target + echo "" + + # Step 3: Enter USBDMSC mode + log_info "=== Step 3: Entering USBDMSC mode ===" + enter_usbdmsc_mode + echo "" + + # Step 4: Wait for block device + log_info "=== Step 4: Waiting for block device ===" + wait_for_block_device + echo "" + + # Step 5: Flash and verify + log_info "=== Step 5: Flashing and verifying ===" + flash_and_verify + echo "" + + # Step 6: Unmount and reset again + log_info "=== Step 6: Unmounting and resetting for boot ===" + unmount_block_device + sleep 1 + reset_target + echo "" + + # Step 7/8: Set storage mode and boot, then capture wolfBoot output + if [[ -n "$STORAGE_MODE" ]]; then + # When storage mode is set, set_storage_and_boot handles both boot and capture + # to avoid missing early wolfBoot output + log_info "=== Step 7: Setting storage mode, booting, and capturing output ===" + set_storage_and_boot "$output_file" + echo "" + else + # No storage mode - just capture output (device boots automatically) + log_info "=== Step 7: Capturing wolfBoot output ===" + capture_wolfboot_output_timed "$output_file" + echo "" + fi + + log_success "=== Workflow completed successfully! ===" +} + +# Parse command line arguments +SKIP_BUILD=0 +OUTPUT_FILE="" +COMMAND="" + +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + usage + exit 0 + ;; + -c|--config) + CONFIG_FILE="$2" + shift 2 + ;; + -d|--device) + BLOCK_DEV="$2" + shift 2 + ;; + -H|--hss-tty) + HSS_TTY="$2" + shift 2 + ;; + -W|--wolfboot-tty) + WOLFBOOT_TTY="$2" + shift 2 + ;; + -r|--reset-method) + RESET_METHOD="$2" + if [[ "$RESET_METHOD" != "hss" && "$RESET_METHOD" != "flashpro" ]]; then + log_error "Invalid reset method: $RESET_METHOD (must be 'hss' or 'flashpro')" + exit 1 + fi + shift 2 + ;; + -o|--output) + OUTPUT_FILE="$2" + shift 2 + ;; + -t|--timeout) + TIMEOUT_WOLFBOOT="$2" + shift 2 + ;; + -s|--storage) + STORAGE_MODE="$2" + if [[ "$STORAGE_MODE" != "emmc" && "$STORAGE_MODE" != "sdcard" ]]; then + log_error "Invalid storage mode: $STORAGE_MODE (must be 'emmc' or 'sdcard')" + exit 1 + fi + shift 2 + ;; + -u|--usbdmsc-storage) + USBDMSC_STORAGE="$2" + if [[ "$USBDMSC_STORAGE" != "emmc" && "$USBDMSC_STORAGE" != "sdcard" ]]; then + log_error "Invalid USBDMSC storage: $USBDMSC_STORAGE (must be 'emmc' or 'sdcard')" + exit 1 + fi + shift 2 + ;; + --skip-build) + SKIP_BUILD=1 + shift + ;; + all|build|flash|capture|reset) + COMMAND="$1" + shift + ;; + *) + log_error "Unknown option: $1" + usage + exit 1 + ;; + esac +done + +# Default to 'all' if no command specified +COMMAND="${COMMAND:-all}" + +# Check dependencies +check_dependencies + +# Execute command +case "$COMMAND" in + all) + run_all "$SKIP_BUILD" "$OUTPUT_FILE" + ;; + build) + build_wolfboot + ;; + flash) + wait_for_block_device + flash_and_verify + unmount_block_device + ;; + capture) + capture_wolfboot_output_timed "$OUTPUT_FILE" + ;; + reset) + reset_target + ;; + *) + log_error "Unknown command: $COMMAND" + usage + exit 1 + ;; +esac + From 5e963dc87a19039267982375cafc063208fe8300 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 30 Jan 2026 13:13:50 -0800 Subject: [PATCH 2/2] Cleanups --- docs/Targets.md | 2 - hal/mpfs250.c | 256 +++++++++++++++++++--------------- hal/mpfs250.h | 22 ++- hal/riscv.h | 13 +- src/elf.c | 8 +- tools/scripts/mpfs_program.sh | 12 +- 6 files changed, 192 insertions(+), 121 deletions(-) diff --git a/docs/Targets.md b/docs/Targets.md index 197df16e72..6b7e4bf5a1 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -857,8 +857,6 @@ The HSS tinyCLI supports the `USBDMSC` command to mount the eMMC or SD card as a sudo dd if=wolfboot.bin of=/dev/sdc1 bs=512 && sudo cmp wolfboot.bin /dev/sdc1 ``` -Note: - ### PolarFire SoC QSPI PolarFire QSPI can be accessed in two ways. The selection is made at build time and affects how wolfBoot diff --git a/hal/mpfs250.c b/hal/mpfs250.c index 2792787426..41b00919ef 100644 --- a/hal/mpfs250.c +++ b/hal/mpfs250.c @@ -74,76 +74,142 @@ void hal_init(void) * and responses are read from the mailbox RAM. * ============================================================================ */ -/** - * mpfs_scb_mailbox_busy - Check if the system controller mailbox is busy - * - * Returns: non-zero if busy, 0 if ready - */ -static int mpfs_scb_mailbox_busy(void) +int mpfs_scb_is_busy(void) { return (SCBCTRL_REG(SERVICES_SR_OFFSET) & SERVICES_SR_BUSY_MASK); } -/** - * mpfs_read_serial_number - Read the device serial number via system services - * @serial: Buffer to store the 16-byte device serial number - * - * This function sends a serial number request (opcode 0x00) to the system - * controller and reads the 16-byte response from the mailbox RAM. - * - * Returns: 0 on success, negative error code on failure - */ -static int mpfs_read_serial_number(uint8_t *serial) +int mpfs_scb_wait_ready(uint32_t timeout) { - uint32_t cmd, status; - int i, timeout; + while (mpfs_scb_is_busy() && timeout > 0) { + timeout--; + } - if (serial == NULL) { + if (timeout == 0) { return -1; } + return 0; +} - /* Check if mailbox is busy */ - if (mpfs_scb_mailbox_busy()) { - wolfBoot_printf("SCB mailbox busy\n"); - return -2; +int mpfs_scb_read_mailbox(uint8_t *out, uint32_t len) +{ + uint32_t i; + + if (out == NULL) { + return -1; } - /* Send serial number request command (opcode 0x00) - * Command format: [31:16] = opcode, [0] = request bit */ - cmd = (SYS_SERV_CMD_SERIAL_NUMBER << SERVICES_CR_COMMAND_SHIFT) | - SERVICES_CR_REQ_MASK; - SCBCTRL_REG(SERVICES_CR_OFFSET) = cmd; + for (i = 0; i < len; i++) { + out[i] = SCBMBOX_BYTE(i); + } - /* Wait for request bit to clear (command accepted) */ - timeout = 10000; - while ((SCBCTRL_REG(SERVICES_CR_OFFSET) & SERVICES_CR_REQ_MASK) && timeout > 0) { - timeout--; + return 0; +} + +static void mpfs_scb_write_mailbox(const uint8_t *data, uint32_t len) +{ + uint32_t i = 0; + + if (data == NULL || len == 0) { + return; } - if (timeout == 0) { - wolfBoot_printf("SCB mailbox request timeout\n"); - return -3; + + /* Write full words (little-endian) */ + while (i + 4 <= len) { + uint32_t word = ((uint32_t)data[i]) | + ((uint32_t)data[i + 1] << 8) | + ((uint32_t)data[i + 2] << 16) | + ((uint32_t)data[i + 3] << 24); + SCBMBOX_REG(i) = word; + i += 4; } - /* Wait for busy bit to clear (command completed) */ - timeout = 10000; - while (mpfs_scb_mailbox_busy() && timeout > 0) { + /* Write remaining bytes */ + while (i < len) { + SCBMBOX_BYTE(i) = data[i]; + i++; + } +} + +static int mpfs_scb_wait_req_clear(uint32_t timeout) +{ + while ((SCBCTRL_REG(SERVICES_CR_OFFSET) & SERVICES_CR_REQ_MASK) && + timeout > 0) { timeout--; } + if (timeout == 0) { - wolfBoot_printf("SCB mailbox busy timeout\n"); - return -4; + return -1; + } + return 0; +} + +int mpfs_scb_service_call_timeout(uint8_t opcode, const uint8_t *mb_data, + uint32_t mb_len, uint32_t req_timeout, + uint32_t busy_timeout) +{ + uint32_t cmd; + uint32_t status; + + if (mpfs_scb_is_busy()) { + return -1; + } + + if (mb_data && mb_len > 0) { + mpfs_scb_write_mailbox(mb_data, mb_len); + } + + cmd = ((opcode & 0x7F) << SERVICES_CR_COMMAND_SHIFT) | + SERVICES_CR_REQ_MASK; + SCBCTRL_REG(SERVICES_CR_OFFSET) = cmd; + + if (mpfs_scb_wait_req_clear(req_timeout) < 0) { + return -2; + } + + if (mpfs_scb_wait_ready(busy_timeout) < 0) { + return -3; } - /* Check status (upper 16 bits of status register) */ status = (SCBCTRL_REG(SERVICES_SR_OFFSET) >> SERVICES_SR_STATUS_SHIFT) & 0xFFFF; if (status != 0) { - wolfBoot_printf("SCB mailbox error: 0x%x\n", status); - return -5; + return -4; } - /* Read serial number from mailbox RAM (16 bytes) */ - for (i = 0; i < DEVICE_SERIAL_NUMBER_SIZE; i++) { - serial[i] = SCBMBOX_BYTE(i); + return 0; +} + +int mpfs_scb_service_call(uint8_t opcode, const uint8_t *mb_data, uint32_t mb_len) +{ + return mpfs_scb_service_call_timeout(opcode, mb_data, mb_len, 10000, 10000); +} +/** + * mpfs_read_serial_number - Read the device serial number via system services + * @serial: Buffer to store the 16-byte device serial number + * + * This function sends a serial number request (opcode 0x00) to the system + * controller and reads the 16-byte response from the mailbox RAM. + * + * Returns: 0 on success, negative error code on failure + */ +static int mpfs_read_serial_number(uint8_t *serial) +{ + int ret; + + if (serial == NULL) { + return -1; + } + + ret = mpfs_scb_service_call(SYS_SERV_CMD_SERIAL_NUMBER, NULL, 0); + if (ret != 0) { + wolfBoot_printf("SCB mailbox error: %d\n", ret); + return ret; + } + + /* Read serial number from mailbox RAM (16 bytes). */ + ret = mpfs_scb_read_mailbox(serial, DEVICE_SERIAL_NUMBER_SIZE); + if (ret != 0) { + return ret; } return 0; @@ -534,16 +600,33 @@ int qspi_enter_4byte_mode(void) /* Read from QSPI flash (4-byte addressing) */ static int qspi_flash_read(uint32_t address, uint8_t *data, uint32_t len) { + const uint32_t max_chunk = 0xFFFF - 5; /* total_bytes is 16-bit, cmd is 5 */ uint8_t cmd[5]; + uint32_t remaining = len; + uint32_t chunk_len; + int ret; - /* Build 4-byte read command */ - cmd[0] = QSPI_CMD_4BYTE_READ_OPCODE; - cmd[1] = (address >> 24) & 0xFF; - cmd[2] = (address >> 16) & 0xFF; - cmd[3] = (address >> 8) & 0xFF; - cmd[4] = address & 0xFF; + while (remaining > 0) { + chunk_len = (remaining > max_chunk) ? max_chunk : remaining; + + /* Build 4-byte read command */ + cmd[0] = QSPI_CMD_4BYTE_READ_OPCODE; + cmd[1] = (address >> 24) & 0xFF; + cmd[2] = (address >> 16) & 0xFF; + cmd[3] = (address >> 8) & 0xFF; + cmd[4] = address & 0xFF; - return qspi_transfer_block(QSPI_MODE_READ, cmd, 5, data, len, 0); + ret = qspi_transfer_block(QSPI_MODE_READ, cmd, 5, data, chunk_len, 0); + if (ret != 0) { + return ret; + } + + address += chunk_len; + data += chunk_len; + remaining -= chunk_len; + } + + return len; } /* Write to QSPI flash - single page (max 256 bytes) */ @@ -629,17 +712,8 @@ static int qspi_flash_sector_erase(uint32_t address) */ static int sc_spi_copy(uint64_t dest_addr, uint32_t flash_addr, uint32_t len) { - uint32_t cmd; - uint32_t status; - int timeout; uint8_t mb_data[20]; /* 17 bytes needed, aligned to 20 */ - /* Check if System Controller is busy */ - if (SCBCTRL_REG(SERVICES_SR_OFFSET) & SERVICES_SR_BUSY_MASK) { - wolfBoot_printf("SC: Busy\n"); - return -1; - } - /* Prepare mailbox data for SPI_COPY service (17 bytes): * Bytes 0-7: destination address (64-bit) * Bytes 8-11: source SPI flash address (32-bit) @@ -663,47 +737,8 @@ static int sc_spi_copy(uint64_t dest_addr, uint32_t flash_addr, uint32_t len) mb_data[14] = (len >> 16) & 0xFF; mb_data[15] = (len >> 24) & 0xFF; mb_data[16] = SPI_COPY_OPT_13MHZ; /* Use 13.33MHz (safest) */ - - /* Write mailbox data (word-aligned writes) */ - SCBMBOX_REG(0) = *(uint32_t*)&mb_data[0]; - SCBMBOX_REG(4) = *(uint32_t*)&mb_data[4]; - SCBMBOX_REG(8) = *(uint32_t*)&mb_data[8]; - SCBMBOX_REG(12) = *(uint32_t*)&mb_data[12]; - SCBMBOX_REG(16) = mb_data[16]; /* Last byte (options) */ - - /* Build and send service command: - * Bits 0-6: opcode (0x50 for SPI_COPY) - * Bits 7-15: mailbox offset (0) - * Bit 0 of CR: REQ (request bit) - */ - cmd = ((SYS_SERV_CMD_SPI_COPY & 0x7F) << SERVICES_CR_COMMAND_SHIFT) | - SERVICES_CR_REQ_MASK; - SCBCTRL_REG(SERVICES_CR_OFFSET) = cmd; - - /* Wait for REQ bit to clear (command accepted) */ - timeout = 100000; - while ((SCBCTRL_REG(SERVICES_CR_OFFSET) & SERVICES_CR_REQ_MASK) && --timeout); - if (timeout == 0) { - wolfBoot_printf("SC: REQ timeout\n"); - return -2; - } - - /* Wait for BUSY bit to clear (command completed) */ - timeout = 10000000; /* Long timeout for large transfers */ - while ((SCBCTRL_REG(SERVICES_SR_OFFSET) & SERVICES_SR_BUSY_MASK) && --timeout); - if (timeout == 0) { - wolfBoot_printf("SC: BUSY timeout\n"); - return -3; - } - - /* Check status (upper 16 bits of status register) */ - status = (SCBCTRL_REG(SERVICES_SR_OFFSET) >> SERVICES_SR_STATUS_SHIFT) & 0xFFFF; - if (status != 0) { - wolfBoot_printf("SC: Error 0x%x\n", status); - return -4; - } - - return 0; + return mpfs_scb_service_call_timeout(SYS_SERV_CMD_SPI_COPY, mb_data, 17, + 100000, 10000000); } #endif /* MPFS_SC_SPI */ @@ -732,18 +767,20 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len) uint32_t page_offset; uint32_t chunk_len; int ret; + int remaining = len; + int total = len; #ifdef DEBUG_QSPI wolfBoot_printf("QSPI: Write 0x%x, len %d\n", (uint32_t)address, len); #endif /* Write data page by page */ - while (len > 0) { + while (remaining > 0) { /* Calculate bytes to write in this page */ page_offset = address & (FLASH_PAGE_SIZE - 1); chunk_len = FLASH_PAGE_SIZE - page_offset; - if (chunk_len > (uint32_t)len) { - chunk_len = len; + if (chunk_len > (uint32_t)remaining) { + chunk_len = remaining; } /* Write page */ @@ -755,10 +792,10 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len) /* Update pointers */ address += chunk_len; data += chunk_len; - len -= chunk_len; + remaining -= chunk_len; } - return 0; + return total; #endif /* MPFS_SC_SPI */ } @@ -796,6 +833,7 @@ int ext_flash_erase(uintptr_t address, int len) uint32_t sector_addr; uint32_t end_addr; int ret; + int total = len; #ifdef DEBUG_QSPI wolfBoot_printf("QSPI: Erase 0x%x, len %d\n", (uint32_t)address, len); @@ -820,7 +858,7 @@ int ext_flash_erase(uintptr_t address, int len) sector_addr += FLASH_SECTOR_SIZE; } - return 0; + return total; #endif /* MPFS_SC_SPI */ } diff --git a/hal/mpfs250.h b/hal/mpfs250.h index 2eacc52f5e..ad22057fec 100644 --- a/hal/mpfs250.h +++ b/hal/mpfs250.h @@ -176,6 +176,27 @@ #define SCBMBOX_REG(off) (*((volatile uint32_t*)(SCBMBOX_BASE + (off)))) #define SCBMBOX_BYTE(off) (*((volatile uint8_t*)(SCBMBOX_BASE + (off)))) +/* ============================================================================ + * System Controller Mailbox API (public) + * ============================================================================ */ +#ifndef __ASSEMBLER__ +/* Returns non-zero if mailbox is busy */ +int mpfs_scb_is_busy(void); + +/* Wait until mailbox is ready (busy cleared). Returns 0 on success. */ +int mpfs_scb_wait_ready(uint32_t timeout); + +/* Read mailbox bytes into buffer. Returns 0 on success. */ +int mpfs_scb_read_mailbox(uint8_t *out, uint32_t len); + +/* Execute a system service command with optional mailbox payload. */ +int mpfs_scb_service_call(uint8_t opcode, const uint8_t *mb_data, uint32_t mb_len); +/* Execute a system service command with explicit timeouts. */ +int mpfs_scb_service_call_timeout(uint8_t opcode, const uint8_t *mb_data, + uint32_t mb_len, uint32_t req_timeout, + uint32_t busy_timeout); +#endif /* __ASSEMBLER__ */ + /* Crypto Engine: Athena F5200 TeraFire Crypto Processor (1x), 200 MHz */ #define ATHENA_BASE (SYSREG_BASE + 0x125000) @@ -376,4 +397,3 @@ int qspi_enter_4byte_mode(void); #endif /* MPFS250_DEF_INCLUDED */ - diff --git a/hal/riscv.h b/hal/riscv.h index c9fe6edad7..64c790c702 100644 --- a/hal/riscv.h +++ b/hal/riscv.h @@ -117,6 +117,18 @@ __asm__ __volatile__ ("csrc " #csr ", %0" : : "rK"(__v)); \ }) +/* ============================================================================ + * Cache / I-Cache Sync Helpers + * ============================================================================ */ +#ifndef __ASSEMBLER__ +static inline void riscv_icache_sync(void) +{ +#ifdef __riscv_zifencei + __asm__ __volatile__("fence.i" ::: "memory"); +#endif +} +#endif /* !__ASSEMBLER__ */ + /* ============================================================================ * Interrupt Numbers (for SIE/SIP and MIE/MIP registers) * ============================================================================ */ @@ -279,4 +291,3 @@ extern void plic_dispatch_irq(uint32_t irq); #endif /* PLIC_BASE && !__ASSEMBLER__ */ #endif /* RISCV_H */ - diff --git a/src/elf.c b/src/elf.c index 0efe5a91c1..fc106a6198 100644 --- a/src/elf.c +++ b/src/elf.c @@ -29,6 +29,10 @@ #include "elf.h" #include "hal.h" +#ifdef __riscv +#include "hal/riscv.h" +#endif + #ifdef ARCH_PPC #include "hal/nxp_ppc.h" #endif @@ -151,8 +155,8 @@ int elf_load_image_mmu(uint8_t *image, uintptr_t *pentry, elf_mmu_map_cb mmu_cb) #ifdef ARCH_PPC flush_cache(paddr, mem_size); #endif - #ifdef __riscv_zifencei - asm volatile("fence.i" ::: "memory"); + #ifdef __riscv + riscv_icache_sync(); #endif } #ifdef DEBUG_ELF diff --git a/tools/scripts/mpfs_program.sh b/tools/scripts/mpfs_program.sh index 31fd8870e2..63328be2cf 100755 --- a/tools/scripts/mpfs_program.sh +++ b/tools/scripts/mpfs_program.sh @@ -7,9 +7,9 @@ set -e # Configuration -HSS_TTY="${HSS_TTY:-/dev/ttyUSB0}" -WOLFBOOT_TTY="${WOLFBOOT_TTY:-/dev/ttyUSB1}" -BLOCK_DEV="${BLOCK_DEV:-/dev/sda}" +HSS_TTY="${HSS_TTY:-/dev/ttyUSB9}" +WOLFBOOT_TTY="${WOLFBOOT_TTY:-/dev/ttyUSB10}" +BLOCK_DEV="${BLOCK_DEV:-/dev/sde}" BAUD_RATE="${BAUD_RATE:-115200}" TIMEOUT_HSS="${TIMEOUT_HSS:-30}" TIMEOUT_BLOCK="${TIMEOUT_BLOCK:-15}" @@ -18,6 +18,7 @@ WOLFBOOT_BIN="${WOLFBOOT_BIN:-wolfboot.bin}" CONFIG_FILE="${CONFIG_FILE:-./config/examples/polarfire_mpfs250.config}" STORAGE_MODE="${STORAGE_MODE:-}" # Can be "emmc" or "sdcard" RESET_METHOD="${RESET_METHOD:-hss}" # "hss" (HSS console reset) or "flashpro" (FlashPro6) +RESET_DELAY="${RESET_DELAY:-0.2}" # Colors for output RED='\033[0;31m' @@ -99,7 +100,7 @@ reset_target_hss() { exec 3>&- log_success "Reset command sent via HSS" - sleep 2 # Give device time to reset + sleep "$RESET_DELAY" } reset_target_flashpro() { @@ -113,7 +114,7 @@ reset_target_flashpro() { log_warn "FlashPro6 reset may have failed (check /tmp/flashpro_reset.log)" fi - sleep 2 # Give device time to reset + sleep "$RESET_DELAY" } reset_target() { @@ -743,4 +744,3 @@ case "$COMMAND" in exit 1 ;; esac -