diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index a84cde0ccc..b5cf0be0eb 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -272,9 +272,16 @@ jobs: with: arch: riscv64 config-file: ./config/examples/polarfire_mpfs250.config - # Only building wolfBoot - not test app for now (cross compiler cannot find suitable multilib set for '-march=rv64imafd_zicsr_zmmul_zaamo_zalrsc'/'-mabi=lp64d') - # Consider building cached RISCV64 toolchain for this target - make-args: wolfboot.bin + microchip_mpfs250_qspi_test: + uses: ./.github/workflows/test-build-riscv.yml + with: + arch: riscv64 + config-file: ./config/examples/polarfire_mpfs250_qspi.config + microchip_mpfs250_m_qspi_test: + uses: ./.github/workflows/test-build-riscv.yml + with: + arch: riscv64 + config-file: ./config/examples/polarfire_mpfs250_m_qspi.config raspi3_test: uses: ./.github/workflows/test-build.yml diff --git a/arch.mk b/arch.mk index 6f562b2286..fb919d2c3d 100644 --- a/arch.mk +++ b/arch.mk @@ -581,7 +581,17 @@ endif ## RISCV64 (64-bit) ifeq ($(ARCH),RISCV64) CROSS_COMPILE?=riscv64-unknown-elf- - CFLAGS+=-DMMU -DWOLFBOOT_DUALBOOT + + # M-mode vs S-mode configuration + ifeq ($(RISCV_MMODE),1) + # Machine Mode: Running directly from eNVM/L2 SRAM + CFLAGS+=-DWOLFBOOT_RISCV_MMODE -DWOLFBOOT_DUALBOOT + # Use M-mode specific linker script + LSCRIPT_IN:=hal/$(TARGET)-m.ld + else + # Supervisor Mode (default): Running under HSS with DDR available + CFLAGS+=-DMMU -DWOLFBOOT_DUALBOOT + endif # If SD card or eMMC is enabled use update_disk loader with GPT support ifneq ($(filter 1,$(DISK_SDCARD) $(DISK_EMMC)),) @@ -595,7 +605,17 @@ ifeq ($(ARCH),RISCV64) UPDATE_OBJS?=src/update_ram.o endif - ARCH_FLAGS=-march=rv64imafd -mabi=lp64d -mcmodel=medany + ifeq ($(RISCV_MMODE),1) + # E51 core: rv64imac (no FPU, no crypto extensions) + ARCH_FLAGS=-march=rv64imac -mabi=lp64 -mcmodel=medany + else + # U54 cores: rv64gc (with FPU) + ARCH_FLAGS=-march=rv64imafd -mabi=lp64d -mcmodel=medany + + # FDT support required + CFLAGS+=-DWOLFBOOT_FDT + OBJS+=src/fdt.o + endif CFLAGS+=-fno-builtin-printf -DUSE_M_TIME -g -nostartfiles -DARCH_RISCV -DARCH_RISCV64 CFLAGS+=$(ARCH_FLAGS) LDFLAGS+=$(ARCH_FLAGS) @@ -607,9 +627,6 @@ ifeq ($(ARCH),RISCV64) # Unified RISC-V boot code (32/64-bit via __riscv_xlen) OBJS+=src/boot_riscv_start.o src/boot_riscv.o src/vector_riscv.o - CFLAGS+=-DWOLFBOOT_FDT - OBJS+=src/fdt.o - ifeq ($(SPMATH),1) MATH_OBJS += $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sp_c64.o endif diff --git a/config/examples/polarfire_mpfs250.config b/config/examples/polarfire_mpfs250.config index 4b3ec64ff9..57fa2ecb77 100644 --- a/config/examples/polarfire_mpfs250.config +++ b/config/examples/polarfire_mpfs250.config @@ -67,7 +67,7 @@ CFLAGS_EXTRA+=-DBOOT_PART_B=2 # Speed up disk partition read (512KB chunks - max DMA size) CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x80000 -# DTS (Device Tree) +# DTS (Device Tree) load address WOLFBOOT_LOAD_DTS_ADDRESS?=0x8A000000 # Optional Encryption @@ -77,7 +77,7 @@ WOLFBOOT_LOAD_DTS_ADDRESS?=0x8A000000 #OBJS_EXTRA=src/my_custom_encrypt_key.o # Optional EMMC_SD debugging logs -#CFLAGS_EXTRA+=-DDEBUG_MMC +#CFLAGS_EXTRA+=-DDEBUG_SDHCI # Optional disk debugging logs #CFLAGS_EXTRA+=-DDEBUG_DISK #CFLAGS_EXTRA+=-DDISK_TEST diff --git a/config/examples/polarfire_mpfs250_m_qspi.config b/config/examples/polarfire_mpfs250_m_qspi.config new file mode 100644 index 0000000000..24a32b30c4 --- /dev/null +++ b/config/examples/polarfire_mpfs250_m_qspi.config @@ -0,0 +1,102 @@ +# PolarFire SoC MPFS250T M-Mode (Machine Mode) with SC QSPI Flash +# +# This configuration runs wolfBoot directly from eNVM in M-mode (Machine Mode), +# and boots a test application from SC QSPI flash to on-chip LIM (no DDR). +# +# Boot flow: +# 1. eNVM (0x20220100) -> L2_SCRATCH (0x0A000000) - wolfBoot starts +# 2. Load signed app image from SC QSPI flash to LIM (0x08000000) +# 3. Verify signature and boot +# +# Flash using mpfsBootmodeProgrammer (bootmode 1): +# java -jar mpfsBootmodeProgrammer.jar --bootmode 1 --die MPFS250T \ +# --package FCG1152 --workdir $PWD wolfboot.elf + +ARCH?=RISCV64 +TARGET?=mpfs250 +SIGN?=ECC384 +HASH?=SHA384 +IMAGE_HEADER_SIZE=512 +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?=0 +SPMATHALL?=1 +DUALBANK_SWAP?=0 +PKA?=0 +ENCRYPT=0 +WOLFTPM?=0 +ELF?=1 +#DEBUG_ELF?=1 + +OPTIMIZATION_LEVEL=1 + +# M-Mode Configuration +# Runs on E51 core in Machine Mode from L2 SRAM +RISCV_MMODE?=1 + +# Stack size per hart (reduced for L2 SRAM constraints) +CFLAGS_EXTRA+=-DSTACK_SIZE_PER_HART=8192 + +# E51 core lacks RISC-V crypto extensions (Zknh), use portable C implementations +NO_ASM?=1 + +# 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 + +# No SD card or eMMC +DISK_SDCARD?=0 +DISK_EMMC?=0 + +# L2 SRAM Address for wolfBoot (256KB available) +# Stack grows down from end of L2_SCRATCH +WOLFBOOT_ORIGIN?=0x0A000000 + +# Load application to LIM (Loosely Integrated Memory, 2MB) +# Note: update_ram places header at (LOAD_ADDRESS - IMAGE_HEADER_SIZE), +# so offset by header size to keep everything within LIM (starts at 0x08000000) +WOLFBOOT_LOAD_ADDRESS?=0x08000200 + +# Flash geometry (64 KB sector to match QSPI flash) +WOLFBOOT_SECTOR_SIZE?=0x10000 + +# Partition layout for 128MB QSPI flash +# Boot partition: 0x00020000 - 0x01FFFFFF (~32MB) +# Update partition: 0x02000000 - 0x03FFFFFF (~32MB) +# Swap partition: 0x04000000 - 0x0400FFFF (64KB) +WOLFBOOT_PARTITION_SIZE?=0x1FE0000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x20000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x2000000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x4000000 + +# Speed up reads from flash by using larger blocks +CFLAGS_EXTRA+=-DWOLFBOOT_SHA_BLOCK_SIZE=4096 + +# Debug options (useful for initial M-mode bring-up) +#CFLAGS_EXTRA+=-DDEBUG_BOOT + +# 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 2c07dcb1f4..7aa7e87946 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -797,9 +797,10 @@ The PolarFire SoC is a 64-bit RISC-V SoC featuring a five-core CPU cluster (1× ### PolarFire SoC Files -`hal/mpfs250.c` - Hardware abstraction layer implementation (UART and uSD) +`hal/mpfs250.c` - Hardware abstraction layer (UART, QSPI, SD/eMMC, multi-hart) `hal/mpfs250.h` - Register definitions and hardware interfaces -`hal/mpfs250.ld` - Linker script for the platform +`hal/mpfs250.ld` - Linker script for S-mode (HSS-based boot) +`hal/mpfs250-m.ld` - Linker script for M-mode (eNVM + L2 SRAM) `hal/mpfs.dts` - Device tree source `hal/mpfs.yaml` - HSS payload generator configuration `hal/mpfs250.its` - Example FIT image creation template @@ -1363,11 +1364,105 @@ ML-DSA 87 verify 200 ops took 1.077 sec, avg 5.385 ms, 185.704 ops/ Benchmark complete ``` -### PolarFire TODO +### PolarFire Machine Mode (M-Mode) Support -* Add support for full HSS replacement using wolfboot - - Machine level assembly startup - - DDR driver +wolfBoot supports running directly in Machine Mode (M-mode) on PolarFire SoC, +replacing the Hart Software Services (HSS) as the first-stage bootloader. In +M-mode, wolfBoot runs on the E51 monitor core and loads a signed application +from SC QSPI flash to on-chip LIM (Loosely Integrated Memory). + +#### M-Mode Features + +* Runs on E51 monitor core (hart 0) directly from eNVM +* Executes from L2 Scratchpad SRAM (256KB at 0x0A000000) +* Loads signed application from SC QSPI flash to LIM (0x08000000) +* No HSS or DDR required — boots entirely from on-chip memory +* Wakes and manages secondary U54 harts via IPI +* Per-hart UART output (each hart uses its own MMUART) +* ECC384 + SHA384 signature verification + +#### M-Mode Files + +| File | Description | +|------|-------------| +| `config/examples/polarfire_mpfs250_m_qspi.config` | M-mode + SC QSPI configuration | +| `hal/mpfs250-m.ld` | M-mode linker script (eNVM + L2 SRAM) | +| `hal/mpfs250.c` | HAL with QSPI driver, UART, L2 cache init | +| `src/boot_riscv_start.S` | M-mode assembly startup | + +#### Building for M-Mode + +```sh +# Copy M-mode QSPI configuration +cp config/examples/polarfire_mpfs250_m_qspi.config .config + +# Build wolfBoot and signed test-app +make clean +make +``` + +This produces: +- `wolfboot.elf` — bootloader for eNVM (~26KB) +- `test-app/image_v1_signed.bin` — signed application for QSPI flash + +#### Flashing + +M-mode requires programming two targets: + +1. **eNVM** (wolfBoot): Programmed via JTAG using mpfsBootmodeProgrammer (bootmode 1) +2. **QSPI flash** (signed application): Programmed via Libero/FPExpress SPI programming + +```sh +# Set SoftConsole installation directory +export SC_INSTALL_DIR=/opt/Microchip/SoftConsole-v2022.2-RISC-V-747 + +# Flash wolfboot.elf to eNVM +$SC_INSTALL_DIR/eclipse/jre/bin/java -jar \ + $SC_INSTALL_DIR/extras/mpfs/mpfsBootmodeProgrammer.jar \ + --bootmode 1 --die MPFS250T --package FCG1152 --workdir $PWD wolfboot.elf + +# Flash test-app/image_v1_signed.bin to QSPI at offset 0x20000 +# (use Libero SoC Design Suite SPI flash programming) +``` + +#### M-Mode Boot Flow + +1. **eNVM Reset Vector** (0x20220100): CPU starts, copies code to L2 SRAM +2. **L2 SRAM Execution** (0x0A000000): wolfBoot runs from scratchpad +3. **Hardware Init**: L2 cache configuration, UART setup +4. **QSPI Init**: SC QSPI controller (0x37020100), JEDEC ID read, 4-byte address mode +5. **Image Load**: Read signed image from QSPI flash (0x20000) to LIM (0x08000200) +6. **Verify & Boot**: SHA384 integrity check, ECC384 signature verification, jump to app + +#### M-Mode QSPI Partition Layout + +The SC QSPI flash (Micron MT25QL01G, 128MB) is partitioned as: + +| Region | Address | Size | +|--------|---------|------| +| Boot partition | 0x00020000 | ~32MB | +| Update partition | 0x02000000 | ~32MB | +| Swap partition | 0x04000000 | 64KB | + +#### M-Mode UART Mapping + +| Hart | Core | MMUART | USB Device | +|------|------|--------|------------| +| 0 | E51 | MMUART0 | /dev/ttyUSB0 | +| 1 | U54_1 | MMUART1 | /dev/ttyUSB1 | +| 2 | U54_2 | MMUART2 | N/A | +| 3 | U54_3 | MMUART3 | N/A | +| 4 | U54_4 | MMUART4 | N/A | + +#### M-Mode Notes + +* The E51 core is rv64imac (no FPU or crypto extensions). wolfBoot is compiled + with `NO_ASM=1` to use portable C crypto implementations and + `-march=rv64imac -mabi=lp64` for correct code generation. +* CLINT MTIME counter is not running in bare-metal M-mode (no HSS), so + `udelay()` uses a calibrated busy loop instead of the timer CSR. +* DDR initialization support is available on the `polarfire_ddr` branch for + use cases that require loading larger applications to DDR memory. ## STM32F7 diff --git a/hal/mpfs250-m.ld b/hal/mpfs250-m.ld new file mode 100644 index 0000000000..a7e74574e4 --- /dev/null +++ b/hal/mpfs250-m.ld @@ -0,0 +1,142 @@ +/* PolarFire SoC MPFS250 M-Mode Linker Script for wolfBoot + * + * This linker script is for running wolfBoot in Machine Mode (M-mode) + * directly from eNVM, executing from L2 SRAM. + * + * Boot flow: + * 1. CPU starts at eNVM reset vector (0x20220100) + * 2. Startup code in eNVM copies main code to L2_SCRATCH + * 3. Jumps to L2_SCRATCH for execution + * + * The first 0x100 bytes of eNVM are reserved for the boot ROM secure boot + * meta information added by mpfsBootmodeProgrammer. + * + * Memory regions: + * FLASH_ENVM - Embedded NVM (128KB - 0x100 for header) + * L2_SCRATCH - L2 Scratchpad SRAM (256KB) - execution and data + */ + +OUTPUT_ARCH( "riscv" ) + +ENTRY( _reset ) + +MEMORY +{ + /* The first 0x100 bytes of eNVM are used for boot ROM secure boot meta information + * This offset is added by mpfsBootmodeProgrammer (bootmode 1) */ + FLASH_ENVM (rx) : ORIGIN = 0x20220100, LENGTH = 128k - 0x100 + + /* L2 Scratchpad SRAM - 256KB available + * Used for code execution, data, and stack in M-mode + * Address range: 0x0A000000 - 0x0A03FFFF */ + L2_SCRATCH (rwx) : ORIGIN = @WOLFBOOT_ORIGIN@, LENGTH = 256k +} + +/* Stack size for the boot hart (E51 in M-mode) + * ECC384 signature verification with sp_int needs significant stack + * for big number temporaries and point multiplication */ +PROVIDE(STACK_SIZE = 64k); + +SECTIONS +{ + /* + * Reset vector and early initialization code + * This section MUST be in eNVM (VMA = LMA) since CPU starts here. + * It copies the main code to L2_SCRATCH and jumps there. + */ + .init : ALIGN(0x10) + { + _start_text = .; + KEEP(*(.init)) + . = ALIGN(0x10); + } > FLASH_ENVM + + /* + * Main code section - runs from L2_SCRATCH, stored in FLASH_ENVM + * The .init code will copy this section to L2_SCRATCH before jumping here. + */ + .text : ALIGN(0x10) + { + _start_text_sram = .; + _start_vector = .; + KEEP(*(.isr_vector)) + KEEP(*(.trap_vector)) + . = ALIGN(0x10); + *(.text*) + *(.rodata*) + *(.srodata*) + . = ALIGN(4); + _end_text = .; + } > L2_SCRATCH AT > FLASH_ENVM + + /* Provide load address for copying from flash */ + _stored_text = LOADADDR(.text); + _stored_data = LOADADDR(.data); + + /* Initialized data section */ + .data : ALIGN(0x10) + { + _start_data = .; + KEEP(*(.ramcode*)) + . = ALIGN(4); + *(.data*) + . = ALIGN(4); + /* Global pointer is set to .sdata + 0x800 for efficient access + * to small data using gp-relative addressing (+/- 2KB range) */ + _global_pointer = . + 0x800; + *(.sdata*) + . = ALIGN(4); + _end_data = .; + } > L2_SCRATCH AT > FLASH_ENVM + + /* Uninitialized data section (cleared to zero on startup) */ + .bss (NOLOAD) : ALIGN(0x10) + { + _start_bss = .; + *(.bss*) + *(.sbss*) + *(COMMON) + . = ALIGN(4); + _end_bss = .; + _end = .; + } > L2_SCRATCH +} + +/* Heap starts after BSS (between _end and stack) */ +PROVIDE(_start_heap = _end); + +/* Stack configuration for multi-hart boot + * Memory layout at end of L2_SCRATCH: + * [code/data/bss/heap] ... [secondary stacks] [main stack] + * + * Stack sizes (defined in config or header): + * STACK_SIZE_PER_HART = 8192 (8KB per hart) + * STACK_SIZE = 64K (64KB for main hart E51) + * + * Total stack area: STACK_SIZE + 4 * STACK_SIZE_PER_HART = 48KB + */ +PROVIDE(STACK_SIZE_PER_HART = 8192); + +/* End of L2 scratchpad */ +PROVIDE(_l2_scratch_end = ORIGIN(L2_SCRATCH) + LENGTH(L2_SCRATCH)); + +/* Main hart (E51) stack at very end, grows downward */ +PROVIDE(_end_stack = _l2_scratch_end); +PROVIDE(_main_hart_stack_top = _end_stack); +PROVIDE(_main_hart_stack_bottom = _main_hart_stack_top - STACK_SIZE); + +/* Main hart HLS location (at top of main stack minus 64 bytes) */ +PROVIDE(_main_hart_hls = _main_hart_stack_top - 64); + +/* Secondary hart stacks below main hart stack + * Hart 1 stack: _main_hart_stack_bottom - STACK_SIZE_PER_HART * 0 to - STACK_SIZE_PER_HART * 1 + * Hart 2 stack: _main_hart_stack_bottom - STACK_SIZE_PER_HART * 1 to - STACK_SIZE_PER_HART * 2 + * etc. + */ +PROVIDE(_secondary_hart_stack_base = _main_hart_stack_bottom - 4 * STACK_SIZE_PER_HART); + +/* Provide symbols for M-mode startup code */ +PROVIDE(__global_pointer$ = _global_pointer); + +/* Size of text section to copy (for startup code) */ +PROVIDE(_text_size = _end_text - _start_text_sram); diff --git a/hal/mpfs250.c b/hal/mpfs250.c index de65b6f672..6c4eef0351 100644 --- a/hal/mpfs250.c +++ b/hal/mpfs250.c @@ -48,127 +48,262 @@ #if defined(DISK_SDCARD) || defined(DISK_EMMC) #include "sdhci.h" -#endif -#if defined(EXT_FLASH) && defined(TEST_EXT_FLASH) && defined(__WOLFBOOT) -static int test_ext_flash(void); +/* Forward declaration of SDHCI IRQ handler */ +extern void sdhci_irq_handler(void); #endif -void hal_init(void) +/* Video Kit DDR/Clock configuration is included in mpfs250.h */ + +/* ============================================================================ + * L2 Cache Controller Configuration + * + * The L2 cache controller must be properly configured before using the + * L2 scratchpad memory. At reset, only 1 cache way is enabled. + * + * This function: + * 1. Enables all cache ways (0-7) and scratchpad ways (8-11) + * 2. Configures way masks for each master (harts, DMA, AXI ports) + * 3. Disables L2 shutdown mode + * ============================================================================ */ +#ifdef WOLFBOOT_RISCV_MMODE +static void mpfs_config_l2_cache(void) { -#if defined(DEBUG_UART) && defined(__WOLFBOOT) -#ifdef WOLFBOOT_REPRODUCIBLE_BUILD - wolfBoot_printf("wolfBoot Version: %s\n", LIBWOLFBOOT_VERSION_STRING); -#else - wolfBoot_printf("wolfBoot Version: %s (%s %s)\n", - LIBWOLFBOOT_VERSION_STRING,__DATE__, __TIME__); -#endif -#endif + uint64_t way_enable_before; + uint64_t way_enable_after; + + /* Read current way enable state */ + way_enable_before = L2_WAY_ENABLE; + + /* Enable selected cache/scratchpad ways. + * Value 0x0B (bits 0,1,3) enables ways 0, 1, and 3. + * This matches the working DDR demo configuration. */ + L2_WAY_ENABLE = 0x0B; + + /* Disable L2 shutdown */ + SYSREG_L2_SHUTDOWN_CR = 0; + + /* Configure way masks - allow all masters to use cache ways 0-7 */ + L2_WAY_MASK_DMA = L2_WAY_MASK_CACHE_ONLY; + L2_WAY_MASK_AXI4_PORT0 = L2_WAY_MASK_CACHE_ONLY; + L2_WAY_MASK_AXI4_PORT1 = L2_WAY_MASK_CACHE_ONLY; + L2_WAY_MASK_AXI4_PORT2 = L2_WAY_MASK_CACHE_ONLY; + L2_WAY_MASK_AXI4_PORT3 = L2_WAY_MASK_CACHE_ONLY; + + /* E51 cache masks */ + L2_WAY_MASK_E51_DCACHE = L2_WAY_MASK_CACHE_ONLY; + L2_WAY_MASK_E51_ICACHE = L2_WAY_MASK_CACHE_ONLY; + + /* U54 cache masks (configure even if not using U54s yet) */ + L2_WAY_MASK_U54_1_DCACHE = L2_WAY_MASK_CACHE_ONLY; + L2_WAY_MASK_U54_1_ICACHE = L2_WAY_MASK_CACHE_ONLY; + L2_WAY_MASK_U54_2_DCACHE = L2_WAY_MASK_CACHE_ONLY; + L2_WAY_MASK_U54_2_ICACHE = L2_WAY_MASK_CACHE_ONLY; + L2_WAY_MASK_U54_3_DCACHE = L2_WAY_MASK_CACHE_ONLY; + L2_WAY_MASK_U54_3_ICACHE = L2_WAY_MASK_CACHE_ONLY; + L2_WAY_MASK_U54_4_DCACHE = L2_WAY_MASK_CACHE_ONLY; + L2_WAY_MASK_U54_4_ICACHE = L2_WAY_MASK_CACHE_ONLY; + + /* Memory barrier to ensure all writes complete */ + __asm__ volatile("fence iorw, iorw" ::: "memory"); + + /* Read back to verify */ + way_enable_after = L2_WAY_ENABLE; + + /* Store for later reporting (can't print yet - UART not initialized) */ + (void)way_enable_before; + (void)way_enable_after; +} -#ifdef EXT_FLASH - if (qspi_init() != 0) { - wolfBoot_printf("QSPI: Init failed\n"); - } -#if defined(TEST_EXT_FLASH) && defined(__WOLFBOOT) - else { - test_ext_flash(); - } -#endif -#endif /* EXT_FLASH */ +/* Microsecond delay using busy loop. + * MTIME counter is not running in bare-metal M-mode (no HSS), + * so use a calibrated loop at ~40 MHz reset clock. */ +static void udelay(uint32_t us) +{ + volatile uint32_t i; + /* ~10 cycles per iteration at -O2, 40 MHz = 4 iterations per us */ + for (i = 0; i < us * 4; i++) + ; } +#endif /* WOLFBOOT_RISCV_MMODE */ + + /* ============================================================================ - * System Controller Mailbox Functions + * Multi-Hart Support (M-Mode only) + * ============================================================================ */ +#ifdef WOLFBOOT_RISCV_MMODE +/* ============================================================================ + * Multi-Hart Support * - * The MPFS system controller provides various system services via a mailbox - * interface. Commands are sent by writing the opcode to the control register - * and responses are read from the mailbox RAM. + * These functions handle waking secondary harts (U54 cores) and the + * communication protocol between E51 (main hart) and U54s. * ============================================================================ */ -/* Wait for SCB register bits to clear, with timeout */ -static int mpfs_scb_wait_clear(uint32_t reg_offset, uint32_t mask, - uint32_t timeout) +/* Linker symbols for hart stacks and HLS */ +extern uint64_t _main_hart_hls; + +/* CLINT MSIP register access for sending IPIs */ +#define CLINT_MSIP_REG(hart) (*(volatile uint32_t*)(CLINT_BASE + (hart) * 4)) + +/** + * mpfs_get_main_hls - Get pointer to main hart's HLS + * Returns: Pointer to HLS_DATA structure + */ +static HLS_DATA* mpfs_get_main_hls(void) { - while ((SCBCTRL_REG(reg_offset) & mask) && --timeout) - ; - return (timeout == 0) ? -1 : 0; + return (HLS_DATA*)&_main_hart_hls; } -int mpfs_scb_read_mailbox(uint8_t *out, uint32_t len) +/** + * mpfs_signal_main_hart_started - Signal to secondary harts that main hart is ready + * + * Called by E51 after basic initialization. Secondary harts are waiting in WFI + * for this signal before they signal their own readiness. + */ +static void mpfs_signal_main_hart_started(void) { - uint32_t i; + HLS_DATA* hls = mpfs_get_main_hls(); - if (out == NULL) { - return -1; - } + hls->in_wfi_indicator = HLS_MAIN_HART_STARTED; + hls->my_hart_id = MPFS_FIRST_HART; + + /* Memory barrier to ensure write is visible to other harts */ + __asm__ volatile("fence iorw, iorw" ::: "memory"); +} - for (i = 0; i < len; i++) { - out[i] = SCBMBOX_BYTE(i); +/** + * mpfs_wake_secondary_harts - Wake all U54 cores via IPI + * + * This function implements the hart wake-up protocol: + * 1. Wait for each hart to signal it's in WFI + * 2. Send IPI to wake the hart + * 3. Wait for hart to acknowledge wake-up + * + * Returns: Number of harts successfully woken + */ +int mpfs_wake_secondary_harts(void) +{ + int hart_id; + int woken_count = 0; + + wolfBoot_printf("Waking secondary harts...\n"); + + for (hart_id = MPFS_FIRST_U54_HART; hart_id <= MPFS_LAST_U54_HART; hart_id++) { + /* Note: In this simplified implementation, we just send IPIs. + * The full implementation would wait for HLS_OTHER_HART_IN_WFI + * from each hart, but we don't have per-hart HLS pointers yet. + * For now, we just send the IPI and the hart will wake when ready. */ + + wolfBoot_printf(" Sending IPI to hart %d...", hart_id); + + /* Send software interrupt (IPI) to this hart */ + CLINT_MSIP_REG(hart_id) = 0x01; + + /* Memory barrier */ + __asm__ volatile("fence iorw, iorw" ::: "memory"); + + /* Small delay for hart to respond (~1ms) */ + udelay(1000); + + woken_count++; + wolfBoot_printf(" done\n"); } - return 0; + wolfBoot_printf("Woke %d secondary harts\n", woken_count); + + return woken_count; } -static void mpfs_scb_write_mailbox(const uint8_t *data, uint32_t len) +/** + * secondary_hart_entry - Entry point for secondary harts (U54 cores) + * + * Each U54 core uses its own MMUART: + * Hart 1 -> MMUART1 (/dev/ttyUSB1), Hart 2 -> MMUART2, etc. + */ +void secondary_hart_entry(unsigned long hartid, HLS_DATA* hls) { - uint32_t i = 0; + /* Message template with placeholder for hart ID at position 5 */ + char msg[] = "Hart X: Woken, waiting for Linux boot...\n"; + (void)hls; - if (data == NULL || len == 0) { - return; - } + /* Initialize this hart's dedicated UART */ + uart_init_hart(hartid); - /* 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; - } + /* Update hart ID in message (position 5) */ + msg[5] = '0' + (char)hartid; + + /* Write to this hart's UART */ + uart_write_hart(hartid, msg, sizeof(msg) - 1); - /* Write remaining bytes */ - while (i < len) { - SCBMBOX_BYTE(i) = data[i]; - i++; + /* Wait for Linux to take over via SBI */ + while (1) { + __asm__ volatile("wfi"); } } +#endif /* WOLFBOOT_RISCV_MMODE */ + +#if defined(EXT_FLASH) && defined(TEST_EXT_FLASH) && defined(__WOLFBOOT) +static int test_ext_flash(void); +#endif -int mpfs_scb_service_call(uint8_t opcode, const uint8_t *mb_data, - uint32_t mb_len, uint32_t timeout) +void hal_init(void) { - uint32_t cmd; - uint32_t status; +#ifdef WOLFBOOT_RISCV_MMODE + /* Configure L2 cache controller first (before using L2 scratchpad heavily) */ + mpfs_config_l2_cache(); - if (mpfs_scb_wait_clear(SERVICES_SR_OFFSET, SERVICES_SR_BUSY_MASK, 1)) { - return -1; - } + /* Signal to secondary harts that main hart is ready */ + mpfs_signal_main_hart_started(); +#endif - if (mb_data && mb_len > 0) { - mpfs_scb_write_mailbox(mb_data, mb_len); - } +#ifdef DEBUG_UART + /* Enable clock and release from soft reset for debug UART */ + SYSREG_SUBBLK_CLOCK_CR |= (MSS_PERIPH_MMUART0 << DEBUG_UART_PORT); + SYSREG_SOFT_RESET_CR &= ~(MSS_PERIPH_MMUART0 << DEBUG_UART_PORT); + uart_init(); +#endif - cmd = ((opcode & 0x7F) << SERVICES_CR_COMMAND_SHIFT) | - SERVICES_CR_REQ_MASK; - SCBCTRL_REG(SERVICES_CR_OFFSET) = cmd; +#ifdef WOLFBOOT_REPRODUCIBLE_BUILD + wolfBoot_printf("wolfBoot Version: %s\n", LIBWOLFBOOT_VERSION_STRING); +#else + wolfBoot_printf("wolfBoot Version: %s (%s %s)\n", + LIBWOLFBOOT_VERSION_STRING, __DATE__, __TIME__); +#endif - if (mpfs_scb_wait_clear(SERVICES_CR_OFFSET, SERVICES_CR_REQ_MASK, - timeout) < 0) { - return -2; - } +#ifdef WOLFBOOT_RISCV_MMODE + wolfBoot_printf("Running on E51 (hart 0) in M-mode\n"); - if (mpfs_scb_wait_clear(SERVICES_SR_OFFSET, SERVICES_SR_BUSY_MASK, - timeout) < 0) { - return -3; - } +#endif - status = (SCBCTRL_REG(SERVICES_SR_OFFSET) >> SERVICES_SR_STATUS_SHIFT) - & 0xFFFF; - if (status != 0) { - return -4; +#ifdef EXT_FLASH + if (qspi_init() != 0) { + wolfBoot_printf("QSPI: Init failed\n"); } +#if defined(TEST_EXT_FLASH) && defined(__WOLFBOOT) + else { + test_ext_flash(); + } +#endif +#endif /* EXT_FLASH */ +} - return 0; +/* ============================================================================ + * System Controller Mailbox Functions + * + * The MPFS system controller provides various system services via a mailbox + * interface. Commands are sent by writing the opcode to the control register + * 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) +{ + return (SCBCTRL_REG(SERVICES_SR_OFFSET) & SERVICES_SR_BUSY_MASK); } /** @@ -182,23 +317,55 @@ int mpfs_scb_service_call(uint8_t opcode, const uint8_t *mb_data, */ int mpfs_read_serial_number(uint8_t *serial) { - int ret; + uint32_t cmd, status; + int i, timeout; if (serial == NULL) { return -1; } - ret = mpfs_scb_service_call(SYS_SERV_CMD_SERIAL_NUMBER, NULL, 0, - MPFS_SCB_TIMEOUT); - if (ret != 0) { - wolfBoot_printf("SCB mailbox error: %d\n", ret); - return ret; + /* Check if mailbox is busy */ + if (mpfs_scb_mailbox_busy()) { + wolfBoot_printf("SCB mailbox busy\n"); + return -2; } - /* Read serial number from mailbox RAM (16 bytes). */ - ret = mpfs_scb_read_mailbox(serial, DEVICE_SERIAL_NUMBER_SIZE); - if (ret != 0) { - return ret; + /* 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; + + /* Wait for request bit to clear (command accepted) */ + timeout = 10000; + while ((SCBCTRL_REG(SERVICES_CR_OFFSET) & SERVICES_CR_REQ_MASK) && timeout > 0) { + timeout--; + } + if (timeout == 0) { + wolfBoot_printf("SCB mailbox request timeout\n"); + return -3; + } + + /* Wait for busy bit to clear (command completed) */ + timeout = 10000; + while (mpfs_scb_mailbox_busy() && timeout > 0) { + timeout--; + } + if (timeout == 0) { + wolfBoot_printf("SCB mailbox busy timeout\n"); + return -4; + } + + /* 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; + } + + /* Read serial number from mailbox RAM (16 bytes) */ + for (i = 0; i < DEVICE_SERIAL_NUMBER_SIZE; i++) { + serial[i] = SCBMBOX_BYTE(i); } return 0; @@ -316,9 +483,10 @@ int hal_dts_fixup(void* dts_addr) return 0; } - void hal_prepare_boot(void) { + /* reset the eMMC/SD card? */ + } @@ -347,6 +515,16 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) return 0; } + +/* Wait for SCB register bits to clear, with timeout */ +static int mpfs_scb_wait_clear(uint32_t reg_offset, uint32_t mask, + uint32_t timeout) +{ + while ((SCBCTRL_REG(reg_offset) & mask) && --timeout) + ; + return (timeout == 0) ? -1 : 0; +} + #ifdef EXT_FLASH /* ========================================================================== * QSPI Flash Controller Implementation @@ -357,13 +535,14 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) * ========================================================================== */ /* Microsecond delay using RISC-V time CSR (1 MHz tick rate) */ +#ifndef WOLFBOOT_RISCV_MMODE static void udelay(uint32_t us) { uint64_t start = csr_read(time); while ((uint64_t)(csr_read(time) - start) < us) ; } - +#endif /* Forward declarations */ static int qspi_transfer_block(uint8_t read_mode, const uint8_t *cmd, uint32_t cmd_len, uint8_t *data, @@ -929,34 +1108,10 @@ int ext_flash_erase(uintptr_t address, int len) #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 -#if defined(DISK_SDCARD) || defined(DISK_EMMC) -/* ============================================================================ - * SDHCI Platform HAL Implementation - * ============================================================================ */ - -/* Register access functions for generic SDHCI driver */ -uint32_t sdhci_reg_read(uint32_t offset) -{ - return *((volatile uint32_t*)(EMMC_SD_BASE + offset)); -} - -void sdhci_reg_write(uint32_t offset, uint32_t val) -{ - *((volatile uint32_t*)(EMMC_SD_BASE + offset)) = val; -} -#endif /* DISK_SDCARD || DISK_EMMC */ - /* ============================================================================ * PLIC - Platform-Level Interrupt Controller (MPFS250-specific) * @@ -966,7 +1121,36 @@ void sdhci_reg_write(uint32_t offset, uint32_t val) * - plic_dispatch_irq(): Dispatch IRQ to appropriate handler * ============================================================================ */ -/* Get the PLIC context for the current hart in S-mode */ +/* Get the PLIC context for the current hart + * + * PLIC Context IDs for MPFS250: + * Hart 0 (E51): Context 0 = M-mode (E51 has no S-mode) + * Hart 1 (U54): Context 1 = M-mode, Context 2 = S-mode + * Hart 2 (U54): Context 3 = M-mode, Context 4 = S-mode + * Hart 3 (U54): Context 5 = M-mode, Context 6 = S-mode + * Hart 4 (U54): Context 7 = M-mode, Context 8 = S-mode + */ +#ifdef WOLFBOOT_RISCV_MMODE +/* M-mode: Read hart ID directly from CSR */ +static uint32_t get_hartid_mmode(void) +{ + uint32_t hartid; + __asm__ volatile("csrr %0, mhartid" : "=r"(hartid)); + return hartid; +} + +uint32_t plic_get_context(void) +{ + uint32_t hart_id = get_hartid_mmode(); + /* E51 (hart 0): M-mode only, context 0 + * U54 (harts 1-4): M-mode context = hart_id * 2 - 1 */ + if (hart_id == 0) { + return 0; /* E51 M-mode context */ + } + return (hart_id * 2) - 1; /* U54 M-mode context */ +} +#else +/* S-mode: Hart ID passed by boot stage, stored in tp register */ extern unsigned long get_boot_hartid(void); uint32_t plic_get_context(void) { @@ -974,10 +1158,6 @@ uint32_t plic_get_context(void) /* Get S-mode context for a given hart (1-4 for U54 cores) */ return hart_id * 2; } - -/* Forward declaration of SDHCI IRQ handler */ -#if defined(DISK_SDCARD) || defined(DISK_EMMC) -extern void sdhci_irq_handler(void); #endif /* Dispatch IRQ to appropriate platform handler */ @@ -1004,7 +1184,7 @@ void plic_dispatch_irq(uint32_t irq) void sdhci_platform_init(void) { /* Release MMC controller from reset */ - SYSREG_SOFT_RESET_CR &= ~SYSREG_SOFT_RESET_CR_MMC; + SYSREG_SOFT_RESET_CR &= ~MSS_PERIPH_MMC; } /* Platform interrupt setup - called from sdhci_init() */ @@ -1031,6 +1211,17 @@ void sdhci_platform_set_bus_mode(int is_emmc) (void)is_emmc; /* Nothing additional needed for MPFS - mode is set in generic driver */ } + +/* Register access functions for generic SDHCI driver */ +uint32_t sdhci_reg_read(uint32_t offset) +{ + return *((volatile uint32_t*)(EMMC_SD_BASE + offset)); +} + +void sdhci_reg_write(uint32_t offset, uint32_t val) +{ + *((volatile uint32_t*)(EMMC_SD_BASE + offset)) = val; +} #endif /* DISK_SDCARD || DISK_EMMC */ /* ============================================================================ @@ -1039,10 +1230,6 @@ void sdhci_platform_set_bus_mode(int is_emmc) #ifdef DEBUG_UART -#ifndef DEBUG_UART_BASE -#define DEBUG_UART_BASE MSS_UART1_LO_BASE -#endif - /* Configure baud divisors with fractional baud rate support. * * UART baud rate divisor formula: divisor = PCLK / (baudrate * 16) @@ -1089,6 +1276,44 @@ static void uart_config_clk(uint32_t baudrate) } } +/* New APB clock after MSS PLL lock + * This should match the configured MSS PLL output 2 (APB/AHB clock). + * From HSS: LIBERO_SETTING_MSS_APB_AHB_CLK = 150000000 (150 MHz) */ + +/* Reconfigure UART baud rate divisor for a specific clock */ +static void uart_config_clk_with_freq(uint32_t baudrate, uint64_t pclk) +{ + /* Scale up for precision: (PCLK * 128) / (baudrate * 16) */ + uint32_t div_x128 = (uint32_t)((8UL * pclk) / baudrate); + uint32_t div_x64 = div_x128 / 2u; + + /* Extract integer and fractional parts */ + uint32_t div_int = div_x64 / 64u; + uint32_t div_frac = div_x64 - (div_int * 64u); + + /* Apply rounding correction from x128 calculation */ + div_frac += (div_x128 - (div_int * 128u)) - (div_frac * 2u); + + if (div_int > (uint32_t)UINT16_MAX) + return; + + /* Write 16-bit divisor: set DLAB, write high/low bytes, clear DLAB */ + MMUART_LCR(DEBUG_UART_BASE) |= DLAB_MASK; + MMUART_DMR(DEBUG_UART_BASE) = (uint8_t)(div_int >> 8); + MMUART_DLR(DEBUG_UART_BASE) = (uint8_t)div_int; + MMUART_LCR(DEBUG_UART_BASE) &= ~DLAB_MASK; + + /* Enable fractional divisor if integer divisor > 1 */ + if (div_int > 1u) { + MMUART_MM0(DEBUG_UART_BASE) |= EFBR_MASK; + MMUART_DFR(DEBUG_UART_BASE) = (uint8_t)div_frac; + } + else { + MMUART_MM0(DEBUG_UART_BASE) &= ~EFBR_MASK; + } +} + + void uart_init(void) { /* Disable special modes: LIN, IrDA, SmartCard */ @@ -1144,3 +1369,193 @@ void uart_write(const char* buf, unsigned int sz) } } #endif /* DEBUG_UART */ + +#ifdef WOLFBOOT_RISCV_MMODE +/** + * uart_init_hart - Initialize UART for a specific hart + * + * Each U54 core uses its own MMUART: + * Hart 0 (E51) -> MMUART0 (already initialized by hal_init) + * Hart 1 (U54_1) -> MMUART1 + * Hart 2 (U54_2) -> MMUART2 + * Hart 3 (U54_3) -> MMUART3 + * Hart 4 (U54_4) -> MMUART4 + * + * @hartid: The hart ID (1-4 for U54 cores) + */ +void uart_init_hart(unsigned long hartid) +{ + unsigned long base; + + if (hartid == 0 || hartid > 4) { + return; /* Hart 0 uses main UART, invalid harts ignored */ + } + + base = UART_BASE_FOR_HART(hartid); + + /* Enable clock and release from soft reset for this UART + * The peripheral bit positions are: + * MMUART0 = bit 5, MMUART1 = bit 6, MMUART2 = bit 7, etc. + * MSS_PERIPH_MMUART0 = (1 << 5), so shift by hartid */ + SYSREG_SUBBLK_CLOCK_CR |= (MSS_PERIPH_MMUART0 << hartid); + + /* Memory barrier before modifying reset */ + __asm__ volatile("fence iorw, iorw" ::: "memory"); + + /* Release from soft reset */ + SYSREG_SOFT_RESET_CR &= ~(MSS_PERIPH_MMUART0 << hartid); + + /* Memory barrier */ + __asm__ volatile("fence iorw, iorw" ::: "memory"); + + /* Longer delay for clock to stabilize (critical for reliable UART) */ + udelay(100); + + /* Disable special modes: LIN, IrDA, SmartCard */ + MMUART_MM0(base) &= ~ELIN_MASK; + MMUART_MM1(base) &= ~EIRD_MASK; + MMUART_MM2(base) &= ~EERR_MASK; + + /* Disable interrupts */ + MMUART_IER(base) = 0u; + + /* Reset and configure FIFOs */ + MMUART_FCR(base) = 0u; + MMUART_FCR(base) |= CLEAR_RX_FIFO_MASK | CLEAR_TX_FIFO_MASK; + MMUART_FCR(base) |= RXRDY_TXRDYN_EN_MASK; + + /* Disable loopback */ + MMUART_MCR(base) &= ~(LOOP_MASK | RLOOP_MASK); + + /* Set LSB-first */ + MMUART_MM1(base) &= ~(E_MSB_TX_MASK | E_MSB_RX_MASK); + + /* Disable AFM, single wire mode */ + MMUART_MM2(base) &= ~(EAFM_MASK | ESWM_MASK); + + /* Disable TX time guard, RX timeout, fractional baud */ + MMUART_MM0(base) &= ~(ETTG_MASK | ERTO_MASK | EFBR_MASK); + + /* Clear timing registers */ + MMUART_GFR(base) = 0u; + MMUART_TTG(base) = 0u; + MMUART_RTO(base) = 0u; + + /* Configure baud rate (115200) + * Using EXACT same calculation as uart_config_clk for consistency */ + { + const uint64_t pclk = MSS_APB_AHB_CLK; + const uint32_t baudrate = 115200; + + /* Scale up for precision: (PCLK * 128) / (baudrate * 16) */ + uint32_t div_x128 = (uint32_t)((8UL * pclk) / baudrate); + uint32_t div_x64 = div_x128 / 2u; + + /* Extract integer and fractional parts */ + uint32_t div_int = div_x64 / 64u; + uint32_t div_frac = div_x64 - (div_int * 64u); + + /* Apply rounding correction from x128 calculation (same as uart_config_clk) */ + div_frac += (div_x128 - (div_int * 128u)) - (div_frac * 2u); + + /* Enable DLAB to access divisor registers */ + MMUART_LCR(base) |= DLAB_MASK; + + /* Write DMR before DLR (same order as uart_config_clk) */ + MMUART_DMR(base) = (uint8_t)(div_int >> 8); + MMUART_DLR(base) = (uint8_t)div_int; + + /* Clear DLAB */ + MMUART_LCR(base) &= ~DLAB_MASK; + + /* Configure fractional baud rate if needed */ + if (div_frac > 0u) { + MMUART_MM0(base) |= EFBR_MASK; + MMUART_DFR(base) = (uint8_t)div_frac; + } else { + MMUART_MM0(base) &= ~EFBR_MASK; + } + } + + /* Set line config: 8N1 */ + MMUART_LCR(base) = MSS_UART_DATA_8_BITS | + MSS_UART_NO_PARITY | + MSS_UART_ONE_STOP_BIT; + + /* Small delay after configuration */ + udelay(10); +} + +/** + * uart_write_hart - Write string to a specific hart's UART + * + * @hartid: The hart ID (0-4) + * @buf: Buffer to write + * @sz: Number of bytes to write + */ +void uart_write_hart(unsigned long hartid, const char* buf, unsigned int sz) +{ + unsigned long base; + uint32_t pos = 0; + + if (hartid > 4) { + return; + } + + base = UART_BASE_FOR_HART(hartid); + + while (sz-- > 0) { + char c = buf[pos++]; + if (c == '\n') { + while ((MMUART_LSR(base) & MSS_UART_THRE) == 0); + MMUART_THR(base) = '\r'; + } + while ((MMUART_LSR(base) & MSS_UART_THRE) == 0); + MMUART_THR(base) = c; + } +} + +/** + * uart_printf_hart - Simple printf to a specific hart's UART + * Only supports %d, %x, %s, %lu formats for minimal footprint + */ +static void uart_printf_hart(unsigned long hartid, const char* fmt, ...) +{ + char buf[128]; + int len = 0; + const char* p = fmt; + + /* Very simple printf implementation */ + while (*p && len < (int)sizeof(buf) - 1) { + if (*p == '%') { + p++; + if (*p == 'l' && *(p+1) == 'u') { + /* %lu - unsigned long */ + p += 2; + /* Skip for now - just print placeholder */ + buf[len++] = '['; + buf[len++] = 'N'; + buf[len++] = ']'; + } else if (*p == 'd') { + p++; + buf[len++] = '['; + buf[len++] = 'N'; + buf[len++] = ']'; + } else if (*p == 's') { + p++; + buf[len++] = '['; + buf[len++] = 'S'; + buf[len++] = ']'; + } else { + buf[len++] = '%'; + buf[len++] = *p++; + } + } else { + buf[len++] = *p++; + } + } + buf[len] = '\0'; + + uart_write_hart(hartid, buf, len); +} +#endif /* WOLFBOOT_RISCV_MMODE */ diff --git a/hal/mpfs250.h b/hal/mpfs250.h index ffaccd2e67..6e31488082 100644 --- a/hal/mpfs250.h +++ b/hal/mpfs250.h @@ -27,8 +27,17 @@ /* PolarFire SoC MPFS250T board specific configuration */ -/* APB/AHB Clock Frequency */ -#define MSS_APB_AHB_CLK 150000000 +/* APB/AHB Clock Frequency + * M-mode (out of reset): 40 MHz + * S-mode (after HSS): 150 MHz + */ +#ifndef MSS_APB_AHB_CLK + #ifdef WOLFBOOT_RISCV_MMODE + #define MSS_APB_AHB_CLK 40000000 + #else + #define MSS_APB_AHB_CLK 150000000 + #endif +#endif /* Hardware Base Address */ #define SYSREG_BASE 0x20002000 @@ -44,21 +53,23 @@ /* 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) -#define SYSREG_SOFT_RESET_CR_MMUART0 (1U << 5) -#define SYSREG_SOFT_RESET_CR_MMUART1 (1U << 6) -#define SYSREG_SOFT_RESET_CR_MMUART2 (1U << 7) -#define SYSREG_SOFT_RESET_CR_MMUART3 (1U << 8) -#define SYSREG_SOFT_RESET_CR_MMUART4 (1U << 9) -#define SYSREG_SOFT_RESET_CR_SPI0 (1U << 10) -#define SYSREG_SOFT_RESET_CR_SPI1 (1U << 11) -#define SYSREG_SOFT_RESET_CR_QSPI (1U << 19) -#define SYSREG_SOFT_RESET_CR_GPIO0 (1U << 20) -#define SYSREG_SOFT_RESET_CR_GPIO1 (1U << 21) -#define SYSREG_SOFT_RESET_CR_GPIO2 (1U << 22) -#define SYSREG_SOFT_RESET_CR_DDRC (1U << 23) -#define SYSREG_SOFT_RESET_CR_ATHENA (1U << 28) /* Crypto hardware accelerator */ + +/* MSS Peripheral control bits (shared by SUBBLK_CLOCK_CR and SOFT_RESET_CR) */ +#define MSS_PERIPH_ENVM (1U << 0) +#define MSS_PERIPH_MMC (1U << 3) +#define MSS_PERIPH_MMUART0 (1U << 5) +#define MSS_PERIPH_MMUART1 (1U << 6) +#define MSS_PERIPH_MMUART2 (1U << 7) +#define MSS_PERIPH_MMUART3 (1U << 8) +#define MSS_PERIPH_MMUART4 (1U << 9) +#define MSS_PERIPH_SPI0 (1U << 10) +#define MSS_PERIPH_SPI1 (1U << 11) +#define MSS_PERIPH_QSPI (1U << 19) +#define MSS_PERIPH_GPIO0 (1U << 20) +#define MSS_PERIPH_GPIO1 (1U << 21) +#define MSS_PERIPH_GPIO2 (1U << 22) +#define MSS_PERIPH_DDRC (1U << 23) +#define MSS_PERIPH_ATHENA (1U << 28) /* Crypto hardware accelerator */ /* UART */ @@ -74,6 +85,42 @@ #define MSS_UART3_HI_BASE 0x28104000UL #define MSS_UART4_HI_BASE 0x28106000UL +/* UART base address array for per-hart access (LO addresses for M-mode) */ +#ifndef __ASSEMBLER__ +static const unsigned long MSS_UART_BASE_ADDR[] = { + MSS_UART0_LO_BASE, /* Hart 0 (E51) -> MMUART0 */ + MSS_UART1_LO_BASE, /* Hart 1 (U54_1) -> MMUART1 */ + MSS_UART2_LO_BASE, /* Hart 2 (U54_2) -> MMUART2 */ + MSS_UART3_LO_BASE, /* Hart 3 (U54_3) -> MMUART3 */ + MSS_UART4_LO_BASE /* Hart 4 (U54_4) -> MMUART4 */ +}; +#define UART_BASE_FOR_HART(hart) (MSS_UART_BASE_ADDR[(hart) < 5 ? (hart) : 0]) +#endif /* __ASSEMBLER__ */ + +/* Debug UART port selection (0-4): M-mode defaults to UART0, S-mode to UART1 */ +#ifndef DEBUG_UART_PORT + #ifdef WOLFBOOT_RISCV_MMODE + #define DEBUG_UART_PORT 0 + #else + #define DEBUG_UART_PORT 1 + #endif +#endif + +/* Derive base address from port number */ +#if DEBUG_UART_PORT == 0 + #define DEBUG_UART_BASE MSS_UART0_LO_BASE +#elif DEBUG_UART_PORT == 1 + #define DEBUG_UART_BASE MSS_UART1_LO_BASE +#elif DEBUG_UART_PORT == 2 + #define DEBUG_UART_BASE MSS_UART2_LO_BASE +#elif DEBUG_UART_PORT == 3 + #define DEBUG_UART_BASE MSS_UART3_LO_BASE +#elif DEBUG_UART_PORT == 4 + #define DEBUG_UART_BASE MSS_UART4_LO_BASE +#else + #error "Invalid DEBUG_UART_PORT (must be 0-4)" +#endif + #define MMUART_RBR(base) *((volatile uint8_t*)((base)) + 0x00) /* Receiver buffer register */ #define MMUART_IER(base) *((volatile uint8_t*)((base)) + 0x04) /* Interrupt enable register */ #define MMUART_IIR(base) *((volatile uint8_t*)((base)) + 0x08) /* Interrupt ID register */ @@ -194,6 +241,139 @@ int mpfs_read_serial_number(uint8_t *serial); #define ATHENA_BASE (SYSREG_BASE + 0x125000) +/* ============================================================================ + * L2 Cache Controller (CACHE_CTRL @ 0x02010000) + * Controls cache ways, way masks, and scratchpad configuration + * ============================================================================ */ +#define L2_CACHE_BASE 0x02010000UL + +/* L2 Cache Control Registers */ +#define L2_CONFIG (*(volatile uint64_t*)(L2_CACHE_BASE + 0x000)) +#define L2_WAY_ENABLE (*(volatile uint64_t*)(L2_CACHE_BASE + 0x008)) +#define L2_FLUSH64 (*(volatile uint64_t*)(L2_CACHE_BASE + 0x200)) +#define L2_FLUSH32 (*(volatile uint32_t*)(L2_CACHE_BASE + 0x240)) + +/* Way Mask Registers - control which cache ways each master can access + * Value 0xFF = access to ways 0-7 (cache ways) + * Scratchpad ways (8-11) require explicit enabling */ +#define L2_WAY_MASK_DMA (*(volatile uint64_t*)(L2_CACHE_BASE + 0x800)) +#define L2_WAY_MASK_AXI4_PORT0 (*(volatile uint64_t*)(L2_CACHE_BASE + 0x808)) +#define L2_WAY_MASK_AXI4_PORT1 (*(volatile uint64_t*)(L2_CACHE_BASE + 0x810)) +#define L2_WAY_MASK_AXI4_PORT2 (*(volatile uint64_t*)(L2_CACHE_BASE + 0x818)) +#define L2_WAY_MASK_AXI4_PORT3 (*(volatile uint64_t*)(L2_CACHE_BASE + 0x820)) +#define L2_WAY_MASK_E51_DCACHE (*(volatile uint64_t*)(L2_CACHE_BASE + 0x828)) +#define L2_WAY_MASK_E51_ICACHE (*(volatile uint64_t*)(L2_CACHE_BASE + 0x830)) +#define L2_WAY_MASK_U54_1_DCACHE (*(volatile uint64_t*)(L2_CACHE_BASE + 0x838)) +#define L2_WAY_MASK_U54_1_ICACHE (*(volatile uint64_t*)(L2_CACHE_BASE + 0x840)) +#define L2_WAY_MASK_U54_2_DCACHE (*(volatile uint64_t*)(L2_CACHE_BASE + 0x848)) +#define L2_WAY_MASK_U54_2_ICACHE (*(volatile uint64_t*)(L2_CACHE_BASE + 0x850)) +#define L2_WAY_MASK_U54_3_DCACHE (*(volatile uint64_t*)(L2_CACHE_BASE + 0x858)) +#define L2_WAY_MASK_U54_3_ICACHE (*(volatile uint64_t*)(L2_CACHE_BASE + 0x860)) +#define L2_WAY_MASK_U54_4_DCACHE (*(volatile uint64_t*)(L2_CACHE_BASE + 0x868)) +#define L2_WAY_MASK_U54_4_ICACHE (*(volatile uint64_t*)(L2_CACHE_BASE + 0x870)) + +/* L2 Shutdown Control Register */ +#define SYSREG_L2_SHUTDOWN_CR (*(volatile uint32_t*)(SYSREG_BASE + 0x174)) + +/* L2 Cache/Scratchpad constants */ +#define L2_NUM_CACHE_WAYS 8 /* Ways 0-7 are cache */ +#define L2_NUM_SCRATCH_WAYS 4 /* Ways 8-11 are scratchpad */ +#define L2_WAY_BYTE_LENGTH 0x20000 /* 128KB per way */ +#define L2_SCRATCH_BASE 0x0A000000UL +#define L2_SCRATCH_SIZE (L2_NUM_SCRATCH_WAYS * L2_WAY_BYTE_LENGTH) /* 512KB */ + +/* Way enable values */ +#define L2_WAY_ENABLE_RESET 0x01 /* Only way 0 at reset */ +#define L2_WAY_ENABLE_ALL_CACHE 0xFF /* Ways 0-7 (all cache ways) */ +#define L2_WAY_ENABLE_WITH_SCRATCH 0x0FFF /* Ways 0-11 (cache + scratchpad) */ + +/* Way mask for cache-only access (no scratchpad) */ +#define L2_WAY_MASK_CACHE_ONLY 0xFF + + +/* ============================================================================ + * NWC (Northwest Corner) - Clock and System Configuration + * + * The NWC contains clocks, PLLs, SGMII, and DDR PHY configuration. + * These registers must be configured for proper system operation. + * ============================================================================ */ + +/* SCB Configuration Block (SCBCFG @ 0x37080000) */ +#define SCBCFG_BASE 0x37080000UL +#define SCBCFG_TIMER (*(volatile uint32_t*)(SCBCFG_BASE + 0x08)) + +/* MSS_SCB_ACCESS_CONFIG value for proper SCB access timing */ +#define MSS_SCB_ACCESS_CONFIG 0x0008A080UL + +/* DDR SGMII PHY Configuration (CFG_DDR_SGMII_PHY @ 0x20007000) */ +#define CFG_DDR_SGMII_PHY_BASE 0x20007000UL +#define DDRPHY_STARTUP (*(volatile uint32_t*)(CFG_DDR_SGMII_PHY_BASE + 0x008)) +#define DDRPHY_DYN_CNTL (*(volatile uint32_t*)(CFG_DDR_SGMII_PHY_BASE + 0xC1C)) + +/* DDR PHY startup configuration value (from working DDR demo) */ +#define DDRPHY_STARTUP_CONFIG 0x003F1F00UL +#define DDRPHY_DYN_CNTL_CONFIG 0x0000047FUL + +/* DFI APB interface control (enables DDR PHY APB access) */ +#define SYSREG_DFIAPB_CR (*(volatile uint32_t*)(SYSREG_BASE + 0x98)) + +/* CLINT - Core Local Interruptor (for timer and software interrupts) + * Note: CLINT macros are defined in hal/riscv.h, only define base if not present */ +#ifndef CLINT_BASE +#define CLINT_BASE 0x02000000UL +#endif + +/* RTC Clock Frequency (1 MHz after divisor) */ +#define RTC_CLOCK_FREQ 1000000UL + + +/* ============================================================================ + * Hart Local Storage (HLS) - Per-hart communication structure + * + * Used for inter-hart communication during boot. + * Located at top of each hart's stack (sp - 64). + * ============================================================================ */ +#define HLS_DEBUG_AREA_SIZE 64 + +#ifndef __ASSEMBLER__ +typedef struct { + volatile uint32_t in_wfi_indicator; /* 0x00: Hart status indicator */ + volatile uint32_t my_hart_id; /* 0x04: Hart ID */ + volatile uint32_t shared_mem_marker; /* 0x08: Init marker */ + volatile uint32_t shared_mem_status; /* 0x0C: Status */ + volatile uint64_t* shared_mem; /* 0x10: Shared memory pointer */ + volatile uint64_t reserved[2]; /* 0x18: Reserved/padding */ +} HLS_DATA; /* Size: 64 bytes (HLS_DEBUG_AREA_SIZE) */ +#endif /* __ASSEMBLER__ */ + +/* HLS status indicator values */ +#define HLS_MAIN_HART_STARTED 0x12344321UL +#define HLS_OTHER_HART_IN_WFI 0x12345678UL +#define HLS_OTHER_HART_PASSED_WFI 0x87654321UL +#define HLS_MAIN_HART_FIN_INIT 0x55555555UL + +/* Number of harts on MPFS */ +#define MPFS_NUM_HARTS 5 +#define MPFS_FIRST_HART 0 /* E51 is hart 0 */ +#define MPFS_FIRST_U54_HART 1 /* First U54 is hart 1 */ +#define MPFS_LAST_U54_HART 4 /* Last U54 is hart 4 */ + +/* Stack configuration per hart */ +#ifndef STACK_SIZE_PER_HART +#define STACK_SIZE_PER_HART 8192 +#endif + +/* Multi-hart function declarations */ +#ifndef __ASSEMBLER__ +#ifdef WOLFBOOT_RISCV_MMODE +int mpfs_wake_secondary_harts(void); +void secondary_hart_entry(unsigned long hartid, HLS_DATA* hls); +void uart_init_hart(unsigned long hartid); +void uart_write_hart(unsigned long hartid, const char* buf, unsigned int sz); +#endif +#endif /* __ASSEMBLER__ */ + + /* ============================================================================ * PLIC - Platform-Level Interrupt Controller (MPFS250-specific configuration) diff --git a/hal/riscv.h b/hal/riscv.h index 64c790c702..4f822986ca 100644 --- a/hal/riscv.h +++ b/hal/riscv.h @@ -22,7 +22,6 @@ #ifndef RISCV_H #define RISCV_H - /* ============================================================================ * RISC-V Privilege Mode Selection * @@ -84,6 +83,8 @@ #define MODE_PREFIX(__suffix) s##__suffix #endif + + /* ============================================================================ * CSR Access Macros * ============================================================================ */ @@ -145,10 +146,40 @@ static inline void riscv_icache_sync(void) /* ============================================================================ * Status Register Bits (mstatus/sstatus) * ============================================================================ */ -#define MSTATUS_MIE (1 << 3) /* Machine-mode global interrupt enable */ -#define MSTATUS_MPIE (1 << 7) /* Machine-mode previous interrupt enable */ -#define SSTATUS_SIE (1 << 1) /* Supervisor-mode global interrupt enable */ -#define SSTATUS_SPIE (1 << 5) /* Supervisor-mode previous interrupt enable */ +/* Privilege Levels */ +#define PRV_U 0 /* User mode */ +#define PRV_S 1 /* Supervisor mode */ +#define PRV_M 3 /* Machine mode */ + +/* MSTATUS Register Bits */ +#define MSTATUS_UIE (1UL << 0) /* User interrupt enable */ +#define MSTATUS_SIE (1UL << 1) /* Supervisor interrupt enable */ +#define MSTATUS_MIE (1UL << 3) /* Machine interrupt enable */ +#define MSTATUS_UPIE (1UL << 4) /* User previous interrupt enable */ +#define MSTATUS_SPIE (1UL << 5) /* Supervisor previous interrupt enable */ +#define MSTATUS_MPIE (1UL << 7) /* Machine previous interrupt enable */ +#define MSTATUS_SPP (1UL << 8) /* Supervisor previous privilege (1 bit) */ +#define MSTATUS_MPP_SHIFT 11 +#define MSTATUS_MPP_MASK (3UL << MSTATUS_MPP_SHIFT) +#define MSTATUS_MPP_M (PRV_M << MSTATUS_MPP_SHIFT) /* MPP = Machine */ +#define MSTATUS_MPP_S (PRV_S << MSTATUS_MPP_SHIFT) /* MPP = Supervisor */ +#define MSTATUS_MPP_U (PRV_U << MSTATUS_MPP_SHIFT) /* MPP = User */ +#define MSTATUS_FS_SHIFT 13 +#define MSTATUS_FS_MASK (3UL << MSTATUS_FS_SHIFT) +#define MSTATUS_FS_OFF (0UL << MSTATUS_FS_SHIFT) /* FPU off */ +#define MSTATUS_FS_INIT (1UL << MSTATUS_FS_SHIFT) /* FPU initial */ +#define MSTATUS_FS_CLEAN (2UL << MSTATUS_FS_SHIFT) /* FPU clean */ +#define MSTATUS_FS_DIRTY (3UL << MSTATUS_FS_SHIFT) /* FPU dirty */ +#define MSTATUS_MPRV (1UL << 17) /* Modify privilege */ +#define MSTATUS_SUM (1UL << 18) /* Supervisor user memory access */ +#define MSTATUS_MXR (1UL << 19) /* Make executable readable */ +#define MSTATUS_TVM (1UL << 20) /* Trap virtual memory */ +#define MSTATUS_TW (1UL << 21) /* Timeout wait */ +#define MSTATUS_TSR (1UL << 22) /* Trap SRET */ + +/* SSTATUS Register Bits (subset visible to S-mode) */ +#define SSTATUS_SIE (1UL << 1) /* Supervisor-mode global interrupt enable */ +#define SSTATUS_SPIE (1UL << 5) /* Supervisor-mode previous interrupt enable */ /* ============================================================================ * Machine Interrupt Enable (MIE) Register Bits @@ -157,6 +188,14 @@ static inline void riscv_icache_sync(void) #define MIE_MTIE (1 << IRQ_M_TIMER) /* Machine timer interrupt enable */ #define MIE_MEIE (1 << IRQ_M_EXT) /* Machine external interrupt enable */ +/* ============================================================================ + * Machine Interrupt Pending (MIP) Register Bits + * Same bit positions as MIE, used to check/set pending interrupts + * ============================================================================ */ +#define MIP_MSIP (1 << IRQ_M_SOFT) /* Machine software interrupt pending */ +#define MIP_MTIP (1 << IRQ_M_TIMER) /* Machine timer interrupt pending */ +#define MIP_MEIP (1 << IRQ_M_EXT) /* Machine external interrupt pending */ + /* ============================================================================ * Supervisor Interrupt Enable (SIE) Register Bits * ============================================================================ */ @@ -164,6 +203,13 @@ static inline void riscv_icache_sync(void) #define SIE_STIE (1 << IRQ_S_TIMER) /* Supervisor timer interrupt enable */ #define SIE_SEIE (1 << IRQ_S_EXT) /* Supervisor external interrupt enable */ +/* ============================================================================ + * Supervisor Interrupt Pending (SIP) Register Bits + * ============================================================================ */ +#define SIP_SSIP (1 << IRQ_S_SOFT) /* Supervisor software interrupt pending */ +#define SIP_STIP (1 << IRQ_S_TIMER) /* Supervisor timer interrupt pending */ +#define SIP_SEIP (1 << IRQ_S_EXT) /* Supervisor external interrupt pending */ + /* ============================================================================ * Exception Cause Register (MCAUSE/SCAUSE) Definitions * ============================================================================ */ @@ -290,4 +336,77 @@ extern void plic_dispatch_irq(uint32_t irq); #endif /* PLIC_BASE && !__ASSEMBLER__ */ +/* ============================================================================ + * CLINT - Core Local Interruptor (M-mode only) + * + * The CLINT provides software interrupts (IPI) and timer functionality + * for machine mode. Used for inter-hart communication and timer-based delays. + * + * CLINT Memory Map (standard offsets from CLINT_BASE): + * 0x0000-0x3FFF: MSIP registers (1 word per hart, software interrupt pending) + * 0x4000-0xBFF7: MTIMECMP registers (8 bytes per hart, timer compare) + * 0xBFF8-0xBFFF: MTIME register (8 bytes, global timer counter) + * ============================================================================ */ +#ifdef WOLFBOOT_RISCV_MMODE + +#ifndef CLINT_BASE +#define CLINT_BASE 0x02000000UL +#endif + +#define CLINT_MSIP_OFFSET 0x0000UL +#define CLINT_MTIMECMP_OFFSET 0x4000UL +#define CLINT_MTIME_OFFSET 0xBFF8UL + +#ifndef __ASSEMBLER__ + +/* MSIP (Machine Software Interrupt Pending) - one per hart */ +#define CLINT_MSIP(hart) \ + (*((volatile uint32_t*)(CLINT_BASE + CLINT_MSIP_OFFSET + ((hart) * 4)))) + +/* MTIMECMP - 64-bit timer compare value, one per hart */ +#define CLINT_MTIMECMP_LO(hart) \ + (*((volatile uint32_t*)(CLINT_BASE + CLINT_MTIMECMP_OFFSET + ((hart) * 8)))) +#define CLINT_MTIMECMP_HI(hart) \ + (*((volatile uint32_t*)(CLINT_BASE + CLINT_MTIMECMP_OFFSET + ((hart) * 8) + 4))) + +/* MTIME - 64-bit global timer counter (shared across all harts) */ +#define CLINT_MTIME_LO \ + (*((volatile uint32_t*)(CLINT_BASE + CLINT_MTIME_OFFSET))) +#define CLINT_MTIME_HI \ + (*((volatile uint32_t*)(CLINT_BASE + CLINT_MTIME_OFFSET + 4))) + +#endif /* !__ASSEMBLER__ */ + +#endif /* WOLFBOOT_RISCV_MMODE */ + +/* ============================================================================ + * L2 Cache Controller (M-mode only) + * + * The L2 cache controller manages the shared L2 cache and LIM (Loosely + * Integrated Memory) / Scratchpad configuration. + * ============================================================================ */ +#ifdef WOLFBOOT_RISCV_MMODE + +#ifndef L2_CACHE_CTRL_BASE +#define L2_CACHE_CTRL_BASE 0x02010000UL +#endif + +/* L2 Cache Controller register offsets */ +#define L2_CONFIG_OFFSET 0x000UL +#define L2_WAYENABLE_OFFSET 0x008UL +#define L2_FLUSH64_OFFSET 0x200UL + +#ifndef __ASSEMBLER__ + +#define L2_CONFIG_REG \ + (*((volatile uint32_t*)(L2_CACHE_CTRL_BASE + L2_CONFIG_OFFSET))) +#define L2_WAYENABLE_REG \ + (*((volatile uint32_t*)(L2_CACHE_CTRL_BASE + L2_WAYENABLE_OFFSET))) +#define L2_FLUSH64_REG \ + (*((volatile uint64_t*)(L2_CACHE_CTRL_BASE + L2_FLUSH64_OFFSET))) + +#endif /* !__ASSEMBLER__ */ + +#endif /* WOLFBOOT_RISCV_MMODE */ + #endif /* RISCV_H */ diff --git a/src/boot_riscv.c b/src/boot_riscv.c index e472626e33..22849e5005 100644 --- a/src/boot_riscv.c +++ b/src/boot_riscv.c @@ -239,8 +239,152 @@ int WEAKFUNCTION hal_dts_fixup(void* dts_addr) } #endif +#ifdef WOLFBOOT_RISCV_MMODE +/* ============================================================================ + * M-mode to S-mode Transition Support + * + * When booting Linux from M-mode, we need to: + * 1. Configure PMP to allow S-mode full memory access + * 2. Delegate appropriate traps to S-mode + * 3. Set up MSTATUS.MPP = S-mode + * 4. Use MRET to atomically switch to S-mode + * ============================================================================ */ + +/** + * setup_pmp_for_smode - Configure PMP for S-mode full access + * + * Sets up PMP entry 0 to allow S-mode full read/write/execute access + * to all of physical memory (0x0 to 0xFFFFFFFFFFFFFFFF for RV64). + */ +static void setup_pmp_for_smode(void) +{ + /* PMP configuration: + * - pmpcfg0[7:0] controls pmpaddr0 + * - A = 3 (NAPOT - naturally aligned power-of-2) + * - R=1, W=1, X=1 (full access) + * + * For NAPOT with all 1s in pmpaddr, we get full address space coverage. + * pmpaddr = (address >> 2) | ((size >> 3) - 1) + * For full 64-bit space: pmpaddr = 0x1FFFFFFFFFFFFFFF (all ones, shifted) + */ + unsigned long pmpaddr_val = -1UL; /* All 1s = cover entire address space */ + unsigned long pmpcfg_val; + + /* A=NAPOT(3), R=1, W=1, X=1 = 0b00011111 = 0x1F */ + pmpcfg_val = 0x1F; + + /* Write pmpaddr0 first, then pmpcfg0 */ + csr_write(pmpaddr0, pmpaddr_val); + csr_write(pmpcfg0, pmpcfg_val); + + /* Memory barrier */ + __asm__ volatile("sfence.vma" ::: "memory"); +} + +/** + * delegate_traps_to_smode - Delegate exceptions and interrupts to S-mode + * + * This allows S-mode (Linux) to handle its own traps without M-mode + * involvement for most cases. + */ +static void delegate_traps_to_smode(void) +{ + unsigned long medeleg_val; + unsigned long mideleg_val; + + /* Delegate these exceptions to S-mode: + * - Instruction misaligned (0) + * - Instruction access fault (1) + * - Illegal instruction (2) + * - Breakpoint (3) + * - Load address misaligned (4) + * - Load access fault (5) + * - Store address misaligned (6) + * - Store access fault (7) + * - Environment call from U-mode (8) + * - Environment call from S-mode (9) - NO, this goes to M-mode for SBI + * - Instruction page fault (12) + * - Load page fault (13) + * - Store page fault (15) + */ + medeleg_val = (1 << 0) | /* Instruction address misaligned */ + (1 << 1) | /* Instruction access fault */ + (1 << 2) | /* Illegal instruction */ + (1 << 3) | /* Breakpoint */ + (1 << 4) | /* Load address misaligned */ + (1 << 5) | /* Load access fault */ + (1 << 6) | /* Store address misaligned */ + (1 << 7) | /* Store access fault */ + (1 << 8) | /* Environment call from U-mode */ + (1 << 12) | /* Instruction page fault */ + (1 << 13) | /* Load page fault */ + (1 << 15); /* Store page fault */ + + /* Delegate these interrupts to S-mode: + * - S-mode software interrupt (1) + * - S-mode timer interrupt (5) + * - S-mode external interrupt (9) + */ + mideleg_val = (1 << IRQ_S_SOFT) | + (1 << IRQ_S_TIMER) | + (1 << IRQ_S_EXT); + + csr_write(medeleg, medeleg_val); + csr_write(mideleg, mideleg_val); +} + +/** + * enter_smode - Transition from M-mode to S-mode and jump to entry point + * + * @entry: Entry point address (will be loaded into MEPC) + * @hartid: Hart ID (passed to kernel in a0) + * @dtb: DTB address (passed to kernel in a1) + * + * This function never returns. It uses MRET to atomically: + * 1. Switch privilege level from M to S + * 2. Jump to the entry point + */ +static void __attribute__((noreturn)) enter_smode(unsigned long entry, + unsigned long hartid, + unsigned long dtb) +{ + unsigned long mstatus_val; + + /* Set up MEPC with entry point */ + csr_write(mepc, entry); + + /* Configure MSTATUS: + * - MPP = 01 (S-mode) - when MRET executes, we'll be in S-mode + * - MPIE = 1 - interrupts will be enabled after MRET + * - Clear MIE to disable interrupts during transition + */ + mstatus_val = csr_read(mstatus); + mstatus_val &= ~MSTATUS_MPP_MASK; /* Clear MPP field */ + mstatus_val |= MSTATUS_MPP_S; /* Set MPP = S-mode */ + mstatus_val |= MSTATUS_MPIE; /* Set MPIE */ + mstatus_val &= ~MSTATUS_MIE; /* Clear MIE */ + csr_write(mstatus, mstatus_val); + + /* Disable virtual memory (satp = 0) */ + csr_write(satp, 0); + + /* Execute MRET with a0=hartid, a1=dtb */ + __asm__ volatile( + "mv a0, %0\n" /* hartid in a0 */ + "mv a1, %1\n" /* dtb in a1 */ + "mret\n" + : : "r"(hartid), "r"(dtb) : "a0", "a1" + ); + + __builtin_unreachable(); +} +#endif /* WOLFBOOT_RISCV_MMODE */ + #if __riscv_xlen == 64 -/* Get the hartid saved by boot_riscv_start.S in the tp register */ +/* Get the hartid saved by boot_riscv_start.S in the tp register + * Note: In M-mode, hartid was read from mhartid CSR and stored in tp. + * In S-mode, hartid was passed by the boot stage in a0 and saved to tp. + */ unsigned long get_boot_hartid(void) { unsigned long hartid; @@ -260,6 +404,8 @@ void do_boot(const uint32_t *app_offset) #endif #ifdef MMU unsigned long dts_addr; +#else + unsigned long dts_addr = 0; #endif #ifdef MMU @@ -298,10 +444,35 @@ void do_boot(const uint32_t *app_offset) * enters the kernel. Secondary harts are started via SBI HSM extension. */ -#if __riscv_xlen == 64 -#ifdef MMU +#ifdef WOLFBOOT_RISCV_MMODE + /* + * M-mode to S-mode transition for booting Linux: + * 1. Set up PMP to allow S-mode full memory access + * 2. Delegate traps/interrupts to S-mode + * 3. Use MRET to switch to S-mode and jump to kernel + */ +#ifdef DEBUG_BOOT + wolfBoot_printf("Setting up M-mode to S-mode transition...\n"); + wolfBoot_printf(" PMP: Configuring for S-mode access\n"); +#endif + setup_pmp_for_smode(); + +#ifdef DEBUG_BOOT + wolfBoot_printf(" Delegating traps to S-mode\n"); +#endif + delegate_traps_to_smode(); + +#ifdef DEBUG_BOOT + wolfBoot_printf(" Entering S-mode: entry=0x%lx, hartid=%lu, dtb=0x%lx\n", + (unsigned long)app_offset, hartid, dts_addr); +#endif + /* This never returns */ + enter_smode((unsigned long)app_offset, hartid, dts_addr); + +#elif __riscv_xlen == 64 asm volatile( - #ifndef WOLFBOOT_RISCV_MMODE + #if defined(MMU) && !defined(WOLFBOOT_RISCV_MMODE) + /* S-mode boot (e.g., when running under HSS/OpenSBI) */ "csrw satp, zero\n" "sfence.vma\n" #endif @@ -310,14 +481,7 @@ void do_boot(const uint32_t *app_offset) "jr %2\n" : : "r"(hartid), "r"(dts_addr), "r"(app_offset) : "a0", "a1" ); -#else - asm volatile( - "mv a0, %0\n" - "mv a1, zero\n" - "jr %1\n" - : : "r"(hartid), "r"(app_offset) : "a0", "a1" - ); -#endif + #else /* RV32 */ /* RV32: typically bare-metal without Linux, simpler boot */ asm volatile("jr %0" : : "r"(app_offset)); @@ -360,8 +524,6 @@ void RAMFUNCTION arch_reboot(void) AON_WDOGKEY = AON_WDOGKEY_VALUE; AON_WDOGFEED = 1; - while(1) - ; wolfBoot_panic(); } @@ -373,8 +535,6 @@ void WEAKFUNCTION arch_reboot(void) SYSREG_MSS_RESET_CR = 0xDEAD; #endif - while(1) - ; wolfBoot_panic(); } diff --git a/src/boot_riscv_start.S b/src/boot_riscv_start.S index 78ba50d51e..48a0147c6f 100644 --- a/src/boot_riscv_start.S +++ b/src/boot_riscv_start.S @@ -25,17 +25,20 @@ #include "hal/mpfs250.h" #endif -/* MODE_PREFIX is now defined in hal/riscv.h */ /* ============================================================================ * RISC-V Boot Entry Point * ============================================================================ * - * For RV64 (typically running under SBI): + * For RV64 S-mode (typically running under SBI): * Entry conditions (passed by prior boot stage / SBI): * a0 = hart ID (hardware thread identifier) * a1 = pointer to device tree blob (DTB) in memory * + * For RV64 M-mode (direct boot from eNVM): + * Runs as first code after reset, reads hart ID from CSR + * Must initialize all hardware from scratch + * * For RV32 (typically bare metal): * Starts fresh, reads hart ID from CSR * @@ -44,16 +47,425 @@ .globl _reset _reset: #if __riscv_xlen == 64 - /* ---------- RV64 Boot Sequence ---------- */ #ifdef WOLFBOOT_RISCV_MMODE + /* ======================================================================== + * RV64 Machine Mode Boot Sequence + * + * This path is for direct boot from eNVM on PolarFire SoC. + * Entry point (_reset) is in eNVM at 0x20220100. + * + * Boot flow: + * 1. Run early init from eNVM (this .init section) + * 2. Copy main code from eNVM to L2 SRAM + * 3. Jump to L2 SRAM to continue execution + * 4. Initialize .data and .bss sections + * 5. Handle multi-hart (E51 runs, U54s park in WFI) + * ======================================================================== */ + +#if 0 //def TARGET_mpfs250 + /* + * Early UART initialization for M-mode boot on MPFS. + * This enables debug output during the boot sequence. + */ + li t0, 0x20000000 /* UART0 base */ + li t1, 0x20002000 /* SYSREG_BASE */ + /* Enable UART0 clock */ + lw t2, 0x84(t1) /* Read SUBBLK_CLOCK_CR */ + ori t2, t2, 0x20 /* Set bit 5 (MMUART0) */ + sw t2, 0x84(t1) + /* Release UART0 from reset */ + lw t2, 0x88(t1) /* Read SOFT_RESET_CR */ + li t3, 0xFFFFFFDF /* ~0x20 - Clear bit 5 */ + and t2, t2, t3 + sw t2, 0x88(t1) + /* Set up UART for 115200 @ 40MHz: divisor ~ 22 */ + lbu t2, 0x0c(t0) /* Read LCR */ + ori t2, t2, 0x80 /* Set DLAB */ + sb t2, 0x0c(t0) + li t2, 22 /* Divisor */ + sb t2, 0x00(t0) /* DLR */ + sb zero, 0x04(t0) /* DMR */ + lbu t2, 0x0c(t0) + andi t2, t2, 0x7f /* Clear DLAB */ + ori t2, t2, 0x03 /* 8N1 */ + sb t2, 0x0c(t0) +#endif + + /* + * Clear the Return Address Stack (RAS) by executing nested calls. + * This prevents stale return addresses from causing misprediction. + */ + call .L_clear_ras + + /* + * Read hart ID from CSR (we're the first code running on this core) + */ + csrr a0, mhartid + mv tp, a0 /* Save hart ID in tp for later use */ + + /* + * Disable and clear all interrupts during initialization + */ + li t0, MSTATUS_MIE + csrc mstatus, t0 /* Clear global interrupt enable */ + csrw mie, zero /* Disable all interrupt sources */ + csrw mip, zero /* Clear any pending interrupts */ + + /* + * Initialize M-mode CSRs + */ + csrw mscratch, zero + csrw mcause, zero + csrw mepc, zero + + /* + * Clear PMP configuration (allow all access initially) + */ + csrw pmpcfg0, zero + csrw pmpcfg2, zero + + /* + * Check if this is the boot hart (hart 0 = E51 on MPFS) + * Other harts (U54 cores) should wait in eNVM (this .init section) + */ + mv a0, tp /* Get saved hart ID */ + bnez a0, .L_secondary_hart_wait_envm + +#ifdef TARGET_mpfs250 + /* + * Configure L2 Cache for LIM/Scratchpad usage BEFORE copying to L2 + * Must enable ways AND disable shutdown before accessing L2 scratchpad. + * L2_WAY_ENABLE (0x02010008) = 0x0B (enable ways 0-3 and 8-11) + * SYSREG_L2_SHUTDOWN_CR (0x20002174) = 0 (disable shutdown) + */ + li t1, 0x02010000 /* L2_CTRL_BASE */ + li t2, 0x0B /* Ways 0-3 (cache) + 8-11 (scratchpad) */ + sd t2, 8(t1) /* L2_WAY_ENABLE */ + fence + + li t1, 0x20002000 /* SYSREG_BASE */ + sw zero, 0x174(t1) /* SYSREG_L2_SHUTDOWN_CR = 0 */ + fence +#endif + + /* + * Copy .text section from eNVM to L2 SRAM + * + * NOTE: We CANNOT use 'la' for L2 SRAM addresses because 'la' uses PC-relative + * addressing (auipc+addi) which fails when the offset exceeds 32-bit range. + * eNVM is at 0x20220000, L2 SRAM is at 0x0A000000 - the difference wraps around + * in 32-bit arithmetic causing incorrect addresses. + * + * Solution: Use absolute addressing for destination and size. + * - Source (_stored_text): Use 'la' - same address range as this code + * - Dest: Use 'li' with hardcoded L2 SRAM base address + * - Size: Use 'li' with value from linker (updated by linker script) + * + * The values are read from the data embedded right after this code. + */ + la t0, _stored_text /* Source: eNVM - la works, same address range */ + la t3, _copy_params /* Load parameters from eNVM */ + ld t1, 0(t3) /* t1 = dest addr (from linker) */ + ld t2, 8(t3) /* t2 = byte count (from linker) */ + add t2, t1, t2 /* t2 = end address */ + +.L_copy_text: + bgeu t1, t2, .L_copy_text_done /* if dest >= end, done */ + ld t3, 0(t0) + sd t3, 0(t1) + addi t0, t0, 8 + addi t1, t1, 8 + j .L_copy_text +.L_copy_text_done: + + /* Flush instruction cache to ensure copied code is visible */ + fence.i + + /* + * Jump to main initialization code in L2 SRAM + * From here on, we execute from L2 SRAM (faster than eNVM) + */ + la t0, .L_sram_entry + jr t0 + +/* + * Clear Return Address Stack helper (in .init section, runs from eNVM) + * Two-deep nested calls to flush RAS prediction state + */ +.L_clear_ras: + mv t0, ra + nop + call .L_clear_ras_inner + nop + mv ra, t0 + ret + +.L_clear_ras_inner: + nop + nop + ret + +/* + * Copy parameters: These values are used by the copy loop. + * They are stored as data in the .init section (eNVM) so they can be loaded + * with 'la' which works for addresses in the same region as the PC. + * The linker will fill these in at link time. + */ +.align 3 +_copy_params: + .dword _start_text_sram /* Destination address (L2 SRAM) */ + .dword _text_size /* Number of bytes to copy */ + +/* + * Secondary harts (U54 cores) initialization and WFI loop + * In M-mode on MPFS, the E51 (hart 0) is the boot hart. + * U54 cores (harts 1-4) wait here until explicitly woken by E51. + * + * The protocol uses Hart Local Storage (HLS) for communication: + * 1. Secondary hart signals HLS_OTHER_HART_IN_WFI + * 2. Main hart sends IPI via CLINT_MSIP + * 3. Secondary hart wakes, signals HLS_OTHER_HART_PASSED_WFI + * 4. Secondary hart jumps to secondary_hart_entry() + */ +.L_secondary_hart_wait_envm: + /* Disable all interrupts initially */ + li t0, MSTATUS_MIE + csrc mstatus, t0 + csrw mie, zero + csrw mip, zero + + /* Enable only machine software interrupt (for IPI wake) */ + li t0, MIP_MSIP + csrw mie, t0 + + /* Set up stack for this hart - each hart gets STACK_SIZE_PER_HART bytes + * Stack layout: hart0 stack, hart1 stack, ... hart4 stack + * We need to calculate: stack_base + (hartid + 1) * STACK_SIZE_PER_HART */ + csrr a0, mhartid + la t0, _secondary_hart_stack_base + li t1, STACK_SIZE_PER_HART + addi a1, a0, 1 /* hartid + 1 */ + mul t2, a1, t1 /* (hartid + 1) * STACK_SIZE_PER_HART */ + add sp, t0, t2 /* stack top for this hart */ + + /* Align stack to 16 bytes */ + li t0, -16 + and sp, sp, t0 + + /* Allocate HLS at top of stack */ + addi sp, sp, -64 /* HLS_DEBUG_AREA_SIZE = 64 */ + mv s11, sp /* Save HLS pointer in s11 (callee-saved) */ + + /* Clear HLS area */ + sd zero, 0(s11) + sd zero, 8(s11) + sd zero, 16(s11) + sd zero, 24(s11) + sd zero, 32(s11) + sd zero, 40(s11) + sd zero, 48(s11) + sd zero, 56(s11) + + /* Wait for main hart (E51) to signal it has started + * Main hart sets HLS_MAIN_HART_STARTED at its HLS location */ + li t3, 0x12344321 /* HLS_MAIN_HART_STARTED */ + la t1, _main_hart_hls /* E51's HLS location */ + +.L_wait_main_hart: + lwu t2, 0(t1) /* Read main hart's indicator */ + bne t3, t2, .L_wait_main_hart /* Wait until main hart started */ + + /* Signal that we're in WFI state */ + li t0, 0x12345678 /* HLS_OTHER_HART_IN_WFI */ + sw t0, 0(s11) /* Write to our HLS */ + + /* Memory barrier to ensure write is visible */ + fence iorw, iorw + +.L_secondary_wfi_loop: + wfi + + /* Check if it was a software interrupt (IPI) */ + csrr t0, mip + andi t0, t0, MIP_MSIP + beqz t0, .L_secondary_wfi_loop /* Keep waiting if not software interrupt */ + + /* Clear the software interrupt by writing 0 to CLINT_MSIP */ + csrr a0, mhartid + li t0, 0x02000000 /* CLINT_BASE */ + slli t1, a0, 2 /* hartid * 4 */ + add t0, t0, t1 /* CLINT_MSIP[hartid] */ + sw zero, 0(t0) /* Clear MSIP */ + + /* Signal that we've passed WFI */ + li t0, 0x87654321 /* HLS_OTHER_HART_PASSED_WFI */ + sw t0, 0(s11) + + /* Memory barrier */ + fence iorw, iorw + + /* Flush instruction cache */ + fence.i + + /* Jump to secondary hart C entry point + * a0 = hartid (already in a0 from mhartid read) + * s11 = HLS pointer + * Use indirect jump since secondary_hart_entry is in .text (L2 SRAM) + * and we're in .init (eNVM) - too far for direct jump */ + csrr a0, mhartid + mv a1, s11 /* Pass HLS pointer as second arg */ + la t0, secondary_hart_entry + jr t0 + +/* + * ============================================================================ + * Code below this point runs from L2 SRAM after being copied from eNVM + * ============================================================================ + */ +.section .text +.L_sram_entry: /* - * Machine Mode: Read hart ID from CSR since we're the first code - * running on this core. In Supervisor mode, the SBI passes it in a0. + * Setup trap handler (now pointing to SRAM location) */ - csrr a0, CSR_MHARTID + la t0, trap_vector_table + csrw mtvec, t0 + /* Ensure mtvec is updated before continuing */ +1: + csrr t1, mtvec + bne t0, t1, 1b + + /* + * Initialize all general-purpose registers to zero + * (tp/x4 already holds hart ID, skip it) + */ + li x1, 0 + li x2, 0 + li x3, 0 + /* x4 (tp) = hart ID, don't clear */ + li x5, 0 + li x6, 0 + li x7, 0 + li x8, 0 + li x9, 0 + li x10, 0 + li x11, 0 + li x12, 0 + li x13, 0 + li x14, 0 + li x15, 0 + li x16, 0 + li x17, 0 + li x18, 0 + li x19, 0 + li x20, 0 + li x21, 0 + li x22, 0 + li x23, 0 + li x24, 0 + li x25, 0 + li x26, 0 + li x27, 0 + li x28, 0 + li x29, 0 + li x30, 0 + li x31, 0 + + /* + * Verify XLEN matches compilation (64-bit) + * MSB of misa is 1 for RV32, 2 for RV64 + */ +.L_xlen_check: + csrr t0, misa + bltz t0, .L_xlen_ok /* RV64: MSB set means negative */ + j .L_xlen_check /* Loop if not RV64 */ +.L_xlen_ok: + + /* + * Initialize global pointer for efficient small data access + * The linker provides __global_pointer$ at .sdata + 0x800 + */ + .option push + .option norelax + la gp, __global_pointer$ + .option pop + + /* + * Initialize stack pointer from linker symbol + * Stack is at end of L2_SCRATCH, grows downward + */ + la sp, _end_stack + li t0, -16 + and sp, sp, t0 /* Ensure 16-byte alignment */ + mv s0, sp /* Set frame pointer */ + + /* + * Primary hart (E51, hart 0) continues with initialization + * (Secondary harts already parked in .init section) + */ + + /* + * Copy .data section from flash to RAM + */ + la t0, _stored_data /* Source: flash */ + la t1, _start_data /* Dest: RAM */ + la t2, _end_data + beq t0, t1, .L_data_copy_done /* Skip if already in place */ +.L_data_copy: + beq t1, t2, .L_data_copy_done + ld t3, 0(t0) + sd t3, 0(t1) + addi t0, t0, 8 + addi t1, t1, 8 + j .L_data_copy +.L_data_copy_done: + + /* + * Clear .bss section + */ + la t0, _start_bss + la t1, _end_bss +.L_bss_clear: + beq t0, t1, .L_bss_clear_done + sd zero, 0(t0) + addi t0, t0, 8 + j .L_bss_clear +.L_bss_clear_done: + +#ifndef TARGET_mpfs250 + /* + * Clear bus error unit accrued register on start-up + * This is cleared by the first hart only + * NOTE: Only for SiFive cores - MPFS does not have BEU at these addresses + */ + la a4,0x01700020UL + sb x0, 0(a4) + la a4,0x01701020UL + sb x0, 0(a4) + la a4,0x01702020UL + sb x0, 0(a4) + la a4,0x01703020UL + sb x0, 0(a4) + la a4,0x01704020UL + sb x0, 0(a4) #endif + /* + * Jump to C entry point + * a0 = hart ID + */ + mv a0, tp + j main + +#else + /* ======================================================================== + * RV64 Supervisor Mode Boot Sequence + * + * Entry conditions (passed by prior boot stage / SBI): + * a0 = hart ID (hardware thread identifier) + * a1 = pointer to device tree blob (DTB) in memory + * ======================================================================== */ + /* * Preserve boot parameters in callee-saved registers: * tp (x4) = hart ID - Used for multi-hart coordination. The RISC-V ABI @@ -74,52 +486,38 @@ _reset: /* * Configure trap/exception handler: * Load address of trap_vector_table into the trap-vector base-address - * register (mtvec in M-mode, stvec in S-mode). All synchronous exceptions + * register (stvec in S-mode). All synchronous exceptions * and interrupts will vector through this table. */ la t0, trap_vector_table - csrw MODE_PREFIX(tvec), t0 + csrw stvec, t0 /* * Disable all interrupt sources initially by clearing the - * interrupt-enable register (mie/sie). This prevents spurious + * interrupt-enable register (sie). This prevents spurious * interrupts during early initialization. */ - csrw MODE_PREFIX(ie), zero + csrw sie, zero /* * Enable interrupt sources: * - Software Interrupts (IPIs) for multi-hart boot coordination * - External Interrupts for PLIC-routed peripheral interrupts (e.g., MMC) - * - * M-mode: MSIE (Software) + MEIE (External) - * S-mode: SSIE (Software) + SEIE (External) */ -#ifndef WOLFBOOT_RISCV_MMODE li t0, (SIE_SSIE | SIE_SEIE) -#else - li t0, (MIE_MSIE | MIE_MEIE) -#endif - csrs MODE_PREFIX(ie), t0 + csrs sie, t0 /* - * Enable global interrupts by setting the SIE/MIE bit in sstatus/mstatus. + * Enable global interrupts by setting the SIE bit in sstatus. * Without this, the CPU will never take interrupts regardless of the - * per-source enables in sie/mie. - * - * M-mode: mstatus.MIE (bit 3) - * S-mode: sstatus.SIE (bit 1) + * per-source enables in sie. */ -#ifndef WOLFBOOT_RISCV_MMODE li t0, SSTATUS_SIE -#else - li t0, MSTATUS_MIE -#endif - csrs MODE_PREFIX(status), t0 + csrs sstatus, t0 /* * Initialize stack pointer: - * WOLFBOOT_STACK_TOP = 0x80000000 (M-mode) or 0x80200000 (S-mode) + * WOLFBOOT_STACK_TOP = 0x80200000 (S-mode, in DDR) * * The stack grows downward from this address. RISC-V calling convention * requires 16-byte stack alignment, enforced by AND with -16 (0xFFFF...FFF0). @@ -149,6 +547,8 @@ _reset: mv a0, tp j main +#endif /* WOLFBOOT_RISCV_MMODE */ + #else /* __riscv_xlen == 32 */ /* ---------- RV32 Boot Sequence ---------- */ diff --git a/src/sdhci.c b/src/sdhci.c index 907687be53..4a3c8ced53 100644 --- a/src/sdhci.c +++ b/src/sdhci.c @@ -47,6 +47,13 @@ static uint32_t g_rca = 0; /* SD Card Relative Address */ static volatile uint32_t g_mmc_irq_status = 0; static volatile int g_mmc_irq_pending = 0; +/* Microsecond delay using hardware timer */ +static void udelay(uint32_t us) +{ + uint64_t start = hal_get_timer_us(); + while ((hal_get_timer_us() - start) < us); +} + /* ============================================================================ * Register Access Helpers * ============================================================================ */ @@ -160,7 +167,7 @@ void sdhci_irq_handler(void) /* Signal that interrupt was handled */ g_mmc_irq_pending = 1; -#ifdef DEBUG_SDHCI +#ifdef DEBUG_SDHCI_IRQ wolfBoot_printf("sdhci_irq_handler: status=0x%08X, flags=0x%02X\n", status, g_mmc_irq_status); #endif @@ -513,7 +520,7 @@ static int sdhci_wait_busy(int check_dat0) } /* Reset data and command lines to recover from errors */ -static void sdhci_reset_lines(void) +static inline void sdhci_reset_lines(void) { sdhci_reg_or(SDHCI_SRS11, SDHCI_SRS11_RESET_DAT_CMD); while (SDHCI_REG(SDHCI_SRS11) & SDHCI_SRS11_RESET_DAT_CMD); @@ -930,7 +937,7 @@ static int emmc_send_op_cond(uint32_t ocr_arg, uint32_t *ocr_reg) } /* Small delay between retries */ - for (volatile int i = 0; i < 1000; i++); + udelay(10); } while (--timeout > 0); @@ -993,6 +1000,13 @@ static int emmc_card_full_init(void) int status; uint32_t ocr_reg; + /* Set power to 3.3v */ + status = sdhci_set_power(SDHCI_SRS10_BVS_3_3V); + if (status != 0) { + wolfBoot_printf("eMMC: Failed to set power\n"); + return status; + } + /* Send CMD0 (GO_IDLE) to reset eMMC */ status = sdhci_cmd(MMC_CMD0_GO_IDLE, 0, SDHCI_RESP_NONE); if (status != 0) { @@ -1001,7 +1015,7 @@ static int emmc_card_full_init(void) } /* Small delay after reset */ - for (volatile int i = 0; i < 10000; i++); + udelay(100); /* Send CMD1 with operating conditions (3.3V, sector mode) */ status = emmc_send_op_cond(MMC_DEVICE_3_3V_VOLT_SET, &ocr_reg); @@ -1429,17 +1443,6 @@ int sdhci_init(void) sdhci_set_clock(SDHCI_CLK_400KHZ); #ifdef DISK_EMMC - /* ========================================================================= - * eMMC Initialization Path - * ========================================================================= */ - - /* Set power to 3.3v */ - status = sdhci_set_power(SDHCI_SRS10_BVS_3_3V); - if (status != 0) { - wolfBoot_printf("eMMC: Failed to set power\n"); - return status; - } - /* Run full eMMC card initialization */ status = emmc_card_full_init(); if (status != 0) { @@ -1448,10 +1451,6 @@ int sdhci_init(void) } #else /* DISK_SDCARD */ - /* ========================================================================= - * SD Card Initialization Path - * ========================================================================= */ - /* Run full SD card initialization */ status = sdcard_card_full_init(); if (status != 0) { @@ -1466,6 +1465,16 @@ int sdhci_init(void) /* Set data timeout to 3000ms */ status = sdhci_set_timeout(SDHCI_DATA_TIMEOUT_US); } + + wolfBoot_printf("sdhci_init: %s status: %d\n", + #ifdef DISK_EMMC + "eMMC" + #else + "SD" + #endif + , status + ); + return status; } diff --git a/src/update_disk.c b/src/update_disk.c index cdf815cd91..7a82fa0800 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -243,7 +243,9 @@ void RAMFUNCTION wolfBoot_start(void) const uint8_t *hdr_ptr = NULL; #ifdef MMU uint8_t *dts_addr = NULL; + #ifdef WOLFBOOT_FDT uint32_t dts_size = 0; + #endif #endif char part_name[4] = {'P', ':', 'X', '\0'}; BENCHMARK_DECLARE(); diff --git a/src/vector_riscv.S b/src/vector_riscv.S index 70c83e930d..88b80c4beb 100644 --- a/src/vector_riscv.S +++ b/src/vector_riscv.S @@ -65,22 +65,15 @@ STORE x29, 29 * REGBYTES(sp) STORE x30, 30 * REGBYTES(sp) STORE x31, 31 * REGBYTES(sp) -#ifndef WOLFBOOT_RISCV_MMODE - csrr a0, scause - csrr a1, sepc - csrr a2, stval -#else - csrr a0, mcause - csrr a1, mepc - csrr a2, mtval -#endif + + csrr a0, MODE_PREFIX(cause) + csrr a1, MODE_PREFIX(epc) + csrr a2, MODE_PREFIX(tval) + mv a3, sp jal handle_trap -#ifndef WOLFBOOT_RISCV_MMODE - csrw sepc, a0 -#else - csrw mepc, a0 -#endif + csrw MODE_PREFIX(epc), a0 + .endm .macro trap_exit @@ -116,11 +109,9 @@ LOAD x31, 31 * REGBYTES(sp) LOAD x2, 2 * REGBYTES(sp) addi sp, sp, 32 * REGBYTES -#ifndef WOLFBOOT_RISCV_MMODE - sret -#else - mret -#endif + + MODE_PREFIX(ret) + .endm #else /* __riscv_xlen == 32 */ diff --git a/test-app/RISCV64-mpfs250.ld b/test-app/RISCV64-mpfs250.ld index e7b367cab9..c298ce4329 100644 --- a/test-app/RISCV64-mpfs250.ld +++ b/test-app/RISCV64-mpfs250.ld @@ -10,6 +10,9 @@ OUTPUT_ARCH( "riscv" ) ENTRY( _reset ) +/* Provide M-mode symbols as dummy values for test-app (not used at runtime) */ +PROVIDE(_main_hart_hls = 0); + /* Memory areas */ MEMORY {