diff --git a/.gitignore b/.gitignore index b1bc7fb6ad..89809fee7c 100644 --- a/.gitignore +++ b/.gitignore @@ -133,6 +133,7 @@ tools/tpm/pcr_extend tools/tpm/policy_create tools/tpm/policy_sign config/*.ld +config/*.sct test-lib lib-fs diff --git a/Makefile b/Makefile index 2051b7900a..e409c9c43b 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,8 @@ SIGN_ALG= OBJCOPY_FLAGS= BIG_ENDIAN?=0 USE_CLANG?=0 -ifeq ($(USE_CLANG),1) +USE_ARMCLANG?=0 +ifneq ($(filter 1,$(USE_CLANG) $(USE_ARMCLANG)),) USE_GCC?=0 else USE_GCC?=1 @@ -251,6 +252,39 @@ ifeq ($(ARCH),AURIX_TC3) endif endif +## ARM Compiler for Embedded (USE_ARMCLANG=1): rebuild the link options from +## scratch and switch the linker script to a scatter file, to support armlink. +ifeq ($(USE_ARMCLANG),1) + CFLAGS+=-D'END_STACK=Image$$$$ARM_LIB_STACK$$$$ZI$$$$Limit' + ifeq ($(WOLFCRYPT_TZ),1) + # Trap stubs for symbols referenced from wolfSSL code paths that are + # dead in this configuration: GNU ld garbage-collects the referencing + # sections, but armlink resolves all symbols before unused section + # elimination + OBJS+=tools/armclang/armclang_stubs.o + CFLAGS+=-DARMCLANG_STUBS_DEAD_REFS + ifeq ($(WOLFCRYPT_TZ_PKCS11),1) + CFLAGS+=-D'_flash_keyvault=Image$$$$KEYVAULT$$$$Base' + CFLAGS+=-D'_flash_keyvault_size=Image$$$$KEYVAULT$$$$ZI$$$$Length' + CFLAGS+=-D'_start_heap=Image$$$$RAM_HEAP$$$$Base' + CFLAGS+=-D'_heap_size=Image$$$$RAM_HEAP$$$$ZI$$$$Length' + # ARM libc malloc needs the C-library init that never runs (entry is + # isr_reset): provide a heap allocator instead + CFLAGS+=-DARMCLANG_STUBS_MALLOC + endif + endif + LDFLAGS:=$(ARMCLANG_LDFLAGS) --map --list=wolfboot.map + LSCRIPT:=config/target.sct + LSCRIPT_IN:=hal/$(TARGET).sct + LSCRIPT_FLAGS:=--scatter=$(LSCRIPT) + LD_START_GROUP:= + LD_END_GROUP:= + SECURE_LDFLAGS:= + ifeq ($(TZEN),1) + SECURE_LDFLAGS:=--import_cmse_lib_out=./src/wolfboot_tz_nsc.o + endif +endif + # Environment variables for sign tool SIGN_ENV=IMAGE_HEADER_SIZE=$(IMAGE_HEADER_SIZE) \ WOLFBOOT_PARTITION_SIZE=$(WOLFBOOT_PARTITION_SIZE) \ @@ -644,7 +678,7 @@ src/flash_otp_keystore.o: $(PRIVATE_KEY) src/flash_otp_keystore.c keys: $(PRIVATE_KEY) clean: - $(Q)rm -f src/*.o hal/*.o hal/spi/*.o test-app/*.o src/x86/*.o + $(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 $(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 diff --git a/arch.mk b/arch.mk index beeffa4122..837bc045bd 100644 --- a/arch.mk +++ b/arch.mk @@ -1510,6 +1510,45 @@ ifeq ($(USE_CLANG),1) LDFLAGS+=-nostdlib endif +ifeq ($(USE_ARMCLANG),1) + ifneq ($(ARCH),ARM) + $(error USE_ARMCLANG=1 is currently supported only for ARCH=ARM) + endif + ARMCLANG_PATH?= + CC=$(ARMCLANG_PATH)armclang --target=arm-arm-none-eabi + AS=$(CC) + LD=$(ARMCLANG_PATH)armlink + AR=$(ARMCLANG_PATH)armar + FROMELF?=$(ARMCLANG_PATH)fromelf + SIZE=$(FROMELF) -z + OBJCOPY=FROMELF="$(FROMELF)" $(WOLFBOOT_ROOT)/tools/armclang/objcopy.sh + + CFLAGS+=-mfloat-abi=soft + CFLAGS+=-fno-unwind-tables -fno-asynchronous-unwind-tables + CFLAGS+=-Wno-unknown-attributes -Wno-error=unknown-attributes + + # Map scatter files region names to GNU ld symbols used by wolfBoot code + CFLAGS+=-D'_start_text=Image$$$$ER_VECTORS$$$$Base' + CFLAGS+=-D'_stored_data=Load$$$$RW_RAM$$$$Base' + CFLAGS+=-D'_start_data=Image$$$$RW_RAM$$$$Base' + CFLAGS+=-D'_end_data=Image$$$$RW_RAM$$$$Limit' + CFLAGS+=-D'_start_bss=Image$$$$RW_RAM$$$$ZI$$$$Base' + CFLAGS+=-D'_end_bss=Image$$$$RW_RAM$$$$ZI$$$$Limit' + + # Base armlink options shared by both images: + # - the unreferenced vector table needs an explicit --keep or unused + # section elimination removes it + # - RW data compression must be off: wolfBoot copies .data to RAM itself, + # word by word, and would copy the compressed image + # - Suppress L6314W (no section matches pattern) is suppressed: the scatter + # files list sections that are only present in some configurations (e.g. + # .ramcode) + # - --no_startup: wolfBoot has its own reset handler. + ARMCLANG_LDFLAGS=--cpu=Cortex-M33 --fpu=SoftVFP --entry=isr_reset \ + --keep="*(.isr_vector)" --datacompressor=off --remove \ + --no_startup --info=sizes,totals,unused --diag_suppress=6314 +endif + ifeq ($(USE_GCC),1) ## Toolchain setup CC=$(CROSS_COMPILE)gcc diff --git a/config/examples/stm32u5-wolfcrypt-tz.config b/config/examples/stm32u5-wolfcrypt-tz.config index 5cd81fef6f..573be823a1 100644 --- a/config/examples/stm32u5-wolfcrypt-tz.config +++ b/config/examples/stm32u5-wolfcrypt-tz.config @@ -20,13 +20,13 @@ RAM_CODE?=0 DUALBANK_SWAP?=0 WOLFBOOT_PARTITION_SIZE?=0x20000 WOLFBOOT_SECTOR_SIZE?=0x2000 -WOLFBOOT_KEYVAULT_ADDRESS?=0x0C020000 +WOLFBOOT_KEYVAULT_ADDRESS?=0x0C040000 WOLFBOOT_KEYVAULT_SIZE?=0x18000 -WOLFBOOT_NSC_ADDRESS?=0x0C038000 +WOLFBOOT_NSC_ADDRESS?=0x0C058000 WOLFBOOT_NSC_SIZE?=0x8000 -WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08040000 -WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x08060000 -WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x08080000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08060000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x08080000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x080A0000 FLAGS_HOME=0 DISABLE_BACKUP=0 WOLFCRYPT_TZ=1 diff --git a/docs/compile.md b/docs/compile.md index 4081d837d9..82738b71ae 100644 --- a/docs/compile.md +++ b/docs/compile.md @@ -341,6 +341,25 @@ iterating over a range of flash sectors and erasing them one at a time. Setting single HAL flash erase invocation with a larger erase length versus the iterative approach. On targets where multi-sector erases are more performant, this option can be used to dramatically speed up the image swap procedure. +### Building with the ARM Compiler for Embedded (armclang) + +wolfBoot can be built with the [ARM Compiler for Embedded](https://developer.arm.com/Tools%20and%20Software/Arm%20Compiler%20for%20Embedded) +(AC6: `armclang`, `armlink`, `armar`, `fromelf`), the toolchain used by Keil +MDK. No tool from the GNU or LLVM toolchains is required. + +At the moment, the only supported and tested target is the STM32U5 +(`config/examples/{stm32u5.config,stm32u5-wolfcrypt-tz.config}`). + +To use this, either have the AC6 tools in your `PATH` or set `ARMCLANG_PATH` to +their location (including a trailing slash). Then build with USE_ARMCLANG=1: + +``` +cp config/examples/stm32u5.config .config +make USE_ARMCLANG=1 +``` + +Alternatively, append `USE_ARMCLANG=1` and/or `ARMCLANG_PATH` to your `.config`. + ### Using Mac OS/X If you see 0xC3 0xBF (C3BF) repeated in your factory.bin then your OS is using Unicode characters. diff --git a/hal/stm32u5.c b/hal/stm32u5.c index 2465df1d02..3ce5eba55c 100644 --- a/hal/stm32u5.c +++ b/hal/stm32u5.c @@ -24,6 +24,7 @@ #include #include "hal/stm32u5.h" #include "hal.h" +#include "uart_drv.h" static void RAMFUNCTION flash_set_waitstates(unsigned int waitstates) @@ -33,7 +34,7 @@ static void RAMFUNCTION flash_set_waitstates(unsigned int waitstates) FLASH_ACR = (reg & ~FLASH_ACR_LATENCY_MASK) | waitstates; } -static RAMFUNCTION void flash_wait_complete(uint8_t bank) +void RAMFUNCTION hal_flash_wait_complete(uint8_t bank) { while ((FLASH_NS_SR & (FLASH_SR_BSY | FLASH_SR_WDW)) != 0) ; @@ -44,7 +45,7 @@ static RAMFUNCTION void flash_wait_complete(uint8_t bank) } -static void RAMFUNCTION flash_clear_errors(uint8_t bank) +void RAMFUNCTION hal_flash_clear_errors(uint8_t bank) { FLASH_NS_SR |= (FLASH_SR_OPERR | FLASH_SR_PROGERR | FLASH_SR_WRPERR | @@ -68,7 +69,7 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) uint32_t qword[4]; volatile uint32_t *sr, *cr; - flash_clear_errors(0); + hal_flash_clear_errors(0); src = (uint32_t*)data; dst = (uint32_t*)address; @@ -105,7 +106,7 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) ISB(); dst[(i >> 2) + 3] = qword[3]; ISB(); - flash_wait_complete(0); + hal_flash_wait_complete(0); if ((*sr & FLASH_SR_EOP) != 0) *sr |= FLASH_SR_EOP; *cr &= ~FLASH_CR_PG; @@ -117,7 +118,7 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) void RAMFUNCTION hal_flash_unlock(void) { - flash_wait_complete(0); + hal_flash_wait_complete(0); #if (TZ_SECURE()) if ((FLASH_CR & FLASH_CR_LOCK) != 0) { FLASH_KEYR = FLASH_KEY1; @@ -140,7 +141,7 @@ void RAMFUNCTION hal_flash_unlock(void) void RAMFUNCTION hal_flash_lock(void) { - flash_wait_complete(0); + hal_flash_wait_complete(0); #if (TZ_SECURE()) if ((FLASH_CR & FLASH_CR_LOCK) == 0) FLASH_CR |= FLASH_CR_LOCK; @@ -151,7 +152,7 @@ void RAMFUNCTION hal_flash_lock(void) void RAMFUNCTION hal_flash_opt_unlock(void) { - flash_wait_complete(0); + hal_flash_wait_complete(0); if ((FLASH_NS_CR & FLASH_CR_OPTLOCK) != 0) { FLASH_NS_OPTKEYR = FLASH_OPTKEY1; @@ -167,7 +168,7 @@ void RAMFUNCTION hal_flash_opt_lock(void) { FLASH_NS_CR |= FLASH_CR_OPTSTRT; - flash_wait_complete(0); + hal_flash_wait_complete(0); FLASH_NS_CR |= FLASH_CR_OBL_LAUNCH; if ((FLASH_NS_CR & FLASH_CR_OPTLOCK) == 0) FLASH_NS_CR |= FLASH_CR_OPTLOCK; @@ -179,7 +180,7 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) uint32_t p; volatile uint32_t *cr = &FLASH_NS_CR; - flash_clear_errors(0); + hal_flash_clear_errors(0); if (len == 0) return -1; @@ -212,7 +213,7 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) *cr = reg; DMB(); *cr |= FLASH_CR_STRT; - flash_wait_complete(0); + hal_flash_wait_complete(0); } /* If the erase operation is completed, disable the associated bits */ *cr &= ~FLASH_CR_PER ; @@ -493,6 +494,8 @@ static void led_unsecure() #define TZSC1_BASE 0x50032400u #define TZSC_SECCFGR1 (*(volatile uint32_t *)(TZSC1_BASE + 0x10u)) #define TZSC_SECCFGR1_USART3SEC (1u << 10) +#define TZSC_SECCFGR2 (*(volatile uint32_t *)(TZSC1_BASE + 0x14u)) +#define TZSC_SECCFGR2_USART1SEC (1u << 3) static void periph_unsecure(void) { @@ -515,6 +518,24 @@ static void periph_unsecure(void) DMB(); TZSC_SECCFGR1 = reg; } + + /* Enable clock for GPIO A (USART1 pins PA9/PA10) */ + RCC_AHB2ENR1_CLOCK_ER |= GPIOA_AHB2ENR1_CLOCK_ER; + + /* Enable clock for USART1 */ + RCC_APB2ENR |= UART1_APB2_CLOCK_ER_VAL; + + /* Unsecure USART1 pins (PA9 TX, PA10 RX) */ + GPIOA_SECCFGR &= ~(1u << UART1_TX_PIN); + GPIOA_SECCFGR &= ~(1u << UART1_RX_PIN); + + /* Unsecure USART1 peripheral in GTZC TZSC */ + reg = TZSC_SECCFGR2; + if (reg & TZSC_SECCFGR2_USART1SEC) { + reg &= ~TZSC_SECCFGR2_USART1SEC; + DMB(); + TZSC_SECCFGR2 = reg; + } } #endif @@ -547,6 +568,12 @@ void hal_init(void) fork_bootloader(); #endif clock_pll_on(0); + +#ifdef DEBUG_UART + uart_init(115200, 8, 'N', 1); + uart_write("wolfBoot Init\n", 14); +#endif + #if TZ_SECURE() hal_tz_sau_init(); hal_gtzc_init(); diff --git a/hal/stm32u5.h b/hal/stm32u5.h index e28cd86ecb..122efaac06 100644 --- a/hal/stm32u5.h +++ b/hal/stm32u5.h @@ -277,6 +277,68 @@ #define RCC_AHB2_CLOCK_ER RCC_AHB2ENR1_CLOCK_ER +/* UART */ +#if (TZ_SECURE()) +#define UART1 (0x50013800) /* USART1 - RM0456 - Table 4 */ +#define GPIOA_BASE (0x52020000) +#else +#define UART1 (0x40013800) /* USART1 - RM0456 - Table 4 */ +#define GPIOA_BASE (0x42020000) +#endif + +#define UART_CR1(base) (*(volatile uint32_t *)((base) + 0x00)) +#define UART_CR2(base) (*(volatile uint32_t *)((base) + 0x04)) +#define UART_CR3(base) (*(volatile uint32_t *)((base) + 0x08)) +#define UART_BRR(base) (*(volatile uint32_t *)((base) + 0x0c)) +#define UART_ISR(base) (*(volatile uint32_t *)((base) + 0x1c)) +#define UART_ICR(base) (*(volatile uint32_t *)((base) + 0x20)) +#define UART_RDR(base) (*(volatile uint32_t *)((base) + 0x24)) +#define UART_TDR(base) (*(volatile uint32_t *)((base) + 0x28)) +#define UART_PRE(base) (*(volatile uint32_t *)((base) + 0x2C)) + +#define UART_CR1_UART_ENABLE (1 << 0) +#define UART_CR1_OVER8 (1 << 15) +#define UART_CR1_SYMBOL_LEN (1 << 12) +#define UART_CR1_PARITY_ENABLED (1 << 10) +#define UART_CR1_PARITY_ODD (1 << 9) +#define UART_CR1_TX_ENABLE (1 << 3) +#define UART_CR1_RX_ENABLE (1 << 2) +#define UART_CR2_STOPBITS (3 << 12) +#define UART_CR2_LINEN (1 << 14) +#define UART_CR2_CLKEN (1 << 11) +#define UART_CR3_HDSEL (1 << 3) +#define UART_CR3_SCEN (1 << 5) +#define UART_CR3_IREN (1 << 1) +#define UART_ISR_TX_EMPTY (1 << 7) +#define UART_ISR_TX_COMPLETE (1 << 6) +#define UART_ISR_RX_NOTEMPTY (1 << 5) +#define UART_EPE (1 << 0) /* Parity error */ +#define UART_EFE (1 << 1) /* Framing error */ +#define UART_ENE (1 << 2) /* Noise error */ +#define UART_ORE (1 << 3) /* Overrun error */ + +#define UART1_APB2_CLOCK_ER_VAL (1 << 14) /* RM0456 - RCC_APB2ENR - USART1EN */ +#define RCC_APB2ENR (*(volatile uint32_t *)(RCC_BASE + 0xA4)) /* RM0456 - Table 108 */ + +#define RCC_CCIPR1 (*(volatile uint32_t *)(RCC_BASE + 0xE0)) /* RM0456 - 11.8.46 */ +#define RCC_CCIPR1_USART1SEL_SHIFT (0) +#define RCC_CCIPR1_USART1SEL_MASK (0x3) +#define RCC_CCIPR1_USART1SEL_HSI16 (0x2) + +#define HSI16_FREQ (16000000) + +/* USART1 pin configuration: PA9 (TX) / PA10 (RX), AF7. + * Connected to the ST-LINK VCP on Nucleo-U575ZI-Q. */ +#define UART1_PIN_AF 7 +#define UART1_TX_PIN 9 +#define UART1_RX_PIN 10 + +#define GPIOA_AHB2ENR1_CLOCK_ER (1 << 0) +#define GPIOA_MODE (*(volatile uint32_t *)(GPIOA_BASE + 0x00)) +#define GPIOA_AFL (*(volatile uint32_t *)(GPIOA_BASE + 0x20)) +#define GPIOA_AFH (*(volatile uint32_t *)(GPIOA_BASE + 0x24)) +#define GPIOA_SECCFGR (*(volatile uint32_t *)(GPIOA_BASE + 0x30)) + /* Reset */ #define OPTR_SWAP_BANK (1 << 20) diff --git a/hal/stm32u5.ld b/hal/stm32u5.ld index 5f0da10385..731053f2bc 100644 --- a/hal/stm32u5.ld +++ b/hal/stm32u5.ld @@ -1,6 +1,6 @@ MEMORY { - FLASH (rx) : ORIGIN = @WOLFBOOT_ORIGIN@, LENGTH = @WOLFBOOT_KEYVAULT_ADDRESS@ - @ARCH_FLASH_OFFSET@ + FLASH (rx) : ORIGIN = @WOLFBOOT_ORIGIN@, LENGTH = @WOLFBOOT_KEYVAULT_ADDRESS@ - @WOLFBOOT_ORIGIN@ RAM (rwx) : ORIGIN = 0x30000000, LENGTH = 0x00012000 RAM_HEAP (rw): ORIGIN = 0x30012000, LENGTH = 0xe000 /* 56KB Heap for wolfcrypt/PKCS11 */ FLASH_KEYVAULT(rw): ORIGIN = @WOLFBOOT_KEYVAULT_ADDRESS@, LENGTH = @WOLFBOOT_KEYVAULT_SIZE@ diff --git a/hal/stm32u5.sct b/hal/stm32u5.sct new file mode 100644 index 0000000000..eb01fa7083 --- /dev/null +++ b/hal/stm32u5.sct @@ -0,0 +1,64 @@ +#! armclang -E --target=arm-arm-none-eabi -mcpu=cortex-m33 -xc +; wolfBoot stm32u5 (TZEN=1) scatter file for armlink, equivalent to +; hal/stm32u5.ld. The @VAR@ placeholders are substituted by the Makefile +; (same rule as the GNU linker scripts), then armlink runs the armclang +; preprocessor on the result (#! line above). +; +; Region symbols consumed by the code (mapped from the GNU-ld symbol names +; via -D in arch.mk / Makefile): +; ER_VECTORS -> _start_text +; RW_RAM -> _stored_data (load), _start_data/_end_data, +; _start_bss/_end_bss +; ARM_LIB_STACK -> END_STACK (stack top, grows down from end of RAM) +; RAM_HEAP -> _start_heap/_heap_size (WOLFCRYPT_TZ PKCS11 heap) +; KEYVAULT -> _flash_keyvault/_flash_keyvault_size + +#define WOLFBOOT_STACK_RESERVE 0x1000 + +LR_FLASH @WOLFBOOT_ORIGIN@ (@WOLFBOOT_KEYVAULT_ADDRESS@ - @WOLFBOOT_ORIGIN@) +{ + ER_VECTORS @WOLFBOOT_ORIGIN@ + { + *(.isr_vector, +FIRST) + } + + ER_TEXT +0 ALIGN 8 + { + .ANY(+RO) + } + + RW_RAM 0x30000000 ALIGN 8 (0x00012000 - WOLFBOOT_STACK_RESERVE) + { + *(.ramcode) + .ANY(+RW) + .ANY(+ZI) + } + + ; Empty region only used to define the initial stack pointer; also lets + ; armlink error out if RW_RAM grows into the stack reserve + ARM_LIB_STACK 0x30012000 EMPTY -WOLFBOOT_STACK_RESERVE + { + } + +#if @WOLFBOOT_KEYVAULT_SIZE@ + 0 + ; 56KB heap for wolfcrypt/PKCS11 + RAM_HEAP 0x30012000 EMPTY 0xe000 + { + } + + KEYVAULT @WOLFBOOT_KEYVAULT_ADDRESS@ EMPTY @WOLFBOOT_KEYVAULT_SIZE@ + { + } +#endif +} + +; CMSE secure gateway veneers, generated by armlink. Placed at NSC + 0x400 +; like the GNU script (hal/stm32u5.ld reserves the first 0x400 bytes of the +; NSC region). +LR_NSC (@WOLFBOOT_NSC_ADDRESS@ + 0x400) (@WOLFBOOT_NSC_SIZE@ - 0x400) +{ + ER_NSC (@WOLFBOOT_NSC_ADDRESS@ + 0x400) + { + *(Veneer$$CMSE) + } +} diff --git a/hal/uart/uart_drv_stm32u5.c b/hal/uart/uart_drv_stm32u5.c new file mode 100644 index 0000000000..32c0662200 --- /dev/null +++ b/hal/uart/uart_drv_stm32u5.c @@ -0,0 +1,157 @@ +/* uart_drv_stm32u5.c + * + * Driver for the back-end of the UART_FLASH module. + * + * Example implementation for stm32U5 + * using USART1 (PA9/PA10, ST-LINK VCP on B-U585I-IOT02A). + * + * + * 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 + */ + +#ifdef TARGET_stm32u5 + +#include +#include "hal/stm32u5.h" + +static void uart_pins_setup(void) +{ + uint32_t reg; + RCC_AHB2ENR1_CLOCK_ER |= GPIOA_AHB2ENR1_CLOCK_ER; + /* Set mode = AF */ + reg = GPIOA_MODE & ~(0x03 << (UART1_RX_PIN * 2)); + GPIOA_MODE = reg | (2 << (UART1_RX_PIN * 2)); + reg = GPIOA_MODE & ~(0x03 << (UART1_TX_PIN * 2)); + GPIOA_MODE = reg | (2 << (UART1_TX_PIN * 2)); + + /* Alternate function: use hi pins (9 and 10) */ + reg = GPIOA_AFH & ~(0xf << ((UART1_TX_PIN - 8) * 4)); + GPIOA_AFH = reg | (UART1_PIN_AF << ((UART1_TX_PIN - 8) * 4)); + reg = GPIOA_AFH & ~(0xf << ((UART1_RX_PIN - 8) * 4)); + GPIOA_AFH = reg | (UART1_PIN_AF << ((UART1_RX_PIN - 8) * 4)); +} + +static void uart_clear_errors(uint32_t base) +{ + UART_ICR(base) = UART_ISR(base) & (UART_ENE | UART_EPE | UART_ORE | UART_EFE); +} + +int uart_tx(const uint8_t c) +{ + volatile uint32_t reg; + do { + reg = UART_ISR(UART1); + if (reg & (UART_ENE | UART_EPE | UART_ORE | UART_EFE)) + uart_clear_errors(UART1); + } while ((reg & UART_ISR_TX_EMPTY) == 0); + UART_TDR(UART1) = c; + return 1; +} + +int uart_rx(uint8_t *c) +{ + volatile uint32_t reg; + reg = UART_ISR(UART1); + if (reg & (UART_ENE | UART_EPE | UART_ORE | UART_EFE)) + uart_clear_errors(UART1); + if (reg & UART_ISR_RX_NOTEMPTY) { + *c = (uint8_t)UART_RDR(UART1); + return 1; + } + return 0; +} + +int uart_init(uint32_t bitrate, uint8_t data, char parity, uint8_t stop) +{ + uint32_t reg; + + /* Enable USART1 peripheral clock */ + RCC_APB2ENR |= UART1_APB2_CLOCK_ER_VAL; + + /* If the UART is already running (e.g. left enabled by the secure + * bootloader), let the last frame finish shifting out before + * reconfiguring, or the character is cut mid-frame */ + if (UART_CR1(UART1) & UART_CR1_UART_ENABLE) { + while ((UART_ISR(UART1) & UART_ISR_TX_COMPLETE) == 0) {}; + } + + uart_pins_setup(); + + /* Use HSI16 as USART1 kernel clock, so the baud rate does not + * depend on the current SYSCLK/PLL configuration */ + RCC_CR |= RCC_CR_HSION; + while ((RCC_CR & RCC_CR_HSIRDY) == 0) {}; + reg = RCC_CCIPR1 & (~(RCC_CCIPR1_USART1SEL_MASK << RCC_CCIPR1_USART1SEL_SHIFT)); + RCC_CCIPR1 = reg | (RCC_CCIPR1_USART1SEL_HSI16 << RCC_CCIPR1_USART1SEL_SHIFT); + + /* Disable UART to configure BRR (only writable while disabled) */ + UART_CR1(UART1) &= ~UART_CR1_UART_ENABLE; + + /* Enable 16-bit oversampling */ + UART_CR1(UART1) &= ~UART_CR1_OVER8; + + /* Configure baud rate */ + UART_BRR(UART1) = (uint16_t)(HSI16_FREQ / bitrate); + + /* Configure data bits */ + if (data == 8) + UART_CR1(UART1) &= ~UART_CR1_SYMBOL_LEN; + else + UART_CR1(UART1) |= UART_CR1_SYMBOL_LEN; + + /* Configure parity */ + switch (parity) { + case 'O': + UART_CR1(UART1) |= UART_CR1_PARITY_ODD; + /* fall through to enable parity */ + /* FALL THROUGH */ + case 'E': + UART_CR1(UART1) |= UART_CR1_PARITY_ENABLED; + break; + default: + UART_CR1(UART1) &= ~(UART_CR1_PARITY_ENABLED | UART_CR1_PARITY_ODD); + } + /* Set stop bits */ + reg = UART_CR2(UART1) & ~UART_CR2_STOPBITS; + if (stop > 1) + UART_CR2(UART1) = reg | (2 << 12); + else + UART_CR2(UART1) = reg; + + /* Clear flags for async mode */ + UART_CR2(UART1) &= ~(UART_CR2_LINEN | UART_CR2_CLKEN); + UART_CR3(UART1) &= ~(UART_CR3_SCEN | UART_CR3_HDSEL | UART_CR3_IREN); + + /* Configure for RX+TX, turn on. */ + UART_CR1(UART1) |= UART_CR1_TX_ENABLE | UART_CR1_RX_ENABLE | UART_CR1_UART_ENABLE; + + return 0; +} + +#ifdef DEBUG_UART +void uart_write(const char *buf, unsigned int len) +{ + while (len--) { + uart_tx(*buf); + buf++; + } +} +#endif + +#endif /* TARGET_stm32u5 */ diff --git a/options.mk b/options.mk index b6adb4a588..cef89f7105 100644 --- a/options.mk +++ b/options.mk @@ -39,7 +39,8 @@ WOLFHSM_SERVER_OBJS := \ $(WOLFBOOT_LIB_WOLFHSM)/src/wh_message_comm.o USE_CLANG?=0 -ifeq ($(USE_CLANG),1) +USE_ARMCLANG?=0 +ifneq ($(filter 1,$(USE_CLANG) $(USE_ARMCLANG)),) USE_GCC?=0 else USE_GCC?=1 @@ -56,6 +57,18 @@ ifeq ($(USE_CLANG),1) endif endif +ifeq ($(USE_ARMCLANG),1) + ifeq ($(USE_GCC),1) + $(error USE_ARMCLANG=1 is incompatible with USE_GCC=1; set USE_GCC=0) + endif + ifeq ($(USE_CLANG),1) + $(error USE_ARMCLANG=1 is incompatible with USE_CLANG=1) + endif + ifeq ($(ARMORED),1) + $(error USE_ARMCLANG=1 requires ARMORED=0) + endif +endif + # Support for Built-in ROT into OTP flash memory ifeq ($(FLASH_OTP_KEYSTORE),1) CFLAGS+=-D"FLASH_OTP_KEYSTORE" diff --git a/test-app/ARM-stm32u5-ns.sct b/test-app/ARM-stm32u5-ns.sct new file mode 100644 index 0000000000..f714bb88a2 --- /dev/null +++ b/test-app/ARM-stm32u5-ns.sct @@ -0,0 +1,40 @@ +#! armclang -E --target=arm-arm-none-eabi -mcpu=cortex-m33 -xc +; wolfBoot test-app stm32u5 (TZEN=1, non-secure app) scatter file for +; armlink, equivalent to test-app/ARM-stm32u5-ns.ld. +; +; Region symbols (mapped from the GNU-ld symbol names via -D in arch.mk and +; test-app/Makefile): +; ER_VECTORS -> _start_text +; RW_RAM -> _stored_data (load), _start_data/_end_data, +; _start_bss/_end_bss, _start_heap (= end of ZI) +; ARM_LIB_STACK -> _end_stack (stack top, grows down from end of RAM) + +#define APP_RAM_BASE 0x20020000 +#define APP_RAM_SIZE 0x00020000 +#define APP_STACK_RESERVE 0x1000 + +LR_FLASH @WOLFBOOT_TEST_APP_ADDRESS@ @WOLFBOOT_TEST_APP_SIZE@ +{ + ER_VECTORS @WOLFBOOT_TEST_APP_ADDRESS@ + { + *(.isr_vector, +FIRST) + } + + ER_TEXT +0 ALIGN 8 + { + .ANY(+RO) + } + + RW_RAM APP_RAM_BASE ALIGN 8 (APP_RAM_SIZE - APP_STACK_RESERVE) + { + *(.ramcode) + .ANY(+RW) + .ANY(+ZI) + } + + ; Empty region only used to define the initial stack pointer; also lets + ; armlink error out if RW_RAM grows into the stack reserve + ARM_LIB_STACK (APP_RAM_BASE + APP_RAM_SIZE) EMPTY -APP_STACK_RESERVE + { + } +} diff --git a/test-app/Makefile b/test-app/Makefile index c8c6fed54e..82925691c6 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -560,6 +560,9 @@ ifeq ($(TARGET),stm32u5) endif CFLAGS+=-mcpu=cortex-m33 LDFLAGS+=-mcpu=cortex-m33 + ifeq ($(DEBUG_UART),1) + APP_OBJS+=../hal/uart/uart_drv_$(UART_TARGET)_ns.o + endif endif ifeq ($(TARGET),stm32u3) @@ -1128,6 +1131,19 @@ ifeq ($(TARGET),aurix_tc3xx) endif endif +## ARM Compiler for Embedded (USE_ARMCLANG=1): rebuild the link options from +## scratch, since the GNU-specific LDFLAGS accumulated above don't apply to +## armlink, and switch the linker script to a scatter file. +ifeq ($(USE_ARMCLANG),1) + CFLAGS+=-D'_end_stack=Image$$$$ARM_LIB_STACK$$$$ZI$$$$Limit' + CFLAGS+=-D'_start_heap=Image$$$$RW_RAM$$$$ZI$$$$Limit' + # GCC-only flags accumulated above + CFLAGS:=$(filter-out -Wstack-usage=% -gstabs,$(CFLAGS)) + LSCRIPT:=../config/target-app.sct + LSCRIPT_TEMPLATE:=ARM-$(TARGET)-ns.sct + LDFLAGS:=$(ARMCLANG_LDFLAGS) --map --list=image.map --scatter=$(LSCRIPT) +endif + # Capture final flags for locally built wolfSSL objects. WOLFSSL_CFLAGS:=$(CFLAGS) WOLFTPM_CFLAGS:=$(CFLAGS) @@ -1201,6 +1217,9 @@ delta-extra-data: image.bin ../hal/spi/spi_drv_$(SPI_TARGET)_ns.o: ../hal/spi/spi_drv_$(SPI_TARGET).c FORCE $(Q)$(CC) $(CFLAGS) -c -o $(@) ../hal/spi/spi_drv_$(SPI_TARGET).c -DNONSECURE_APP +../hal/uart/uart_drv_$(UART_TARGET)_ns.o: ../hal/uart/uart_drv_$(UART_TARGET).c FORCE + $(Q)$(CC) $(CFLAGS) -c -o $(@) ../hal/uart/uart_drv_$(UART_TARGET).c -DNONSECURE_APP + board_$(BOARD).o: ../hal/boards/$(BOARD)/board.c @echo "\t[CC-$(ARCH)] $@" $(Q)$(CC) $(CFLAGS) -DDEBUG_UART -c $(OUTPUT_FLAG) $@ $< diff --git a/test-app/wcs/user_settings.h b/test-app/wcs/user_settings.h index b09accff89..095b748bc2 100644 --- a/test-app/wcs/user_settings.h +++ b/test-app/wcs/user_settings.h @@ -27,7 +27,8 @@ #include #include -int clock_gettime (clockid_t clock_id, struct timespec *tp); +struct timespec; +int clock_gettime (unsigned long clock_id, struct timespec *tp); #include "wolfboot/wc_secure.h" #define WOLFCRYPT_ONLY diff --git a/tools/armclang/armclang_stubs.c b/tools/armclang/armclang_stubs.c new file mode 100644 index 0000000000..4ff6296be1 --- /dev/null +++ b/tools/armclang/armclang_stubs.c @@ -0,0 +1,170 @@ +/* armclang_stubs.c + * + * C library glue for building wolfBoot with the ARM Compiler for Embedded + * (USE_ARMCLANG=1). + * + * - ARMCLANG_STUBS_MALLOC (secure image): heap allocator over the RAM_HEAP + * scatter region. We can't use the ARM C library malloc because it requires + * the C library runtime initialization, which never runs since wolfBoot + * enters at isr_reset. + * + * - ARMCLANG_STUBS_DEAD_REFS: trap stubs for symbols that are only + * referenced from sections that GNU ld garbage-collects; armlink needs + * the definitions to exist before its own unused section elimination. + * + * 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, see . + */ + +#ifdef ARMCLANG_STUBS_MALLOC + +#include +#include +#include + +/* Renamed to the RAM_HEAP region symbols by the -D options in the Makefile */ +extern unsigned int _start_heap; +extern unsigned int _heap_size; + +struct heap_blk { + size_t size; + struct heap_blk *next; +}; + +#define HEAP_ALIGN(sz) (((sz) + 7U) & ~(size_t)7U) +#define HEAP_MIN_SPLIT (sizeof(struct heap_blk) + 8U) + +static struct heap_blk *heap_free_list; +static int heap_initialized; + +static void heap_init(void) +{ + heap_free_list = (struct heap_blk *)HEAP_ALIGN((uintptr_t)&_start_heap); + heap_free_list->size = (size_t)((uintptr_t)&_start_heap + + (uintptr_t)&_heap_size - (uintptr_t)heap_free_list) + & ~(size_t)7U; + heap_free_list->next = NULL; + heap_initialized = 1; +} + +void *malloc(size_t size) +{ + struct heap_blk *blk, **prev; + + if (!heap_initialized) + heap_init(); + if (size == 0) + return NULL; + size = HEAP_ALIGN(size) + sizeof(struct heap_blk); + + for (prev = &heap_free_list; (blk = *prev) != NULL; prev = &blk->next) { + if (blk->size < size) + continue; + if (blk->size - size >= HEAP_MIN_SPLIT) { + struct heap_blk *rest = (struct heap_blk *)((uint8_t *)blk + size); + rest->size = blk->size - size; + rest->next = blk->next; + blk->size = size; + *prev = rest; + } else { + *prev = blk->next; + } + blk->next = NULL; + return (uint8_t *)blk + sizeof(struct heap_blk); + } + return NULL; +} + +void free(void *ptr) +{ + struct heap_blk *blk, *cur, **prev; + + if (ptr == NULL) + return; + blk = (struct heap_blk *)((uint8_t *)ptr - sizeof(struct heap_blk)); + + /* Keep the free list sorted by address */ + for (prev = &heap_free_list; (cur = *prev) != NULL; prev = &cur->next) { + if (cur > blk) + break; + } + blk->next = cur; + *prev = blk; + + /* Coalesce with the next and previous blocks when adjacent */ + if (cur != NULL && (uint8_t *)blk + blk->size == (uint8_t *)cur) { + blk->size += cur->size; + blk->next = cur->next; + } + if (prev != &heap_free_list) { + struct heap_blk *before = (struct heap_blk *)((uint8_t *)prev + - offsetof(struct heap_blk, next)); + if ((uint8_t *)before + before->size == (uint8_t *)blk) { + before->size += blk->size; + before->next = blk->next; + } + } +} + +void *calloc(size_t nmemb, size_t size) +{ + void *ptr; + size_t total = nmemb * size; + + if (size != 0 && total / size != nmemb) + return NULL; + ptr = malloc(total); + if (ptr != NULL) + memset(ptr, 0, total); + return ptr; +} + +void *realloc(void *ptr, size_t size) +{ + void *new_ptr; + struct heap_blk *blk; + size_t old_payload; + + if (ptr == NULL) + return malloc(size); + if (size == 0) { + free(ptr); + return NULL; + } + blk = (struct heap_blk *)((uint8_t *)ptr - sizeof(struct heap_blk)); + old_payload = blk->size - sizeof(struct heap_blk); + if (old_payload >= size) + return ptr; + new_ptr = malloc(size); + if (new_ptr != NULL) { + memcpy(new_ptr, ptr, old_payload); + free(ptr); + } + return new_ptr; +} + +#endif /* ARMCLANG_STUBS_MALLOC */ + +#ifdef ARMCLANG_STUBS_DEAD_REFS + +#define ARMCLANG_DEAD_REF_STUB(name) \ + void name(void); \ + void name(void) { while (1) ; } + +ARMCLANG_DEAD_REF_STUB(wc_CryptKey) +ARMCLANG_DEAD_REF_STUB(Base64_Encode) +ARMCLANG_DEAD_REF_STUB(wc_PKCS12_PBKDF) + +#endif /* ARMCLANG_STUBS_DEAD_REFS */ diff --git a/tools/armclang/objcopy.sh b/tools/armclang/objcopy.sh new file mode 100755 index 0000000000..b9f94525d7 --- /dev/null +++ b/tools/armclang/objcopy.sh @@ -0,0 +1,80 @@ +#!/bin/sh +# GNU objcopy command-line adapter over ARM fromelf, used when building with +# the ARM Compiler for Embedded (USE_ARMCLANG=1) so that the .bin/.hex/.srec +# Makefile recipes can be shared with the GNU toolchains. +# +# Only the invocation shapes used by the wolfBoot Makefiles are supported: +# objcopy [--gap-fill ] -O binary +# objcopy -O ihex +# objcopy -O srec +# +# The fromelf executable is taken from the FROMELF environment variable +# (default: fromelf). + +set -e + +FROMELF="${FROMELF:-fromelf}" +GAP_FILL= +FORMAT= +INPUT= +OUTPUT= + +while [ $# -gt 0 ]; do + case "$1" in + --gap-fill) + GAP_FILL="$2" + shift 2 + ;; + --gap-fill=*) + GAP_FILL="${1#*=}" + shift + ;; + -O) + FORMAT="$2" + shift 2 + ;; + -*) + echo "$0: unsupported objcopy option: $1" >&2 + exit 1 + ;; + *) + if [ -z "$INPUT" ]; then + INPUT="$1" + elif [ -z "$OUTPUT" ]; then + OUTPUT="$1" + else + echo "$0: too many arguments: $1" >&2 + exit 1 + fi + shift + ;; + esac +done + +if [ -z "$FORMAT" ] || [ -z "$INPUT" ] || [ -z "$OUTPUT" ]; then + echo "usage: $0 [--gap-fill ] -O binary|ihex|srec " >&2 + exit 1 +fi + +case "$FORMAT" in + binary) + # --bincombined merges all load regions into a single image, padding + # the gaps between them (GNU objcopy -O binary behavior) + set -- --bincombined + if [ -n "$GAP_FILL" ]; then + set -- "$@" "--bincombined_padding=1,$GAP_FILL" + fi + ;; + ihex) + set -- --i32combined + ;; + srec) + set -- --m32combined + ;; + *) + echo "$0: unsupported output format: $FORMAT" >&2 + exit 1 + ;; +esac + +exec "$FROMELF" "$@" --output="$OUTPUT" "$INPUT"