diff --git a/.gitignore b/.gitignore index 5e682bf9fe..357d6b6b0f 100644 --- a/.gitignore +++ b/.gitignore @@ -444,3 +444,6 @@ aarch64_efi-stage/ tools/qemu-esp/ # UEFI Secure Boot keys/certs generated by tools/scripts/sign-efi-secureboot.sh tools/efi-secureboot-keys/ + +# TI cl2000 (ARCH=C2000) intermediate assembly listings left in the repo root +/*.asm diff --git a/Makefile b/Makefile index 158e8655e1..2e813de921 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,10 @@ ifneq ($(LIBERO_FPGA_CONFIG_DIR),) endif CFLAGS:=-D"__WOLFBOOT" +# gcc/clang warning flags; the TI cl2000 driver (ARCH=C2000) rejects them. +ifneq ($(ARCH),C2000) CFLAGS+=-Werror -Wextra -Wno-array-bounds +endif LSCRIPT:=config/target.ld LSCRIPT_FLAGS:= LDFLAGS:= @@ -238,9 +241,12 @@ $(WOLFHSM_OBJS): CFLAGS += -Wno-error=unused-parameter CFLAGS+= \ -I"." -I"include/" -I"$(WOLFBOOT_LIB_WOLFSSL)" \ - -Wno-array-bounds \ -D"WOLFSSL_USER_SETTINGS" \ -D"WOLFTPM_USER_SETTINGS" +# -Wno-array-bounds is a gcc/clang option; the TI cl2000 driver rejects it. +ifneq ($(ARCH),C2000) +CFLAGS+=-Wno-array-bounds +endif CFLAGS+=$(WOLFPSA_CFLAGS) # Setup default optimizations (for GCC) @@ -260,6 +266,10 @@ endif ifeq ($(TARGET),ti_hercules) LSCRIPT_FLAGS+=--run_linker $(LSCRIPT) endif +ifeq ($(ARCH),C2000) + # cl2000 enters link mode via -z (in LDFLAGS); the .cmd is a positional arg. + LSCRIPT_FLAGS+=$(LSCRIPT) +endif ifeq ($(ARCH),AURIX_TC3) ifneq ($(USE_GCC_HEADLESS),1) LSCRIPT_FLAGS+=-T $(LSCRIPT) @@ -376,6 +386,12 @@ ifeq ($(TARGET),tegra234) MAIN_TARGET:=wolfboot.bin test-app/image_v1_signed.bin endif +ifeq ($(TARGET),f28p55x) + # C28x flash is word-addressed; DSLite loads the cl2000 .out (ELF) directly. + # No objcopy / no flat .bin. + MAIN_TARGET:=wolfboot.elf +endif + ifeq ($(TARGET),sim) CFLAGS+=-fno-pie LDFLAGS+=-no-pie @@ -721,6 +737,7 @@ keys: $(PRIVATE_KEY) clean: $(Q)rm -f src/*.o hal/*.o hal/spi/*.o hal/uart/*.o test-app/*.o src/x86/*.o $(Q)rm -f src/wolfboot_tz_nsc.o + $(Q)rm -f *.asm # TI cl2000 (ARCH=C2000) intermediate listings in repo root $(Q)rm -f $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/*.o $(WOLFBOOT_LIB_WOLFTPM)/src/*.o $(WOLFBOOT_LIB_WOLFTPM)/src/fwtpm/*.o $(WOLFBOOT_LIB_WOLFTPM)/hal/*.o $(WOLFBOOT_LIB_WOLFTPM)/examples/pcr/*.o $(Q)rm -f $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/Renesas/*.o $(Q)rm -f wolfboot.bin wolfboot.elf wolfboot.map test-update.rom wolfboot.hex wolfboot.srec factory.srec diff --git a/arch.mk b/arch.mk index 867e9ac4f6..4f32675f18 100644 --- a/arch.mk +++ b/arch.mk @@ -2065,6 +2065,67 @@ ifeq ($(ARCH),sim) endif endif +# TI C2000 C28x DSP (TMS320F28P550SJ / LAUNCHXL-F28P55X), cl2000 toolchain. +# Word-addressed, CHAR_BIT==16. Modeled on the ti_hercules (armcl) TI-CGT flow. +ifeq ($(ARCH),C2000) + # cl2000 is not gcc: turn off the gcc/headless CFLAGS+LDFLAGS blocks that + # follow the arch.mk include (Makefile ~line 247) before they are evaluated. + USE_GCC:=0 + USE_GCC_HEADLESS:=0 + + C2000WARE?=$(HOME)/ti/C2000Ware_26_01_00_00 + ifeq ($(CGT_ROOT),) + $(error Set CGT_ROOT to a TI C2000 codegen install (the dir with bin/cl2000)) + endif + C2000_DEV:=$(C2000WARE)/device_support/f28p55x + C2000_DRV:=$(C2000WARE)/driverlib/f28p55x/driverlib + C2000_FAPI:=$(C2000WARE)/libraries/flash_api/f28p55x + + CC=$(CGT_ROOT)/bin/cl2000 + LD=$(CGT_ROOT)/bin/cl2000 + AS=$(CGT_ROOT)/bin/cl2000 + AR=$(CGT_ROOT)/bin/ar2000 + OUTPUT_FLAG=--output_file + + # --float_support/--abi must match the prebuilt driverlib.lib + Fapi lib (EABI). + ARCH_FLAGS=-v28 --float_support=fpu32 --tmu_support=tmu1 --abi=eabi \ + --gen_func_subsections=on + # Set the level here so options.mk emits -O2 (matching cl2000) instead of its + # default gcc-only -Os, which would otherwise be appended after our flags. + OPTIMIZATION_LEVEL=2 + CFLAGS+=$(ARCH_FLAGS) -D_LAUNCHXL_F28P55X -D_FLASH \ + -I$(CGT_ROOT)/include -I$(C2000_DRV) \ + -I$(C2000_DEV)/common/include -I$(C2000_DEV)/headers/include \ + -I$(C2000_FAPI)/include -I$(C2000_FAPI)/include/FlashAPI + # The C28x has no 8-bit type, so ISO omits int8_t/uint8_t; supply + # them (as 16-bit) via a preinclude for every TU. #303 is the harmless + # "typedef already declared (same type)" clash with driverlib's hw_types.h. + # #169 is the expected uint8_t*(=uint16_t*) vs wolfSSL byte*(=unsigned char*) + # pointer mismatch; both are 16-bit cells holding one octet, so it is safe. + CFLAGS+=--preinclude=c2000_stdint.h --diag_suppress=303 --diag_suppress=169 + LDFLAGS+=$(ARCH_FLAGS) -z --reread_libs --warn_sections \ + -i$(CGT_ROOT)/lib -i$(C2000_DRV)/ccs/Release -i$(C2000_FAPI)/lib \ + -m wolfboot.map + LD_START_GROUP:= + LD_END_GROUP:=-l driverlib.lib -l FAPI_F28P55x_EABI_v4.00.00.lib -l libc.a + ARCH_FLASH_OFFSET=0x80000 + + # TI device startup: reset codestart -> _c_int00 (RTS) -> main. + OBJS+=$(C2000_DEV)/common/source/device.o + OBJS+=$(C2000_DEV)/common/source/f28p55x_codestartbranch.o + OBJS+=src/boot_c2000.o + + ifeq ($(SPMATH),1) + # SECP256R1 fast SP path (wide-byte hand-patched octet masks live here). + MATH_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sp_c32.o + endif + + # TI assembler sources use the .asm suffix. +%.o:%.asm + @echo "\t[AS-C2000] $@" + $(Q)$(CC) $(CFLAGS) -c $(OUTPUT_FLAG) $@ $^ +endif + # Infineon AURIX Tricore ifeq ($(ARCH), AURIX_TC3) # TC3xx specific diff --git a/config/examples/f28p55x.config b/config/examples/f28p55x.config new file mode 100644 index 0000000000..90a77d140b --- /dev/null +++ b/config/examples/f28p55x.config @@ -0,0 +1,54 @@ +# TI LAUNCHXL-F28P55X (TMS320F28P550SJ, C2000 C28x DSP) - secure-boot MVP +# +# The C28x is a word-addressed, CHAR_BIT==16 DSP built with the TI cl2000 +# compiler. wolfBoot runs from flash bank0 and XIP-boots a signed application +# resident in bank1. See docs/Targets.md and the port notes in hal/f28p55x.c. +# +# Build (point CGT_ROOT at a TI C2000 codegen install, C2000WARE at C2000Ware): +# cp config/examples/f28p55x.config .config +# make CGT_ROOT=$HOME/ti/ccs/ccs/tools/compiler/ti-cgt-c2000_ \ +# C2000WARE=$HOME/ti/C2000Ware_26_01_00_00 +# +# Flash offset (bank0 codestart) is fixed by the arch in arch.mk: +# ARCH_FLASH_OFFSET=0x80000 + +ARCH?=C2000 +TARGET?=f28p55x +SIGN?=ECC256 +HASH?=SHA256 +DEBUG?=0 +# Route wolfBoot_printf to SCIA (GPIO28/29 -> XDS110 virtual COM, 115200 8N1) +DEBUG_UART?=1 +VTOR?=0 +CORTEX_M0?=0 +NO_ASM?=1 +NO_MPU?=1 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +# Fapi programs whole flash words with ECC; single-byte trailer writes are not +# possible, so use the whole-sector read-modify-write cache path. +NVM_FLASH_WRITEONCE?=1 +WOLFBOOT_VERSION?=0 +V?=0 +# ECC P-256 verify via SP single-precision math (32-bit words, sp_c32.c). +SPMATH?=1 +# HAL flash program/erase (Fapi) must execute from RAM. +RAM_CODE?=1 +DUALBANK_SWAP?=0 + +# Flash bank map (word addresses; hardware banks are 0x80000, 0xA0000, 0xC0000, +# 0xE0000, 0x100000). wolfBoot owns bank0; BOOT is bank1, UPDATE bank2, SWAP +# bank3 (UPDATE/SWAP are declared for the phase-2 A/B update path and are not +# exercised by the MVP verify+jump). The signed app's codestart is linked at +# WOLFBOOT_PARTITION_BOOT_ADDRESS + IMAGE_HEADER_SIZE = 0xA0100. +# +# WOLFBOOT_SECTOR_SIZE must be >= IMAGE_HEADER_SIZE and match the Fapi erase +# granularity. Confirm the 2KB F28P55x sector in words against the Fapi sector +# table before relying on the phase-2 erase/trailer path; 0x800 is a safe +# conservative value for the MVP (no erase/write on the boot-critical path). +WOLFBOOT_SECTOR_SIZE?=0x800 +WOLFBOOT_PARTITION_SIZE?=0x20000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0xA0000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0xC0000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0xE0000 diff --git a/hal/f28p55x.c b/hal/f28p55x.c new file mode 100644 index 0000000000..37eef1c68f --- /dev/null +++ b/hal/f28p55x.c @@ -0,0 +1,223 @@ +/* f28p55x.c + * + * HAL for the TI LAUNCHXL-F28P55X (TMS320F28P550SJ, C2000 C28x DSP). + * + * wolfBoot runs from flash bank0 (0x80000) in a word-addressed, CHAR_BIT==16 + * environment built with the TI cl2000 compiler. It verifies a signed image + * resident in bank1 (BOOT partition) and branches to it in place (XIP); see + * src/boot_c2000.c for the handoff and docs/Targets.md for the flash map. + * + * Clocks/flash-waitstates/GPIO come from the C2000Ware device support + * (Device_init); flash program/erase use the TI Flash API (Fapi) and must run + * from RAM (RAMFUNCTION -> .TI.ramfunc). The console is SCIA on GPIO28/29 + * (the XDS110 virtual COM, 115200 8N1), wired to wolfBoot_printf via DEBUG_UART. + * + * Copyright (C) 2026 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 +#include "image.h" + +#include "driverlib.h" +#include "device.h" +#include "FlashTech.h" + +/* Flash controller base the Fapi library operates on (matches Flash_initModule + * in Device_init). Confirm against the Fapi sector table for the phase-2 + * program/erase path. */ +#ifndef WOLFBOOT_C2000_FLASH_BASE +#define WOLFBOOT_C2000_FLASH_BASE FLASH0CTRL_BASE +#endif + +/* Fapi programs a 64-bit (8 x 16-bit word) main-array slice at a time. */ +#define C2000_FLASH_PGM_WORDS 8 + +/* --------------------------------------------------------------------- */ +/* SCIA console (XDS110 virtual COM) - wolfBoot_printf via DEBUG_UART */ +/* --------------------------------------------------------------------- */ +/* Debug mirror of all UART output into a RAM buffer, so boot progress can be + * read back over JTAG (symbols g_log / g_logpos) independent of SCI flow (an + * attached debugger garbles/stalls the XDS110 backchannel). Placed in a + * NOINIT section (RAMGS_SURV, 0xD000) that neither wolfBoot's nor the booted app's .bss + * clears, so the boot log survives the do_boot() handoff and can be read back + * after the app is running (g_logpos is reset in uart_init). */ +#define WOLF_LOG_SZ 2048 +#pragma DATA_SECTION(g_log, ".survivelog") +#pragma DATA_SECTION(g_logpos, ".survivelog") +volatile char g_log[WOLF_LOG_SZ]; +volatile unsigned long g_logpos; + +void uart_init(void) +{ + g_logpos = 0; /* reset the survive-log at each boot */ + /* RX pin */ + GPIO_setPinConfig(DEVICE_GPIO_CFG_SCIRXDA); + GPIO_setDirectionMode(DEVICE_GPIO_PIN_SCIRXDA, GPIO_DIR_MODE_IN); + GPIO_setPadConfig(DEVICE_GPIO_PIN_SCIRXDA, GPIO_PIN_TYPE_STD); + GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCIRXDA, GPIO_QUAL_ASYNC); + + /* TX pin */ + GPIO_setPinConfig(DEVICE_GPIO_CFG_SCITXDA); + GPIO_setDirectionMode(DEVICE_GPIO_PIN_SCITXDA, GPIO_DIR_MODE_OUT); + GPIO_setPadConfig(DEVICE_GPIO_PIN_SCITXDA, GPIO_PIN_TYPE_STD); + GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCITXDA, GPIO_QUAL_ASYNC); + + SCI_performSoftwareReset(SCIA_BASE); + SCI_setConfig(SCIA_BASE, DEVICE_LSPCLK_FREQ, 115200, + (SCI_CONFIG_WLEN_8 | SCI_CONFIG_STOP_ONE | SCI_CONFIG_PAR_NONE)); + SCI_resetChannels(SCIA_BASE); + SCI_resetRxFIFO(SCIA_BASE); + SCI_resetTxFIFO(SCIA_BASE); + SCI_enableFIFO(SCIA_BASE); + SCI_enableModule(SCIA_BASE); + SCI_performSoftwareReset(SCIA_BASE); +} + +/* Bounded-spin SCI put: wait a limited time for FIFO space, then drop. This + * flushes cleanly when the JTAG probe is detached (FIFO drains) yet never + * stalls the CPU when a debug session is holding the XDS110 backchannel. */ +static void sci_putc(uint16_t c) +{ + uint32_t spin = 20000; + while ((SCI_getTxFIFOStatus(SCIA_BASE) == SCI_FIFO_TX16) && (spin-- > 0)) + ; + /* If the bounded wait expired with the FIFO still full, drop the byte + * rather than writing into a full FIFO (a stalled backchannel must not + * wedge the console). */ + if (SCI_getTxFIFOStatus(SCIA_BASE) == SCI_FIFO_TX16) + return; + SCI_writeCharNonBlocking(SCIA_BASE, c); +} + +void uart_write(const char *buf, unsigned int sz) +{ + unsigned int i; + for (i = 0; i < sz; i++) { + if (g_logpos < (unsigned long)sizeof(g_log)) + g_log[g_logpos++] = buf[i]; + if (buf[i] == '\n') + sci_putc((uint16_t)'\r'); + sci_putc((uint16_t)(buf[i] & 0xFF)); + } +} + +/* --------------------------------------------------------------------- */ +/* Flash API (Fapi) helpers - execute from RAM */ +/* --------------------------------------------------------------------- */ +void RAMFUNCTION hal_flash_unlock(void) +{ + /* (Re)initialize the Flash API for the active system frequency and select + * bank0 as the FMC context. Fapi has no global write-enable; per-command + * sector protection is cleared in the write/erase paths. */ + (void)Fapi_initializeAPI((Fapi_FmcRegistersType *)WOLFBOOT_C2000_FLASH_BASE, + DEVICE_SYSCLK_FREQ / 1000000U); + (void)Fapi_setActiveFlashBank(Fapi_FlashBank0); +} + +void RAMFUNCTION hal_flash_lock(void) +{ + /* No persistent lock state to restore for Fapi. */ +} + +int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) +{ + /* address is a C28x flash word address; data cells each hold one octet + * (the octet-per-cell header/trailer storage). Program 8 words per Fapi + * command; pad a short tail with 0xFFFF (leaves those cells erased). */ + const uint16_t *src = (const uint16_t *)data; + uint32_t addr = address; + int remaining = len; + Fapi_StatusType st; + uint16_t block[C2000_FLASH_PGM_WORDS]; + int i; + + while (remaining > 0) { + for (i = 0; i < C2000_FLASH_PGM_WORDS; i++) { + if (i < remaining) + block[i] = src[i]; + else + block[i] = 0xFFFFU; + } + st = Fapi_issueProgrammingCommand((uint32 *)addr, (uint16 *)block, + C2000_FLASH_PGM_WORDS, 0, 0, + Fapi_AutoEccGeneration); + if (st != Fapi_Status_Success) + return -1; + while (Fapi_checkFsmForReady() == Fapi_Status_FsmBusy) + ; + if (Fapi_getFsmStatus() != 3) + return -1; + addr += C2000_FLASH_PGM_WORDS; + src += C2000_FLASH_PGM_WORDS; + remaining -= C2000_FLASH_PGM_WORDS; + } + return 0; +} + +int RAMFUNCTION hal_flash_erase(uint32_t address, int len) +{ + /* Erase every flash sector overlapping [address, address+len). The Fapi + * sector granularity is WOLFBOOT_SECTOR_SIZE (confirm against the Fapi + * sector table). */ + uint32_t addr = address; + uint32_t end = address + (uint32_t)len; + Fapi_StatusType st; + + while (addr < end) { + st = Fapi_issueAsyncCommandWithAddress(Fapi_EraseSector, + (uint32 *)addr); + if (st != Fapi_Status_Success) + return -1; + while (Fapi_checkFsmForReady() != Fapi_Status_FsmReady) + ; + if (Fapi_getFsmStatus() != 3) + return -1; + addr += WOLFBOOT_SECTOR_SIZE; + } + return 0; +} + +/* --------------------------------------------------------------------- */ +/* wolfBoot HAL entry points */ +/* --------------------------------------------------------------------- */ +#ifdef __WOLFBOOT +void hal_init(void) +{ + /* Device_init: 150 MHz PLL, flash wait states, and the .TI.ramfunc + * copy-to-RAM (RamfuncsLoadStart -> RamfuncsRunStart). */ + Device_init(); + Device_initGPIO(); + uart_init(); + /* Prepare the Flash API for any later program/erase (phase-2 updates). */ + hal_flash_unlock(); +} + +void hal_prepare_boot(void) +{ + uint32_t spin = 200000; + /* Quiesce before the XIP handoff: drain the SCI TX FIFO so the last log + * line makes it out, then mask interrupts (do_boot also does DINT). + * Bounded so a stalled FIFO (e.g. JTAG holding the backchannel) cannot + * hang the handoff. */ + while ((SCI_getTxFIFOStatus(SCIA_BASE) != SCI_FIFO_TX0) && (spin-- > 0)) + ; + DINT; +} +#endif /* __WOLFBOOT */ diff --git a/hal/f28p55x.ld b/hal/f28p55x.ld new file mode 100644 index 0000000000..2eb62ebff6 --- /dev/null +++ b/hal/f28p55x.ld @@ -0,0 +1,87 @@ +/* f28p55x.ld (TI cl2000 linker command file, tokenized by "make config") + * + * wolfBoot linker layout for the TI TMS320F28P550SJ (LAUNCHXL-F28P55X). + * wolfBoot owns flash bank0 only (@ARCH_FLASH_OFFSET@, @BOOTLOADER_PARTITION_SIZE@ + * bytes); banks 1-4 hold the BOOT/UPDATE/SWAP partitions and are NOT linked + * here (the resident application is linked separately at its BOOT address). + * + * The C28x stack pointer is 16-bit, so .stack must live below 0x10000. + * Flash program/erase code (RAMFUNCTION -> .TI.ramfunc) is loaded from flash + * and copied to RAM by Device_init's Ramfuncs memcpy. + * + * Copyright (C) 2026 wolfSSL Inc. GPLv3 - see project headers. + */ + +-stack 0x4000 /* 16 KW C28x stack (full RAMLS0-7, 0x8000..0xBFFF); below + * 0x10000. ECC P-256 verify keeps the ecc_key struct and the + * SP *_NO_MALLOC digit buffers on the stack (WOLFSSL_NO_MALLOC, + * no heap); 8 KW then 12 KW overflowed into the adjacent + * .TI.ramfunc region, corrupting saved frames (wild return -> + * ITRAP / hang). Matches the validated wolfCrypt example's + * 16 KW stack. */ +-heap 0x1000 /* 4 KW heap (unused: WOLFSSL_NO_MALLOC) */ + +MEMORY +{ + BEGIN : origin = @ARCH_FLASH_OFFSET@, length = 0x000002 + + BOOT_RSVD : origin = 0x000002, length = 0x000126 /* M0, boot-ROM stack */ + RAMM0 : origin = 0x000128, length = 0x0002D8 + RAMM1 : origin = 0x000400, length = 0x000400 + + /* Low RAM (< 0x10000). The 16-bit-SP stack fills RAMLS0-7 (16 KW). + * .TI.ramfunc RUNs from RAMGS0 (0xC000): on a cold flash-boot only RAMGS0 + * (and RAMLS0-5) are CPU instruction-fetchable early - RAMLS6-7 default to + * CLA ownership, so Flash_initModule() (called from Device_init before any + * MemCfg) ITRAPs if placed there. This matches the wolfCrypt example. */ + RAMLS_STACK : origin = 0x008000, length = 0x004000 /* RAMLS0-7, 16 KW */ + RAMGS_RAMCODE : origin = 0x00C000, length = 0x001000 /* RAMGS0 lo, .TI.ramfunc */ + RAMGS_SURV : origin = 0x00D000, length = 0x001000 /* survive-log (persists across do_boot) */ + RAMGS_HEAP : origin = 0x00E000, length = 0x002000 /* RAMGS1, heap (unused) */ + + /* High RAM (>= 0x10000): data only. */ + RAMGS_HI : origin = 0x010000, length = 0x004000 /* RAMGS2-3, 16 KW */ + RAMLS_HI : origin = 0x014000, length = 0x004000 /* RAMLS8-9, 16 KW */ + + /* wolfBoot code + const live in flash bank0 only. */ + FLASH_BANK0 : origin = 0x080002, length = 0x01FFFE + + RESET : origin = 0x3FFFC0, length = 0x000002 +} + +SECTIONS +{ + /* _start_text marks the base of the wolfBoot image (used by update_flash.c + * under RAM_CODE for the bootloader region / self-hash). */ + codestart : > BEGIN, START(_start_text) + + /* RAM-resident flash program/erase routines: loaded from flash, copied to + * RAM by Device_init. --gen_func_subsections=on makes each function its + * own input section; the ramfunc attribute places them in .TI.ramfunc. */ + .TI.ramfunc : LOAD = FLASH_BANK0, + RUN = RAMGS_RAMCODE, + LOAD_START(RamfuncsLoadStart), + LOAD_SIZE(RamfuncsLoadSize), + LOAD_END(RamfuncsLoadEnd), + RUN_START(RamfuncsRunStart), + RUN_SIZE(RamfuncsRunSize), + RUN_END(RamfuncsRunEnd), + ALIGN(8) + + .text : > FLASH_BANK0, ALIGN(8), END(_end_text) + .cinit : > FLASH_BANK0, ALIGN(8) + .switch : > FLASH_BANK0, ALIGN(8) + .init_array : > FLASH_BANK0, ALIGN(8) + .const : > FLASH_BANK0, ALIGN(8) + /* wolfBoot public-key keystore (src/keystore.c) */ + .keystore : > FLASH_BANK0, ALIGN(8) + .reset : > RESET, TYPE = DSECT /* unused; boot ROM owns it */ + + .stack : > RAMLS_STACK + + .bss : >> RAMGS_HI | RAMLS_HI + .bss:output : > RAMGS_HI + .data : >> RAMGS_HI | RAMLS_HI + .sysmem : > RAMGS_HEAP + .survivelog : > RAMGS_SURV, type = NOINIT +} diff --git a/include/c2000_stdint.h b/include/c2000_stdint.h new file mode 100644 index 0000000000..791bbe4663 --- /dev/null +++ b/include/c2000_stdint.h @@ -0,0 +1,44 @@ +/* c2000_stdint.h + * + * Force-included (--preinclude) for the TI C2000 C28x (ARCH=C2000) build. + * + * The C28x is word-addressed with CHAR_BIT==16 and has NO 8-bit integer type, + * so ISO correctly does not define int8_t/uint8_t on this target. + * wolfBoot (and TI's own driverlib hw_types.h) represents an octet in a 16-bit + * cell, so provide the exact-width 8-bit aliases as 16-bit types. When a + * translation unit also pulls in driverlib's hw_types.h, its identical + * typedefs produce diagnostic #303, suppressed for this arch in arch.mk. + * + * Copyright (C) 2026 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 WOLFBOOT_C2000_STDINT_H +#define WOLFBOOT_C2000_STDINT_H + +#include + +/* Guarded to the C28x: this header is --preinclude'd only for ARCH=C2000, but + * the guard keeps the 8-bit aliases from ever redefining a real int8_t/uint8_t + * should it be pulled into a normal-byte translation unit. */ +#if defined(__TMS320C28XX__) +typedef uint16_t uint8_t; +typedef int16_t int8_t; +#endif + +#endif /* WOLFBOOT_C2000_STDINT_H */ diff --git a/include/user_settings.h b/include/user_settings.h index 791a675467..f44314dab0 100644 --- a/include/user_settings.h +++ b/include/user_settings.h @@ -43,6 +43,33 @@ #define HAVE_EMPTY_AGGREGATES 0 #define HAVE_ANONYMOUS_INLINE_AGGREGATES 0 +/* TI C2000 C28x: word-addressed, 16-bit int, CHAR_BIT==16. wolfCrypt's + * WOLFSSL_WIDE_BYTE support auto-enables on __TMS320C28XX__; configure the + * integer widths, pull in (for CHAR_BIT and sp_int size detection), + * and disable asm/inline paths that don't apply. */ +#if defined(WOLFBOOT_ARCH_C2000) || defined(__TMS320C28XX__) +# undef SIZEOF_LONG +# define SIZEOF_LONG 4 +# undef HAVE_LIMITS_H +# define HAVE_LIMITS_H +# undef WC_16BIT_CPU +# define WC_16BIT_CPU +# undef WOLFSSL_GENERAL_ALIGNMENT +# define WOLFSSL_GENERAL_ALIGNMENT 2 +# undef WOLFSSL_NO_ASM +# define WOLFSSL_NO_ASM +# undef WC_SHA3_NO_ASM +# define WC_SHA3_NO_ASM + /* cl2000 treats plain inline as C99 extern-inline, leaving misc.c helpers + * unresolved at link; make them ordinary extern functions (misc.o linked). */ +# undef NO_INLINE +# define NO_INLINE +# undef WOLFSSL_SP_ALLOW_16BIT_CPU +# define WOLFSSL_SP_ALLOW_16BIT_CPU +# undef WOLFSSL_SP_NO_MALLOC +# define WOLFSSL_SP_NO_MALLOC +#endif + /* Stdlib Types */ #define CTYPE_USER /* don't let wolfCrypt types.h include ctype.h */ @@ -465,8 +492,10 @@ extern int tolower(int c); # define SP_WORD_SIZE 32 # endif - /* SP Math needs to understand long long */ -# ifndef ULLONG_MAX + /* SP Math needs to understand long long. Skip this fallback when limits.h + * is available (HAVE_LIMITS_H), which defines ULLONG_MAX itself - otherwise + * the two definitions clash (e.g. TI cl2000 / CHAR_BIT!=8 builds). */ +# if !defined(ULLONG_MAX) && !defined(HAVE_LIMITS_H) # define ULLONG_MAX 18446744073709551615ULL # endif #endif diff --git a/include/wolfboot/wolfboot.h b/include/wolfboot/wolfboot.h index 19fb54e7e5..14a133edaa 100644 --- a/include/wolfboot/wolfboot.h +++ b/include/wolfboot/wolfboot.h @@ -59,6 +59,12 @@ extern "C" { # endif # elif defined(ARCH_PPC) # define RAMFUNCTION __attribute__((used,section(".ramcode"),longcall)) +# elif defined(__TMS320C28XX__) + /* TI C2000 cl2000: place in .TI.ramfunc; the linker LOAD/RUN pair + the + * device startup Ramfuncs memcpy relocate it to RAM (see hal/f28p55x.ld). + * Gated to the C28x specifically so the ti_hercules (armcl) .ramcode + * path above is not affected. */ +# define RAMFUNCTION __attribute__((ramfunc)) # else # define RAMFUNCTION __attribute__((used,section(".ramcode"))) # endif @@ -68,7 +74,7 @@ extern "C" { #endif #ifndef WEAKFUNCTION -# if defined(__GNUC__) || defined(__CC_ARM) +# if defined(__GNUC__) || defined(__CC_ARM) || defined(__TMS320C28XX__) # define WEAKFUNCTION __attribute__((weak)) # else # define WEAKFUNCTION @@ -101,7 +107,7 @@ extern "C" { /* Helpers for memory alignment */ #ifndef XALIGNED #if defined(__GNUC__) || defined(__llvm__) || \ - defined(__IAR_SYSTEMS_ICC__) + defined(__IAR_SYSTEMS_ICC__) || defined(__TMS320C28XX__) #define XALIGNED(x) __attribute__ ( (aligned (x))) #elif defined(__KEIL__) #define XALIGNED(x) __align(x) @@ -168,7 +174,37 @@ extern "C" { # endif #endif /* IMAGE_HEADER_SIZE */ -#define IMAGE_HEADER_OFFSET (2 * sizeof(uint32_t)) + +/* Image-header fixed-field access. + * + * The header's serialized 32-/16-bit fields (magic, size, version, type) are a + * stream of octets. On normal targets one octet == one addressable byte and a + * u32 field is sizeof(uint32_t)==4 units. On the C28x (CHAR_BIT==16, header + * stored one octet per 16-bit cell) a u32 field is 4 octets == 4 cells, but + * sizeof(uint32_t) is only 2 cells - so field sizes/offsets must be counted in + * octets and the values reconstructed octet-by-octet (a uint32_t cast would + * span just two cells). WOLFBOOT_HDR_U32_SZ/U16_SZ give the octet width used + * for both pointer offsets and find_header() length checks. */ +/* A fixed header field is a little-endian octet stream. Reconstruct it from + * individually masked cells rather than a single (possibly unaligned) load: + * - on the C28x each octet occupies one 16-bit cell (CHAR_BIT==16), so a cell + * may carry non-octet upper bits - the & 0xFF keeps the value octet-exact; + * - on every other target the & 0xFF is a no-op and byte reconstruction avoids + * an unaligned 32/16-bit load (wolfBoot_find_header only guarantees 2-byte + * alignment) and is endian-neutral. + * The _SZ macros are the field's octet width, used for both pointer offsets and + * find_header() length checks (4 octets == 4 cells on the C28x). */ +#define WOLFBOOT_HDR_U32_SZ 4 +#define WOLFBOOT_HDR_U16_SZ 2 +#define WOLFBOOT_HDR_GET_U32(p) \ + (((uint32_t)(((const uint8_t*)(p))[0] & 0xFF)) | \ + ((uint32_t)(((const uint8_t*)(p))[1] & 0xFF) << 8) | \ + ((uint32_t)(((const uint8_t*)(p))[2] & 0xFF) << 16) | \ + ((uint32_t)(((const uint8_t*)(p))[3] & 0xFF) << 24)) +#define WOLFBOOT_HDR_GET_U16(p) \ + ((uint16_t)((((const uint8_t*)(p))[0] & 0xFF) | \ + ((((const uint8_t*)(p))[1] & 0xFF) << 8))) +#define IMAGE_HEADER_OFFSET (2 * WOLFBOOT_HDR_U32_SZ) #ifndef FLASHBUFFER_SIZE # ifdef NVM_FLASH_WRITEONCE diff --git a/lib/wolfssl b/lib/wolfssl index 5418d6cfdc..e71b086fc8 160000 --- a/lib/wolfssl +++ b/lib/wolfssl @@ -1 +1 @@ -Subproject commit 5418d6cfdc4acd126983331a498ba2a8c70839c7 +Subproject commit e71b086fc83758ca05bf80f7896fa6d770a1787a diff --git a/src/boot_c2000.c b/src/boot_c2000.c new file mode 100644 index 0000000000..29c5d81033 --- /dev/null +++ b/src/boot_c2000.c @@ -0,0 +1,65 @@ +/* boot_c2000.c + * + * Architecture boot handoff for the TI C2000 C28x DSP (TMS320F28P550SJ). + * + * The reset/startup path is provided by the C2000Ware codestart + * (f28p55x_codestartbranch.asm -> _c_int00 -> main); wolfBoot's main() + * (src/loader.c) then runs hal_init() and the verify state machine. This file + * provides the two arch hooks wolfBoot requires: do_boot(), which branches to + * the verified application resident in the BOOT partition (execute-in-place), + * and arch_reboot(). + * + * Copyright (C) 2026 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 "hal.h" + +#include "driverlib.h" +#include "device.h" + +/* + * Branch to the verified application. + * + * app_offset is the firmware base (BOOT partition address + IMAGE_HEADER_SIZE), + * i.e. the application's own codestart, linked to execute in place. There is + * no vector table to reload on the C28x: interrupts are masked here and the + * application's codestart re-establishes its stack pointer and re-runs the + * C-runtime init before calling its main(). This never returns. + */ +void do_boot(const uint32_t *app_offset) +{ + void (*app_entry)(void); + + DINT; /* mask maskable interrupts across the handoff */ + + app_entry = (void (*)(void))(uintptr_t)app_offset; + app_entry(); + + /* Not reached. */ + while (1) + ; +} + +void arch_reboot(void) +{ + SysCtl_resetDevice(); + while (1) + ; +} diff --git a/src/image.c b/src/image.c index 50458ab9ea..11bef5e446 100644 --- a/src/image.c +++ b/src/image.c @@ -282,7 +282,17 @@ static void wolfBoot_verify_signature_ecc(uint8_t key_slot, struct wolfBoot_image *img, uint8_t *sig) { int ret, verify_res = 0; +#if defined(__TMS320C28XX__) || defined(WOLFBOOT_ARCH_C2000) + /* C28x: the ecc_key struct is large relative to the 16-bit-SP low-RAM stack + * (WOLFSSL_NO_MALLOC keeps SP-256 verify temporaries on the stack too), so + * keep it in .bss to avoid overflowing the stack into adjacent RAM during + * verify. wolfBoot verifies images sequentially and wc_ecc_init_ex/ + * wc_ecc_free bracket each use, so a single shared instance is safe. The + * mp_ints r/s are small and stay on the stack, freshly mp_init'd per call. */ + static ecc_key ecc; +#else ecc_key ecc; +#endif mp_int r, s; #if !defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) && \ !defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) @@ -1103,7 +1113,7 @@ static int header_sha256(wc_Sha256 *sha256_ctx, struct wolfBoot_image *img) stored_sha_len = get_header(img, HDR_SHA256, &stored_sha); if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE) return -1; - end_sha = stored_sha - (2 * sizeof(uint16_t)); /* Subtract 2 Type + 2 Len */ + end_sha = stored_sha - (2 * WOLFBOOT_HDR_U16_SZ); /* Subtract 2 Type + 2 Len */ #ifdef WOLFBOOT_IMG_HASH_ONESHOT if (end_sha <= p) { return -1; @@ -1140,7 +1150,39 @@ static int image_sha256(struct wolfBoot_image *img, uint8_t *hash) if (header_sha256(&sha256_ctx, img) != 0) return -1; -#ifdef WOLFBOOT_IMG_HASH_ONESHOT +#if defined(WOLFBOOT_ARCH_C2000) + /* C28x (CHAR_BIT==16): the firmware is stored as native, executable 16-bit + * program words, but the host signed an octet stream in which each program + * word was serialized low-octet-then-high-octet. Reproduce that ordering + * so the on-target digest matches the host's. img->fw_size is the octet + * count (2 octets per program word); each buf[] cell holds one octet, and + * the wide-byte wc_Sha256Update consumes one octet per cell. */ + { + const uint16_t *w = (const uint16_t *)img->fw_base; + uint32_t position = 0; + uint8_t buf[64]; /* even; each cell holds one octet */ + int n; + uint16_t val; + if (img->fw_base == NULL) { + wc_Sha256Free(&sha256_ctx); + return -1; + } + while (position < img->fw_size) { + n = 0; + while ((n <= (int)sizeof(buf) - 2) && (position < img->fw_size)) { + val = *w++; + buf[n++] = (uint8_t)(val & 0xFF); /* low octet */ + position++; + if (position < img->fw_size) { + buf[n++] = (uint8_t)((val >> 8) & 0xFF); /* high octet */ + position++; + } + } + wc_Sha256Update(&sha256_ctx, buf, n); + wolfBoot_watchdog_feed(); + } + } +#elif defined(WOLFBOOT_IMG_HASH_ONESHOT) if (img->fw_base == NULL) { wc_Sha256Free(&sha256_ctx); return -1; @@ -1212,7 +1254,7 @@ static int header_sha384(wc_Sha384 *sha384_ctx, struct wolfBoot_image *img) stored_sha_len = get_header(img, HDR_SHA384, &stored_sha); if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE) return -1; - end_sha = stored_sha - (2 * sizeof(uint16_t)); /* Subtract 2 Type + 2 Len */ + end_sha = stored_sha - (2 * WOLFBOOT_HDR_U16_SZ); /* Subtract 2 Type + 2 Len */ #ifdef WOLFBOOT_IMG_HASH_ONESHOT if (end_sha <= p) { return -1; @@ -1334,7 +1376,7 @@ static int header_sha3_384(wc_Sha3 *sha3_ctx, struct wolfBoot_image *img) stored_sha_len = get_header(img, HDR_SHA3_384, &stored_sha); if (stored_sha_len != WOLFBOOT_SHA_DIGEST_SIZE) return -1; - end_sha = stored_sha - (2 * sizeof(uint16_t)); /* Subtract 2 Type + 2 Len */ + end_sha = stored_sha - (2 * WOLFBOOT_HDR_U16_SZ); /* Subtract 2 Type + 2 Len */ #ifdef WOLFBOOT_IMG_HASH_ONESHOT if (end_sha <= p) { return -1; @@ -1462,8 +1504,7 @@ static inline uint32_t im2n(uint32_t val) */ uint32_t wolfBoot_image_size(uint8_t *image) { - uint32_t *size = (uint32_t *)(image + sizeof (uint32_t)); - return im2n(*size); + return im2n(WOLFBOOT_HDR_GET_U32(image + WOLFBOOT_HDR_U32_SZ)); } /** @@ -1481,10 +1522,10 @@ uint32_t wolfBoot_image_size(uint8_t *image) */ int wolfBoot_open_image_address(struct wolfBoot_image *img, uint8_t *image) { - uint32_t *magic = (uint32_t *)(image); - if (*magic != WOLFBOOT_MAGIC) { + uint32_t magic = WOLFBOOT_HDR_GET_U32(image); + if (magic != WOLFBOOT_MAGIC) { wolfBoot_printf("Partition %d header magic 0x%08x invalid at %p\n", - img->part, (unsigned int)*magic, img->hdr); + img->part, (unsigned int)magic, img->hdr); return -1; } img->fw_size = wolfBoot_image_size(image); @@ -1784,7 +1825,7 @@ int wolfBoot_open_self_address(struct wolfBoot_image* img, uint8_t* hdr, XMEMSET(img, 0, sizeof(struct wolfBoot_image)); - magic = *((uint32_t*)hdr); + magic = WOLFBOOT_HDR_GET_U32(hdr); if (magic != WOLFBOOT_MAGIC) { return -1; } @@ -2487,7 +2528,7 @@ int wolfBoot_verify_authenticity(struct wolfBoot_image *img) return -1; /* Invalid hash size for public key hint */ } image_type_size = get_header(img, HDR_IMG_TYPE, &image_type_buf); - if (image_type_size != sizeof(uint16_t)) + if (image_type_size != WOLFBOOT_HDR_U16_SZ) return -1; image_type = (uint16_t)(image_type_buf[0] + (image_type_buf[1] << 8)); if ((image_type & HDR_IMG_TYPE_AUTH_MASK) != HDR_IMG_TYPE_AUTH) diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 10f8d584b9..2d6f4605ca 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -1500,7 +1500,6 @@ static int decrypt_header(uint8_t *src) uint32_t wolfBoot_get_blob_version(uint8_t *blob) { uint32_t *volatile version_field = NULL; - uint32_t *magic = NULL; uint8_t *img_bin = blob; if (blob == NULL) return 0; @@ -1510,14 +1509,13 @@ uint32_t wolfBoot_get_blob_version(uint8_t *blob) decrypt_header(blob); img_bin = dec_hdr; #endif - magic = (uint32_t *)img_bin; - if (*magic != WOLFBOOT_MAGIC) + if (WOLFBOOT_HDR_GET_U32(img_bin) != WOLFBOOT_MAGIC) return 0; if (wolfBoot_find_header(img_bin + IMAGE_HEADER_OFFSET, HDR_VERSION, - (void *)&version_field) != sizeof(uint32_t)) + (void *)&version_field) != WOLFBOOT_HDR_U32_SZ) return 0; if (version_field) - return im2n(*version_field); + return im2n(WOLFBOOT_HDR_GET_U32(version_field)); return 0; } @@ -1535,7 +1533,6 @@ uint32_t wolfBoot_get_blob_version(uint8_t *blob) uint16_t wolfBoot_get_blob_type(uint8_t *blob) { uint16_t *volatile type_field = NULL; - uint32_t *magic = NULL; uint8_t *img_bin = blob; #if defined(EXT_ENCRYPTED) && defined(MMU) if (wolfBoot_initialize_encryption() < 0) @@ -1543,14 +1540,13 @@ uint16_t wolfBoot_get_blob_type(uint8_t *blob) decrypt_header(blob); img_bin = dec_hdr; #endif - magic = (uint32_t *)img_bin; - if (*magic != WOLFBOOT_MAGIC) + if (WOLFBOOT_HDR_GET_U32(img_bin) != WOLFBOOT_MAGIC) return 0; if (wolfBoot_find_header(img_bin + IMAGE_HEADER_OFFSET, HDR_IMG_TYPE, - (void *)&type_field) != sizeof(uint16_t)) + (void *)&type_field) != WOLFBOOT_HDR_U16_SZ) return 0; if (type_field) - return im2ns(*type_field); + return im2ns(WOLFBOOT_HDR_GET_U16(type_field)); return 0; } @@ -1636,7 +1632,7 @@ uint8_t* wolfBoot_get_self_header(void) ext_flash_read((uintptr_t)WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS, hdr_buf, IMAGE_HEADER_SIZE); - magic = *((uint32_t*)hdr_buf); + magic = WOLFBOOT_HDR_GET_U32(hdr_buf); if (magic != WOLFBOOT_MAGIC) { return NULL; } @@ -1644,7 +1640,7 @@ uint8_t* wolfBoot_get_self_header(void) return hdr_buf; #else uint8_t* hdr = (uint8_t*)WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS; - uint32_t magic = *((uint32_t*)hdr); + uint32_t magic = WOLFBOOT_HDR_GET_U32(hdr); if (magic != WOLFBOOT_MAGIC) { return NULL; diff --git a/src/string.c b/src/string.c index b9c94491ca..bf31b5669c 100644 --- a/src/string.c +++ b/src/string.c @@ -288,6 +288,15 @@ size_t strlen(const char *s) #define RAMFUNCTION #pragma section FRAM #endif +#if defined(__TMS320C28XX__) + /* On the C28x, the C-runtime device init copies the .TI.ramfunc section into + * RAM using memcpy() at startup. memcpy() must therefore stay in flash - if + * it were a RAMFUNCTION it would live in the not-yet-copied .TI.ramfunc region + * and the copy would call an uninitialized RAM address, ITRAPing on a cold + * flash boot (a JTAG load masks this by pre-copying every section). */ + #undef RAMFUNCTION + #define RAMFUNCTION +#endif void RAMFUNCTION *memcpy(void *dst, const void *src, size_t n) { size_t i; diff --git a/test-app/app_f28p55x.c b/test-app/app_f28p55x.c new file mode 100644 index 0000000000..193a87676c --- /dev/null +++ b/test-app/app_f28p55x.c @@ -0,0 +1,86 @@ +/* app_f28p55x.c + * + * Minimal wolfBoot test application for the TI LAUNCHXL-F28P55X + * (TMS320F28P550SJ, C2000 C28x DSP). + * + * This is the XIP payload wolfBoot verifies and branches to. It is linked so + * its codestart lands at WOLFBOOT_PARTITION_BOOT_ADDRESS + IMAGE_HEADER_SIZE + * (0xA0100, see test-app/f28p55x_app.cmd), which is exactly the firmware base + * do_boot() jumps to. On boot it prints a banner on SCIA (GPIO28/29, the + * XDS110 virtual COM, 115200 8N1) so a successful verify+jump is visible. + * + * Copyright (C) 2026 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 "driverlib.h" +#include "device.h" + +static void sci_init(void) +{ + GPIO_setPinConfig(DEVICE_GPIO_CFG_SCIRXDA); + GPIO_setDirectionMode(DEVICE_GPIO_PIN_SCIRXDA, GPIO_DIR_MODE_IN); + GPIO_setPadConfig(DEVICE_GPIO_PIN_SCIRXDA, GPIO_PIN_TYPE_STD); + GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCIRXDA, GPIO_QUAL_ASYNC); + + GPIO_setPinConfig(DEVICE_GPIO_CFG_SCITXDA); + GPIO_setDirectionMode(DEVICE_GPIO_PIN_SCITXDA, GPIO_DIR_MODE_OUT); + GPIO_setPadConfig(DEVICE_GPIO_PIN_SCITXDA, GPIO_PIN_TYPE_STD); + GPIO_setQualificationMode(DEVICE_GPIO_PIN_SCITXDA, GPIO_QUAL_ASYNC); + + SCI_performSoftwareReset(SCIA_BASE); + SCI_setConfig(SCIA_BASE, DEVICE_LSPCLK_FREQ, 115200, + (SCI_CONFIG_WLEN_8 | SCI_CONFIG_STOP_ONE | SCI_CONFIG_PAR_NONE)); + SCI_resetChannels(SCIA_BASE); + SCI_resetRxFIFO(SCIA_BASE); + SCI_resetTxFIFO(SCIA_BASE); + SCI_enableFIFO(SCIA_BASE); + SCI_enableModule(SCIA_BASE); + SCI_performSoftwareReset(SCIA_BASE); +} + +static void sci_puts(const char *s) +{ + while (*s != '\0') { + if (*s == '\n') + SCI_writeCharBlockingFIFO(SCIA_BASE, (uint16_t)'\r'); + SCI_writeCharBlockingFIFO(SCIA_BASE, (uint16_t)(*s & 0xFF)); + s++; + } +} + +/* The signed wolfBoot header lives at 0xA0000 (see f28p55x_app.cmd); reference + * it so the linker retains the .wolfboot_hdr section in the image. */ +extern const unsigned int wolfboot_header[]; + +int main(void) +{ + volatile long d; + volatile unsigned int hdr0 = wolfboot_header[0]; + (void)hdr0; + + Device_init(); + Device_initGPIO(); + sci_init(); + + for (;;) { + sci_puts("hello from the wolfBoot app on F28P55x\n"); + for (d = 0; d < 4000000; d++) + ; + } +} diff --git a/test-app/f28p55x_app.cmd b/test-app/f28p55x_app.cmd new file mode 100644 index 0000000000..ec5fbcb403 --- /dev/null +++ b/test-app/f28p55x_app.cmd @@ -0,0 +1,67 @@ +/* f28p55x_app.cmd (TI cl2000 linker command file) + * + * Linker layout for the wolfBoot test application on the TI TMS320F28P550SJ. + * The application executes in place from the BOOT partition (flash bank1). Its + * codestart is placed at 0xA0100 = WOLFBOOT_PARTITION_BOOT_ADDRESS (0xA0000) + * + IMAGE_HEADER_SIZE (256 words), which is the firmware base wolfBoot's + * do_boot() branches to. The 256-word signed header occupies 0xA0000..0xA00FF + * (programmed separately from the c2000_flashimg.py header blob). + * + * Copyright (C) 2026 wolfSSL Inc. GPLv3 - see project headers. + */ + +-stack 0x2000 +-heap 0x1000 + +MEMORY +{ + HDR : origin = 0x0A0000, length = 0x000100 /* wolfBoot signed header (256 cells) */ + BEGIN : origin = 0x0A0100, length = 0x000002 /* app codestart */ + + BOOT_RSVD : origin = 0x000002, length = 0x000126 + RAMM0 : origin = 0x000128, length = 0x0002D8 + RAMM1 : origin = 0x000400, length = 0x000400 + + RAMLS_STACK : origin = 0x008000, length = 0x002000 /* RAMLS0-3, stack */ + RAMGS_RAMCODE : origin = 0x00A000, length = 0x002000 /* .TI.ramfunc */ + RAMGS_HEAP : origin = 0x00C000, length = 0x002000 /* RAMGS0-1, heap */ + RAMGS_HI : origin = 0x010000, length = 0x004000 /* RAMGS2-3 */ + RAMLS_HI : origin = 0x014000, length = 0x004000 /* RAMLS8-9 */ + + /* Application flash: rest of BOOT partition bank1 (after codestart), plus + * bank2 if needed. Header cells occupy the low 256 words of bank1. */ + APP_FLASH1 : origin = 0x0A0102, length = 0x01FEFE /* bank1 remainder */ + APP_FLASH2 : origin = 0x0C0000, length = 0x020000 /* bank2 */ + + RESET : origin = 0x3FFFC0, length = 0x000002 +} + +SECTIONS +{ + /* wolfBoot signed header at the BOOT partition base (0xA0000), one octet + * per 16-bit cell; the app codestart follows at 0xA0100 (= fw_base). */ + .wolfboot_hdr : > HDR + codestart : > BEGIN + /* Device_init copies these to RAM (RamfuncsLoadStart -> RamfuncsRunStart). */ + .TI.ramfunc : LOAD = APP_FLASH1, + RUN = RAMGS_RAMCODE, + LOAD_START(RamfuncsLoadStart), + LOAD_SIZE(RamfuncsLoadSize), + LOAD_END(RamfuncsLoadEnd), + RUN_START(RamfuncsRunStart), + RUN_SIZE(RamfuncsRunSize), + RUN_END(RamfuncsRunEnd), + ALIGN(8) + .text : >> APP_FLASH1 | APP_FLASH2, ALIGN(8) + .cinit : > APP_FLASH1 | APP_FLASH2, ALIGN(8) + .switch : > APP_FLASH1 | APP_FLASH2, ALIGN(8) + .init_array : > APP_FLASH1 | APP_FLASH2, ALIGN(8) + .const : >> APP_FLASH1 | APP_FLASH2, ALIGN(8) + .reset : > RESET, TYPE = DSECT + + .stack : > RAMLS_STACK + .bss : >> RAMGS_HI | RAMLS_HI + .bss:output : > RAMGS_HI + .data : >> RAMGS_HI | RAMLS_HI + .sysmem : > RAMGS_HEAP +} diff --git a/test-app/f28p55x_sign.sh b/test-app/f28p55x_sign.sh new file mode 100755 index 0000000000..ce28258d11 --- /dev/null +++ b/test-app/f28p55x_sign.sh @@ -0,0 +1,92 @@ +#!/bin/bash +# f28p55x_sign.sh +# +# Build, sign, and package the wolfBoot C28x test application for the +# TI LAUNCHXL-F28P55X (TMS320F28P550SJ). This documents the full MVP flow: +# compile the XIP app, extract its firmware words as the host octet stream, +# sign it, and synthesize the octet-per-cell header blob. +# +# Prereqs (override via env): +# CGT_ROOT TI C2000 codegen install (dir containing bin/cl2000) +# C2000WARE C2000Ware install (default ~/ti/C2000Ware_26_01_00_00) +# WOLFBOOT wolfBoot root (default: parent of this script's dir) +# +# Copyright (C) 2026 wolfSSL Inc. GPLv3 - see project headers. +set -e + +HERE="$(cd "$(dirname "$0")" && pwd)" +WOLFBOOT="${WOLFBOOT:-$(cd "$HERE/.." && pwd)}" +C2000WARE="${C2000WARE:-$HOME/ti/C2000Ware_26_01_00_00}" +: "${CGT_ROOT:?Set CGT_ROOT to a TI C2000 codegen install (dir with bin/cl2000)}" + +CL="$CGT_ROOT/bin/cl2000" +HEX="$CGT_ROOT/bin/hex2000" +DEV="$C2000WARE/device_support/f28p55x" +DRV="$C2000WARE/driverlib/f28p55x/driverlib" +OUT="$HERE/out_f28p55x" +KEY="$WOLFBOOT/wolfboot_signing_private_key.der" +SIGN="$WOLFBOOT/tools/keytools/sign" +CONV="$WOLFBOOT/tools/scripts/c2000_flashimg.py" + +BOOT_ADDR=0xA0000 # WOLFBOOT_PARTITION_BOOT_ADDRESS +FW_ADDR=0xA0100 # BOOT_ADDR + IMAGE_HEADER_SIZE(256 words) +HDR_SIZE=256 + +mkdir -p "$OUT" + +echo "[1/5] Compile + link the XIP app (codestart at $FW_ADDR)" +"$CL" -v28 --float_support=fpu32 --tmu_support=tmu1 --abi=eabi -O2 \ + --gen_func_subsections=on \ + -D_LAUNCHXL_F28P55X -D_FLASH \ + -I"$CGT_ROOT/include" -I"$DRV" \ + -I"$DEV/common/include" -I"$DEV/headers/include" \ + "$HERE/app_f28p55x.c" \ + "$DEV/common/source/device.c" \ + "$DEV/common/source/f28p55x_codestartbranch.asm" \ + -z --reread_libs --warn_sections \ + -i"$CGT_ROOT/lib" -i"$DRV/ccs/Release" \ + -m "$OUT/app.map" \ + "$HERE/f28p55x_app.cmd" \ + --output_file="$OUT/app.out" \ + -l driverlib.lib -l libc.a + +echo "[2/5] Extract the firmware region as a flat little-endian word image" +# hex2000 -> flat binary of the firmware address range. Each C28x 16-bit word +# is emitted as 2 little-endian host bytes (2 bytes/word), which is exactly the +# octet stream `sign` must hash. VERIFY these hex2000 options against your +# installed TI utility version; the goal is a raw binary of [FW_ADDR..end). +"$HEX" "$OUT/app.out" -o "$OUT/app_fw_words.bin" \ + --memwidth=16 --romwidth=16 --binary \ + --fill=0xFFFF || { + echo "hex2000 flat-binary extraction failed - adjust options for your" + echo "toolchain version (or dump the firmware region another way) so that" + echo "$OUT/app_fw_words.bin is a raw LE 16-bit-word image of [$FW_ADDR..end)." + exit 1 +} + +echo "[3/5] Firmware word image -> host octet stream for signing" +python3 "$CONV" fw2oct "$OUT/app_fw_words.bin" "$OUT/app_fw.oct" + +echo "[4/5] Sign the firmware octet stream (ECC P-256 + SHA-256)" +"$SIGN" --ecc256 --sha256 "$OUT/app_fw.oct" "$KEY" 1 +# sign writes app_fw_v1_signed.bin next to the input +SIGNED="$OUT/app_fw_v1_signed.bin" + +echo "[5/5] Header blob (octet-per-cell) for load address $BOOT_ADDR" +python3 "$CONV" hdr2cells "$SIGNED" "$OUT/header_cells.bin" \ + --header-size "$HDR_SIZE" --addr "$BOOT_ADDR" + +cat <> 8). +# +# 2) hdr2cells - from the signed image (`sign` output = header octets + +# firmware octets), take the fixed-size header and expand each +# header octet into its own 16-bit flash cell (value = octet, +# high byte 0). This "header blob" is flashed at the BOOT +# partition base; the application .out is flashed natively (its +# codestart is linked at BOOT_ADDRESS + IMAGE_HEADER_SIZE), so no +# firmware repack is needed. +# +# Copyright (C) 2026 wolfSSL Inc. GPLv3 - see project headers. + +import argparse +import struct +import sys + + +def read_file(path): + with open(path, "rb") as f: + return f.read() + + +def write_file(path, data): + with open(path, "wb") as f: + f.write(data) + + +def fw2oct(args): + """Firmware native-word image -> host octet stream (low, high per word).""" + words = read_file(args.infile) + if len(words) % 2 != 0: + sys.stderr.write("error: input length %d is not a whole number of " + "16-bit words\n" % len(words)) + return 1 + out = bytearray() + for i in range(0, len(words), 2): + # Input is a little-endian 16-bit-word image (2 bytes/word). + w = struct.unpack_from("> 8) & 0xFF) # high octet + write_file(args.outfile, out) + sys.stderr.write("fw2oct: %d words -> %d octets -> %s\n" + % (len(words) // 2, len(out), args.outfile)) + return 0 + + +def hdr2cells(args): + """Signed image header octets -> C28x flash cells (one octet per word).""" + signed = read_file(args.signed) + hdr_sz = args.header_size + if len(signed) < hdr_sz: + sys.stderr.write("error: signed image (%d) shorter than header size " + "(%d)\n" % (len(signed), hdr_sz)) + return 1 + header = signed[:hdr_sz] + out = bytearray() + for octet in header: + # Each header octet occupies its own 16-bit flash cell (high byte 0), + # so wolfBoot's octet parser reads it back byte-identically. + out += struct.pack(" %d-word blob for load " + "address 0x%X -> %s\n" + % (hdr_sz, hdr_sz, args.addr, args.outfile)) + return 0 + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + sub = ap.add_subparsers(dest="cmd", required=True) + + p1 = sub.add_parser("fw2oct", + help="firmware word-image -> octet stream for sign") + p1.add_argument("infile", help="firmware native-word image (LE 16-bit words)") + p1.add_argument("outfile", help="output octet stream to feed to sign") + p1.set_defaults(func=fw2oct) + + p2 = sub.add_parser("hdr2cells", + help="signed image -> header blob (octet-per-cell)") + p2.add_argument("signed", help="signed image (sign output)") + p2.add_argument("outfile", help="output header cell blob (LE 16-bit words)") + p2.add_argument("--header-size", type=lambda x: int(x, 0), default=256, + help="IMAGE_HEADER_SIZE in octets (default 256 for ECC256)") + p2.add_argument("--addr", type=lambda x: int(x, 0), default=0xA0000, + help="BOOT partition base word address (default 0xA0000)") + p2.set_defaults(func=hdr2cells) + + args = ap.parse_args() + return args.func(args) + + +if __name__ == "__main__": + sys.exit(main())