From 2f8ce149384246605222ea827c0d6ef3cc58846b Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 3 Feb 2026 16:20:43 -0800 Subject: [PATCH] Add MPFS250 QSPI support --- arch.mk | 22 +- config/examples/polarfire_mpfs250.config | 6 +- config/examples/polarfire_mpfs250_qspi.config | 103 +++ docs/Targets.md | 47 +- hal/mpfs250.c | 693 ++++++++++++++++-- hal/mpfs250.h | 160 +++- hal/riscv.h | 46 +- src/boot_riscv.c | 2 +- src/boot_riscv_start.S | 11 +- src/elf.c | 16 +- src/vector_riscv.S | 6 +- test-app/RISCV64-mpfs250.ld | 17 +- test-app/startup_riscv.c | 12 +- test-app/vector_riscv.S | 53 ++ 14 files changed, 1090 insertions(+), 104 deletions(-) create mode 100644 config/examples/polarfire_mpfs250_qspi.config 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..c76ccb0c18 --- /dev/null +++ b/config/examples/polarfire_mpfs250_qspi.config @@ -0,0 +1,103 @@ +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 SC QSPI Controller (0x37020100) for fabric-connected flash. +# Direct register access to System Controller's QSPI instance. +# DEFAULT: Use MSS QSPI Controller (0x21000000) for external flash +# on 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 + +# Optional QSPI flash test (erase/write/read on update partition) +# Uncomment to run test during hal_init() +#CFLAGS_EXTRA+=-DTEST_EXT_FLASH diff --git a/docs/Targets.md b/docs/Targets.md index bfcc448b09..041e119638 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -857,7 +857,51 @@ 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 SoC has two CoreQSPI v2 controllers with identical register layouts. The selection +is made at build time via `MPFS_SC_SPI` and affects which QSPI base address wolfBoot uses: + +```text + +-------------------+ +----------------------+ + | U54 cores | | U54 cores | + | (wolfBoot) | | (wolfBoot) | + +---------+---------+ +----------+-----------+ + | | + | direct register access | direct register access + | (MSS QSPI @ 0x2100_0000) | (SC QSPI @ 0x3702_0100) + v v + +-------------------+ +----------------------+ + | MSS QSPI IP | | SC QSPI IP | + | (CoreQSPI v2) | | (CoreQSPI v2) | + +---------+---------+ +----------+-----------+ + | | + v v + External QSPI flash Fabric-connected flash +``` + +Build options: + +- MSS QSPI controller (direct register access at 0x21000000, 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. + +- SC QSPI controller (direct register access at 0x37020100, read/write/erase) + - `EXT_FLASH=1` + - `CFLAGS_EXTRA+=-DMPFS_SC_SPI` + - Example config: `config/examples/polarfire_mpfs250_qspi.config` as-is. + - Both controllers share the same CoreQSPI v2 register interface. + The only difference is that SC QSPI does not need MSS clock/reset setup. + +Example single-shot build: `cp config/examples/polarfire_mpfs250_qspi.config .config && make clean && make wolfboot.bin && hss-payload-generator -vvv -c ./hal/mpfs.yaml wolfboot.bin && make test-app/image.elf && ./tools/keytools/sign --ecc384 --sha384 test-app/image.elf wolfboot_signing_private_key.der 1` + +Notes: +- Both modes support full read, write, and erase operations. +- 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 SC QSPI path is for + fabric-connected flash (design flash) accessed via the System Controller's QSPI instance. ### PolarFire testing @@ -1321,7 +1365,6 @@ Benchmark complete ### PolarFire TODO -* Add support for QSPI NOR flash * Add support for full HSS replacement using wolfboot - Machine level assembly startup - DDR driver diff --git a/hal/mpfs250.c b/hal/mpfs250.c index 50511fe42e..332a100650 100644 --- a/hal/mpfs250.c +++ b/hal/mpfs250.c @@ -50,6 +50,10 @@ #include "sdhci.h" #endif +#if defined(EXT_FLASH) && defined(TEST_EXT_FLASH) && defined(__WOLFBOOT) +static int test_ext_flash(void); +#endif + void hal_init(void) { #if defined(DEBUG_UART) && defined(__WOLFBOOT) @@ -60,6 +64,14 @@ void hal_init(void) LIBWOLFBOOT_VERSION_STRING,__DATE__, __TIME__); #endif #endif + +#ifdef EXT_FLASH + qspi_init(); + +#if defined(TEST_EXT_FLASH) && defined(__WOLFBOOT) + test_ext_flash(); +#endif +#endif /* EXT_FLASH */ } /* ============================================================================ @@ -70,76 +82,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) +static 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) +static 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; +static 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; +} + +static 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; + } + + return 0; +} + +static 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) */ - for (i = 0; i < DEVICE_SERIAL_NUMBER_SIZE; i++) { - serial[i] = SCBMBOX_BYTE(i); + /* 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; @@ -290,26 +368,523 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) } #ifdef EXT_FLASH -/* External flash support +/* ========================================================================== + * QSPI Flash Controller Implementation * - * 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. + * Both MSS QSPI (0x21000000) and SC QSPI (0x37020100) use CoreQSPI v2 + * with identical register layouts. The controller is selected at build + * time via MPFS_SC_SPI, which changes QSPI_BASE in the header. + * ========================================================================== */ + +/* Microsecond delay using RISC-V time CSR (1 MHz tick rate) */ +static void udelay(uint32_t us) +{ + uint64_t start = csr_read(time); + while ((uint64_t)(csr_read(time) - start) < us) + ; +} + +/* Forward declarations */ +static 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); +static int qspi_read_id(uint8_t *id_buf); +static int qspi_enter_4byte_mode(void); + +/* 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 */ + udelay(10); +} + +void qspi_init(void) +{ + uint8_t id[3]; + +#ifdef MPFS_SC_SPI + wolfBoot_printf("QSPI: Using SC QSPI Controller (0x%x)\n", QSPI_BASE); + + /* Wait for system controller to finish any pending operations before + * taking direct control of the SC QSPI peripheral */ + mpfs_scb_wait_ready(100000); + +#ifdef DEBUG_QSPI + wolfBoot_printf("QSPI: Initial CTRL=0x%x, STATUS=0x%x, DIRECT=0x%x\n", + QSPI_CONTROL, QSPI_STATUS, QSPI_DIRECT); +#endif + + /* Disable direct access / XIP mode (SC may have left it enabled) */ + QSPI_DIRECT = 0; +#else + wolfBoot_printf("QSPI: Using MSS QSPI Controller (0x%x)\n", QSPI_BASE); + + /* Enable QSPI peripheral clock (MSS only) */ + SYSREG_SUBBLK_CLOCK_CR |= SYSREG_SUBBLK_CLOCK_CR_QSPI; + udelay(1); + + /* Release MSS QSPI from reset (MSS only) */ + SYSREG_SOFT_RESET_CR &= ~SYSREG_SOFT_RESET_CR_QSPI; + udelay(10); +#endif + + /* 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(); +} + +/* 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 */ +static 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; + } + + /* Drain RX FIFO of any stale data from previous transfers. */ + while (QSPI_STATUS & QSPI_STATUS_RXAVAIL) { + (void)QSPI_RX_DATA; + } + + /* Configure FRAMES register: + * - Total bytes: command + data (idle cycles handled by hardware) + * - Command bytes: TX-only bytes before data phase + * - Idle cycles: inserted by hardware between command and data + * - FBYTE: status flags (RXAVAIL/TXAVAIL) refer to individual bytes + * + * For write-mode transfers, set CMDBYTES = TOTALBYTES so the entire + * transfer occurs in the command phase (TX-only). The CoreQSPI data + * phase shifts TX FIFO output by a fixed offset on writes, causing + * data rotation in the programmed page. Keeping everything in the + * command phase avoids this. The flash determines command vs data + * boundaries from the opcode, not the controller's phase. */ + { + uint32_t frame_cmd = read_mode ? cmd_len : total_bytes; + frames = ((total_bytes & 0xFFFF) << QSPI_FRAMES_TOTALBYTES_OFFSET) | + ((frame_cmd & 0x1FF) << QSPI_FRAMES_CMDBYTES_OFFSET) | + ((dummy_cycles & 0xF) << QSPI_FRAMES_IDLE_OFFSET) | + (1u << QSPI_FRAMES_FBYTE_OFFSET); + } + + QSPI_FRAMES = frames; + + /* Send command bytes (opcode + address). + * Use TXAVAIL (bit 3) to check for FIFO space -- CoreQSPI v2 does NOT + * have a TXFULL status bit (bit 5 is reserved/always 0). + * A fence after each TX write ensures the store reaches the peripheral + * before we read STATUS again (RISC-V RVWMO allows posted stores that + * could cause stale TXAVAIL reads and FIFO overflow). */ + for (i = 0; i < cmd_len; i++) { + timeout = 100000; + while (!(QSPI_STATUS & QSPI_STATUS_TXAVAIL) && --timeout); + if (timeout == 0) { + wolfBoot_printf("QSPI: TX FIFO full timeout\n"); + return -2; + } + QSPI_TX_DATA = cmd[i]; + __asm__ __volatile__("fence o,i" ::: "memory"); + } + + if (read_mode) { + /* Read mode: poll RXAVAIL for each data byte. */ + 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 = 1000000; + while (!(QSPI_STATUS & QSPI_STATUS_RXDONE) && --timeout); + } else { + /* Write mode: send data bytes. + * Must push bytes without delay -- any gap causes FIFO underflow + * since CoreQSPI continues clocking with empty FIFO. + * Fence after each write ensures the store reaches the FIFO before + * we re-read STATUS (prevents FIFO overflow from posted stores). */ + if (data && data_len > 0) { + for (i = 0; i < data_len; i++) { + timeout = 100000; + while (!(QSPI_STATUS & QSPI_STATUS_TXAVAIL) && --timeout); + if (timeout == 0) { + wolfBoot_printf("QSPI: TX data timeout\n"); + return -4; + } + QSPI_TX_DATA = data[i]; + __asm__ __volatile__("fence o,i" ::: "memory"); + } + } + /* Wait for transmit complete */ + timeout = 100000; + while (!(QSPI_STATUS & QSPI_STATUS_TXDONE) && --timeout); + } + +#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 + + return 0; +} + +/* Read JEDEC ID from flash */ +static 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 */ +static 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) */ +static 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) */ +static 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) +{ + 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; + + 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; + + 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) */ +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 */ +} + +/* ========================================================================== + * 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) +{ + 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 (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)remaining) { + chunk_len = remaining; + } + + /* Write page */ + ret = qspi_flash_write_page(address, data, chunk_len); + if (ret != 0) { + return ret; + } + + /* Update pointers */ + address += chunk_len; + data += chunk_len; + remaining -= chunk_len; + } + + return total; +} + +int ext_flash_read(uintptr_t address, uint8_t *data, int len) +{ + #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); +} + +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); + #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 total; +} + +/* Test for external QSPI flash erase/write/read */ +#ifdef TEST_EXT_FLASH + +#ifndef TEST_EXT_ADDRESS + #define TEST_EXT_ADDRESS WOLFBOOT_PARTITION_UPDATE_ADDRESS +#endif + +static int test_ext_flash(void) +{ + int ret; + uint32_t i; + uint8_t pageData[FLASH_PAGE_SIZE]; + + wolfBoot_printf("Ext Flash Test at 0x%x\n", TEST_EXT_ADDRESS); + +#ifndef TEST_FLASH_READONLY + /* Erase sector */ + ret = ext_flash_erase(TEST_EXT_ADDRESS, FLASH_SECTOR_SIZE); + wolfBoot_printf("Sector Erase: Ret %d\n", ret); + if (ret < 0) + return ret; + + /* Verify erase (should be all 0xFF) */ + memset(pageData, 0, sizeof(pageData)); + ext_flash_read(TEST_EXT_ADDRESS, pageData, sizeof(pageData)); + wolfBoot_printf("Erase verify: "); + for (i = 0; i < 16; i++) { + wolfBoot_printf("%02x ", pageData[i]); + } + wolfBoot_printf("\n"); + + /* Write Page */ + for (i = 0; i < sizeof(pageData); i++) { + pageData[i] = (i & 0xff); + } + ret = ext_flash_write(TEST_EXT_ADDRESS, pageData, sizeof(pageData)); + wolfBoot_printf("Page Write: Ret %d\n", ret); + if (ret < 0) + return ret; +#endif /* !TEST_FLASH_READONLY */ + + /* Read page */ + memset(pageData, 0, sizeof(pageData)); + ret = ext_flash_read(TEST_EXT_ADDRESS, pageData, sizeof(pageData)); + wolfBoot_printf("Page Read: Ret %d\n", ret); + if (ret < 0) + return ret; + + /* Check data */ + for (i = 0; i < sizeof(pageData); i++) { + if (pageData[i] != (i & 0xff)) { + wolfBoot_printf("Check Data @ %d failed (0x%02x != 0x%02x)\n", + i, pageData[i], (i & 0xff)); + wolfBoot_printf("First 16 bytes: "); + for (i = 0; i < 16; i++) { + wolfBoot_printf("%02x ", pageData[i]); + } + wolfBoot_printf("\n"); + return -1; + } + } + + wolfBoot_printf("Ext Flash Test Passed\n"); + return 0; +} +#endif /* TEST_EXT_FLASH */ + +#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 +893,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 +901,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..d7ab5bd384 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,7 @@ /* System Service command opcodes */ #define SYS_SERV_CMD_SERIAL_NUMBER 0x00u +#define SYS_SERV_CMD_SPI_COPY 0x50u /* Device serial number size in bytes */ #define DEVICE_SERIAL_NUMBER_SIZE 16 @@ -164,7 +171,6 @@ #define SCBMBOX_REG(off) (*((volatile uint32_t*)(SCBMBOX_BASE + (off)))) #define SCBMBOX_BYTE(off) (*((volatile uint8_t*)(SCBMBOX_BASE + (off)))) - /* Crypto Engine: Athena F5200 TeraFire Crypto Processor (1x), 200 MHz */ #define ATHENA_BASE (SYSREG_BASE + 0x125000) @@ -209,5 +215,153 @@ #define PLIC_CONTEXT_U54_4_S 8 -#endif /* MPFS250_DEF_INCLUDED */ +#ifdef EXT_FLASH +/* ========================================================================== + * QSPI Flash Controller Definitions + * + * PolarFire SoC has two CoreQSPI v2 controllers with identical registers: + * + * 1. System Controller QSPI (MPFS_SC_SPI=1, default): + * - SC QSPI at 0x37020100 (size 0x100) + * - For fabric-connected flash (design flash) + * - Direct register access (same register layout as MSS QSPI) + * - Supports read, write, and erase operations + * - Does NOT require MSS clock enable or soft reset + * + * 2. MSS QSPI Controller (MPFS_SC_SPI=0): + * - MSS QSPI at 0x21000000 (size 0x1000) + * - For external flash connected to MSS QSPI pins + * - Requires MSS QSPI clock enable and soft reset release + * - Supports read, write, and erase operations + * + * ========================================================================== */ + +/* QSPI Controller Base Address */ +#ifndef QSPI_BASE +#ifdef MPFS_SC_SPI +#define QSPI_BASE 0x37020100u /* SC QSPI Controller (fabric-connected flash) */ +#else +#define QSPI_BASE 0x21000000u /* MSS QSPI Controller (external flash) */ +#endif +#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) +/* Bit 5 is reserved in CoreQSPI v2 */ +#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); +#endif /* __ASSEMBLER__ */ + +#endif /* EXT_FLASH */ + +#endif /* MPFS250_DEF_INCLUDED */ diff --git a/hal/riscv.h b/hal/riscv.h index 4195645ce5..64c790c702 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 * ============================================================================ */ @@ -108,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) * ============================================================================ */ @@ -270,4 +291,3 @@ extern void plic_dispatch_irq(uint32_t irq); #endif /* PLIC_BASE && !__ASSEMBLER__ */ #endif /* RISCV_H */ - 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..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 @@ -136,11 +140,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 +155,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 + riscv_icache_sync(); + #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