diff --git a/Makefile b/Makefile index 775822c7..94abd9b1 100644 --- a/Makefile +++ b/Makefile @@ -334,6 +334,10 @@ wolfboot.efi: wolfboot.elf wolfboot.bin: wolfboot.elf @echo "\t[BIN] $@" $(Q)$(OBJCOPY) $(OBJCOPY_FLAGS) -O binary $^ $@ +ifeq ($(TARGET),nxp_lpc54s018m) + @echo "\t[LPC] enhanced boot block" + $(Q)python3 -c "import struct,os;f=open('$@','r+b');sz=os.path.getsize('$@');f.seek(0x24);f.write(struct.pack('<2I',0xEDDC94BD,0x160));f.seek(0x160);f.write(struct.pack('<25I',0xFEEDA5A5,3,0x10000000,sz-4,0,0,0,0,0,0xEDDC94BD,0,0,0,0x001640EF,0,0,0x1301001D,0,0,0,0x00000100,0,0,0x04030050,0x14110D09));f.seek(0);d=f.read(28);w=struct.unpack('<7I',d);s=sum(w)&0xFFFFFFFF;ck=(0x100000000-s)&0xFFFFFFFF;f.seek(0x1C);f.write(struct.pack(' +#include +#include +#include "image.h" + +/* -------------------------------------------------------------------------- */ +/* SPIFI controller registers (base 0x40080000) */ +/* -------------------------------------------------------------------------- */ +#define SPIFI_BASE 0x40080000 +#define SPIFI_CTRL (*(volatile uint32_t *)(SPIFI_BASE + 0x00)) +#define SPIFI_CMD (*(volatile uint32_t *)(SPIFI_BASE + 0x04)) +#define SPIFI_ADDR (*(volatile uint32_t *)(SPIFI_BASE + 0x08)) +#define SPIFI_IDATA (*(volatile uint32_t *)(SPIFI_BASE + 0x0C)) +#define SPIFI_CLIMIT (*(volatile uint32_t *)(SPIFI_BASE + 0x10)) +#define SPIFI_DATA (*(volatile uint32_t *)(SPIFI_BASE + 0x14)) +#define SPIFI_MCMD (*(volatile uint32_t *)(SPIFI_BASE + 0x18)) +#define SPIFI_STAT (*(volatile uint32_t *)(SPIFI_BASE + 0x1C)) + +/* STAT register bits */ +#define SPIFI_STAT_MCINIT (1 << 0) /* Memory command init done */ +#define SPIFI_STAT_CMD (1 << 1) /* Command active */ +#define SPIFI_STAT_RESET (1 << 4) /* Reset in progress */ + +/* CMD register field positions */ +#define SPIFI_CMD_DATALEN(n) ((n) & 0x3FFF) +#define SPIFI_CMD_POLL (1 << 14) +#define SPIFI_CMD_DOUT (1 << 15) /* 1=output, 0=input */ +#define SPIFI_CMD_INTLEN(n) (((n) & 7) << 16) +#define SPIFI_CMD_FIELDFORM(n) (((n) & 3) << 19) +#define SPIFI_CMD_FRAMEFORM(n) (((n) & 7) << 21) +#define SPIFI_CMD_OPCODE(n) (((n) & 0xFF) << 24) + +/* Frame/field format values */ +#define FRAMEFORM_OPCODE_ONLY 1 +#define FRAMEFORM_OPCODE_3ADDR 4 +#define FIELDFORM_ALL_SERIAL 0 +#define FIELDFORM_DATA_QUAD 2 + +/* W25Q32JV flash commands */ +#define W25Q_CMD_WRITE_ENABLE 0x06 +#define W25Q_CMD_READ_STATUS1 0x05 +#define W25Q_CMD_PAGE_PROGRAM 0x02 +#define W25Q_CMD_SECTOR_ERASE 0x20 /* 4KB sector erase */ +#define W25Q_CMD_FAST_READ_QUAD 0x6B /* Quad output fast read */ + +/* W25Q status register bits */ +#define W25Q_STATUS_BUSY 0x01 + +/* Flash geometry */ +#define FLASH_PAGE_SIZE 0x100 /* 256 bytes */ +#define SPIFI_FLASH_BASE 0x10000000 + +static uint8_t flash_page_cache[FLASH_PAGE_SIZE]; + +/* Pre-computed SPIFI CMD register values for each flash operation */ +#define CMD_WRITE_ENABLE \ + (SPIFI_CMD_DOUT | SPIFI_CMD_FRAMEFORM(FRAMEFORM_OPCODE_ONLY) | \ + SPIFI_CMD_OPCODE(W25Q_CMD_WRITE_ENABLE)) + +#define CMD_READ_STATUS \ + (SPIFI_CMD_DATALEN(1) | SPIFI_CMD_POLL | \ + SPIFI_CMD_FRAMEFORM(FRAMEFORM_OPCODE_ONLY) | \ + SPIFI_CMD_OPCODE(W25Q_CMD_READ_STATUS1)) + +#define CMD_SECTOR_ERASE \ + (SPIFI_CMD_DOUT | SPIFI_CMD_FRAMEFORM(FRAMEFORM_OPCODE_3ADDR) | \ + SPIFI_CMD_OPCODE(W25Q_CMD_SECTOR_ERASE)) + +#define CMD_PAGE_PROGRAM \ + (SPIFI_CMD_DATALEN(FLASH_PAGE_SIZE) | SPIFI_CMD_DOUT | \ + SPIFI_CMD_FRAMEFORM(FRAMEFORM_OPCODE_3ADDR) | \ + SPIFI_CMD_OPCODE(W25Q_CMD_PAGE_PROGRAM)) + +/* Memory-mode command: quad output fast read with 1 intermediate (dummy) byte */ +#define MCMD_READ_QUAD \ + (SPIFI_CMD_INTLEN(1) | SPIFI_CMD_FIELDFORM(FIELDFORM_DATA_QUAD) | \ + SPIFI_CMD_FRAMEFORM(FRAMEFORM_OPCODE_3ADDR) | \ + SPIFI_CMD_OPCODE(W25Q_CMD_FAST_READ_QUAD)) + +#ifdef NVM_FLASH_WRITEONCE +# error "wolfBoot LPC54S018M HAL: WRITEONCE not supported on SPIFI flash." +#endif + +/* -------------------------------------------------------------------------- */ +/* Boot-time initialization (runs from flash / XIP) */ +/* -------------------------------------------------------------------------- */ + +#ifdef __WOLFBOOT + +/* Assert hook (in case any remaining SDK code uses assert) */ +void __assert_func(const char *a, int b, const char *c, const char *d) +{ + (void)a; (void)b; (void)c; (void)d; + while (1) + ; +} + +void hal_init(void) +{ + /* The boot ROM has already configured basic clocks and SPIFI for XIP. + * We must NOT reconfigure clocks or SPIFI from flash (XIP) because + * changing the clock source or SPIFI controller while executing from + * SPIFI flash will cause an instruction fetch fault. + * + * Clock and SPIFI reconfiguration can only be done from RAM functions. + * The flash erase/write paths (all RAMFUNCTION) handle SPIFI mode + * switching as needed. + */ +} + +void hal_prepare_boot(void) +{ +} + +#endif /* __WOLFBOOT */ + +/* -------------------------------------------------------------------------- */ +/* SPIFI flash helper functions — all MUST run from RAM */ +/* -------------------------------------------------------------------------- */ + +/* + * Issue a SPIFI command. Exits memory mode if active, waits for ready, + * then writes CMD register. Entirely register-based — no SDK calls. + */ +static void RAMFUNCTION spifi_set_cmd(uint32_t cmd_val) +{ + /* If in memory mode (MCINIT set), reset to exit */ + if (SPIFI_STAT & SPIFI_STAT_MCINIT) { + SPIFI_STAT = SPIFI_STAT_RESET; + while (SPIFI_STAT & SPIFI_STAT_RESET) + ; + } + + /* Wait for any active command to complete */ + while (SPIFI_STAT & SPIFI_STAT_CMD) + ; + + SPIFI_CMD = cmd_val; +} + +/* + * Enter memory-mapped (XIP) mode using quad output fast read. + */ +static void RAMFUNCTION spifi_enter_memmode(void) +{ + /* Wait for any active command */ + while (SPIFI_STAT & SPIFI_STAT_CMD) + ; + + SPIFI_MCMD = MCMD_READ_QUAD; + + /* Wait for memory mode to initialize */ + while (!(SPIFI_STAT & SPIFI_STAT_MCINIT)) + ; +} + +static void RAMFUNCTION spifi_write_enable(void) +{ + spifi_set_cmd(CMD_WRITE_ENABLE); +} + +static void RAMFUNCTION spifi_wait_busy(void) +{ + uint8_t status; + + /* Issue read-status command in poll mode */ + spifi_set_cmd(CMD_READ_STATUS); + do { + status = *(volatile uint8_t *)&SPIFI_DATA; + } while (status & W25Q_STATUS_BUSY); +} + +/* + * Flash write — 256-byte page program via SPIFI + * + * Handles unaligned writes by decomposing into page-aligned operations. + * All flash data goes through a RAM page cache to ensure proper alignment. + */ +int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) +{ + int idx = 0; + uint32_t page_address; + uint32_t offset; + int size; + int i; + + while (idx < len) { + page_address = ((address + idx) / FLASH_PAGE_SIZE) * FLASH_PAGE_SIZE; + if ((address + idx) > page_address) + offset = (address + idx) - page_address; + else + offset = 0; + size = FLASH_PAGE_SIZE - offset; + if (size > (len - idx)) + size = len - idx; + if (size > 0) { + /* Read current page content (flash is memory-mapped) */ + memcpy(flash_page_cache, (void *)(uintptr_t)page_address, + FLASH_PAGE_SIZE); + memcpy(flash_page_cache + offset, data + idx, size); + + /* Write enable */ + spifi_write_enable(); + + /* Set address and issue page program command */ + SPIFI_ADDR = page_address - SPIFI_FLASH_BASE; + spifi_set_cmd(CMD_PAGE_PROGRAM); + + /* Write page data as 32-bit words */ + for (i = 0; i < FLASH_PAGE_SIZE; i += 4) { + uint32_t word; + memcpy(&word, &flash_page_cache[i], 4); + SPIFI_DATA = word; + } + + /* Wait for program to complete */ + spifi_wait_busy(); + + /* Re-enter memory mode */ + spifi_enter_memmode(); + } + idx += size; + } + return 0; +} + +void RAMFUNCTION hal_flash_unlock(void) +{ +} + +void RAMFUNCTION hal_flash_lock(void) +{ +} + +/* + * Flash erase — 4KB sector erase via SPIFI + * + * Address must be aligned to WOLFBOOT_SECTOR_SIZE (4KB). + * Length must be a multiple of WOLFBOOT_SECTOR_SIZE. + */ +int RAMFUNCTION hal_flash_erase(uint32_t address, int len) +{ + uint32_t end = address + len; + + while (address < end) { + /* Write enable before each sector erase */ + spifi_write_enable(); + + /* Set address and issue sector erase command */ + SPIFI_ADDR = address - SPIFI_FLASH_BASE; + spifi_set_cmd(CMD_SECTOR_ERASE); + + /* Wait for erase to complete */ + spifi_wait_busy(); + + address += WOLFBOOT_SECTOR_SIZE; + } + + /* Re-enter memory mode */ + spifi_enter_memmode(); + + return 0; +} diff --git a/hal/nxp_lpc54s018m.ld b/hal/nxp_lpc54s018m.ld new file mode 100644 index 00000000..e1d7086e --- /dev/null +++ b/hal/nxp_lpc54s018m.ld @@ -0,0 +1,54 @@ +MEMORY +{ + FLASH (rx) : ORIGIN = 0x10000000, LENGTH = @BOOTLOADER_PARTITION_SIZE@ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 160K +} + +SECTIONS +{ + .text : + { + _start_text = .; + KEEP(*(.isr_vector)) + . = ALIGN(0x400); + *(.text*) + *(.rodata*) + *(.init*) + *(.fini*) + . = ALIGN(4); + _end_text = .; + } > FLASH + + .edidx : + { + . = ALIGN(4); + *(.ARM.exidx*) + } > FLASH + + _stored_data = .; + + .data : AT (_stored_data) + { + _start_data = .; + KEEP(*(.data*)) + . = ALIGN(4); + KEEP(*(.ramcode)) + . = ALIGN(4); + _end_data = .; + } > RAM + + .bss (NOLOAD) : + { + _start_bss = .; + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _end_bss = .; + __bss_end__ = .; + _end = .; + } > RAM + . = ALIGN(4); +} + +END_STACK = ORIGIN(RAM) + LENGTH(RAM); diff --git a/test-app/ARM-nxp_lpc54s018m.ld b/test-app/ARM-nxp_lpc54s018m.ld new file mode 100644 index 00000000..b6826d6b --- /dev/null +++ b/test-app/ARM-nxp_lpc54s018m.ld @@ -0,0 +1,58 @@ +MEMORY +{ + FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K +} + +SECTIONS +{ + .text : + { + _start_text = .; + KEEP(*(.isr_vector)) + *(.init) + *(.fini) + *(.text*) + KEEP(*(.rodata*)) + . = ALIGN(4); + _end_text = .; + } > FLASH + + .ARM : + { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } > FLASH + + _stored_data = .; + + .data : AT (_stored_data) + { + _start_data = .; + KEEP(*(.data*)) + . = ALIGN(4); + KEEP(*(.ramcode)) + . = ALIGN(4); + _end_data = .; + } > RAM + + .bss : + { + _start_bss = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + _end_bss = .; + _end = .; + } > RAM +} + +_wolfboot_partition_boot_address = @WOLFBOOT_PARTITION_BOOT_ADDRESS@; +_wolfboot_partition_size = @WOLFBOOT_PARTITION_SIZE@; +_wolfboot_partition_update_address = @WOLFBOOT_PARTITION_UPDATE_ADDRESS@; +_wolfboot_partition_swap_address = @WOLFBOOT_PARTITION_SWAP_ADDRESS@; + +PROVIDE(_start_heap = _end); +PROVIDE(end = _end); +PROVIDE(_end_stack = ORIGIN(RAM) + LENGTH(RAM)); diff --git a/test-app/Makefile b/test-app/Makefile index bb50facb..1b4f1c07 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -699,6 +699,16 @@ ifeq ($(TARGET),lpc55s69) LDFLAGS+=-Wl,--no-warn-rwx-segments endif +ifeq ($(TARGET),nxp_lpc54s018m) + LSCRIPT_TEMPLATE=ARM-nxp_lpc54s018m.ld + # Enable RAMFUNCTION for test app (flash ops must run from RAM) + CFLAGS+=-DRAM_CODE -D__WOLFBOOT + ifeq (,$(findstring nosys.specs,$(LDFLAGS))) + LDFLAGS+=--specs=nosys.specs + endif + LDFLAGS+=-Wl,--no-warn-rwx-segments +endif + ifeq ($(TARGET),imx_rt) LDFLAGS+=\ -mcpu=cortex-m7 -Wall --specs=nosys.specs -fno-common -ffunction-sections -fdata-sections \ diff --git a/test-app/app_nxp_lpc54s018m.c b/test-app/app_nxp_lpc54s018m.c new file mode 100644 index 00000000..bf75872d --- /dev/null +++ b/test-app/app_nxp_lpc54s018m.c @@ -0,0 +1,133 @@ +/* app_nxp_lpc54s018m.c + * + * Test application for LPC54S018M-EVK + * + * 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 +#include "target.h" +#include "wolfboot/wolfboot.h" + +/* LPC54S018M-EVK GPIO register definitions */ +/* GPIO base: 0x4008C000 */ +#define GPIO_BASE 0x4008C000 +#define GPIO_DIR(port) (*(volatile uint32_t *)(GPIO_BASE + 0x2000 + (port) * 4)) +#define GPIO_SET(port) (*(volatile uint32_t *)(GPIO_BASE + 0x2200 + (port) * 4)) +#define GPIO_CLR(port) (*(volatile uint32_t *)(GPIO_BASE + 0x2280 + (port) * 4)) + +/* SYSCON base for clock gating */ +#define SYSCON_BASE 0x40000000 +#define SYSCON_AHBCLKCTRL0 (*(volatile uint32_t *)(SYSCON_BASE + 0x200)) + +/* AHB clock control bits for GPIO ports (AHBCLKCTRL[0]) */ +#define AHBCLKCTRL0_GPIO2 (1UL << 16) +#define AHBCLKCTRL0_GPIO3 (1UL << 17) + +/* LPC54S018M-EVK User LEDs (accent LEDs active low) */ +#define LED1_PORT 3 +#define LED1_PIN 14 /* USR_LED1: P3.14 */ +#define LED2_PORT 3 +#define LED2_PIN 3 /* USR_LED2: P3.3 */ +#define LED3_PORT 2 +#define LED3_PIN 2 /* USR_LED3: P2.2 */ + +static void leds_init(void) +{ + /* Enable GPIO port 2 and 3 clocks */ + SYSCON_AHBCLKCTRL0 |= AHBCLKCTRL0_GPIO2 | AHBCLKCTRL0_GPIO3; + + /* Set LED pins as output, initially off (high = off for active-low LEDs) */ + GPIO_SET(LED1_PORT) = (1UL << LED1_PIN); + GPIO_DIR(LED1_PORT) |= (1UL << LED1_PIN); + + GPIO_SET(LED2_PORT) = (1UL << LED2_PIN); + GPIO_DIR(LED2_PORT) |= (1UL << LED2_PIN); + + GPIO_SET(LED3_PORT) = (1UL << LED3_PIN); + GPIO_DIR(LED3_PORT) |= (1UL << LED3_PIN); +} + +static void led_on(int port, int pin) +{ + GPIO_CLR(port) = (1UL << pin); /* Active low */ +} + +static void led_off(int port, int pin) +{ + GPIO_SET(port) = (1UL << pin); +} + +void main(void) +{ + uint32_t boot_ver, update_ver; + uint8_t boot_state, update_state; + + /* Clock and SPIFI already initialized by wolfBoot before jump to app */ + leds_init(); + + boot_ver = wolfBoot_current_firmware_version(); + update_ver = wolfBoot_update_firmware_version(); + if (wolfBoot_get_partition_state(PART_BOOT, &boot_state) != 0) + boot_state = IMG_STATE_NEW; + if (wolfBoot_get_partition_state(PART_UPDATE, &update_state) != 0) + update_state = IMG_STATE_NEW; + + /* Show LED indicator first (before flash writes which may crash if + * RAMFUNCTION isn't working — e.g. SPIFI XIP conflict) */ + if (boot_ver == 1) { + led_on(LED1_PORT, LED1_PIN); + } + else { + led_on(LED2_PORT, LED2_PIN); + } + + /* Confirm boot if state is TESTING or NEW */ + if (boot_ver != 0 && + (boot_state == IMG_STATE_TESTING || boot_state == IMG_STATE_NEW)) + { + wolfBoot_success(); + } + + if (boot_ver == 1 && update_ver != 0) { + /* Update available: LED3 on to indicate update activity */ + led_on(LED3_PORT, LED3_PIN); + wolfBoot_update_trigger(); + } + + while (1) { + __asm__ volatile ("wfi"); + } +} + + +#include "sys/stat.h" +int _getpid(void) { return 1; } +int _kill(int pid, int sig) { (void)pid; (void)sig; return -1; } +void _exit(int status) { _kill(status, -1); while (1) {} } +int _read(int file, char *ptr, int len) + { (void)file; (void)ptr; (void)len; return -1; } +int _write(int file, char *ptr, int len) + { (void)file; (void)ptr; return len; } +int _close(int file) { (void)file; return -1; } +int _isatty(int file) { (void)file; return 1; } +int _lseek(int file, int ptr, int dir) + { (void)file; (void)ptr; (void)dir; return 0; } +int _fstat(int file, struct stat *st) + { (void)file; st->st_mode = S_IFCHR; return 0; }