diff --git a/arch.mk b/arch.mk index a3dbd857c6..d4467bf886 100644 --- a/arch.mk +++ b/arch.mk @@ -542,7 +542,7 @@ ifeq ($(ARCH),RENESAS_RX) endif -## RISCV +## RISCV (32-bit) ifeq ($(ARCH),RISCV) CROSS_COMPILE?=riscv32-unknown-elf- ARCH_FLAGS=-march=rv32imac -mabi=ilp32 -mcmodel=medany @@ -555,11 +555,12 @@ ifeq ($(ARCH),RISCV) CFLAGS +=-ffunction-sections -fdata-sections LDFLAGS+=-Wl,--gc-sections - OBJS+=src/boot_riscv.o src/vector_riscv.o + # 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 ARCH_FLASH_OFFSET=0x20010000 endif -## RISCV64 +## RISCV64 (64-bit) ifeq ($(ARCH),RISCV64) CROSS_COMPILE?=riscv64-unknown-elf- CFLAGS+=-DMMU -DWOLFBOOT_DUALBOOT @@ -568,7 +569,7 @@ ifeq ($(ARCH),RISCV64) OBJS += src/gpt.o OBJS += src/disk.o ARCH_FLAGS=-march=rv64imafd -mabi=lp64d -mcmodel=medany - CFLAGS+=-fno-builtin-printf -DUSE_M_TIME -g -nostartfiles -DARCH_RISCV64 + CFLAGS+=-fno-builtin-printf -DUSE_M_TIME -g -nostartfiles -DARCH_RISCV -DARCH_RISCV64 CFLAGS+=$(ARCH_FLAGS) LDFLAGS+=$(ARCH_FLAGS) @@ -576,7 +577,8 @@ ifeq ($(ARCH),RISCV64) CFLAGS +=-ffunction-sections -fdata-sections LDFLAGS+=-Wl,--gc-sections - OBJS+=src/boot_riscv64_start.o src/boot_riscv64.o src/vector_riscv64.o + # 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 diff --git a/config/examples/polarfire_mpfs250.config b/config/examples/polarfire_mpfs250.config index 7a709403a6..36e3411fea 100644 --- a/config/examples/polarfire_mpfs250.config +++ b/config/examples/polarfire_mpfs250.config @@ -25,9 +25,6 @@ WOLFTPM?=0 ELF?=1 #DEBUG_ELF?=1 -# Optionally allow downgrade to older valid version in update partition -ALLOW_DOWNGRADE?=0 - # Use RISC-V assembly version of ECDSA and SHA NO_ASM?=0 NO_ARM_ASM?=0 @@ -48,12 +45,16 @@ WOLFBOOT_LOAD_ADDRESS?=0x8E000000 WOLFBOOT_NO_PARTITIONS=1 CFLAGS_EXTRA+=-DBOOT_PART_A=1 CFLAGS_EXTRA+=-DBOOT_PART_B=2 -# Speed up disk partition read (1MB chunks) -CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x100000 +# Speed up disk partition read (512KB chunks) +CFLAGS_EXTRA+=-DDISK_BLOCK_SIZE=0x80000 # DTS (Device Tree) WOLFBOOT_LOAD_DTS_ADDRESS?=0x8A000000 +# Optional Encryption +#ENCRYPT=1 +#ENCRYPT_WITH_AES256=1 + # Optional EMMC_SD debugging logs #CFLAGS_EXTRA+=-DDEBUG_MMC # Optional disk debugging logs diff --git a/docs/Targets.md b/docs/Targets.md index 87dbd47215..e3c5a70cb1 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -883,6 +883,7 @@ n n 4 + t 1 4 @@ -920,6 +921,9 @@ Device Start End Sectors Size Type 2) Build, Sign and copy images ```sh +# Copy wolfBoot to "BIOS" partition +sudo dd if=wolfboot.bin of=/dev/sdc1 bs=512 && sudo cmp wolfboot.bin /dev/sdc1 + # make test-app make test-app/image.elf @@ -930,8 +934,22 @@ sudo dd if=test-app/image_v1_signed.bin of=/dev/sdc2 bs=512 && sudo cmp test-app 4) Insert SDCARD into PolarFire and let HSS start wolfBoot. You may need to use `boot sdcard` or configure/build HSS to disable MMC / enable SDCARD. +### PolarFire Building Hart Software Services (HSS) + +The Hart Software Services (HSS) is the zero-stage bootloader for the PolarFire SoC. It runs on the E51 monitor core and is responsible for system initialization, hardware configuration, and booting the U54 application cores. The HSS provides essential services including watchdog management, inter-processor communication (IPC), and loading payloads from various boot sources (eMMC, SD card, or SPI flash). + +```sh +git clone https://github.com/polarfire-soc/hart-software-services.git +cd hart-software-services +make clean +make BOARD=mpfs-video-kit +make BOARD=mpfs-video-kit program +``` + ### PolarFire Building Yocto-SDK Linux +The Yocto Project provides a customizable embedded Linux distribution for PolarFire SoC. Microchip maintains the `meta-mchp` layer with board support packages (BSP), drivers, and example applications for their devices. The build system uses OpenEmbedded and produces bootable images that can be flashed to eMMC or SD card. + See: * https://github.com/linux4microchip/meta-mchp/blob/scarthgap/meta-mchp-common/README.md * https://github.com/linux4microchip/meta-mchp/blob/scarthgap/meta-mchp-polarfire-soc/README.md @@ -958,6 +976,9 @@ Build images are output to: `./tmp-glibc/deploy/images/mpfs-video-kit/` #### Custom FIT image, signing and coping to SDCard ```sh +# Copy wolfBoot to "BIOS" partition +sudo dd if=wolfboot.bin of=/dev/sdc1 bs=512 && sudo cmp wolfboot.bin /dev/sdc1 + # Extract GZIP compressed linux kernel to wolfboot root gzip -cdvk ../yocto-dev-polarfire/build/tmp-glibc/work/mpfs_video_kit-oe-linux/linux-mchp/6.12.22+git/build/linux.bin > kernel.bin @@ -1003,9 +1024,51 @@ sudo dd if=fitImage_v1_signed.bin of=/dev/sdc2 bs=512 status=progress && sudo cm sudo dd if=fitImage_v1_signed.bin of=/dev/sdc3 bs=512 status=progress && sudo cmp fitImage_v1_signed.bin /dev/sdc3 # Copy root file system -sudo dd if=../yocto-dev-polarfire/build/tmp-glibc/deploy/images/mpfs-video-kit/mchp-base-image-sdk-mpfs-video-kit.rootfs.ext4 of=/dev/sdb4 bs=512 status=progress +sudo dd if=../yocto-dev-polarfire/build/tmp-glibc/deploy/images/mpfs-video-kit/mchp-base-image-sdk-mpfs-video-kit.rootfs.ext4 of=/dev/sdc4 bs=4M status=progress +``` + +### PolarFire SoC Encryption + +PolarFire SoC uses MMU mode with disk-based updates. The encryption key is stored in RAM rather than flash. + +Enable encryption in your configuration with `ENCRYPT=1` and one of: `ENCRYPT_WITH_AES256=1`, `ENCRYPT_WITH_AES128=1`, or `ENCRYPT_WITH_CHACHA=1`. + +| Algorithm | Key Size | Nonce/IV Size | +|-----------|----------|---------------| +| ChaCha20 | 32 bytes | 12 bytes | +| AES-128 | 16 bytes | 16 bytes | +| AES-256 | 32 bytes | 16 bytes | + +The `libwolfboot` API provides the following functions for managing the encryption key: + +```c +int wolfBoot_set_encrypt_key(const uint8_t *key, const uint8_t *nonce); +int wolfBoot_get_encrypt_key(uint8_t *key, uint8_t *nonce); +int wolfBoot_erase_encrypt_key(void); /* called automatically by wolfBoot_success() */ +``` + +To sign and encrypt an image, create a key file with the concatenated key and nonce, then use the sign tool: + +```sh +# Create key file (32-byte key + 16-byte IV for AES-256) +echo -n "0123456789abcdef0123456789abcdef0123456789abcdef" > enc_key.der + +# Sign and encrypt +./tools/keytools/sign --ecc384 --sha384 --aes256 --encrypt enc_key.der \ + fitImage wolfboot_signing_private_key.der 1 ``` +In your application, set the encryption key before triggering an update: + +```c +wolfBoot_set_encrypt_key(enc_key, enc_iv); +wolfBoot_update_trigger(); +``` + +During boot, wolfBoot decrypts the image headers from disk to select the best candidate, loads and decrypts the full image to RAM, then verifies integrity and authenticity before booting. On successful boot, `wolfBoot_success()` clears the key from RAM. + +See the [Encrypted Partitions](encrypted_partitions.md) documentation for additional details. + ### PolarFire Soc Debugging Start GDB server: @@ -1034,7 +1097,7 @@ set architecture riscv:rv64 ### PolarFire Example Boot Output ``` -wolfBoot Version: 2.7.0 (Dec 22 2025 14:14:37) +wolfBoot Version: 2.7.0 (Dec 29 2025 11:34:01) Reading MBR... Found GPT PTE at sector 1 Found valid boot signature in MBR @@ -1054,19 +1117,18 @@ Checking secondary OS image in 0,2... Versions, A:1 B:1 Load address 0x8E000000 Attempting boot from P:A -Boot partition: 0x801FFDA0 (sz 19767000, ver 0x1, type 0x601) -Loading image from disk...done. -Boot partition: 0x8E000000 (sz 19767000, ver 0x1, type 0x601) -Checking image integrity...done. -Verifying image signature...done. +Boot partition: 0x801FFD80 (sz 19767004, ver 0x1, type 0x601) +Loading image from disk...done. (846 ms) +Boot partition: 0x8E000000 (sz 19767004, ver 0x1, type 0x601) +Checking image integrity...done. (1507 ms) +Verifying image signature...done. (68 ms) Firmware Valid. -Flattened uImage Tree: Version 17, Size 19767000 +Flattened uImage Tree: Version 17, Size 19767004 Loading Image kernel-1: 0x8E0002C8 -> 0x80200000 (19745280 bytes) Image kernel-1: 0x80200000 (19745280 bytes) Loading Image fdt-1: 0x8F2D4DCC -> 0x8A000000 (19897 bytes) Image fdt-1: 0x8A000000 (19897 bytes) Loading DTS: 0x8A000000 -> 0x8A000000 (19897 bytes) -Loading elf at 0x80200000 Invalid elf, falling back to raw binary Booting at 80200000 [ 0.000000] Linux version 6.12.22-linux4microchip+fpga-2025.07-g032a7095303a (oe-user@oe-host) (riscv64-oe-linux-gcc (GCC) 13.3.0, GNU ld (GNU Binutils) 2.42.0.20240723) #1 SMP Tue Jul 22 10:04:20 UTC 2025 @@ -1078,15 +1140,63 @@ Booting at 80200000 [ 0.000000] SBI RFENCE extension detected [ 0.000000] SBI SRST extension detected [ 0.000000] earlycon: ns16550a0 at MMIO32 0x0000000020100000 (options '115200n8') +[ 0.000000] printk: legacy bootconsole [ns16550a0] enabled ... ``` +### PolarFire Benchmarks + +RISC-V 64-bit U54 (RV64GC1) 625 MHz + +``` +------------------------------------------------------------------------------ + wolfSSL version 5.8.4 +------------------------------------------------------------------------------ +Math: Multi-Precision: Wolf(SP) word-size=64 bits=3072 sp_int.c + Assembly Speedups: RISCVASM ALIGN +wolfCrypt Benchmark (block bytes 1048576, min 1.0 sec each) +RNG 5 MiB took 1.232 seconds, 4.058 MiB/s +AES-128-CBC-enc 10 MiB took 1.182 seconds, 8.457 MiB/s +AES-128-CBC-dec 10 MiB took 1.166 seconds, 8.573 MiB/s +AES-192-CBC-enc 10 MiB took 1.378 seconds, 7.257 MiB/s +AES-192-CBC-dec 10 MiB took 1.362 seconds, 7.344 MiB/s +AES-256-CBC-enc 10 MiB took 1.569 seconds, 6.373 MiB/s +AES-256-CBC-dec 10 MiB took 1.556 seconds, 6.426 MiB/s +AES-128-GCM-enc 10 MiB took 1.956 seconds, 5.113 MiB/s +AES-128-GCM-dec 10 MiB took 1.955 seconds, 5.115 MiB/s +AES-192-GCM-enc 5 MiB took 1.075 seconds, 4.650 MiB/s +AES-192-GCM-dec 5 MiB took 1.074 seconds, 4.654 MiB/s +AES-256-GCM-enc 5 MiB took 1.172 seconds, 4.268 MiB/s +AES-256-GCM-dec 5 MiB took 1.170 seconds, 4.275 MiB/s +GMAC Table 4-bit 15 MiB took 1.133 seconds, 13.245 MiB/s +CHACHA 20 MiB took 1.107 seconds, 18.064 MiB/s +CHA-POLY 15 MiB took 1.060 seconds, 14.152 MiB/s +POLY1305 75 MiB took 1.044 seconds, 71.812 MiB/s +SHA 20 MiB took 1.139 seconds, 17.561 MiB/s +SHA-256 10 MiB took 1.069 seconds, 9.350 MiB/s +SHA-384 15 MiB took 1.072 seconds, 13.994 MiB/s +SHA-512 15 MiB took 1.072 seconds, 13.990 MiB/s +SHA-512/224 15 MiB took 1.068 seconds, 14.041 MiB/s +SHA-512/256 15 MiB took 1.066 seconds, 14.070 MiB/s +HMAC-SHA 20 MiB took 1.140 seconds, 17.542 MiB/s +HMAC-SHA256 10 MiB took 1.068 seconds, 9.366 MiB/s +HMAC-SHA384 15 MiB took 1.066 seconds, 14.076 MiB/s +HMAC-SHA512 15 MiB took 1.066 seconds, 14.077 MiB/s +PBKDF2 1 KiB took 1.024 seconds, 1.129 KiB/s +RSA 2048 public 800 ops took 1.142 sec, avg 1.427 ms, 700.575 ops/sec +RSA 2048 private 100 ops took 8.450 sec, avg 84.504 ms, 11.834 ops/sec +DH 2048 key gen 60 ops took 1.010 sec, avg 16.841 ms, 59.379 ops/sec +DH 2048 agree 100 ops took 3.421 sec, avg 34.211 ms, 29.231 ops/sec +ECC [ SECP256R1] 256 key gen 100 ops took 1.304 sec, avg 13.039 ms, 76.691 ops/sec +ECDHE [ SECP256R1] 256 agree 100 ops took 1.299 sec, avg 12.992 ms, 76.970 ops/sec +ECDSA [ SECP256R1] 256 sign 100 ops took 1.338 sec, avg 13.383 ms, 74.723 ops/sec +ECDSA [ SECP256R1] 256 verify 200 ops took 1.846 sec, avg 9.231 ms, 108.333 ops/sec +Benchmark complete +``` + ### PolarFire TODO * Add eMMC/SD features: - - Improve mmc_delay and timeout handling - - DMA read support - - Write support - eMMC support (not just SD) * Add support for reading serial number and modifying ethernet MAC in device tree * Add support for QSPI NOR flash diff --git a/hal/mpfs.dtb b/hal/mpfs.dtb deleted file mode 100644 index dc4e8bd5c8..0000000000 Binary files a/hal/mpfs.dtb and /dev/null differ diff --git a/hal/mpfs250.c b/hal/mpfs250.c index ef80fee784..65e7176c11 100644 --- a/hal/mpfs250.c +++ b/hal/mpfs250.c @@ -41,25 +41,70 @@ #include "printf.h" #include "loader.h" +#include "hal.h" #include "disk.h" #include "gpt.h" +#include "fdt.h" + +#ifdef DISK_TEST +static int disk_test(int drv); +#endif void hal_init(void) { wolfBoot_printf("wolfBoot Version: %s (%s %s)\n", LIBWOLFBOOT_VERSION_STRING,__DATE__, __TIME__); - } +/* Linux kernel command line arguments */ +#ifndef LINUX_BOOTARGS +#ifndef LINUX_BOOTARGS_ROOT +#define LINUX_BOOTARGS_ROOT "/dev/mmcblk0p4" +#endif + +#define LINUX_BOOTARGS \ + "earlycon root="LINUX_BOOTARGS_ROOT" rootwait uio_pdrv_genirq.of_id=generic-uio" +#endif + int hal_dts_fixup(void* dts_addr) { - /* TODO: Consider FDT fixups: + int off, ret; + struct fdt_header *fdt = (struct fdt_header *)dts_addr; + + /* Verify FDT header */ + ret = fdt_check_header(dts_addr); + if (ret != 0) { + wolfBoot_printf("FDT: Invalid header! %d\n", ret); + return ret; + } + + wolfBoot_printf("FDT: Version %d, Size %d\n", + fdt_version(fdt), fdt_totalsize(fdt)); + + /* Expand total size to allow adding/modifying properties */ + fdt_set_totalsize(fdt, fdt_totalsize(fdt) + 512); + + /* Find /chosen node */ + off = fdt_find_node_offset(fdt, -1, "chosen"); + if (off < 0) { + /* Create /chosen node if it doesn't exist */ + off = fdt_add_subnode(fdt, 0, "chosen"); + } + + if (off >= 0) { + /* Set bootargs property */ + fdt_fixup_str(fdt, off, "chosen", "bootargs", LINUX_BOOTARGS); + } + + /* TODO: Consider additional FDT fixups: * ethernet0: local-mac-address {0x00, 0x04, 0xA3, SERIAL2, SERIAL1, SERIAL0} */ - (void)dts_addr; + return 0; } void hal_prepare_boot(void) { + /* reset the eMMC/SD card? */ + } @@ -134,15 +179,222 @@ void* hal_get_dts_address(void) } #endif - static uint32_t g_sector_count; static uint32_t g_sector_size; static uint32_t g_bus_width = 1; static uint32_t g_rca = 0; /* SD Card Relative Address */ -#ifndef DEFAULT_DELAY -#define DEFAULT_DELAY 0xFFFF +/* MMC Interrupt state - volatile for interrupt handler access */ +static volatile uint32_t g_mmc_irq_status = 0; +static volatile int g_mmc_irq_pending = 0; + +/* ========================================================================== + * PHY Register Access Functions + * ========================================================================== */ + +/* Write to SD/eMMC PHY register via HRS04 */ +static void mmc_phy_write(uint8_t phy_addr, uint8_t delay_val) +{ + uint32_t phycfg; + +#ifdef DEBUG_MMC + wolfBoot_printf("mmc_phy_write: phyaddr: 0x%08x, delay_value: %d\n", + phy_addr, delay_val); +#endif + + /* Wait for ACK to clear */ + while ((EMMC_SD_HRS04 & EMMC_SD_HRS04_UIS_ACK) == 0); + + /* Set address and delay value */ + phycfg = ((uint32_t)phy_addr & EMMC_SD_HRS04_UIS_ADDR_MASK) | + ((uint32_t)delay_val << EMMC_SD_HRS04_UIS_WDATA_SHIFT); + EMMC_SD_HRS04 = phycfg; + + /* Send write request */ + EMMC_SD_HRS04 = phycfg | EMMC_SD_HRS04_UIS_WR; + /* Wait for ACK */ + while ((EMMC_SD_HRS04 & EMMC_SD_HRS04_UIS_ACK) == 0); + + /* Clear write request */ + EMMC_SD_HRS04 = phycfg; + EMMC_SD_HRS04 = 0; +} + +/* ============================================================================ + * PLIC - Platform-Level Interrupt Controller Functions + * ============================================================================ */ + +/* Get the PLIC context for the current hart in S-mode */ +extern unsigned long get_boot_hartid(void); +static inline uint32_t plic_get_context(void) +{ + uint32_t hart_id = get_boot_hartid(); + return PLIC_HART_TO_SMODE_CTX(hart_id); +} + +/* Set priority for an interrupt source */ +void plic_set_priority(uint32_t irq, uint32_t priority) +{ + if (irq > 0 && irq < PLIC_NUM_SOURCES && priority <= PLIC_PRIORITY_MAX) { + PLIC_PRIORITY(irq) = priority; + } +} + +/* Enable an interrupt for the current hart's context */ +void plic_enable_interrupt(uint32_t irq) +{ + uint32_t ctx = plic_get_context(); + if (irq > 0 && irq < PLIC_NUM_SOURCES) { + PLIC_ENABLE(ctx, irq) |= PLIC_ENABLE_BIT(irq); + } +} + +/* Disable an interrupt for the current hart's context */ +void plic_disable_interrupt(uint32_t irq) +{ + uint32_t ctx = plic_get_context(); + if (irq > 0 && irq < PLIC_NUM_SOURCES) { + PLIC_ENABLE(ctx, irq) &= ~PLIC_ENABLE_BIT(irq); + } +} + +/* Set the priority threshold for the current hart's context */ +void plic_set_threshold(uint32_t threshold) +{ + uint32_t ctx = plic_get_context(); + if (threshold <= PLIC_PRIORITY_MAX) { + PLIC_THRESHOLD(ctx) = threshold; + } +} + +/* Claim the highest priority pending interrupt (returns IRQ number, 0 if none) */ +uint32_t plic_claim(void) +{ + uint32_t ctx = plic_get_context(); + return PLIC_CLAIM(ctx); +} + +/* Signal completion of interrupt handling */ +void plic_complete(uint32_t irq) +{ + uint32_t ctx = plic_get_context(); + PLIC_COMPLETE(ctx) = irq; +} + +/* Initialize PLIC for MMC interrupt handling */ +void plic_init_mmc(void) +{ + /* Set priority for MMC main interrupt */ + plic_set_priority(PLIC_INT_MMC_MAIN, PLIC_PRIORITY_DEFAULT); + + /* Set threshold to 0 (allow all priorities > 0) */ + plic_set_threshold(0); + + /* Enable MMC interrupt for this hart */ + plic_enable_interrupt(PLIC_INT_MMC_MAIN); + +#ifdef DEBUG_MMC + wolfBoot_printf("plic_init_mmc: hart %d, context %d, irq %d enabled\n", + get_boot_hartid(), plic_get_context(), PLIC_INT_MMC_MAIN); +#endif +} + +/* ============================================================================ + * MMC Interrupt Handler + * ============================================================================ */ + +/* MMC interrupt handler - called from PLIC dispatch */ +void mmc_irq_handler(void) +{ + uint32_t status = EMMC_SD_SRS12; + + /* Check for DMA interrupt */ + if (status & EMMC_SD_SRS12_DMAINT) { + g_mmc_irq_status |= MMC_IRQ_FLAG_DMAINT; + EMMC_SD_SRS12 = EMMC_SD_SRS12_DMAINT; /* Clear interrupt */ + } + + /* Check for transfer complete */ + if (status & EMMC_SD_SRS12_TC) { + g_mmc_irq_status |= MMC_IRQ_FLAG_TC; + EMMC_SD_SRS12 = EMMC_SD_SRS12_TC; /* Clear interrupt */ + } + + /* Check for command complete */ + if (status & EMMC_SD_SRS12_CC) { + g_mmc_irq_status |= MMC_IRQ_FLAG_CC; + EMMC_SD_SRS12 = EMMC_SD_SRS12_CC; /* Clear interrupt */ + } + + /* Check for data timeout error */ + if (status & EMMC_SD_SRS12_EDT) { + g_mmc_irq_status |= MMC_IRQ_FLAG_ERROR; + EMMC_SD_SRS12 = EMMC_SD_SRS12_EDT; /* Clear interrupt */ + } + + /* Check for any other errors */ + if (status & EMMC_SD_SRS12_EINT) { + g_mmc_irq_status |= MMC_IRQ_FLAG_ERROR; + /* Clear all error status bits */ + EMMC_SD_SRS12 = (status & EMMC_SD_SRS12_ERR_STAT); + } + + /* Signal that interrupt was handled */ + g_mmc_irq_pending = 1; + +#ifdef DEBUG_MMC + wolfBoot_printf("mmc_irq_handler: status=0x%08X, flags=0x%02X\n", + status, g_mmc_irq_status); #endif +} + +/* Enable MMC interrupts for SDMA transfer */ +static void mmc_enable_sdma_interrupts(void) +{ + /* Enable signal interrupts for: DMA, Transfer Complete, Command Complete, + * Data Timeout Error */ + uint32_t sig_enable = EMMC_SD_SRS14_DMAINT_IE | + EMMC_SD_SRS14_TC_IE | + EMMC_SD_SRS14_CC_IE | + EMMC_SD_SRS14_EDT_IE; + EMMC_SD_SRS14 |= sig_enable; + + /* Clear any pending interrupt state */ + g_mmc_irq_status = 0; + g_mmc_irq_pending = 0; +} + +/* Disable MMC signal interrupts (status enables remain for polling) */ +static void mmc_disable_sdma_interrupts(void) +{ + EMMC_SD_SRS14 &= ~(EMMC_SD_SRS14_DMAINT_IE | + EMMC_SD_SRS14_TC_IE | + EMMC_SD_SRS14_CC_IE | + EMMC_SD_SRS14_EDT_IE); +} + +/* Wait for MMC interrupt with timeout */ +static int mmc_wait_irq(uint32_t expected_flags, uint32_t timeout) +{ + while (timeout-- > 0) { + if (g_mmc_irq_pending) { + g_mmc_irq_pending = 0; + + /* Check for error */ + if (g_mmc_irq_status & MMC_IRQ_FLAG_ERROR) { + return -1; + } + + /* Check for expected flags */ + if (g_mmc_irq_status & expected_flags) { + return 0; + } + } + /* Brief delay while waiting */ + asm volatile("nop"); + } + return -1; /* Timeout */ +} static int mmc_set_timeout(uint32_t timeout_us) { @@ -198,13 +450,6 @@ static int mmc_set_timeout(uint32_t timeout_us) return 0; } -static void mmc_delay(uint32_t delay) -{ - while (delay--) { - asm volatile("nop"); - } -} - /* voltage values: * 0 = off * EMMC_SD_SRS10_BVS_1_8V @@ -241,7 +486,6 @@ static int mmc_set_power(uint32_t voltage) } /* should be - 0xf06 */ EMMC_SD_SRS10 = reg; - mmc_delay(DEFAULT_DELAY); /* delay after bus power is applied */ } return 0; } @@ -300,8 +544,6 @@ static uint32_t mmc_set_clock(uint32_t clock_khz) clock_khz, freq_khz); #endif - mmc_delay(DEFAULT_DELAY); /* delay after clock changed */ - return freq_khz; } @@ -349,27 +591,21 @@ static uint32_t mmc_get_response_type(uint8_t resp_type) return cmd_reg; } -#define DEVICE_BUSY 1 -int mmc_send_cmd(uint32_t cmd_index, uint32_t cmd_arg, uint8_t resp_type) +static int mmc_send_cmd_internal(uint32_t cmd_type, + uint32_t cmd_index, uint32_t cmd_arg, uint8_t resp_type) { int status = 0; uint32_t cmd_reg; - uint32_t cmd_type = EMMC_SD_SRS03_CMD_NORMAL; + uint32_t timeout = 0x000FFFFF; #ifdef DEBUG_MMC wolfBoot_printf("mmc_send_cmd: cmd_index: %d, cmd_arg: %08X, resp_type: %d\n", cmd_index, cmd_arg, resp_type); #endif - /* wait for command line to be idle - TODO: Add timeout */ + /* wait for command line to be idle */ while ((EMMC_SD_SRS09 & EMMC_SD_SRS09_CICMD) != 0); - /* clear all status interrupts (except current limit, card interrupt/removal/insert) */ - EMMC_SD_SRS12 = ~(EMMC_SD_SRS12_ECL | - EMMC_SD_SRS12_CINT | - EMMC_SD_SRS12_CR | - EMMC_SD_SRS12_CIN); - /* set command argument and command transfer registers */ EMMC_SD_SRS02 = cmd_arg; cmd_reg = @@ -379,19 +615,41 @@ int mmc_send_cmd(uint32_t cmd_index, uint32_t cmd_arg, uint8_t resp_type) EMMC_SD_SRS03 = cmd_reg; - /* wait for command complete or error - TODO: Add timeout */ - while ((EMMC_SD_SRS12 & (EMMC_SD_SRS12_CC | EMMC_SD_SRS12_EINT)) == 0); + /* wait for command complete or error */ + while ((EMMC_SD_SRS12 & (EMMC_SD_SRS12_CC | EMMC_SD_SRS12_TC | + EMMC_SD_SRS12_EINT)) == 0 && --timeout > 0); + + if (timeout == 0 || (EMMC_SD_SRS12 & EMMC_SD_SRS12_EINT)) { + wolfBoot_printf("mmc_send_cmd:%s error SRS12: 0x%08X\n", + (timeout == 0) ? " timeout" : "", EMMC_SD_SRS12); + status = -1; /* error */ + } - /* check for device busy */ - if (resp_type == EMMC_SD_RESP_R1 || resp_type == EMMC_SD_RESP_R1B) { - uint32_t resp = EMMC_SD_SRS04; - #define CARD_STATUS_READY_FOR_DATA (1U << 8) - if ((resp & CARD_STATUS_READY_FOR_DATA) == 0) { - status = DEVICE_BUSY; /* card is busy */ + EMMC_SD_SRS12 = EMMC_SD_SRS12_CC; /* clear command complete */ + while ((EMMC_SD_SRS09 & EMMC_SD_SRS09_CICMD) != 0); + + return status; +} + +#define DEVICE_BUSY 1 +int mmc_send_cmd(uint32_t cmd_index, uint32_t cmd_arg, uint8_t resp_type) +{ + /* send command */ + int status = mmc_send_cmd_internal(EMMC_SD_SRS03_CMD_NORMAL, cmd_index, + cmd_arg, resp_type); + if (status == 0) { + /* check for device busy */ + if (resp_type == EMMC_SD_RESP_R1 || resp_type == EMMC_SD_RESP_R1B) { + uint32_t resp = EMMC_SD_SRS04; + #define CARD_STATUS_READY_FOR_DATA (1U << 8) + if ((resp & CARD_STATUS_READY_FOR_DATA) == 0) { + status = DEVICE_BUSY; /* card is busy */ + } } } - /* clear all status interrupts (except current limit, card interrupt/removal/insert) */ + /* clear all status interrupts + * (except current limit, card interrupt/removal/insert) */ EMMC_SD_SRS12 = ~(EMMC_SD_SRS12_ECL | EMMC_SD_SRS12_CINT | EMMC_SD_SRS12_CR | @@ -425,8 +683,6 @@ int mmc_power_init_seq(uint32_t voltage) status = mmc_send_cmd(MMC_CMD0_GO_IDLE, 0, EMMC_SD_RESP_NONE); } if (status == 0) { - mmc_delay(DEFAULT_DELAY); - /* send the operating conditions command */ status = mmc_send_cmd(SD_CMD8_SEND_IF_COND, IF_COND_27V_33V, EMMC_SD_RESP_R7); @@ -458,18 +714,29 @@ int mmc_read(uint32_t cmd_index, uint32_t block_addr, uint32_t* dst, uint32_t block_count; uint32_t reg, cmd_reg; + /* get block count (round up) */ + block_count = (sz + (EMMC_SD_BLOCK_SIZE - 1)) / EMMC_SD_BLOCK_SIZE; + +#ifdef DEBUG_MMC + wolfBoot_printf("mmc_read: cmd_index: %d, block_addr: %08X, dst %p, sz: %d (%d blocks)\n", + cmd_index, block_addr, dst, sz, block_count); +#endif + /* wait for idle */ status = mmc_wait_busy(0); + if (status != 0) { + #ifdef DEBUG_MMC + wolfBoot_printf("mmc_read: wait busy error\n"); + #endif + return status; + } /* reset data and command lines */ EMMC_SD_SRS11 |= EMMC_SD_SRS11_RESET_DAT_CMD; - mmc_delay(0xFF); /* wait for command and data line busy to clear */ while ((EMMC_SD_SRS09 & (EMMC_SD_SRS09_CICMD | EMMC_SD_SRS09_CIDAT)) != 0); - /* get block count (round up) */ - block_count = (sz + (EMMC_SD_BLOCK_SIZE - 1)) / EMMC_SD_BLOCK_SIZE; /* set transfer block count */ EMMC_SD_SRS01 = (block_count << EMMC_SD_SRS01_BCCT_SHIFT) | sz; @@ -488,56 +755,282 @@ int mmc_read(uint32_t cmd_index, uint32_t block_addr, uint32_t* dst, } else if (cmd_index == MMC_CMD18_READ_MULTIPLE) { cmd_reg |= EMMC_SD_SRS03_MSBS; /* enable multi-block select */ - EMMC_SD_SRS01 = (block_count << EMMC_SD_SRS01_BCCT_SHIFT) | - EMMC_SD_BLOCK_SIZE; + + if (sz >= (512 * 1024)) { /* use DMA */ + cmd_reg |= EMMC_SD_SRS03_DMAE; /* enable DMA */ + + EMMC_SD_SRS01 = (block_count << EMMC_SD_SRS01_BCCT_SHIFT) | + EMMC_SD_SRS01_DMA_BUFF_512KB | EMMC_SD_BLOCK_SIZE; + + /* SDMA mode (for 32-bit transfers) */ + EMMC_SD_SRS10 |= EMMC_SD_SRS10_DMA_SDMA; + EMMC_SD_SRS15 |= EMMC_SD_SRS15_HV4E; + EMMC_SD_SRS16 &= ~EMMC_SD_SRS16_A64S; + /* set SDMA destination address */ + EMMC_SD_SRS22 = (uint32_t)(uintptr_t)dst; + EMMC_SD_SRS23 = (uint32_t)(((uint64_t)(uintptr_t)dst) >> 32); + + /* Enable SDMA interrupts */ + mmc_enable_sdma_interrupts(); + } + } + + EMMC_SD_SRS02 = block_addr; /* cmd argument */ + EMMC_SD_SRS03 = cmd_reg; /* execute command */ + + if (cmd_reg & EMMC_SD_SRS03_DMAE) { + while (1) { /* DMA mode with interrupt support */ + /* Wait for DMA interrupt, transfer complete, or error */ + status = mmc_wait_irq(MMC_IRQ_FLAG_DMAINT | MMC_IRQ_FLAG_TC, + 0x00FFFFFF); + if (status != 0) { + /* Timeout or error */ + wolfBoot_printf("mmc_read: SDMA interrupt timeout/error\n"); + status = -1; /* error */ + break; + } + + /* Check for transfer complete */ + if (g_mmc_irq_status & MMC_IRQ_FLAG_TC) { + g_mmc_irq_status &= ~MMC_IRQ_FLAG_TC; + break; /* Transfer complete */ + } + + /* Check for DMA boundary interrupt - need to update address */ + if (g_mmc_irq_status & MMC_IRQ_FLAG_DMAINT) { + g_mmc_irq_status &= ~MMC_IRQ_FLAG_DMAINT; + /* Read updated DMA address - engine will have incremented */ + dst = (uint32_t*)(uintptr_t)((((uint64_t)EMMC_SD_SRS23) << 32) | + EMMC_SD_SRS22); + /* Set new DMA address for next boundary */ + EMMC_SD_SRS22 = (uint32_t)(uintptr_t)dst; + EMMC_SD_SRS23 = (uint32_t)(((uint64_t)(uintptr_t)dst) >> 32); + } + } + + /* Disable SDMA interrupts after transfer */ + mmc_disable_sdma_interrupts(); + } + else { + while (sz > 0) { /* blocking mode */ + /* wait for buffer read ready (or error) */ + while (((reg = EMMC_SD_SRS12) & + (EMMC_SD_SRS12_BRR | EMMC_SD_SRS12_EINT)) == 0); + + /* read in buffer - read 4 bytes at a time */ + if (reg & EMMC_SD_SRS12_BRR) { + uint32_t i, read_sz = sz; + if (read_sz > EMMC_SD_BLOCK_SIZE) { + read_sz = EMMC_SD_BLOCK_SIZE; + } + for (i=0; i= (512 * 1024)) { /* use DMA for large transfers */ + cmd_reg |= EMMC_SD_SRS03_DMAE; /* enable DMA */ + + EMMC_SD_SRS01 = (block_count << EMMC_SD_SRS01_BCCT_SHIFT) | + EMMC_SD_SRS01_DMA_BUFF_512KB | EMMC_SD_BLOCK_SIZE; + + /* SDMA mode (for 32-bit transfers) */ + EMMC_SD_SRS10 |= EMMC_SD_SRS10_DMA_SDMA; + EMMC_SD_SRS15 |= EMMC_SD_SRS15_HV4E; + EMMC_SD_SRS16 &= ~EMMC_SD_SRS16_A64S; + /* set SDMA source address */ + EMMC_SD_SRS22 = (uint32_t)(uintptr_t)src; + EMMC_SD_SRS23 = (uint32_t)(((uint64_t)(uintptr_t)src) >> 32); + + /* Enable SDMA interrupts */ + mmc_enable_sdma_interrupts(); + } + } + + /* wait for cmd/data line not busy */ + while ((EMMC_SD_SRS09 & + (EMMC_SD_SRS09_CICMD | EMMC_SD_SRS09_CIDAT)) != 0); + EMMC_SD_SRS02 = block_addr; /* cmd argument */ EMMC_SD_SRS03 = cmd_reg; /* execute command */ - while (sz > 0) { - /* wait for buffer read ready */ - while (((reg = EMMC_SD_SRS12) & - (EMMC_SD_SRS12_BRR | EMMC_SD_SRS12_EINT)) == 0); - - /* read in buffer - read 4 bytes at a time */ - if (reg & EMMC_SD_SRS12_BRR) { - uint32_t i, read_sz = sz; - if (read_sz > EMMC_SD_BLOCK_SIZE) { - read_sz = EMMC_SD_BLOCK_SIZE; + + if (cmd_reg & EMMC_SD_SRS03_DMAE) { + while (1) { /* DMA mode with interrupt support */ + /* Wait for DMA interrupt, transfer complete, or error */ + status = mmc_wait_irq(MMC_IRQ_FLAG_DMAINT | MMC_IRQ_FLAG_TC, + 0x00FFFFFF); + if (status != 0) { + /* Timeout or error */ + wolfBoot_printf("mmc_write: SDMA interrupt timeout/error\n"); + status = -1; /* error */ + break; } - for (i=0; i> 32); } - sz -= read_sz; } + + /* Disable SDMA interrupts after transfer */ + mmc_disable_sdma_interrupts(); } + else { + while (sz > 0) { /* blocking mode */ + /* wait for buffer write ready (or error) */ + while (((reg = EMMC_SD_SRS12) & + (EMMC_SD_SRS12_BWR | EMMC_SD_SRS12_EINT)) == 0); + + /* write buffer - write 4 bytes at a time */ + if (reg & EMMC_SD_SRS12_BWR) { + uint32_t i, write_sz = sz; + if (write_sz > EMMC_SD_BLOCK_SIZE) { + write_sz = EMMC_SD_BLOCK_SIZE; + } + for (i=0; i 0; count--) { + status = mmc_send_tuning_block(tuning_data); + if (status != 0) { + /* Reset data/cmd lines on failure */ + EMMC_SD_SRS11 |= EMMC_SD_SRS11_RESET_DAT_CMD; + while (EMMC_SD_SRS11 & EMMC_SD_SRS11_RESET_DAT_CMD); + break; + } + + /* Check if Execute Tuning has cleared (hardware completed) */ + reg = EMMC_SD_SRS15; + if ((reg & EMMC_SD_SRS15_EXTNG) == 0) { + break; + } + } + + /* Check result: Sampling Clock Select should be set on success */ + reg = EMMC_SD_SRS15; + if ((reg & EMMC_SD_SRS15_SCS) == 0) { + #ifdef DEBUG_MMC + wolfBoot_printf("mmc_sd_tuning: FAILED (SCS not set)\n"); + #endif + /* Clear Execute Tuning if still set */ + if (reg & EMMC_SD_SRS15_EXTNG) { + EMMC_SD_SRS15 = reg & ~EMMC_SD_SRS15_EXTNG; + } + return -1; + } + +#ifdef DEBUG_MMC + wolfBoot_printf("mmc_sd_tuning: SUCCESS after %d iterations\n", + EMMC_SD_TUNING_MAX_LOOPS - count + 1); +#endif + return 0; +} + +/* PHY training - find optimal delay value by testing reads + * Based on HSS phy_training_mmc() implementation */ +static int mmc_tune(uint8_t phy_addr, uint32_t clk_khz) +{ + int status; + uint8_t delay, max_delay; + uint8_t pos = 0, length = 0, curr_length = 0; + uint32_t tmp_block[EMMC_SD_BLOCK_SIZE / sizeof(uint32_t)]; + + /* Calculate max delay based on clock rate (from HSS) */ + if (clk_khz <= 12500) { + max_delay = 20; + } else { + max_delay = (uint8_t)((200000 / clk_khz) * 2); + } + if (max_delay > 40) { + max_delay = 40; + } + +#ifdef DEBUG_MMC + wolfBoot_printf("mmc_tune: phy_addr=0x%02x, clk=%d kHz, max_delay=%d\n", + phy_addr, clk_khz, max_delay); +#endif + + /* Test each delay value to find longest valid range */ + for (delay = 0; delay < max_delay; delay++) { + mmc_phy_write(phy_addr, delay); + + /* Try a single block read to test this delay setting */ + status = mmc_read(MMC_CMD17_READ_SINGLE, 0, tmp_block, + EMMC_SD_BLOCK_SIZE); + if (status == 0) { + curr_length++; + if (curr_length > length) { + pos = delay - length; + length++; + } + } else { + /* Reset data/cmd lines on failure */ + EMMC_SD_SRS11 |= EMMC_SD_SRS11_RESET_DAT_CMD; + while (EMMC_SD_SRS11 & EMMC_SD_SRS11_RESET_DAT_CMD); + curr_length = 0; + } + } + + /* Set optimal delay (middle of longest valid range) */ + if (length > 0) { + uint8_t new_delay = pos + (length / 2); + mmc_phy_write(phy_addr, new_delay); + #ifdef DEBUG_MMC + wolfBoot_printf("mmc_tune: PHY delay=%d (range: pos=%d, len=%d)\n", + new_delay, pos, length); + #endif + + /* For SDR50/SDR104, also run SD tuning (CMD19) if required. + * Check SRS17 bit 13 (TSDR50) - Tuning for SDR50 required */ + if (EMMC_SD_SRS17 & EMMC_SD_SRS17_TSDR50) { + status = mmc_sd_tuning(); + if (status != 0) { + #ifdef DEBUG_MMC + wolfBoot_printf("mmc_tune: SD tuning failed\n"); + #endif + return status; + } + } + + return 0; + } + +#ifdef DEBUG_MMC + wolfBoot_printf("mmc_tune: FAILED - no valid PHY delay found\n"); +#endif + return -1; +} +#endif /* ENABLE_MMC_SD_TUNING */ + int mmc_init(void) { int status = 0; @@ -698,8 +1386,12 @@ int mmc_init(void) EMMC_SD_SRS13_BGE_SE | EMMC_SD_SRS13_TC_SE | EMMC_SD_SRS13_CC_SE | EMMC_SD_SRS13_ERSP_SE | EMMC_SD_SRS13_CQINT_SE ); - /* Clear all signal enables */ + /* Clear all signal enables (will be enabled per-transfer for SDMA) */ EMMC_SD_SRS14 = 0; + + /* Initialize PLIC for MMC interrupts */ + plic_init_mmc(); + /* Set initial timeout to 500ms */ status = mmc_set_timeout(EMMC_SD_DATA_TIMEOUT_US); if (status != 0) { @@ -860,7 +1552,6 @@ int mmc_init(void) /* disable card insert interrupt while changing bus width to avoid false triggers */ irq_restore = EMMC_SD_SRS13; EMMC_SD_SRS13 = (irq_restore & ~EMMC_SD_SRS13_CINT_SE); - mmc_delay(DEFAULT_DELAY); status = mmc_set_bus_width(4); } @@ -894,7 +1585,16 @@ int mmc_init(void) if (status == 0) { mmc_set_clock(EMMC_SD_CLK_50MHZ); - /* TODO: Phy training at SDR25 (50MHz) */ +#ifdef ENABLE_MMC_SD_TUNING + /* PHY training for SDR25 at 50MHz */ + status = mmc_tune(EMMC_SD_PHY_ADDR_UHSI_SDR25, EMMC_SD_CLK_50MHZ); + if (status != 0) { + #ifdef DEBUG_MMC + wolfBoot_printf("mmc_init: tuning failed, continuing\n"); + #endif + status = 0; /* Don't fail init on tuning failure */ + } +#endif EMMC_SD_SRS13 = irq_restore; /* re-enable interrupt */ } @@ -911,7 +1611,7 @@ int disk_read(int drv, uint64_t start, uint32_t count, uint8_t *buf) uint32_t start_offset = (start % EMMC_SD_BLOCK_SIZE); (void)drv; /* only one drive supported */ -#ifdef DEBUG_MMC +#if 1 //def DEBUG_MMC wolfBoot_printf("disk_read: drv:%d, start:%llu, count:%d, dst:%p\n", drv, start, count, buf); #endif @@ -957,12 +1657,56 @@ int disk_read(int drv, uint64_t start, uint32_t count, uint8_t *buf) int disk_write(int drv, uint64_t start, uint32_t count, const uint8_t *buf) { - /* not supported */ - (void)drv; - (void)start; - (void)count; - (void)buf; - return 0; + int status = 0; + uint32_t write_sz, block_addr; + uint32_t tmp_block[EMMC_SD_BLOCK_SIZE/sizeof(uint32_t)]; + uint32_t start_offset = (start % EMMC_SD_BLOCK_SIZE); + (void)drv; /* only one drive supported */ + +#if 1 //def DEBUG_MMC + wolfBoot_printf("disk_write: drv:%d, start:%llu, count:%d, src:%p\n", + drv, start, count, buf); +#endif + + while (count > 0) { + block_addr = (start / EMMC_SD_BLOCK_SIZE); + write_sz = count; + if (write_sz > EMMC_SD_BLOCK_SIZE) { + write_sz = EMMC_SD_BLOCK_SIZE; + } + if (write_sz < EMMC_SD_BLOCK_SIZE || /* partial block */ + start_offset != 0 || /* start not block aligned */ + ((uintptr_t)buf % 4) != 0) /* buf not 4-byte aligned */ + { + /* read-modify-write for partial block */ + status = mmc_read(MMC_CMD17_READ_SINGLE, block_addr, + tmp_block, EMMC_SD_BLOCK_SIZE); + if (status == 0) { + uint8_t* tmp_buf = (uint8_t*)tmp_block; + memcpy(tmp_buf + start_offset, buf, write_sz); + status = mmc_write(MMC_CMD24_WRITE_SINGLE, block_addr, + tmp_block, EMMC_SD_BLOCK_SIZE); + start_offset = 0; + } + } + else { + /* direct full block(s) write */ + uint32_t blocks = (count / EMMC_SD_BLOCK_SIZE); + write_sz = (blocks * EMMC_SD_BLOCK_SIZE); + status = mmc_write(blocks > 1 ? + MMC_CMD25_WRITE_MULTIPLE : + MMC_CMD24_WRITE_SINGLE, + block_addr, (const uint32_t*)buf, write_sz); + } + if (status != 0) { + break; + } + + start += write_sz; + buf += write_sz; + count -= write_sz; + } + return status; } int disk_init(int drv) @@ -972,6 +1716,9 @@ int disk_init(int drv) wolfBoot_printf("Failed to initialize MMC\n"); } (void)drv; +#ifdef DISK_TEST + disk_test(drv); +#endif return r; } @@ -980,6 +1727,103 @@ void disk_close(int drv) (void)drv; } +#ifdef DISK_TEST +/* Test block address in update partition */ +#ifndef DISK_TEST_BLOCK_ADDR +#define DISK_TEST_BLOCK_ADDR 149504 +#endif + +/* disk_test: Test read/write functionality at update partition + * Tests sizes: 128, 512, 1024, 512KB (524288), 1MB (1048576) bytes + * Uses DDR at WOLFBOOT_LOAD_ADDRESS for test buffer + * Returns 0 on success, negative on failure */ +static int disk_test(int drv) +{ + int status = 0; + int test_num = 0; + uint32_t i; + static const uint32_t test_sizes[] = { + 128, /* partial block */ + 512, /* single block */ + 1024, /* two blocks */ + 512 * 1024, /* 512KB - DMA threshold */ + 1024 * 1024 /* 1MB */ + }; + /* Use DDR memory at WOLFBOOT_LOAD_ADDRESS for test buffer */ + uint32_t* tmp_buf32 = (uint32_t*)WOLFBOOT_LOAD_ADDRESS; + uint8_t* tmp_buf = (uint8_t*)WOLFBOOT_LOAD_ADDRESS; + + wolfBoot_printf("disk_test: Starting tests at block %d (buf @ %p)\n", + DISK_TEST_BLOCK_ADDR, tmp_buf); + + for (test_num = 0; test_num < (int)(sizeof(test_sizes)/sizeof(test_sizes[0])); test_num++) { + uint32_t test_sz = test_sizes[test_num]; + uint64_t test_addr = (uint64_t)DISK_TEST_BLOCK_ADDR * EMMC_SD_BLOCK_SIZE; + uint32_t blocks_needed = (test_sz + EMMC_SD_BLOCK_SIZE - 1) / EMMC_SD_BLOCK_SIZE; + + wolfBoot_printf(" Test %d: size=%u bytes (%u blocks)... ", + test_num + 1, test_sz, blocks_needed); + + /* Fill with test pattern */ + for (i = 0; i < test_sz / sizeof(uint32_t); i++) { + tmp_buf32[i] = (test_num << 24) | i; + } + /* Handle remaining bytes for non-word-aligned sizes */ + for (i = (test_sz / sizeof(uint32_t)) * sizeof(uint32_t); i < test_sz; i++) { + tmp_buf[i] = (uint8_t)((test_num << 4) | (i & 0x0F)); + } + + /* Write */ + status = disk_write(drv, test_addr, test_sz, tmp_buf); + if (status != 0) { + wolfBoot_printf("FAIL (write error %d)\n", status); + continue; + } + + /* Clear buffer */ + memset(tmp_buf, 0, test_sz); + + /* Read back */ + status = disk_read(drv, test_addr, test_sz, tmp_buf); + if (status != 0) { + wolfBoot_printf("FAIL (read error %d)\n", status); + continue; + } + + /* Verify pattern */ + for (i = 0; i < test_sz / sizeof(uint32_t); i++) { + uint32_t expected = (test_num << 24) | i; + if (tmp_buf32[i] != expected) { + wolfBoot_printf("FAIL (verify @ word %u: got 0x%08X, expected 0x%08X)\n", + i, tmp_buf32[i], expected); + status = -1; + break; + } + } + /* Verify remaining bytes for non-word-aligned sizes */ + if (status == 0) { + for (i = (test_sz / sizeof(uint32_t)) * sizeof(uint32_t); i < test_sz; i++) { + uint8_t expected = (uint8_t)((test_num << 4) | (i & 0x0F)); + if (tmp_buf[i] != expected) { + wolfBoot_printf("FAIL (verify @ byte %u: got 0x%02X, expected 0x%02X)\n", + i, tmp_buf[i], expected); + status = -1; + break; + } + } + } + + if (status == 0) { + wolfBoot_printf("PASS\n"); + } + } + + wolfBoot_printf("disk_test: Complete\n"); + return status; +} +#endif /* DISK_TEST */ + + #ifdef DEBUG_UART #ifndef DEBUG_UART_BASE diff --git a/hal/mpfs250.h b/hal/mpfs250.h index af48aab2a6..d2055698d9 100644 --- a/hal/mpfs250.h +++ b/hal/mpfs250.h @@ -22,6 +22,9 @@ #ifndef MPFS250_DEF_INCLUDED #define MPFS250_DEF_INCLUDED +/* Include generic RISC-V definitions */ +#include "hal/riscv.h" + /* PolarFire SoC MPFS250T board specific configuration */ /* APB/AHB Clock Frequency */ @@ -52,55 +55,6 @@ #define SYSREG_SOFT_RESET_CR_ATHENA (1U << 28) /* Crypto hardware accelerator */ -/* TODO: Add support for machine mode wolfBoot */ -#if 1 - #define WOLFBOOT_RISCV_SMODE /* supervisor mode */ -#else - #define WOLFBOOT_RISCV_MMODE /* machine mode */ -#endif - -/* size of each trap frame register */ -#define REGBYTES (1 << 3) - -/* Machine Information Registers */ -#define CSR_MVENDORID 0xF11 -#define CSR_MARCHID 0xF12 -#define CSR_MIMPID 0xF13 -#define CSR_MHARTID 0xF14 - -/* Initial stack pointer address (stack grows downward from here) */ -#ifdef WOLFBOOT_RISCV_SMODE -#define WOLFBOOT_STACK_TOP 0x80200000 -#else -#define WOLFBOOT_STACK_TOP 0x80000000 -#endif - -#ifdef WOLFBOOT_RISCV_SMODE -#define MODE_PREFIX(__suffix) s##__suffix -#else -#define MODE_PREFIX(__suffix) m##__suffix -#endif - -/* SIE (Interrupt Enable) and SIP (Interrupt Pending) flags */ -#define IRQ_U_SOFT 0 -#define IRQ_S_SOFT 1 -#define IRQ_M_SOFT 3 -#define IRQ_U_TIMER 4 -#define IRQ_S_TIMER 5 -#define IRQ_M_TIMER 7 -#define IRQ_U_EXT 8 -#define IRQ_S_EXT 9 -#define IRQ_M_EXT 11 -#define MIE_MSIE (1 << IRQ_M_SOFT) -#define SIE_SSIE (1 << IRQ_S_SOFT) -#define SIE_STIE (1 << IRQ_S_TIMER) -#define SIE_SEIE (1 << IRQ_S_EXT) - -#define MCAUSE64_INT 0x8000000000000000LLU -#define MCAUSE64_CAUSE 0x7FFFFFFFFFFFFFFFLLU - - - /* UART */ #define MSS_UART0_LO_BASE 0x20000000UL #define MSS_UART1_LO_BASE 0x20100000UL @@ -850,6 +804,7 @@ #define SD_CMD16 16 /* R1 Rsp */ #define MMC_CMD17_READ_SINGLE 17 /* Read single block */ #define MMC_CMD18_READ_MULTIPLE 18 /* Read multiple blocks */ +#define SD_CMD19_SEND_TUNING 19 /* SD: Send 64-byte tuning block */ #define MMC_CMD24_WRITE_SINGLE 24 /* Write single block */ #define MMC_CMD25_WRITE_MULTIPLE 25 /* Write multiple blocks */ @@ -862,7 +817,7 @@ #define EMMC_SD_DEBOUNCE_TIME 0x300000U /* Timeout values */ -#define EMMC_SD_DATA_TIMEOUT_US 500000U /* 500ms data timeout */ +#define EMMC_SD_DATA_TIMEOUT_US 750000U /* 750ms data timeout */ #define EMMC_SD_CMD_TIMEOUT_MS 3000U /* 3s command timeout */ #define WOLFBOOT_CARDTYPE_SD 1 @@ -897,5 +852,101 @@ #define ATHENA_BASE (SYSREG_BASE + 0x125000) +/* ============================================================================ + * PLIC - Platform-Level Interrupt Controller (SiFive compatible) + * Base Address: 0x0c000000, Size: 64MB + * ============================================================================ */ +#define PLIC_BASE 0x0C000000UL +#define PLIC_SIZE 0x04000000UL /* 64MB */ + +/* Number of interrupt sources and contexts */ +#define PLIC_NUM_SOURCES 186 /* riscv,ndev = 0xBA = 186 */ +#define PLIC_NUM_HARTS 5 /* 1x E51 + 4x U54 */ +#define PLIC_NUM_CONTEXTS 10 /* 2 contexts per hart (M-mode + S-mode) */ + +/* MSS Global Interrupt offset - PLIC interrupts 0-12 are local, 13+ are MSS */ +#define OFFSET_TO_MSS_GLOBAL_INTS 13 + +/* PLIC Interrupt Sources (PLIC IRQ numbers) */ +#define PLIC_INT_MMC_MAIN 88 /* MMC/SD controller main interrupt */ +#define PLIC_INT_MMC_WAKEUP 89 /* MMC/SD controller wakeup interrupt */ + +/* PLIC Register Layout: + * 0x000000: Reserved + * 0x000004: Priority for interrupt 1 + * ... + * 0x000FFC: Priority for interrupt 1023 + * 0x001000: Pending bits for interrupts 0-31 + * ... + * 0x002000: Enable bits for context 0, interrupts 0-31 + * ... + * 0x200000: Priority threshold for context 0 + * 0x200004: Claim/complete for context 0 + * 0x201000: Priority threshold for context 1 + * 0x201004: Claim/complete for context 1 + * ... + */ + +/* Priority registers: one 32-bit word per interrupt source */ +#define PLIC_PRIORITY_BASE (PLIC_BASE + 0x000000UL) +#define PLIC_PRIORITY(irq) (*((volatile uint32_t*)(PLIC_PRIORITY_BASE + ((irq) * 4)))) + +/* Pending bits: 32 interrupts per 32-bit word */ +#define PLIC_PENDING_BASE (PLIC_BASE + 0x001000UL) +#define PLIC_PENDING(irq) (*((volatile uint32_t*)(PLIC_PENDING_BASE + (((irq) / 32) * 4)))) +#define PLIC_PENDING_BIT(irq) (1U << ((irq) % 32)) + +/* Enable bits: 32 interrupts per 32-bit word, per context + * Each context has 0x80 bytes (32 words) for enable bits + */ +#define PLIC_ENABLE_BASE (PLIC_BASE + 0x002000UL) +#define PLIC_ENABLE_STRIDE 0x80UL +#define PLIC_ENABLE(ctx, irq) (*((volatile uint32_t*)(PLIC_ENABLE_BASE + \ + ((ctx) * PLIC_ENABLE_STRIDE) + (((irq) / 32) * 4)))) +#define PLIC_ENABLE_BIT(irq) (1U << ((irq) % 32)) + +/* Context registers: threshold and claim/complete, 0x1000 bytes per context */ +#define PLIC_CONTEXT_BASE (PLIC_BASE + 0x200000UL) +#define PLIC_CONTEXT_STRIDE 0x1000UL +#define PLIC_THRESHOLD(ctx) (*((volatile uint32_t*)(PLIC_CONTEXT_BASE + \ + ((ctx) * PLIC_CONTEXT_STRIDE) + 0x00))) +#define PLIC_CLAIM(ctx) (*((volatile uint32_t*)(PLIC_CONTEXT_BASE + \ + ((ctx) * PLIC_CONTEXT_STRIDE) + 0x04))) +#define PLIC_COMPLETE(ctx) PLIC_CLAIM(ctx) /* Same register for claim and complete */ + +/* PLIC Context IDs for each hart + * Hart 0 (E51): Context 0 = M-mode (no S-mode on E51) + * 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 + */ +#define PLIC_CONTEXT_E51_M 0 +#define PLIC_CONTEXT_U54_1_M 1 +#define PLIC_CONTEXT_U54_1_S 2 +#define PLIC_CONTEXT_U54_2_M 3 +#define PLIC_CONTEXT_U54_2_S 4 +#define PLIC_CONTEXT_U54_3_M 5 +#define PLIC_CONTEXT_U54_3_S 6 +#define PLIC_CONTEXT_U54_4_M 7 +#define PLIC_CONTEXT_U54_4_S 8 + +/* Helper macro to get S-mode context for a given hart (1-4 for U54 cores) */ +#define PLIC_HART_TO_SMODE_CTX(hart) (((hart) * 2)) + +/* PLIC Priority levels (0 = disabled, 1-7 = priority, 7 = highest) */ +#define PLIC_PRIORITY_DISABLED 0 +#define PLIC_PRIORITY_MIN 1 +#define PLIC_PRIORITY_MAX 7 +#define PLIC_PRIORITY_DEFAULT 4 + +/* MMC Interrupt flags for handler state */ +#define MMC_IRQ_FLAG_NONE 0x00 +#define MMC_IRQ_FLAG_CC 0x01 /* Command Complete */ +#define MMC_IRQ_FLAG_TC 0x02 /* Transfer Complete */ +#define MMC_IRQ_FLAG_DMAINT 0x04 /* DMA Interrupt */ +#define MMC_IRQ_FLAG_ERROR 0x80 /* Error occurred */ + + #endif /* MPFS250_DEF_INCLUDED */ diff --git a/hal/riscv.h b/hal/riscv.h new file mode 100644 index 0000000000..04d4e07e0a --- /dev/null +++ b/hal/riscv.h @@ -0,0 +1,164 @@ +/* riscv.h + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef RISCV_H +#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 + +/* 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 +#endif + +/* ============================================================================ + * Generic RISC-V definitions (32-bit and 64-bit) + * ============================================================================ */ + +/* ============================================================================ + * XLEN-Dependent Definitions + * ============================================================================ */ + +#if __riscv_xlen == 64 + #define STORE sd + #define LOAD ld + #define REGBYTES 8 + #define VECTOR_ALIGN 3 /* 8-byte alignment for RV64 */ +#else + #define STORE sw + #define LOAD lw + #define REGBYTES 4 + #define VECTOR_ALIGN 2 /* 4-byte alignment for RV32 */ +#endif + + +/* RISC-V S-mode timer frequency (1 MHz default, can be overridden by platform) */ +#ifndef RISCV_SMODE_TIMER_FREQ +#define RISCV_SMODE_TIMER_FREQ 1000000 /* 1 MHz */ +#endif + +/* ============================================================================ + * Machine Information Registers (CSRs) + * ============================================================================ */ +#define CSR_TIME 0xC01 /* Timer register (read-only) */ +#define CSR_TIMEH 0xC81 /* Timer register high (RV32 only) */ +#define CSR_MVENDORID 0xF11 /* Vendor ID */ +#define CSR_MARCHID 0xF12 /* Architecture ID */ +#define CSR_MIMPID 0xF13 /* Implementation ID */ +#define CSR_MHARTID 0xF14 /* Hardware thread ID */ + +/* ============================================================================ + * CSR Access Macros + * ============================================================================ */ + +/* Read CSR using inline assembly */ +#define csr_read(csr) \ +({ \ + register unsigned long __v; \ + __asm__ __volatile__ ("csrr %0, " #csr : "=r"(__v) : ); \ + __v; \ +}) + +/* Write CSR using inline assembly */ +#define csr_write(csr, val) \ +({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__ ("csrw " #csr ", %0" : : "rK"(__v)); \ +}) + +/* Set bits in CSR */ +#define csr_set(csr, val) \ +({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__ ("csrs " #csr ", %0" : : "rK"(__v)); \ +}) + +/* Clear bits in CSR */ +#define csr_clear(csr, val) \ +({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__ ("csrc " #csr ", %0" : : "rK"(__v)); \ +}) + +/* ============================================================================ + * Interrupt Numbers (for SIE/SIP and MIE/MIP registers) + * ============================================================================ */ +#define IRQ_U_SOFT 0 /* User software interrupt */ +#define IRQ_S_SOFT 1 /* Supervisor software interrupt */ +#define IRQ_M_SOFT 3 /* Machine software interrupt */ +#define IRQ_U_TIMER 4 /* User timer interrupt */ +#define IRQ_S_TIMER 5 /* Supervisor timer interrupt */ +#define IRQ_M_TIMER 7 /* Machine timer interrupt */ +#define IRQ_U_EXT 8 /* User external interrupt */ +#define IRQ_S_EXT 9 /* Supervisor external interrupt */ +#define IRQ_M_EXT 11 /* Machine external interrupt */ + +/* ============================================================================ + * 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 */ + +/* ============================================================================ + * Machine Interrupt Enable (MIE) Register Bits + * ============================================================================ */ +#define MIE_MSIE (1 << IRQ_M_SOFT) /* Machine software interrupt enable */ +#define MIE_MTIE (1 << IRQ_M_TIMER) /* Machine timer interrupt enable */ +#define MIE_MEIE (1 << IRQ_M_EXT) /* Machine external interrupt enable */ + +/* ============================================================================ + * Supervisor Interrupt Enable (SIE) Register Bits + * ============================================================================ */ +#define SIE_SSIE (1 << IRQ_S_SOFT) /* Supervisor software interrupt enable */ +#define SIE_STIE (1 << IRQ_S_TIMER) /* Supervisor timer interrupt enable */ +#define SIE_SEIE (1 << IRQ_S_EXT) /* Supervisor external interrupt enable */ + +/* ============================================================================ + * Exception Cause Register (MCAUSE/SCAUSE) Definitions + * ============================================================================ */ +#if __riscv_xlen == 64 +#define MCAUSE_INT 0x8000000000000000ULL /* Interrupt bit (MSB) */ +#define MCAUSE_CAUSE 0x7FFFFFFFFFFFFFFFULL /* Exception code mask */ +#else +#define MCAUSE_INT 0x80000000UL /* Interrupt bit (MSB) */ +#define MCAUSE_CAUSE 0x7FFFFFFFUL /* Exception code mask */ +#endif + +/* Legacy aliases for compatibility */ +#define MCAUSE64_INT 0x8000000000000000ULL +#define MCAUSE64_CAUSE 0x7FFFFFFFFFFFFFFFULL +#define MCAUSE32_INT 0x80000000UL +#define MCAUSE32_CAUSE 0x7FFFFFFFUL + +#endif /* RISCV_H */ + diff --git a/include/hal.h b/include/hal.h index cfa6893a01..1804e05465 100644 --- a/include/hal.h +++ b/include/hal.h @@ -52,6 +52,12 @@ void hal_deinit(); #endif void hal_init(void); + +#ifdef WOLFBOOT_UPDATE_DISK +/* Timer functions (platform-specific) */ +uint64_t hal_get_timer_us(void); +#endif + #ifdef ARCH_64BIT typedef uintptr_t haladdr_t; /* 64-bit platforms */ int hal_flash_write(uintptr_t address, const uint8_t *data, int len); diff --git a/src/boot_riscv.c b/src/boot_riscv.c index c46df2a6bb..2a7fc4d305 100644 --- a/src/boot_riscv.c +++ b/src/boot_riscv.c @@ -19,14 +19,37 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ +/* RISC-V boot code (32-bit and 64-bit unified) */ + #include #include "image.h" #include "loader.h" +#ifdef DEBUG_BOOT +#include "printf.h" +#endif +#include "hal/riscv.h" + +#ifdef TARGET_mpfs250 +#include "hal/mpfs250.h" +#endif extern void trap_entry(void); extern void trap_exit(void); +/* Linker symbols - use native pointer-sized types */ +#if __riscv_xlen == 64 +extern uint64_t _start_vector; +extern uint64_t _stored_data; +extern uint64_t _start_data; +extern uint64_t _end_data; +extern uint64_t _start_bss; +extern uint64_t _end_bss; +extern uint64_t _end_stack; +extern uint64_t _start_heap; +extern uint64_t _global_pointer; +extern void (* const trap_vector_table[])(void); +#else extern uint32_t _start_vector; extern uint32_t _stored_data; extern uint32_t _start_data; @@ -37,63 +60,228 @@ extern uint32_t _end_stack; extern uint32_t _start_heap; extern uint32_t _global_pointer; extern void (* const IV[])(void); +#endif extern void main(void); -void RAMFUNCTION reloc_iv(const uint32_t *address) + +/* reloc_trap_vector is implemented in boot_riscv_start.S */ +extern void reloc_trap_vector(const uint32_t *address); + +#ifdef TARGET_mpfs250 +/* PLIC functions from mpfs250.c */ +extern uint32_t plic_claim(void); +extern void plic_complete(uint32_t irq); +extern void mmc_irq_handler(void); +#endif + +/* ============================================================================ + * Trap Handling + * ============================================================================ */ + +#if __riscv_xlen == 64 +static uint64_t last_cause = 0; +static uint64_t last_epc = 0; +static uint64_t last_tval = 0; +#else +static uint32_t last_cause = 0; +static uint32_t last_epc = 0; +static uint32_t last_tval = 0; +#endif + +#ifdef TARGET_mpfs250 +/* Handle external interrupts via PLIC */ +static void handle_external_interrupt(void) { - asm volatile("csrw mtvec, %0":: "r"(address + 1)); -} + uint32_t irq; + + /* Claim the interrupt from PLIC */ + while ((irq = plic_claim()) != 0) { + /* Dispatch to appropriate handler based on IRQ number */ + switch (irq) { + case PLIC_INT_MMC_MAIN: + mmc_irq_handler(); + break; + default: + /* Unknown interrupt - just complete it */ + break; + } -void __attribute__((naked,section(".init"))) _reset(void) { - register uint32_t *src, *dst; - asm volatile("la gp, _global_pointer"); - asm volatile("la sp, _end_stack"); - - /* Set up vectored interrupt, with IV starting at offset 0x100 */ - asm volatile("csrw mtvec, %0":: "r"((uint8_t *)(&_start_vector) + 1)); - - src = (uint32_t *) &_stored_data; - dst = (uint32_t *) &_start_data; - /* Copy the .data section from flash to RAM. */ - while (dst < (uint32_t *)&_end_data) { - *dst = *src; - dst++; - src++; + /* Signal completion to PLIC */ + plic_complete(irq); } +} +#endif - /* Initialize the BSS section to 0 */ - dst = &_start_bss; - while (dst < (uint32_t *)&_end_bss) { - *dst = 0U; - dst++; +unsigned long WEAKFUNCTION handle_trap(unsigned long cause, unsigned long epc, + unsigned long tval) +{ + last_cause = cause; + last_epc = epc; + last_tval = tval; + +#ifdef TARGET_mpfs250 + /* Check if this is an interrupt (MSB set) */ + if (cause & MCAUSE_INT) { + unsigned long exception_code = cause & MCAUSE_CAUSE; + + /* Check for external interrupt (S-mode external = 9, M-mode external = 11) */ + if (exception_code == IRQ_S_EXT || exception_code == IRQ_M_EXT) { + handle_external_interrupt(); + } + /* Other interrupts (timer, software) can be handled here if needed */ } + /* Synchronous exceptions are not handled - just record them */ +#endif - /* Run wolfboot */ - main(); + return epc; +} - /* Should never return */ - wolfBoot_panic(); +/* ============================================================================ + * Timer Functions + * ============================================================================ */ + +uint64_t hal_get_timer(void) +{ +#if __riscv_xlen == 64 + /* For RV64, CSR time contains full 64-bit value */ + return csr_read(time); +#else + /* For RV32, read both timeh and time with wrap-around protection */ + uint32_t hi, lo; + + do { + hi = csr_read(timeh); + lo = csr_read(time); + } while (hi != csr_read(timeh)); + + return ((uint64_t)hi << 32) | lo; +#endif +} + +/* Get timer value in microseconds + * Formula: time_us = (ticks * 1000) / (rate / 1000) + * = (ticks * 1000000) / rate + */ +uint64_t hal_get_timer_us(void) +{ + uint64_t ticks = hal_get_timer(); + uint32_t rate = RISCV_SMODE_TIMER_FREQ; + + /* Avoid overflow: (ticks * 1000) / (rate / 1000) */ + return (ticks * 1000) / (rate / 1000); +} + +/* ============================================================================ + * Boot Functions + * ============================================================================ */ + +#ifdef MMU +int WEAKFUNCTION hal_dts_fixup(void* dts_addr) +{ + (void)dts_addr; + return 0; } +#endif +#if __riscv_xlen == 64 +/* Get the hartid saved by boot_riscv_start.S in the tp register */ +unsigned long get_boot_hartid(void) +{ + unsigned long hartid; + asm volatile("mv %0, tp" : "=r"(hartid)); + return hartid; +} +#endif + +#ifdef MMU +void do_boot(const uint32_t *app_offset, const uint32_t* dts_offset) +#else void do_boot(const uint32_t *app_offset) +#endif { -#if 1 - /* workaround for long jump */ - asm volatile("la a2, reloc_iv;" \ - "jalr a2" ::: "a2"); +#if __riscv_xlen == 64 + unsigned long hartid; +#endif +#ifdef MMU + unsigned long dts_addr; +#endif + +#ifdef MMU + hal_dts_fixup((uint32_t*)dts_offset); + dts_addr = (unsigned long)dts_offset; +#endif + +#if __riscv_xlen == 64 + /* Get the hartid that was saved by boot_riscv_start.S in tp register. + * This is the hartid passed to wolfBoot by the prior boot stage (e.g., HSS). + * For MPFS, this should be 1-4 (U54 cores), never 0 (E51 monitor core). */ + hartid = get_boot_hartid(); +#endif + +#ifdef DEBUG_BOOT + wolfBoot_printf("do_boot: entry=0x%lx", (unsigned long)app_offset); +#if __riscv_xlen == 64 + wolfBoot_printf(", hartid=%lu", hartid); +#endif +#ifdef MMU + wolfBoot_printf(", dts=0x%lx", dts_addr); +#endif + wolfBoot_printf("\n"); +#endif + + /* Relocate trap vector table to application */ + reloc_trap_vector(app_offset); + + /* + * RISC-V Linux kernel boot requirements (Documentation/arch/riscv/boot.rst): + * a0 = hartid of the current core + * a1 = physical address of the device tree blob (DTB) + * satp = 0 (MMU disabled) + * + * For SMP systems using ordered booting (preferred), only the boot hart + * enters the kernel. Secondary harts are started via SBI HSM extension. + */ + +#if __riscv_xlen == 64 +#ifdef MMU + asm volatile( + #ifdef WOLFBOOT_RISCV_SMODE + "csrw satp, zero\n" + "sfence.vma\n" + #endif + "mv a0, %0\n" + "mv a1, %1\n" + "jr %2\n" + : : "r"(hartid), "r"(dts_addr), "r"(app_offset) : "a0", "a1" + ); #else - reloc_iv(app_offset); + asm volatile( + "mv a0, %0\n" + "mv a1, zero\n" + "jr %1\n" + : : "r"(hartid), "r"(app_offset) : "a0", "a1" + ); #endif - asm volatile("jr %0":: "r"((uint8_t *)(app_offset))); +#else /* RV32 */ + /* RV32: typically bare-metal without Linux, simpler boot */ + asm volatile("jr %0" : : "r"(app_offset)); +#endif + + /* Should never reach here */ + __builtin_unreachable(); } void isr_empty(void) { - + /* Empty interrupt handler */ } -#ifdef RAM_CODE +/* ============================================================================ + * Reboot Functions + * ============================================================================ */ +#if __riscv_xlen == 32 && defined(RAM_CODE) +/* RV32 HiFive1 watchdog-based reboot */ #define AON_WDOGCFG *(volatile uint32_t *)(0x10000000UL) #define AON_WDOGKEY *(volatile uint32_t *)(0x1000001CUL) #define AON_WDOGFEED *(volatile uint32_t *)(0x10000018UL) @@ -105,14 +293,14 @@ void isr_empty(void) #define AON_WDOGCFG_ZEROCMP 0x00000200 #define AON_WDOGCFG_ENALWAYS 0x00001000 - void RAMFUNCTION arch_reboot(void) { AON_WDOGKEY = AON_WDOGKEY_VALUE; AON_WDOGCMP = 0; - //wdogconfig: : wdogrsten | enablealways | reset to 0 | max scale + /* wdogconfig: wdogrsten | enablealways | reset to 0 | max scale */ AON_WDOGKEY = AON_WDOGKEY_VALUE; - AON_WDOGCFG |= (AON_WDOGCFG_RSTEN | AON_WDOGCFG_ENALWAYS | AON_WDOGCFG_ZEROCMP | AON_WDOGCFG_SCALE) ; + AON_WDOGCFG |= (AON_WDOGCFG_RSTEN | AON_WDOGCFG_ENALWAYS | + AON_WDOGCFG_ZEROCMP | AON_WDOGCFG_SCALE); AON_WDOGKEY = AON_WDOGKEY_VALUE; AON_WDOGFEED = 1; @@ -121,4 +309,17 @@ void RAMFUNCTION arch_reboot(void) wolfBoot_panic(); } -#endif /* RAM_CODE */ +#else /* RV64 or non-RAM_CODE */ + +void WEAKFUNCTION arch_reboot(void) +{ +#ifdef TARGET_mpfs250 + SYSREG_MSS_RESET_CR = 0xDEAD; +#endif + + while(1) + ; + wolfBoot_panic(); +} + +#endif /* __riscv_xlen == 32 && RAM_CODE */ diff --git a/src/boot_riscv64.c b/src/boot_riscv64.c deleted file mode 100644 index b71733002f..0000000000 --- a/src/boot_riscv64.c +++ /dev/null @@ -1,101 +0,0 @@ -/* boot_riscv64.c - * - * Copyright (C) 2025 wolfSSL Inc. - * - * This file is part of wolfBoot. - * - * wolfBoot is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * wolfBoot is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA - */ - -/* RISC-V 64-bit boot code */ - -#include - -#include "image.h" -#include "loader.h" - -#ifdef TARGET_mpfs250 -#include "hal/mpfs250.h" -#endif - -extern void trap_entry(void); -extern void trap_exit(void); - -extern uint64_t _start_vector; -extern uint64_t _stored_data; -extern uint64_t _start_data; -extern uint64_t _end_data; -extern uint64_t _start_bss; -extern uint64_t _end_bss; -extern uint64_t _end_stack; -extern uint64_t _start_heap; -extern uint64_t _global_pointer; -extern void (* const trap_vector_table[])(void); - -/* reloc_trap_vector is implemented in boot_riscv64_start.S */ -extern void reloc_trap_vector(const uint32_t *address); - -static uint64_t last_cause = 0; -static uint64_t last_epc = 0; -static uint64_t last_tval = 0; - -unsigned long WEAKFUNCTION handle_trap(unsigned long cause, unsigned long epc, unsigned long tval) -{ - last_cause = cause; - last_epc = epc; - last_tval = tval; - return epc; -} - -#ifdef MMU -int WEAKFUNCTION hal_dts_fixup(void* dts_addr) -{ - (void)dts_addr; - return 0; -} -#endif - -#ifdef MMU -void do_boot(const uint32_t *app_offset, const uint32_t* dts_offset) -#else -void do_boot(const uint32_t *app_offset) -#endif -{ -#ifdef MMU - hal_dts_fixup((uint32_t*)dts_offset); -#endif - - /* Relocate trap vector table to application */ - reloc_trap_vector(app_offset); - - /* Jump to application entry point */ - asm volatile("jr %0":: "r"((uint8_t *)(app_offset))); -} - -void isr_empty(void) -{ - /* Empty interrupt handler */ -} - -void WEAKFUNCTION arch_reboot(void) -{ -#ifdef TARGET_mpfs250 - SYSREG_MSS_RESET_CR = 0xDEAD; -#endif - - while(1) - ; - wolfBoot_panic(); -} diff --git a/src/boot_riscv64_start.S b/src/boot_riscv_start.S similarity index 58% rename from src/boot_riscv64_start.S rename to src/boot_riscv_start.S index 82b7e1c446..4ed11d5efc 100644 --- a/src/boot_riscv64_start.S +++ b/src/boot_riscv_start.S @@ -1,5 +1,5 @@ /** - * RISC-V 64-bit bootup + * RISC-V bootup (32-bit and 64-bit unified) * Copyright (C) 2025 wolfSSL Inc. * * This file is part of wolfBoot. @@ -19,22 +19,38 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ +#include "hal/riscv.h" + #ifdef TARGET_mpfs250 #include "hal/mpfs250.h" #endif -/* - * ============================================================ - * RISC-V 64-bit Boot Entry Point - * ============================================================ - * Entry conditions (passed by prior boot stage / SBI): - * a0 = hart ID (hardware thread identifier) - * a1 = pointer to device tree blob (DTB) in memory - * ============================================================ - */ +#ifdef WOLFBOOT_RISCV_SMODE +#define MODE_PREFIX(__suffix) s##__suffix +#else +#define MODE_PREFIX(__suffix) m##__suffix +#endif + + +/* ============================================================================ + * RISC-V Boot Entry Point + * ============================================================================ + * + * For RV64 (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 RV32 (typically bare metal): + * Starts fresh, reads hart ID from CSR + * + * ============================================================================ */ .section .init .globl _reset _reset: +#if __riscv_xlen == 64 + /* ---------- RV64 Boot Sequence ---------- */ + #ifdef WOLFBOOT_RISCV_MMODE /* * Machine Mode: Read hart ID from CSR since we're the first code @@ -77,19 +93,35 @@ _reset: csrw MODE_PREFIX(ie), zero /* - * Selectively enable Software Interrupts (IPIs) only. - * IPIs are required for multi-hart boot coordination - secondary harts - * may need to be woken via software interrupt after initialization. - * - M-mode: Set MSIE (Machine Software Interrupt Enable) bit - * - S-mode: Set SSIE (Supervisor Software Interrupt Enable) bit + * 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) */ #ifdef WOLFBOOT_RISCV_SMODE - li t0, SIE_SSIE + li t0, (SIE_SSIE | SIE_SEIE) #else - li t0, MIE_MSIE + li t0, (MIE_MSIE | MIE_MEIE) #endif csrs MODE_PREFIX(ie), t0 + /* + * Enable global interrupts by setting the SIE/MIE bit in sstatus/mstatus. + * 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) + */ +#ifdef WOLFBOOT_RISCV_SMODE + li t0, SSTATUS_SIE +#else + li t0, MSTATUS_MIE +#endif + csrs MODE_PREFIX(status), t0 + /* * Initialize stack pointer: * WOLFBOOT_STACK_TOP = 0x80000000 (M-mode) or 0x80200000 (S-mode) @@ -122,7 +154,49 @@ _reset: mv a0, tp j main -/* +#else /* __riscv_xlen == 32 */ + /* ---------- RV32 Boot Sequence ---------- */ + + /* Initialize global pointer for position-independent access to globals */ + la gp, _global_pointer + + /* Initialize stack pointer to top of stack */ + la sp, _end_stack + + /* Set up vectored interrupt, with IV starting at offset 0x100 */ + la t0, _start_vector + addi t0, t0, 1 + csrw mtvec, t0 + + /* Copy the .data section from flash to RAM */ + la t0, _stored_data + la t1, _start_data + la t2, _end_data +1: + bge t1, t2, 2f + LOAD t3, 0(t0) + STORE t3, 0(t1) + addi t0, t0, REGBYTES + addi t1, t1, REGBYTES + j 1b +2: + + /* Initialize the BSS section to 0 */ + la t0, _start_bss + la t1, _end_bss +3: + bge t0, t1, 4f + STORE zero, 0(t0) + addi t0, t0, REGBYTES + j 3b +4: + + /* Run wolfboot - main() should never return */ + j main + +#endif /* __riscv_xlen */ + +/* ============================================================================ * reloc_trap_vector - Relocate trap vector table * * Parameters: @@ -130,9 +204,14 @@ _reset: * * Sets the trap-vector base-address register to (address + 4), * accounting for the header offset in the application image. - */ + * ============================================================================ */ .globl reloc_trap_vector reloc_trap_vector: addi a0, a0, 4 /* address + 1 (uint32_t* = +4 bytes) */ +#if __riscv_xlen == 64 csrw MODE_PREFIX(tvec), a0 +#else + csrw mtvec, a0 +#endif ret + diff --git a/src/update_disk.c b/src/update_disk.c index fa20f2fe7b..dbc832355b 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -47,6 +47,13 @@ #include "elf.h" #endif +/* Disk encryption support for AES-256, AES-128, or ChaCha20 */ +#if defined(ENCRYPT_WITH_AES256) || defined(ENCRYPT_WITH_AES128) || \ + defined(ENCRYPT_WITH_CHACHA) +#define DISK_ENCRYPT +#include "encrypt.h" +#endif + #include #include @@ -83,6 +90,66 @@ #define DISK_BLOCK_SIZE 512 #endif +#ifdef DISK_ENCRYPT +/** + * @brief Decrypt an image header in RAM. + * + * This function decrypts the image header using the configured encryption + * algorithm (AES-256/AES-128 CTR mode or ChaCha20). + * + * @param src Pointer to the encrypted header. + * @param dst Pointer to the destination buffer for decrypted header. + * + * @return 0 if successful, -1 on failure. + */ +static int decrypt_header(const uint8_t *src, uint8_t *dst) +{ + uint32_t i; + uint32_t magic; + + for (i = 0; i < IMAGE_HEADER_SIZE; i += ENCRYPT_BLOCK_SIZE) { + wolfBoot_crypto_set_iv(NULL, i / ENCRYPT_BLOCK_SIZE); + crypto_decrypt(dst + i, src + i, ENCRYPT_BLOCK_SIZE); + } + magic = *((uint32_t*)dst); + if (magic != WOLFBOOT_MAGIC) + return -1; + return 0; +} + +/** + * @brief Decrypt an image in RAM. + * + * This function decrypts the full image (header + firmware) using the + * configured encryption algorithm. The decryption is done in-place. + * + * @param data Pointer to the encrypted image data. + * @param size Size of the image (header + firmware). + * + * @return 0 if successful, -1 on failure. + */ +static int decrypt_image(uint8_t *data, uint32_t size) +{ + uint32_t iv_counter = 0; + uint32_t offset = 0; + uint8_t dec_block[ENCRYPT_BLOCK_SIZE]; + + while (offset < size) { + uint32_t chunk = size - offset; + if (chunk > ENCRYPT_BLOCK_SIZE) + chunk = ENCRYPT_BLOCK_SIZE; + + wolfBoot_crypto_set_iv(NULL, iv_counter); + crypto_decrypt(dec_block, data + offset, chunk); + memcpy(data + offset, dec_block, chunk); + + offset += ENCRYPT_BLOCK_SIZE; + iv_counter++; + } + return 0; +} +#endif /* DISK_ENCRYPT */ + extern int wolfBoot_get_dts_size(void *dts_addr); #if defined(WOLFBOOT_NO_LOAD_ADDRESS) || !defined(WOLFBOOT_LOAD_ADDRESS) @@ -100,6 +167,9 @@ extern uint8_t _end_wb[]; void RAMFUNCTION wolfBoot_start(void) { uint8_t p_hdr[IMAGE_HEADER_SIZE] XALIGNED_STACK(16); +#ifdef DISK_ENCRYPT + uint8_t dec_hdr[IMAGE_HEADER_SIZE] XALIGNED_STACK(16); +#endif #ifdef WOLFBOOT_FSP struct stage2_parameter *stage2_params; #endif @@ -116,6 +186,15 @@ void RAMFUNCTION wolfBoot_start(void) uint32_t dts_size = 0; #endif char part_name[4] = {'P', ':', 'X', '\0'}; + uint64_t start_us, elapsed_ms; + +#ifdef DISK_ENCRYPT + /* Initialize encryption */ + if (wolfBoot_initialize_encryption() != 0) { + wolfBoot_printf("Error initializing encryption\r\n"); + wolfBoot_panic(); + } +#endif ret = disk_init(BOOT_DISK); if (ret != 0) { @@ -131,14 +210,26 @@ void RAMFUNCTION wolfBoot_start(void) BOOT_PART_A); if (disk_part_read(BOOT_DISK, BOOT_PART_A, 0, IMAGE_HEADER_SIZE, p_hdr) == IMAGE_HEADER_SIZE) { +#ifdef DISK_ENCRYPT + if (decrypt_header(p_hdr, dec_hdr) == 0) { + pA_ver = wolfBoot_get_blob_version(dec_hdr); + } +#else pA_ver = wolfBoot_get_blob_version((uint8_t*)p_hdr); +#endif } wolfBoot_printf("Checking secondary OS image in %d,%d...\r\n", BOOT_DISK, BOOT_PART_B); if (disk_part_read(BOOT_DISK, BOOT_PART_B, 0, IMAGE_HEADER_SIZE, p_hdr) == IMAGE_HEADER_SIZE) { +#ifdef DISK_ENCRYPT + if (decrypt_header(p_hdr, dec_hdr) == 0) { + pB_ver = wolfBoot_get_blob_version(dec_hdr); + } +#else pB_ver = wolfBoot_get_blob_version((uint8_t*)p_hdr); +#endif } if ((pB_ver == 0) && (pA_ver == 0)) { @@ -208,6 +299,7 @@ void RAMFUNCTION wolfBoot_start(void) /* Read the image into RAM */ wolfBoot_printf("Loading image from disk..."); + start_us = hal_get_timer_us(); load_off = 0; do { ret = disk_part_read(BOOT_DISK, cur_part, load_off, @@ -223,7 +315,23 @@ void RAMFUNCTION wolfBoot_start(void) selected ^= 1; continue; } - wolfBoot_printf("done.\r\n"); + elapsed_ms = (hal_get_timer_us() - start_us) / 1000; + wolfBoot_printf("done. (%lu ms)\r\n", (unsigned long)elapsed_ms); + +#ifdef DISK_ENCRYPT + /* Decrypt the image in RAM */ + wolfBoot_printf("Decrypting image..."); + start_us = hal_get_timer_us(); + ret = decrypt_image((uint8_t*)load_address, + os_image.fw_size + IMAGE_HEADER_SIZE); + if (ret != 0) { + wolfBoot_printf("Error decrypting image\r\n"); + selected ^= 1; + continue; + } + elapsed_ms = (hal_get_timer_us() - start_us) / 1000; + wolfBoot_printf("done. (%lu ms)\r\n", (unsigned long)elapsed_ms); +#endif memset(&os_image, 0, sizeof(os_image)); ret = wolfBoot_open_image_address(&os_image, (void*)load_address); @@ -234,21 +342,25 @@ void RAMFUNCTION wolfBoot_start(void) } wolfBoot_printf("Checking image integrity..."); + start_us = hal_get_timer_us(); if (wolfBoot_verify_integrity(&os_image) != 0) { wolfBoot_printf("Error validating integrity for %s\r\n", part_name); selected ^= 1; continue; } - wolfBoot_printf("done.\r\n"); + elapsed_ms = (hal_get_timer_us() - start_us) / 1000; + wolfBoot_printf("done. (%lu ms)\r\n", (unsigned long)elapsed_ms); wolfBoot_printf("Verifying image signature..."); + start_us = hal_get_timer_us(); if (wolfBoot_verify_authenticity(&os_image) != 0) { wolfBoot_printf("Error validating authenticity for %s\r\n", part_name); selected ^= 1; continue; } else { - wolfBoot_printf("done.\r\n"); + elapsed_ms = (hal_get_timer_us() - start_us) / 1000; + wolfBoot_printf("done. (%lu ms)\r\n", (unsigned long)elapsed_ms); failures = 0; break; /* Success case */ } diff --git a/src/vector_riscv.S b/src/vector_riscv.S index b2f5b09b5f..a79f89a900 100644 --- a/src/vector_riscv.S +++ b/src/vector_riscv.S @@ -1,5 +1,5 @@ /** - * RISC-V bootup + * RISC-V vector table (32-bit and 64-bit unified) * Copyright (C) 2025 wolfSSL Inc. * * This file is part of wolfBoot. @@ -19,122 +19,238 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ +#include "hal/riscv.h" + +#ifdef TARGET_mpfs250 +#include "hal/mpfs250.h" +#endif + +/* ============================================================================ + * Trap Entry/Exit Macros + * ============================================================================ */ + +#if __riscv_xlen == 64 + +/* RV64: Save all caller-saved registers and call handle_trap */ .macro trap_entry - addi sp, sp, -64 - sw x1, 0(sp) - sw x5, 4(sp) - sw x6, 8(sp) - sw x7, 12(sp) - sw x10, 16(sp) - sw x11, 20(sp) - sw x12, 24(sp) - sw x13, 28(sp) - sw x14, 32(sp) - sw x15, 36(sp) - sw x16, 40(sp) - sw x17, 44(sp) - sw x28, 48(sp) - sw x29, 52(sp) - sw x30, 56(sp) - sw x31, 60(sp) + addi sp, sp, -32 * REGBYTES + STORE x1, 1 * REGBYTES(sp) + STORE x2, 2 * REGBYTES(sp) + STORE x3, 3 * REGBYTES(sp) + STORE x4, 4 * REGBYTES(sp) + STORE x5, 5 * REGBYTES(sp) + STORE x6, 6 * REGBYTES(sp) + STORE x7, 7 * REGBYTES(sp) + STORE x8, 8 * REGBYTES(sp) + STORE x9, 9 * REGBYTES(sp) + STORE x10, 10 * REGBYTES(sp) + STORE x11, 11 * REGBYTES(sp) + STORE x12, 12 * REGBYTES(sp) + STORE x13, 13 * REGBYTES(sp) + STORE x14, 14 * REGBYTES(sp) + STORE x15, 15 * REGBYTES(sp) + STORE x16, 16 * REGBYTES(sp) + STORE x17, 17 * REGBYTES(sp) + STORE x18, 18 * REGBYTES(sp) + STORE x19, 19 * REGBYTES(sp) + STORE x20, 20 * REGBYTES(sp) + STORE x21, 21 * REGBYTES(sp) + STORE x22, 22 * REGBYTES(sp) + STORE x23, 23 * REGBYTES(sp) + STORE x24, 24 * REGBYTES(sp) + STORE x25, 25 * REGBYTES(sp) + STORE x26, 26 * REGBYTES(sp) + STORE x27, 27 * REGBYTES(sp) + STORE x28, 28 * REGBYTES(sp) + STORE x29, 29 * REGBYTES(sp) + STORE x30, 30 * REGBYTES(sp) + STORE x31, 31 * REGBYTES(sp) +#ifdef WOLFBOOT_RISCV_SMODE + csrr a0, scause + csrr a1, sepc + csrr a2, stval +#else + csrr a0, mcause + csrr a1, mepc + csrr a2, mtval +#endif + mv a3, sp + jal handle_trap +#ifdef WOLFBOOT_RISCV_SMODE + csrw sepc, a0 +#else + csrw mepc, a0 +#endif +.endm + +.macro trap_exit + LOAD x1, 1 * REGBYTES(sp) + LOAD x3, 3 * REGBYTES(sp) + LOAD x4, 4 * REGBYTES(sp) + LOAD x5, 5 * REGBYTES(sp) + LOAD x6, 6 * REGBYTES(sp) + LOAD x7, 7 * REGBYTES(sp) + LOAD x8, 8 * REGBYTES(sp) + LOAD x9, 9 * REGBYTES(sp) + LOAD x10, 10 * REGBYTES(sp) + LOAD x11, 11 * REGBYTES(sp) + LOAD x12, 12 * REGBYTES(sp) + LOAD x13, 13 * REGBYTES(sp) + LOAD x14, 14 * REGBYTES(sp) + LOAD x15, 15 * REGBYTES(sp) + LOAD x16, 16 * REGBYTES(sp) + LOAD x17, 17 * REGBYTES(sp) + LOAD x18, 18 * REGBYTES(sp) + LOAD x19, 19 * REGBYTES(sp) + LOAD x20, 20 * REGBYTES(sp) + LOAD x21, 21 * REGBYTES(sp) + LOAD x22, 22 * REGBYTES(sp) + LOAD x23, 23 * REGBYTES(sp) + LOAD x24, 24 * REGBYTES(sp) + LOAD x25, 25 * REGBYTES(sp) + LOAD x26, 26 * REGBYTES(sp) + LOAD x27, 27 * REGBYTES(sp) + LOAD x28, 28 * REGBYTES(sp) + LOAD x29, 29 * REGBYTES(sp) + LOAD x30, 30 * REGBYTES(sp) + LOAD x31, 31 * REGBYTES(sp) + LOAD x2, 2 * REGBYTES(sp) + addi sp, sp, 32 * REGBYTES +#ifdef WOLFBOOT_RISCV_SMODE + sret +#else + mret +#endif .endm +#else /* __riscv_xlen == 32 */ + +/* RV32: Save caller-saved registers (minimal set) */ +.macro trap_entry + addi sp, sp, -64 + STORE x1, 0(sp) + STORE x5, 4(sp) + STORE x6, 8(sp) + STORE x7, 12(sp) + STORE x10, 16(sp) + STORE x11, 20(sp) + STORE x12, 24(sp) + STORE x13, 28(sp) + STORE x14, 32(sp) + STORE x15, 36(sp) + STORE x16, 40(sp) + STORE x17, 44(sp) + STORE x28, 48(sp) + STORE x29, 52(sp) + STORE x30, 56(sp) + STORE x31, 60(sp) +.endm .macro trap_exit - lw x1, 0(sp) - lw x5, 4(sp) - lw x6, 8(sp) - lw x7, 12(sp) - lw x10, 16(sp) - lw x11, 20(sp) - lw x12, 24(sp) - lw x13, 28(sp) - lw x14, 32(sp) - lw x15, 36(sp) - lw x16, 40(sp) - lw x17, 44(sp) - lw x28, 48(sp) - lw x29, 52(sp) - lw x30, 56(sp) - lw x31, 60(sp) - addi sp, sp, 64 - mret + LOAD x1, 0(sp) + LOAD x5, 4(sp) + LOAD x6, 8(sp) + LOAD x7, 12(sp) + LOAD x10, 16(sp) + LOAD x11, 20(sp) + LOAD x12, 24(sp) + LOAD x13, 28(sp) + LOAD x14, 32(sp) + LOAD x15, 36(sp) + LOAD x16, 40(sp) + LOAD x17, 44(sp) + LOAD x28, 48(sp) + LOAD x29, 52(sp) + LOAD x30, 56(sp) + LOAD x31, 60(sp) + addi sp, sp, 64 + mret .endm +#endif /* __riscv_xlen */ + +/* ============================================================================ + * Vector Table + * ============================================================================ */ + .section .isr_vector .align 8 +#if __riscv_xlen == 64 +.global trap_vector_table +trap_vector_table: +#else .global IV - IV: +#endif j _synctrap - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN j trap_empty - .align 2 + .align VECTOR_ALIGN _synctrap: - trap_entry - trap_exit + trap_entry + trap_exit trap_empty: nop diff --git a/src/vector_riscv64.S b/src/vector_riscv64.S deleted file mode 100644 index a960f841e6..0000000000 --- a/src/vector_riscv64.S +++ /dev/null @@ -1,179 +0,0 @@ -/** - * RISC-V 64-bit vector table - * Copyright (C) 2025 wolfSSL Inc. - * - * This file is part of wolfBoot. - * - * wolfBoot is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * wolfBoot is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA - */ - -#include "hal/mpfs250.h" - -/* RISC-V 64-bit trap/exception handling macros */ -.macro trap_entry - addi sp, sp, -32 * REGBYTES - sd x1, 1 * REGBYTES(sp) - sd x2, 2 * REGBYTES(sp) - sd x3, 3 * REGBYTES(sp) - sd x4, 4 * REGBYTES(sp) - sd x5, 5 * REGBYTES(sp) - sd x6, 6 * REGBYTES(sp) - sd x7, 7 * REGBYTES(sp) - sd x8, 8 * REGBYTES(sp) - sd x9, 9 * REGBYTES(sp) - sd x10, 10 * REGBYTES(sp) - sd x11, 11 * REGBYTES(sp) - sd x12, 12 * REGBYTES(sp) - sd x13, 13 * REGBYTES(sp) - sd x14, 14 * REGBYTES(sp) - sd x15, 15 * REGBYTES(sp) - sd x16, 16 * REGBYTES(sp) - sd x17, 17 * REGBYTES(sp) - sd x18, 18 * REGBYTES(sp) - sd x19, 19 * REGBYTES(sp) - sd x20, 20 * REGBYTES(sp) - sd x21, 21 * REGBYTES(sp) - sd x22, 22 * REGBYTES(sp) - sd x23, 23 * REGBYTES(sp) - sd x24, 24 * REGBYTES(sp) - sd x25, 25 * REGBYTES(sp) - sd x26, 26 * REGBYTES(sp) - sd x27, 27 * REGBYTES(sp) - sd x28, 28 * REGBYTES(sp) - sd x29, 29 * REGBYTES(sp) - sd x30, 30 * REGBYTES(sp) - sd x31, 31 * REGBYTES(sp) - csrr a0, MODE_PREFIX(cause) - csrr a1, MODE_PREFIX(epc) - csrr a2, MODE_PREFIX(tval) - mv a3, sp - jal handle_trap - csrw MODE_PREFIX(epc), a0 -.endm - -.macro trap_exit - ld x1, 1 * REGBYTES(sp) - ld x3, 3 * REGBYTES(sp) - ld x4, 4 * REGBYTES(sp) - ld x5, 5 * REGBYTES(sp) - ld x6, 6 * REGBYTES(sp) - ld x7, 7 * REGBYTES(sp) - ld x8, 8 * REGBYTES(sp) - ld x9, 9 * REGBYTES(sp) - ld x10, 10 * REGBYTES(sp) - ld x11, 11 * REGBYTES(sp) - ld x12, 12 * REGBYTES(sp) - ld x13, 13 * REGBYTES(sp) - ld x14, 14 * REGBYTES(sp) - ld x15, 15 * REGBYTES(sp) - ld x16, 16 * REGBYTES(sp) - ld x17, 17 * REGBYTES(sp) - ld x18, 18 * REGBYTES(sp) - ld x19, 19 * REGBYTES(sp) - ld x20, 20 * REGBYTES(sp) - ld x21, 21 * REGBYTES(sp) - ld x22, 22 * REGBYTES(sp) - ld x23, 23 * REGBYTES(sp) - ld x24, 24 * REGBYTES(sp) - ld x25, 25 * REGBYTES(sp) - ld x26, 26 * REGBYTES(sp) - ld x27, 27 * REGBYTES(sp) - ld x28, 28 * REGBYTES(sp) - ld x29, 29 * REGBYTES(sp) - ld x30, 30 * REGBYTES(sp) - ld x31, 31 * REGBYTES(sp) - ld x2, 2 * REGBYTES(sp) - addi sp, sp, 32 * REGBYTES - mret -.endm - -.section .isr_vector -.align 8 - -.global trap_vector_table -trap_vector_table: - j _synctrap - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - j trap_empty - .align 3 - -_synctrap: - trap_entry - trap_exit - -trap_empty: - nop diff --git a/src/x86/common.c b/src/x86/common.c index 4f0b8ac880..051d8c896b 100644 --- a/src/x86/common.c +++ b/src/x86/common.c @@ -263,6 +263,37 @@ void delay(int msec) io_write8(0x80, 0x41); } +/* x86 TSC (Time Stamp Counter) frequency in MHz + * Default 2 GHz, can be overridden in target configuration */ +#ifndef X86_TSC_FREQ_MHZ +#define X86_TSC_FREQ_MHZ 2000 +#endif + +/** + * @brief Read the x86 Time Stamp Counter (TSC). + * + * @return The current TSC value as a 64-bit integer. + */ +static inline uint64_t rdtsc(void) +{ + uint32_t lo, hi; + __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); + return ((uint64_t)hi << 32) | lo; +} + +/** + * @brief Get the current timer value in microseconds. + * + * Uses the x86 TSC for timing measurements. The TSC frequency + * is configured via X86_TSC_FREQ_MHZ (default 2000 MHz). + * + * @return The current time in microseconds. + */ +uint64_t hal_get_timer_us(void) +{ + return rdtsc() / X86_TSC_FREQ_MHZ; +} + /** * @brief Enter an infinite loop, causing a panic state. * @@ -441,9 +472,9 @@ int x86_run_fsp_32bit(void* api, void* arg) /* save status */ "mov %%ebx, %[status]\n" :[status]"=m"(status) - :[SEG_COMP]"i"(GDT_CS_32BIT_COMPAT), + :[SEG_COMP]"i"(GDT_CS_32BIT_COMPAT), [SEG_LONG]"i"(GDT_CS_64BIT), - [PG]"i"(CR0_PG_BIT), + [PG]"i"(CR0_PG_BIT), [LME]"i"(IA32_EFER_LME), [PAE]"i"(CR4_PAE_BIT), [api]"r"(api),