diff --git a/.github/workflows/test-build-cmake-dot-config.yml b/.github/workflows/test-build-cmake-dot-config.yml index d34758d4..31a288bf 100644 --- a/.github/workflows/test-build-cmake-dot-config.yml +++ b/.github/workflows/test-build-cmake-dot-config.yml @@ -7,9 +7,30 @@ on: jobs: wolfboot_dot_config_test: + name: cmake .config test (${{ matrix.target }}) runs-on: ubuntu-latest timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + target: + # These are exact file names from config/examples (including .config) + # - imx-rt1040.config # Disabled, requires NXP SDK + - sim.config + - stm32c0.config + - stm32f1.config + - stm32f4-small-blocks-uart-update.config + - stm32f7.config + - stm32g0.config + - stm32h5.config + - stm32h7.config + - stm32l0.config + - stm32l4-cube.config + - stm32l5.config + - stm32u5.config + - stm32wb.config + steps: - uses: actions/checkout@v4 with: @@ -60,7 +81,6 @@ jobs: echo "--- apt-mirrors.txt ---" cat /etc/apt/apt-mirrors.txt || true - - name: Install requirements run: | # Run system updates and install toolchain @@ -73,31 +93,59 @@ jobs: set -euo pipefail - LOG_FILE="run.log" + TARGET="${{ matrix.target }}" + TARGET="${TARGET%.config}" + TARGET="${TARGET%%-*}" + + BUILD_DIR="build-${TARGET}" + LOG_FILE="run-${TARGET}.log" KEYWORD="Config mode: dot" - echo "Saving output to $LOG_FILE" - echo "Fetch stm32h7 example .config" - cp ./config/examples/stm32h7.config ./.config - ls .config - cat .config - echo "" + echo "Target: ${TARGET}" + echo "Build dir: ${BUILD_DIR}" + echo "Saving output to ${LOG_FILE}" - echo "Clean" - rm -rf ./build-stm32h7 + CONFIG_SRC="./config/examples/${{ matrix.target }}" - # Here we should see the .config file values read and displayed: - cmake -S . -B build-stm32h7 \ - -DUSE_DOT_CONFIG=ON \ - -DWOLFBOOT_TARGET=stm32h7 2>&1 | tee "$LOG_FILE" - - # Config dot-config mode - if grep -q -- "$KEYWORD" "$LOG_FILE"; then - echo "Keyword found: $KEYWORD" - else - echo "Keyword not found: $KEYWORD" >&2 + echo "Fetch ${TARGET} example .config (source: ${CONFIG_SRC})" + if [ ! -f "${CONFIG_SRC}" ]; then + echo "Missing config file: ${CONFIG_SRC}" >&2 exit 1 fi - # Sample build - cmake --build build-stm32h7 --parallel 8 + cp "${CONFIG_SRC}" ./.config + ls .config + cat .config + echo "" + + echo "Clean" + rm -rf "./${BUILD_DIR}" + + # Here we should see the .config file values read and displayed: + cmake -S . -B "${BUILD_DIR}" \ + -DUSE_DOT_CONFIG=ON \ + -DWOLFBOOT_TARGET="${TARGET}" 2>&1 | tee "${LOG_FILE}" + + # Config dot-config mode + if grep -q -- "${KEYWORD}" "${LOG_FILE}"; then + echo "Keyword found: ${KEYWORD}" + else + echo "Keyword not found: ${KEYWORD}" >&2 + exit 1 + fi + + # First: build keygen explicitly and inspect it + echo "Building keygen_build for ${TARGET}" + cmake --build "${BUILD_DIR}" --parallel 1 --target keygen_build + + if [ -f "${BUILD_DIR}/keygen" ]; then + echo "Inspecting keygen:" + ls -l "${BUILD_DIR}/keygen" + file "${BUILD_DIR}/keygen" || true + else + echo "keygen not found at ${BUILD_DIR}/keygen" + fi + + # Now run the normal build + echo "Running full build" + cmake --build "${BUILD_DIR}" --parallel 8 diff --git a/.github/workflows/test-build-cmake-mac.yml b/.github/workflows/test-build-cmake-mac.yml index 601692d5..babe7761 100644 --- a/.github/workflows/test-build-cmake-mac.yml +++ b/.github/workflows/test-build-cmake-mac.yml @@ -4,7 +4,7 @@ on: push: branches: [ 'master', 'main', 'release/**' ] pull_request: - branches: [ '*' ] + branches: [ "*" ] jobs: macos-cmake: @@ -15,7 +15,20 @@ jobs: strategy: fail-fast: false matrix: - target: [stm32l4, stm32h7, stm32c0, stm32g0] + target: + # - imx-rt # Disabled, requires NXP SDK + - stm32c0 + - stm32f1 + - stm32f4 + - stm32f7 + - stm32g0 + - stm32h5 + - stm32h7 + - stm32l0 + - stm32l4 + - stm32l5 + - stm32u5 + - stm32wb env: HOMEBREW_NO_AUTO_UPDATE: "1" # avoid updating taps during install diff --git a/.github/workflows/test-build-cmake-presets.yml b/.github/workflows/test-build-cmake-presets.yml index a0be0af3..964a0738 100644 --- a/.github/workflows/test-build-cmake-presets.yml +++ b/.github/workflows/test-build-cmake-presets.yml @@ -2,7 +2,7 @@ name: WolfBoot CMake Presets Build on: push: - # TODO: branches: [ 'master', 'main', 'release/**' ] + branches: [ 'master', 'main', 'release/**' ] branches: [ '*' ] pull_request: branches: [ "*" ] @@ -23,9 +23,21 @@ jobs: fail-fast: false matrix: target: - - stm32l4 - - stm32h7 + - sim + # - imx-rt # Disabled, requires NXP SDK + - stm32c0 + - stm32f1 + - stm32f4 + - stm32f7 - stm32g0 + - stm32h5 + - stm32h7 + - stm32l0 + - stm32l4 + - stm32l5 + - stm32u5 + - stm32wb + include: # Optional per-target cache variables you might want to pass later. # Keep empty for now to avoid guessing addresses. @@ -48,6 +60,7 @@ jobs: # ARM GCC toolchain (adds the bin dir to PATH) - name: Set up ARM none-eabi GCC 14.x + if: matrix.target != 'sim' uses: carlosperate/arm-none-eabi-gcc-action@v1 with: release: "14.2.Rel1" # <-- use 'release', not 'version' diff --git a/.github/workflows/test-build-cmake-script.yml b/.github/workflows/test-build-cmake-script.yml index 9a19ba18..30278aaa 100644 --- a/.github/workflows/test-build-cmake-script.yml +++ b/.github/workflows/test-build-cmake-script.yml @@ -1,14 +1,42 @@ +# Tests the /tools/scripts/wolfboot_cmake_full_build.sh +# See also test-build-cmake-presets.yml +# Presets are found in /CMakePresets.json +# name: wolfboot CMake Script on: push: branches: [ 'master', 'main', 'release/**' ] pull_request: - branches: [ '*' ] + branches: [ "*" ] + jobs: wolfboot_build_script_test: + name: Build wolfBoot (target=${{ matrix.target }}) runs-on: ubuntu-latest timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + target: + - "~No config (wolfBoot sim)" + # - imx-rt # Disabled, requires NXP SDK + - stm32c0 + - stm32f1 + - stm32f4 + - stm32f7 + - stm32g0 + - stm32h5 + - stm32h7 + - stm32l0 + - stm32l4 + - stm32l5 + - stm32u5 + - stm32wb + # add more targets here later, e.g.: + # - stm32v8 + # - sim options, etc + steps: - uses: actions/checkout@v4 with: @@ -59,19 +87,23 @@ jobs: echo "--- apt-mirrors.txt ---" cat /etc/apt/apt-mirrors.txt || true - - name: Install requirements run: | sudo apt-get update sudo apt-get install -y gcc-arm-none-eabi gcc-powerpc-linux-gnu cmake - - name: Run wolfboot_build script + - name: View Presets run: | - rm -rf ./build + # Use CMake to list all presets defined in CMakePresets.json + echo "All presets:" + cmake -S . -B build --list-presets=configure + chmod +x ./tools/scripts/wolfboot_cmake_full_build.sh - ./tools/scripts/wolfboot_cmake_full_build.sh --CLEAN "stm32l4" - ./tools/scripts/wolfboot_cmake_full_build.sh --target "stm32l4" + - name: Run wolfboot_cmake_full_build script + run: | + rm -rf ./build + rm -rf ./build-"${{ matrix.target }}" - ./tools/scripts/wolfboot_cmake_full_build.sh --CLEAN "stm32g0" - ./tools/scripts/wolfboot_cmake_full_build.sh --target "stm32g0" + ./tools/scripts/wolfboot_cmake_full_build.sh --CLEAN "${{ matrix.target }}" + ./tools/scripts/wolfboot_cmake_full_build.sh --target "${{ matrix.target }}" diff --git a/.github/workflows/test-build-cmake-windows.yml b/.github/workflows/test-build-cmake-windows.yml index 9eab7b62..1d55333c 100644 --- a/.github/workflows/test-build-cmake-windows.yml +++ b/.github/workflows/test-build-cmake-windows.yml @@ -4,7 +4,7 @@ on: push: branches: [ 'master', 'main', 'release/**' ] pull_request: - branches: [ '*' ] + branches: [ "*" ] permissions: contents: read @@ -20,9 +20,20 @@ jobs: fail-fast: false matrix: target: - - stm32l4 - - stm32h7 + # - imx-rt # Disabled, requires NXP SDK + - stm32c0 + - stm32f1 + - stm32f4 + - stm32f7 - stm32g0 + - stm32h5 + - stm32h7 + - stm32l0 + - stm32l4 + - stm32l5 + - stm32u5 + - stm32wb + include: # Optional per-target cache variables you might want to pass later. # Keep empty for now to avoid guessing addresses. diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ab52659..02c35497 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -272,6 +272,20 @@ endif() message(STATUS "Host CC: ${HOST_CC}") message(STATUS "Host compiler treated as MSVC: ${HOST_IS_MSVC}") +if(WOLFBOOT_TARGET STREQUAL "imx_rt") + # This target depends on the NXP MCUXpresso SDK for EVKB-IMXRT1040. + # Without it, the build will fail anyway on missing fsl_* headers. + set(IMXRT_SDK_ROOT "" CACHE PATH "Path to MCUXpresso SDK for EVKB-IMXRT1040") + + if(NOT IMXRT_SDK_ROOT OR NOT EXISTS "${IMXRT_SDK_ROOT}") + message(FATAL_ERROR + "WOLFBOOT_TARGET=imx_rt requires IMXRT_SDK_ROOT to point to a valid " + "MCUXpresso SDK for EVKB-IMXRT1040 (fsl_cache.h, etc.). " + "You currently do not have this SDK installed." + ) + endif() +endif() + #--------------------------------------------------------------------------------------------- # Common includes/defines for host tools #--------------------------------------------------------------------------------------------- @@ -365,7 +379,35 @@ if(NOT DEFINED WOLFBOOT_SECTOR_SIZE) endif() if(NOT DEFINED ARM_TARGETS) - list(APPEND ARM_TARGETS cypsoc6 imx kinetis lpc54606j512 mcxa mcxw nrf52 nrf52840 nrf5340 nrf5340_net rp2350 sama5d3 same51 stm32c0 stm32f1 stm32f4 stm32f7 stm32g0 stm32h5 stm32h7 stm32l0 stm32l4 stm32l5 stm32u5 stm32wb ti zynqmp) + list(APPEND ARM_TARGETS cypsoc6 + imx + imx_rt + kinetis + lpc54606j512 + mcxa + mcxw + nrf52 + nrf52840 + nrf5340 + nrf5340_net + rp2350 + sama5d3 + same51 + stm32c0 + stm32f1 + stm32f4 + stm32f7 + stm32g0 + stm32h5 + stm32h7 + stm32l0 + stm32l4 + stm32l5 + stm32u5 + stm32wb + ti + zynqmp + ) set(ARM_TARGETS "${ARM_TARGETS}" CACHE INTERNAL "") @@ -380,9 +422,9 @@ endif() if("${WOLFBOOT_TARGET}" IN_LIST ARM_TARGETS) set(ARCH ARM) -elseif(WOLFBOOT_TARGET STREQUAL "x86_64_efi") +elseif("${WOLFBOOT_TARGET}" STREQUAL "x86_64_efi") set(ARCH x86_64) -elseif(WOLFBOOT_TARGET STREQUAL "sim") +elseif("${WOLFBOOT_TARGET}" STREQUAL "sim") set(ARCH sim) else() message(FATAL_ERROR "Unable to configure ARCH for target ${WOLFBOOT_TARGET}") @@ -549,6 +591,7 @@ set(UPDATE_SOURCES src/update_flash.c) # Default flash offset if(NOT DEFINED ARCH_FLASH_OFFSET) + message(STATUS "WARNING: ARCH_FLASH_OFFSET not defined, setting to default 0x0") set(ARCH_FLASH_OFFSET 0x0) endif() @@ -916,21 +959,29 @@ list(TRANSFORM WOLFBOOT_SOURCES PREPEND ${WOLFBOOT_ROOT}/) # Hash settings #--------------------------------------------------------------------------------------------- if(HASH STREQUAL "SHA256") - list(APPEND WOLFBOOT_DEFS WOLFBOOT_HASH_SHA256) + list(APPEND WOLFBOOT_DEFS WOLFBOOT_HASH_SHA256) list(APPEND SIGN_OPTIONS WOLFBOOT_HASH_SHA256) message(STATUS "Using SHA256 hash") endif() if(HASH STREQUAL "SHA384") - list(APPEND WOLFBOOT_DEFS WOLFBOOT_HASH_SHA384) + list(APPEND WOLFBOOT_DEFS WOLFBOOT_HASH_SHA384) list(APPEND SIGN_OPTIONS WOLFBOOT_HASH_SHA384) - list(APPEND KEYTOOL_OPTIONS --sha384) + # Note: the keygen tool in this tree does not accept a --sha384 CLI flag. + # Hash selection is controlled via WOLFBOOT_HASH_SHA384 in the image header, + # so do not append a hash option to KEYTOOL_OPTIONS here. + # list(APPEND KEYTOOL_OPTIONS --sha384) + message(STATUS "Using SHA384 hash") endif() if(HASH STREQUAL "SHA3") - list(APPEND WOLFBOOT_DEFS WOLFBOOT_HASH_SHA3_384) + list(APPEND WOLFBOOT_DEFS WOLFBOOT_HASH_SHA3_384) list(APPEND SIGN_OPTIONS WOLFBOOT_HASH_SHA3_384) - list(APPEND KEYTOOL_OPTIONS --sha3) + # Note: the keygen tool in this tree does not accept a --sha3 CLI flag. + # Hash selection is controlled via WOLFBOOT_HASH_SHA3_384 in the image header, + # so do not append a hash option to KEYTOOL_OPTIONS here. + # list(APPEND KEYTOOL_OPTIONS --sha3) + message(STATUS "Using SHA3_384 hash") endif() #--------------------------------------------------------------------------------------------- @@ -1225,26 +1276,26 @@ endif() add_subdirectory(lib) - if (TARGET wolfcrypt) - set(WOLFSSL_TGT wolfcrypt) - elseif (TARGET wolfssl) - set(WOLFSSL_TGT wolfssl) - else() - message(FATAL_ERROR "wolfSSL submodule did not define a 'wolfcrypt' or 'wolfssl' target.") - endif() - message(STATUS "Using wolfSSL target: ${WOLFSSL_TGT}") +if(TARGET wolfcrypt) + set(WOLFSSL_TGT wolfcrypt) +elseif(TARGET wolfssl) + set(WOLFSSL_TGT wolfssl) +else() + message(FATAL_ERROR "wolfSSL submodule did not define a 'wolfcrypt' or 'wolfssl' target.") +endif() +message(STATUS "Using wolfSSL target: ${WOLFSSL_TGT}") - if (TARGET ${WOLFSSL_TGT}) +if(TARGET ${WOLFSSL_TGT}) target_compile_definitions(${WOLFSSL_TGT} PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN) target_compile_options(${WOLFSSL_TGT} PRIVATE - $<$:/Umin> - $<$:/Umax> - $<$:/USIGN> - $<$:/UHASH> - $<$:/Zc:preprocessor> + $<$:/Umin> + $<$:/Umax> + $<$:/USIGN> + $<$:/UHASH> + $<$:/Zc:preprocessor> ) - endif() +endif() # TARGET ${WOLFSSL_TGT} if(BUILD_TEST_APPS OR BUILD_IMAGE) message(STATUS "Building wolfBoot image") diff --git a/CMakePresets.json b/CMakePresets.json index 4fd1e660..796b0f93 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -63,7 +63,17 @@ "inherits": "base_build_presetName", "cacheVariables": { "WOLFBOOT_CONFIG_MODE": "preset", - "WOLFBOOT_HAS_BASE_PRESET": true + "WOLFBOOT_HAS_BASE_PRESET": true, + "BUILD_TEST_APPS": "ON" + } + }, + { + "name": "imx", + "hidden": true, + "generator": "Ninja", + "cacheVariables": { + "ARCH": "ARM", + "CMAKE_TOOLCHAIN_FILE": "cmake/toolchain_arm-none-eabi.cmake" } }, { @@ -71,6 +81,8 @@ "hidden": true, "generator": "Ninja", "cacheVariables": { + "ARCH": "ARM", + "CMAKE_TOOLCHAIN_FILE": "cmake/toolchain_arm-none-eabi.cmake" } }, { @@ -91,22 +103,42 @@ } }, { - "name": "vs-debug-trace", - "displayName": "VS Debug Trace (STM32H7)", - "inherits": [ - "base", - "stm32h7" - ], - "binaryDir": "${sourceDir}/out/build/${presetName}", - "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_VERBOSE_MAKEFILE": "ON", - "CMAKE_FIND_DEBUG_MODE": "ON" - } + "name": "imx_rt", + "displayName": "imx_rt", + "inherits": [ + "base", + "imx", + "sign_hash_config" + ], + "generator": "Ninja", + "binaryDir": "${sourceDir}/build-imx_rt", + "cacheVariables": { + "ARCH": "ARM", + "WOLFBOOT_TARGET": "imx_rt", + "MCUXSDK": "OFF", + "DEBUG": "OFF", + "CORTEX_M0": "OFF", + "NO_ASM": "OFF", + "NO_MPU": "ON", + "EXT_FLASH": "OFF", + "SPI_FLASH": "OFF", + "ALLOW_DOWNGRADE": "OFF", + "NVM_FLASH_WRITEONCE": "ON", + "WOLFBOOT_VERSION": "OFF", + "SPMATH": "ON", + "RAM_CODE": "ON", + "DUALBANK_SWAP": "OFF", + "WOLFBOOT_PARTITION_SIZE": "0x20000", + "WOLFBOOT_SECTOR_SIZE": "0x1000", + "WOLFBOOT_PARTITION_BOOT_ADDRESS": "0x60010000", + "WOLFBOOT_PARTITION_UPDATE_ADDRESS": "0x60030000", + "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x60050000", + "WOLFBOOT_SMALL_STACK": "ON" + } }, { "name": "stm32c0", - "displayName": "STM32C0", + "displayName": "stm32c0", "inherits": [ "base", "sign_hash_config", @@ -128,9 +160,7 @@ "WOLFBOOT_SECTOR_SIZE": "0x800", "WOLFBOOT_PARTITION_BOOT_ADDRESS": "0x08002800", "WOLFBOOT_PARTITION_UPDATE_ADDRESS": "0x08005000", - "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x08007800", - "CMAKE_TOOLCHAIN_FILE": "cmake/toolchain_arm-none-eabi.cmake", - "BUILD_TEST_APPS": "ON" + "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x08007800" } }, { @@ -138,7 +168,8 @@ "displayName": "stm32f1", "inherits": [ "base", - "stm32" + "stm32", + "sign_hash_config" ], "cacheVariables": { "WOLFBOOT_TARGET": "stm32f1", @@ -150,9 +181,75 @@ "WOLFBOOT_PARTITION_UPDATE_ADDRESS": "0x08009400", "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x0800F800", "WOLFBOOT_PARTITION_SIZE": "0x6400", - "WOLFBOOT_SECTOR_SIZE": "0x400", - "CMAKE_TOOLCHAIN_FILE": "cmake/toolchain_arm-none-eabi.cmake", - "BUILD_TEST_APPS": "ON" + "WOLFBOOT_SECTOR_SIZE": "0x400" + } + }, + { + "name": "stm32f4", + "displayName": "stm32f4", + "inherits": [ + "base", + "stm32", + "sign_hash_config" + ], + "generator": "Ninja", + "binaryDir": "${sourceDir}/build-stm32f4", + "cacheVariables": { + "WOLFBOOT_TARGET": "stm32f4", + "NO_ASM": "OFF", + "EXT_FLASH": "OFF", + "SPI_FLASH": "OFF", + "NO_XIP": "OFF", + "UART_FLASH": "ON", + "ALLOW_DOWNGRADE": "OFF", + "NVM_FLASH_WRITEONCE": "OFF", + "WOLFBOOT_VERSION": "OFF", + "SPMATH": "ON", + "RAM_CODE": "OFF", + "WOLFBOOT_PARTITION_SIZE": "0x4000", + "WOLFBOOT_SECTOR_SIZE": "0x4000", + "WOLFBOOT_PARTITION_BOOT_ADDRESS": "0x8000", + "WOLFBOOT_PARTITION_UPDATE_ADDRESS": "0x00000", + "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x4000", + "WOLFBOOT_LOAD_ADDRESS": "0x200000", + "WOLFBOOT_LOAD_DTS_ADDRESS": "0x400000", + "CLOCK_SPEED": "160000000", + "STM32_PLLM": "8", + "STM32_PLLN": "336", + "STM32_PLLP": "2", + "STM32_PLLQ": "7" + } + }, + { + "name": "stm32f7", + "displayName": "stm32f7", + "inherits": [ + "base", + "stm32", + "sign_hash_config" + ], + "generator": "Ninja", + "binaryDir": "${sourceDir}/build-stm32f7", + "cacheVariables": { + "ARCH": "ARM", + "WOLFBOOT_TARGET": "stm32f7", + "DEBUG": "OFF", + "VTOR": "ON", + "CORTEX_M0": "OFF", + "NO_ASM": "OFF", + "EXT_FLASH": "OFF", + "SPI_FLASH": "OFF", + "ALLOW_DOWNGRADE": "OFF", + "NVM_FLASH_WRITEONCE": "OFF", + "WOLFBOOT_VERSION": "ON", + "V": "OFF", + "SPMATH": "ON", + "RAM_CODE": "ON", + "WOLFBOOT_PARTITION_SIZE": "0x60000", + "WOLFBOOT_SECTOR_SIZE": "0x20000", + "WOLFBOOT_PARTITION_BOOT_ADDRESS": "0x8020000", + "WOLFBOOT_PARTITION_UPDATE_ADDRESS": "0x8080000", + "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x80E0000" } }, { @@ -160,8 +257,8 @@ "displayName": "stm32g0", "inherits": [ "base", - "sign_hash_config", - "stm32" + "stm32", + "sign_hash_config" ], "cacheVariables": { "WOLFBOOT_TARGET": "stm32g0", @@ -178,25 +275,68 @@ "WOLFBOOT_PARTITION_SIZE": "0xB000", "WOLFBOOT_SECTOR_SIZE": "0x800", "ARCH_FLASH_OFFSET": "0x08000000", + "WOLFBOOT_ORIGIN": "0x08000000", "WOLFBOOT_PARTITION_BOOT_ADDRESS": "0x08008000", "WOLFBOOT_PARTITION_UPDATE_ADDRESS": "0x08013000", - "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x0801E000", - "BUILD_TEST_APPS": "ON" + "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x0801E000" + } + }, + { + "name": "stm32h5", + "displayName": "stm32h5", + "inherits": [ + "base", + "stm32", + "sign_hash_config" + ], + "generator": "Ninja", + "binaryDir": "${sourceDir}/build-stm32h5", + "cacheVariables": { + "BUILD_TEST_APPS": "OFF", + "ARCH": "ARM", + "TZEN": "ON", + "WOLFBOOT_TARGET": "stm32h5", + "DEBUG": "OFF", + "VTOR": "ON", + "CORTEX_M0": "OFF", + "CORTEX_M33": "ON", + "NO_ASM": "OFF", + "NO_MPU": "ON", + "EXT_FLASH": "OFF", + "SPI_FLASH": "OFF", + "ALLOW_DOWNGRADE": "OFF", + "NVM_FLASH_WRITEONCE": "ON", + "WOLFBOOT_VERSION": "ON", + "V": "OFF", + "SPMATH": "ON", + "RAM_CODE": "OFF", + "DUALBANK_SWAP": "OFF", + "WOLFBOOT_PARTITION_SIZE": "0xA0000", + "WOLFBOOT_SECTOR_SIZE": "0x2000", + "WOLFBOOT_KEYVAULT_ADDRESS": "0x0C040000", + "WOLFBOOT_KEYVAULT_SIZE": "0x1C000", + "WOLFBOOT_NSC_ADDRESS": "0x0C05C000", + "WOLFBOOT_NSC_SIZE": "0x4000", + "WOLFBOOT_PARTITION_BOOT_ADDRESS": "0x08060000", + "WOLFBOOT_PARTITION_UPDATE_ADDRESS": "0x08100000", + "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x081A0000", + "FLAGS_HOME": "OFF", + "DISABLE_BACKUP": "OFF", + "IMAGE_HEADER_SIZE": "1024", + "ARMORED": "ON" } }, { "name": "stm32h7", - "displayName": "STM32H7", + "displayName": "stm32h7", "inherits": [ "base", - "sign_hash_config", - "stm32" + "stm32", + "sign_hash_config" ], "generator": "Ninja", "cacheVariables": { "WOLFBOOT_TARGET": "stm32h7", - "BUILD_TEST_APPS": "yes", - "ARCH": "ARM", "DEBUG_UART": "OFF", "NO_ASM": "OFF", "EXT_FLASH": "OFF", @@ -210,25 +350,60 @@ "RAM_CODE": "OFF", "WOLFBOOT_PARTITION_SIZE": "0xD0000", "WOLFBOOT_SECTOR_SIZE": "0x20000", + "ARCH_FLASH_OFFSET": "0x08000000", + "WOLFBOOT_ORIGIN": "0x08000000", "WOLFBOOT_PARTITION_BOOT_ADDRESS": "0x8020000", "WOLFBOOT_PARTITION_UPDATE_ADDRESS": "0x80F0000", - "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x81C0000" + "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x81C0000", + "DEBUG": "OFF", + "VTOR": "ON", + "V": "OFF", + "DUALBANK_SWAP": "OFF" + } + }, + { + "name": "stm32l0", + "displayName": "stm32l0", + "inherits": [ + "base", + "stm32", + "sign_hash_config" + ], + "generator": "Ninja", + "binaryDir": "${sourceDir}/build-stm32l0", + "cacheVariables": { + "ARCH": "ARM", + "WOLFBOOT_TARGET": "stm32l0", + "SIGN": "ED25519", + "NO_ASM": "OFF", + "EXT_FLASH": "OFF", + "SPI_FLASH": "OFF", + "ALLOW_DOWNGRADE": "OFF", + "NVM_FLASH_WRITEONCE": "ON", + "FLAGS_INVERT": "ON", + "WOLFBOOT_VERSION": "OFF", + "SPMATH": "ON", + "RAM_CODE": "OFF", + "WOLFBOOT_PARTITION_SIZE": "0x10000", + "WOLFBOOT_SECTOR_SIZE": "0x1000", + "WOLFBOOT_PARTITION_BOOT_ADDRESS": "0x8000", + "WOLFBOOT_PARTITION_UPDATE_ADDRESS": "0x18000", + "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x28000" } }, { "name": "stm32l4", - "displayName": "STM32L4", + "displayName": "stm32l4", "inherits": [ "base", - "sign_hash_config", - "stm32" + "stm32", + "sign_hash_config" ], "environment": { "VSCMD_DEBUG": "3" }, "cacheVariables": { "WOLFBOOT_TARGET": "stm32l4", - "ARCH": "ARM", "VTOR": "ON", "NO_ASM": "OFF", "EXT_FLASH": "OFF", @@ -243,9 +418,114 @@ "WOLFBOOT_PARTITION_SIZE": "0x7A000", "WOLFBOOT_PARTITION_BOOT_ADDRESS": "0x0800A000", "WOLFBOOT_PARTITION_UPDATE_ADDRESS": "0x08084000", - "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x080FE000", - "CMAKE_TOOLCHAIN_FILE": "cmake/toolchain_arm-none-eabi.cmake", - "BUILD_TEST_APPS": "ON" + "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x080FE000" + } + }, + { + "name": "stm32l5", + "displayName": "stm32l5", + "inherits": [ + "base", + "stm32", + "sign_hash_config" + ], + "generator": "Ninja", + "binaryDir": "${sourceDir}/build-stm32l5", + "cacheVariables": { + "BUILD_TEST_APPS": "OFF", + "WOLFBOOT_TARGET": "stm32l5", + "ARCH_FLASH_OFFSET": "0x08000000", + "WOLFBOOT_NSC_ADDRESS": "0x08110000", + "WOLFBOOT_NSC_SIZE": "0x00010000", + "NO_ASM": "OFF", + "NO_MPU": "ON", + "EXT_FLASH": "OFF", + "SPI_FLASH": "OFF", + "ALLOW_DOWNGRADE": "OFF", + "NVM_FLASH_WRITEONCE": "ON", + "WOLFBOOT_VERSION": "ON", + "SPMATH": "ON", + "RAM_CODE": "OFF", + "WOLFBOOT_PARTITION_SIZE": "0x30000", + "WOLFBOOT_SECTOR_SIZE": "0x2000", + "WOLFBOOT_PARTITION_BOOT_ADDRESS": "0x08010000", + "WOLFBOOT_PARTITION_UPDATE_ADDRESS": "0x08110000", + "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0xFFFFFFFF", + "IMAGE_HEADER_SIZE": "1024" + } + }, + { + "name": "stm32u5", + "displayName": "stm32u5", + "inherits": [ + "base", + "stm32", + "sign_hash_config" + ], + "generator": "Ninja", + "binaryDir": "${sourceDir}/build-stm32u5", + "cacheVariables": { + "ARCH": "ARM", + "TZEN": "ON", + "WOLFBOOT_TARGET": "stm32u5", + "NO_ASM": "OFF", + "NO_MPU": "ON", + "EXT_FLASH": "OFF", + "SPI_FLASH": "OFF", + "QSPI_FLASH": "OFF", + "OCTOSPI_FLASH": "OFF", + "ALLOW_DOWNGRADE": "OFF", + "NVM_FLASH_WRITEONCE": "ON", + "WOLFBOOT_VERSION": "ON", + "SPMATH": "ON", + "RAM_CODE": "OFF", + "DUALBANK_SWAP": "OFF", + "WOLFBOOT_PARTITION_SIZE": "0x1F800", + "WOLFBOOT_SECTOR_SIZE": "0x800", + "WOLFBOOT_KEYVAULT_ADDRESS": "0x0C020000", + "WOLFBOOT_KEYVAULT_SIZE": "0x18000", + "WOLFBOOT_NSC_ADDRESS": "0x0C038000", + "WOLFBOOT_NSC_SIZE": "0x8000", + "WOLFBOOT_PARTITION_BOOT_ADDRESS": "0x08040000", + "WOLFBOOT_PARTITION_UPDATE_ADDRESS": "0x805F800", + "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x0807F000", + "FLAGS_HOME": "OFF", + "DISABLE_BACKUP": "OFF" + } + }, + { + "name": "stm32wb", + "displayName": "stm32wb", + "inherits": [ + "base", + "stm32", + "sign_hash_config" + ], + "generator": "Ninja", + "binaryDir": "${sourceDir}/build-stm32wb", + "cacheVariables": { + "BUILD_TEST_APPS": "OFF", + "WOLFBOOT_TARGET": "stm32wb", + "WOLFBOOT_SECTOR_SIZE": "0x1000", + "WOLFBOOT_PARTITION_SIZE": "0x20000", + "WOLFBOOT_PARTITION_BOOT_ADDRESS": "0x08008000", + "WOLFBOOT_PARTITION_UPDATE_ADDRESS": "0x08028000", + "WOLFBOOT_PARTITION_SWAP_ADDRESS": "0x08048000", + "NVM_FLASH_WRITEONCE": "ON" + } + }, + { + "name": "vs-debug-trace", + "displayName": "VS Debug Trace (STM32H7)", + "inherits": [ + "base", + "stm32h7" + ], + "binaryDir": "${sourceDir}/out/build/${presetName}", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_VERBOSE_MAKEFILE": "ON", + "CMAKE_FIND_DEBUG_MODE": "ON" } } ], @@ -259,6 +539,15 @@ "all" ] }, + { + "name": "imx_rt", + "configurePreset": "imx_rt", + "jobs": 4, + "verbose": true, + "targets": [ + "all" + ] + }, { "name": "stm32c0", "configurePreset": "stm32c0", @@ -277,6 +566,24 @@ "all" ] }, + { + "name": "stm32f4", + "configurePreset": "stm32f4", + "jobs": 4, + "verbose": true, + "targets": [ + "all" + ] + }, + { + "name": "stm32f7", + "configurePreset": "stm32f7", + "jobs": 4, + "verbose": true, + "targets": [ + "all" + ] + }, { "name": "stm32g0", "configurePreset": "stm32g0", @@ -286,6 +593,15 @@ "all" ] }, + { + "name": "stm32h5", + "configurePreset": "stm32h5", + "jobs": 4, + "verbose": true, + "targets": [ + "all" + ] + }, { "name": "stm32h7", "configurePreset": "stm32h7", @@ -295,6 +611,15 @@ "all" ] }, + { + "name": "stm32l0", + "configurePreset": "stm32l0", + "jobs": 4, + "verbose": true, + "targets": [ + "all" + ] + }, { "name": "stm32l4", "configurePreset": "stm32l4", @@ -304,6 +629,33 @@ "all" ] }, + { + "name": "stm32l5", + "configurePreset": "stm32l5", + "jobs": 4, + "verbose": true, + "targets": [ + "all" + ] + }, + { + "name": "stm32u5", + "configurePreset": "stm32u5", + "jobs": 4, + "verbose": true, + "targets": [ + "all" + ] + }, + { + "name": "stm32wb", + "configurePreset": "stm32wb", + "jobs": 4, + "verbose": true, + "targets": [ + "all" + ] + }, { "name": "vs-debug-trace", "configurePreset": "vs-debug-trace", diff --git a/IDE/VSCode/README.md b/IDE/VSCode/README.md index c0442319..f955fc0a 100644 --- a/IDE/VSCode/README.md +++ b/IDE/VSCode/README.md @@ -1,4 +1,4 @@ -# VS Code wolfBoot Project +# VS Code wolfBoot Project CMake presets are support in VS Code. See also details in the [cmake/README.md](../../cmake/README). @@ -24,6 +24,32 @@ code wolfBoot.code-workspace If all of the prerequisites are NOT installed, see the [install.sh](./install.sh) script. +## Windows + +Windows users may need one of these: + +- [Visual Studio 2022](https://visualstudio.microsoft.com/) +- [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/). See `C:\Program Files(x86)\Windows kits`. + +The MSVC kit may be needed if VS 2022 is not installed. + +Select `View` - `Command Palette`, search for CMake: Select a Compiler + +See also: CMake: Delete Cache and Reconfigure + +### Launch VS Code from VS 2022 Command prompt. + +Delete any existing `build` or `build-[target]` directories as needed. + +Open a VS 2022 Developer command prompt. + +From the command prompt, open the `wolfBoot.code-workspace` VS Code workspace: + +```dos +cd c:\workspace\wolfboot-%USERNAME% +code ./IDE/VSCode/wolfBoot.code-workspace +``` + ----- ## Additional Settings diff --git a/IDE/VSCode/install.sh b/IDE/VSCode/install.sh index c9fbff3b..9b544082 100644 --- a/IDE/VSCode/install.sh +++ b/IDE/VSCode/install.sh @@ -46,7 +46,8 @@ echo "Starting $0 from $(pwd -P)" # End common dir init pwd -git clone https://github.com/gojimmypi/wolfBoot.git + +git clone https://github.com/master/wolfBoot.git cd wolfBoot git submodule update --init diff --git a/config/examples/stm32g0.config b/config/examples/stm32g0.config index 8d4f953e..7e99b284 100644 --- a/config/examples/stm32g0.config +++ b/config/examples/stm32g0.config @@ -17,7 +17,7 @@ RAM_CODE?=1 DUALBANK_SWAP?=0 # Enable this feature for secure memory support -# Makes the flash sectors for the bootloader unaccessible from the application +# Makes the flash sectors for the bootloader inaccessible from the application # Requires using the STM32CubeProgrammer to set FLASH_SECR -> SEC_SIZE pages #CFLAGS_EXTRA+=-DFLASH_SECURABLE_MEMORY_SUPPORT diff --git a/docs/PQ.md b/docs/PQ.md index 346a5779..ab1c54ab 100644 --- a/docs/PQ.md +++ b/docs/PQ.md @@ -1,4 +1,4 @@ -# Post-Quantum Signatures +# Post-Quantum Signatures wolfBoot is continuously adding support for post-quantum (PQ) signature algorithms as they mature. At present, support has been added for three NIST diff --git a/docs/README.md b/docs/README.md index 3ecd13c5..758e556d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,4 @@ -## wolfBoot Docs and Platform-Specific Details +## wolfBoot Docs and Platform-Specific Details See also: [wolfBoot Product Overview](https://www.wolfssl.com/products/wolfboot/) and [wolfBoot Manual](https://www.wolfssl.com/documentation/manuals/wolfboot/). diff --git a/docs/STM32.md b/docs/STM32.md deleted file mode 100644 index b5ff24c6..00000000 --- a/docs/STM32.md +++ /dev/null @@ -1,148 +0,0 @@ -## wolfBoot for STM32 devices - -The default [Makefile](../Makefile) needs at least the `gcc-arm-none-eabi`. - -```bash -sudo apt update -sudo apt install -y build-essential gcc-arm-none-eabi binutils-arm-none-eabi -# optional (often handy): gdb-multiarch or gdb-arm-none-eabi -arm-none-eabi-gcc --version # should print the version -``` - -A device toolchain _also_ needs to be installed. For example without the appropriate support files, -errors like this may otherwise be encountered: - -``` - [CC ARM] hal/stm32l4.o -hal/stm32l4.c:24:10: fatal error: stm32l4xx_hal.h: No such file or directory - 24 | #include "stm32l4xx_hal.h" - | ^~~~~~~~~~~~~~~~~ -``` - -There are a variety of other sources for device-specific toolchains, for example: - -- [arm-gnu-toolchain-downloads](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) -- [STM32CubeIDE Software](https://www.st.com/en/development-tools/stm32cubeide.html) -- [gnutoolchains.com](https://gnutoolchains.com/) -- [IAR Embedded Workbench](https://www.iar.com/embedded-development-tools/iar-embedded-workbench) -- [keil](https://www.keil.com/download/product/) -- [llvm](https://releases.llvm.org/download.html) - -wolfBoot s supported on nearly every development environment. - -## Quick Start - -wolfBoot can be used with either `make` or `CMake`. - -Be sure to include the submodules when cloning: - -```bash -git clone https://github.com/wolfssl/wolfBoot.git -cd wolfBoot -git submodule update --init -``` - -### make - -The `make` builds use the `.config` files. See [config/examples](../config/examples) - -``` -## Use make -# edit your .config or copy from config/examples -make -``` - - -### CMake - -``` -## Use CMake -## See wolfboot_cmake_full_build.sh script: - -./tools/scripts/wolfboot_cmake_full_build.sh --CLEAN -./tools/scripts/wolfboot_cmake_full_build.sh --CLEAN stm32h7 -./tools/scripts/wolfboot_cmake_full_build.sh --target stm32h7 -``` - -## VS Code - -Windows users may need one of these: - -- [Visual Studio 2022](https://visualstudio.microsoft.com/) -- [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/). See `C:\Program Files(x86)\Windows kits`. - -#### Launch Stand-alone VS Code - -The MSVC kit may be needed if VS 2022 is not installed. - -Select `View` - `Command Palette`, search for CMake: Select a Compiler - -See also: CMake: Delete Cache and Reconfigure - - -#### Launch VS Code from VS 2022 Command prompt. - -Delete any existing `build` or `build-[target]` directories as needed. - -Open a VS 2022 Developer command prompt. - -From the command prompt, open the `wolfBoot.code-workspace` VS Code workspace: - -```dos -cd c:\workspace\wolfboot-%USERNAME% -code ./IDE/VSCode/wolfBoot.code-workspace -``` - -### Visual Studio IDE - -For the `Select Startup Item`, leave at default. Do not select `image`, wolfboot_name[], etc. - -Right click on `CMakeLists.txt` and select `Delete Cache and Reconfigure`. - -Right click on `CMakeLists.txt` and select `Build`. - -### Visual Studio Command Prompt - -Select `View` - `Terminal` from the menu bar. - -* Configure: `cmake --preset ` -* Build: `cmake --build --preset ` - -```bash -# delete build directory -rmdir /s /q build-stm32l4 - -# configure -cmake --preset stm32l4 - -# build -cmake --build --preset stm32l4 -``` - -If there are no devices listed in the `Manage Configurations` drop-down, ensure the `CMakePresets.json` is valid. -A single json syntax error will spoil the entire project. - -## Your own toolchain - -Create a `CMakeUserPresets.json` (ignored by git, see and rename `cmake/preset-examples/CMakeUserPresets.json.sample` ): - -```json -{ - "version": 3, - "configurePresets": [ - { - "name": "my-arm-bin", - "inherits": "stm32l4", - "cacheVariables": { - "ARM_GCC_BIN": "C:/Tools/arm-none-eabi-14.2/bin" - } - } - ], - "buildPresets": [ - { - "name": "my-arm-bin", - "configurePreset": "my-arm-bin" - } - ] -} -``` diff --git a/docs/Signing.md b/docs/Signing.md index d06e4341..5c278189 100644 --- a/docs/Signing.md +++ b/docs/Signing.md @@ -12,6 +12,8 @@ These can be built in `tools/keytools` using `make` or from the wolfBoot root us #### Windows Visual Studio +Project files can be found in [`[WOLFBOOT_ROOT]/IDE/VisualStudio`](../IDE/VisualStudio/README.md). + Use the `wolfBootSignTool.vcxproj` Visual Studio project to build the `sign.exe` and `keygen.exe` tools for use on Windows. If you see any error about missing `target.h` this is a generated file based on your .config using the make process. It is needed for `WOLFBOOT_SECTOR_SIZE` used in delta updates. diff --git a/tools/keytools/README.md b/tools/keytools/README.md index cee4e06a..87ad18bd 100644 --- a/tools/keytools/README.md +++ b/tools/keytools/README.md @@ -1,3 +1,59 @@ # Key Tools for signing and key generation -See documentation [here](../../docs/Signing.md). +## Sign + +See [code file `./tools/keytools/sign.c`](./sign.c) and documentation in [docs/Signing.md](../../docs/Signing.md). + +## KeyGen and KeyStore + +See [code file `./tools/keytools/keygen.c`](./keygen.c) and documentation [docs/keystore.md](../../docs/keystore.md). + +## Flash OTP Keystore Generation, Primer, Startup + +See documentation [docs/flash-OTP.md](../../docs/flash-OTP.md). + +### Keystore Generation + +Pack public keys into a single binary (`otp.bin`) formatted the way wolfBoot expects for +provisioning the device’s OTP/NVM keystore. No signing, no encryption—just a correctly laid-out image +with a header plus fixed-size "slots" for each key. + +See [code file `./tools/keytools/otp/otp-keystore-gen.c`](./otp/otp-keystore-gen.c) + +### Flash OTP Primer + +See [code file `./tools/keytools/otp/otp-keystore-primer.c`](./otp/otp-keystore-primer.c) + +## Flash OTP Startup + +See [code file `./tools/keytools/otp/startup.c`](./otp/startup.c) + + +## Quick Start (Linux) + +``` +make wolfboot_signing_private_key.der SIGN=ED25519 + +# or + +./tools/keytools/keygen --ed25519 -g wolfboot_signing_private_key.der +``` + +## Debugging and Development + +### `DEBUG_SIGNTOOL` + +Enables additional diagnostic messages that may be useful during development and initial bring-up. + +### `WOLFBOOT_SHOW_INCLUDE` + +Enables compile-time verbosity to indicate which `user_settings.h` file is being used. + +Unless otherwise specified the `keygen` app will create: + +1. `./wolfboot_signing_private_key.der` - the private key used for signing. +2. `./keystore.der` - the public key. +3. `src/keystore.c` - the public key, converted to c array. + +Common pitfalls: mismatched keystore files in different directories. There should be exactly one +instance of each of the above files in the build tree. diff --git a/tools/scripts/cmake_dot_config.sh b/tools/scripts/cmake_dot_config.sh index c84bd461..ea249fbd 100644 --- a/tools/scripts/cmake_dot_config.sh +++ b/tools/scripts/cmake_dot_config.sh @@ -22,6 +22,16 @@ fi set -euo pipefail +# Require a target argument, e.g. stm32h7 +TARGET="${1-}" +if [ -z "$TARGET" ]; then + echo "Usage: $0 " >&2 + echo "Example: $0 stm32h7" >&2 + exit 1 +fi + +BUILD_DIR="build-${TARGET}" + # Begin common dir init, for /tools/scripts # Resolve this script's absolute path and its directories SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" @@ -49,6 +59,7 @@ esac # Always work from the repo root, regardless of where the script was invoked cd -- "$REPO_ROOT_P" || { printf 'Failed to cd to: %s\n' "$REPO_ROOT_P" >&2; exit 1; } echo "Starting $0 from $(pwd -P)" +echo "Using target: $TARGET" # End common dir init @@ -59,9 +70,9 @@ LOG_FILE="run.log" KEYWORD="Config mode: dot" echo "Saving output to $LOG_FILE" -echo "Fetch stm32h7 example .config" +echo "Fetch ${TARGET} example .config" -SRC="./config/examples/stm32h7.config" +SRC="./config/examples/${TARGET}.config" DST="./.config" # Exit if the .config file already exists (perhaps it is valid? we will delete our copy when done here) @@ -96,7 +107,7 @@ fi echo "OK: $DST created and verified." -cp ./config/examples/stm32h7.config ./.config +cp "./config/examples/${TARGET}.config" ./.config ls .config echo "" @@ -105,10 +116,10 @@ cat .config echo "" echo "Clean" -rm -rf ./build-stm32h7 -cmake -S . -B build-stm32h7 \ - -DUSE_DOT_CONFIG=ON \ - -DWOLFBOOT_TARGET=stm32h7 2>&1 | tee "$LOG_FILE" >/dev/tty +rm -rf "./${BUILD_DIR}" +cmake -S . -B "${BUILD_DIR}" \ + -DUSE_DOT_CONFIG=ON \ + -DWOLFBOOT_TARGET="${TARGET}" 2>&1 | tee "$LOG_FILE" >/dev/tty # Config dot-config mode if grep -q -- "$KEYWORD" "$LOG_FILE"; then @@ -119,7 +130,6 @@ else fi # Sample build -cmake --build build-stm32h7 -j10 +cmake --build "${BUILD_DIR}" -j10 rm .config - diff --git a/tools/scripts/config2presets.py b/tools/scripts/config2presets.py index 76a2059d..442a9365 100644 --- a/tools/scripts/config2presets.py +++ b/tools/scripts/config2presets.py @@ -5,7 +5,12 @@ # Example: # python3 ./tools/scripts/config2presets.py ./config/examples/stm32h7.config -import argparse, json, os, re, sys +import argparse +import json +import os +import re +import sys +import subprocess from collections import OrderedDict from pathlib import Path @@ -15,6 +20,17 @@ LINE_RE = re.compile(r'^([A-Za-z_][A-Za-z0-9_]*)\s*\??=\s*(.*)$') BOOL_TRUE = {"1", "on", "true", "yes", "y"} BOOL_FALSE = {"0", "off", "false", "no", "n"} +# Known inherited values: these are provided implicitly by presets/build +# If the .config value matches, do NOT write to cacheVariables. +# If it differs, keep it and warn that it overrides the inherited default. +KNOWN_INHERITED = { + "WOLFBOOT_CONFIG_MODE": "preset", + "CMAKE_TOOLCHAIN_FILE": "cmake/toolchain_arm-none-eabi.cmake", + "SIGN": "ECC256", + "HASH": "SHA256", + "BUILD_TEST_APPS": "ON" +} + def normalize_bool(s: str): v = s.strip().lower() @@ -40,6 +56,40 @@ def parse_config(path: Path): return kv +def filter_inherited_values(kv): + """ + Remove keys whose values match KNOWN_INHERITED defaults (do not + emit them into cacheVariables), and collect messages about + inherited/overridden values. + """ + new_kv = OrderedDict() + messages = [] + + for k, v in kv.items(): + if k in KNOWN_INHERITED: + expected = KNOWN_INHERITED[k] + v_stripped = v.strip() + if v_stripped == expected: + messages.append( + "Note: '{}' is an inherited value with default '{}'; " + "omitting from preset cacheVariables.".format(k, expected) + ) + # Do not add to new_kv; rely on inherited value + continue + else: + messages.append( + "WARNING: '{}' in .config is '{}', overriding inherited " + "default '{}' in preset cacheVariables.".format( + k, v_stripped, expected + ) + ) + # Fall through to keep the override + + new_kv[k] = v + + return new_kv, messages + + def choose_target(kv): # Prefer explicit wolfBoot var; else accept TARGET if present if "WOLFBOOT_TARGET" in kv and kv["WOLFBOOT_TARGET"]: @@ -63,13 +113,14 @@ def to_cache_vars(kv): return cache -def ensure_base_vars(cache, toolchain_path): - # Always ensure toolchain file is set - cache.setdefault("CMAKE_TOOLCHAIN_FILE", toolchain_path) +def ensure_base_vars(cache, toolchain_value): + # These should be inherited from [base] in the JSON presets: + # Always ensure toolchain file is set using the literal value passed in + # cache.setdefault("CMAKE_TOOLCHAIN_FILE", toolchain_value) # Typically desired - cache.setdefault("BUILD_TEST_APPS", "ON") + # cache.setdefault("BUILD_TEST_APPS", "ON") # Force preset mode when generating from .config into presets - cache["WOLFBOOT_CONFIG_MODE"] = "preset" + # cache["WOLFBOOT_CONFIG_MODE"] = "preset" return cache @@ -81,6 +132,32 @@ def make_binary_dir(source_dir, target): return os.path.join(source_dir, f"build-{target}") +def validate_presets_json(presets_path: Path): + """ + If the presets file exists but is not valid JSON, print a clear message + and exit with a non-zero status instead of raising an exception. + """ + if not presets_path.exists(): + return + + try: + with presets_path.open("r", encoding="utf-8") as f: + json.load(f) + except json.JSONDecodeError as e: + print( + "Error: '{}' exists but is not valid JSON (CMakePresets.json is malformed).".format( + presets_path + ), + file=sys.stderr, + ) + print("Details: {}".format(e), file=sys.stderr) + print( + "Please fix the JSON (for example, remove any trailing commas) and rerun this script.", + file=sys.stderr, + ) + sys.exit(3) + + def load_existing_presets(presets_path: Path): try: with presets_path.open("r", encoding="utf-8") as f: @@ -89,7 +166,150 @@ def load_existing_presets(presets_path: Path): return None +def _merge_configure_preset_list(preset_list, cfg_preset): + """ + Update or insert a configure preset. + + - If a preset with the same name exists: + * Preserve all top-level keys (inherits, environment, binaryDir, etc.) + * Merge cacheVariables: existing keys plus new ones, with new values winning. + + - If it does not exist: + * Insert the new preset among the non-hidden presets in a best-fit + alphabetical position, ignoring hidden presets when choosing where + to insert. Existing entries (hidden or not) are not reordered. + """ + name = cfg_preset.get("name") + + # First: update existing entry if present + for idx, existing in enumerate(preset_list): + if existing.get("name") == name: + merged = existing.copy() + + existing_cache = existing.get("cacheVariables", {}) + new_cache = cfg_preset.get("cacheVariables", {}) + + merged_cache = {} + if isinstance(existing_cache, dict): + merged_cache.update(existing_cache) + if isinstance(new_cache, dict): + merged_cache.update(new_cache) + + merged["cacheVariables"] = merged_cache + merged["name"] = name + preset_list[idx] = merged + return preset_list + + # Only reach here if this is a new preset + + if not isinstance(name, str): + # Fallback: no sensible name to compare; just append + preset_list.append(cfg_preset) + return preset_list + + # Build a list of (real_index, preset_name) for NON-hidden presets + visible = [] + for idx, existing in enumerate(preset_list): + # Treat missing "hidden" as visible + if existing.get("hidden") is True: + continue + existing_name = existing.get("name") + if isinstance(existing_name, str): + visible.append((idx, existing_name)) + + # Decide insertion point among visible presets + insert_at_real_index = None + for real_idx, existing_name in visible: + if name < existing_name: + insert_at_real_index = real_idx + break + + if insert_at_real_index is None: + if visible: + # After the last visible preset + last_visible_index = visible[-1][0] + insert_at_real_index = last_visible_index + 1 + else: + # No visible presets at all: append + insert_at_real_index = len(preset_list) + + preset_list.insert(insert_at_real_index, cfg_preset) + return preset_list + + +def _merge_build_preset_list(preset_list, bld_preset): + """ + Update or insert a build preset, keeping existing entries in their + current order and placing new ones in a best-fit alphabetical position. + + - If a preset with the same name exists: + * Preserve existing jobs/verbose/targets and any other fields. + * Only ensure configurePreset is set if it was missing. + + - If it does not exist: + * Consider only entries whose configurePreset is NOT 'sim' when + choosing the insertion point. + * Insert the new preset before the first such entry whose name is + lexicographically greater; if none, insert after the last such entry. + """ + name = bld_preset.get("name") + + # First: update existing entry if present + for idx, existing in enumerate(preset_list): + if existing.get("name") == name: + merged = existing.copy() + if "configurePreset" not in merged and "configurePreset" in bld_preset: + merged["configurePreset"] = bld_preset["configurePreset"] + merged["name"] = name + preset_list[idx] = merged + return preset_list + + # Only reach here for a new preset + if not isinstance(name, str): + # No reasonable name to compare; just append + preset_list.append(bld_preset) + return preset_list + + # Build list of (real_index, preset_name) for entries we care about + # Ignore any entry whose configurePreset is 'sim'. + visible = [] + for idx, existing in enumerate(preset_list): + if existing.get("configurePreset") == "sim": + continue + existing_name = existing.get("name") + if isinstance(existing_name, str): + visible.append((idx, existing_name)) + + insert_at_real_index = None + + # Find first visible preset whose name is lexicographically greater + for real_idx, existing_name in visible: + if name < existing_name: + insert_at_real_index = real_idx + break + + if insert_at_real_index is None: + if visible: + # After the last visible (non-sim) preset + last_visible_index = visible[-1][0] + insert_at_real_index = last_visible_index + 1 + else: + # No visible presets at all: append to the end + insert_at_real_index = len(preset_list) + + preset_list.insert(insert_at_real_index, bld_preset) + return preset_list + + def merge_preset(doc, cfg_preset, bld_preset): + """ + Merge a configure/build preset into an existing CMakePresets.json document. + + - If doc is None, create a fresh schema v3 doc with just these presets. + - Otherwise: + * Ensure configurePresets/buildPresets arrays exist. + * Update or append the specific presets without reordering any others. + """ if doc is None: return { "version": 3, @@ -97,48 +317,200 @@ def merge_preset(doc, cfg_preset, bld_preset): "buildPresets": [bld_preset], } - # If file has newer schema that your CMake can't handle, you can still append, - # but CMake 3.22.1 will choke. We keep the existing version as-is. - if "configurePresets" not in doc: + if "configurePresets" not in doc or not isinstance(doc["configurePresets"], list): doc["configurePresets"] = [] - if "buildPresets" not in doc: + if "buildPresets" not in doc or not isinstance(doc["buildPresets"], list): doc["buildPresets"] = [] - # Replace presets with same name - doc["configurePresets"] = [p for p in doc["configurePresets"] if p.get("name") != cfg_preset["name"]] + [cfg_preset] - doc["buildPresets"] = [p for p in doc["buildPresets"] if p.get("name") != bld_preset["name"]] + [bld_preset] + _merge_configure_preset_list(doc["configurePresets"], cfg_preset) + _merge_build_preset_list(doc["buildPresets"], bld_preset) + return doc +def extract_unused_vars(output: str): + """ + Parse CMake output and return a list of + 'Manually-specified variables were not used by the project' names. + """ + lines = output.splitlines() + unused = [] + capture = False + + for line in lines: + if "Manually-specified variables were not used by the project:" in line: + capture = True + continue + + if not capture: + continue + + stripped = line.strip() + + # Skip leading blank lines after the header + if stripped == "": + # If we have already collected some variables, a blank line + # means the list is over. + if unused: + break + continue + + # Only take reasonably-indented lines as variable names + if line.startswith(" ") or line.startswith(" "): + unused.append(stripped) + else: + # Non-indented line once capture has started means the block is over + if unused: + break + + return unused + + +def run_cmake_and_report_unused(preset_name: str, repo_root: Path): + """ + Run 'cmake --preset ' from repo_root and + print any unused manually-specified variables reported. + """ + print("") + print("Running CMake configure to check for unused manually-specified variables...") + cmd = ["cmake", "--preset", preset_name] + print("Command:", " ".join(cmd)) + + try: + proc = subprocess.run( + cmd, + cwd=str(repo_root), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + except FileNotFoundError: + print("cmake not found on PATH; skipping unused-variable check.", file=sys.stderr) + return + + combined = "" + if proc.stdout: + combined += proc.stdout + if proc.stderr: + if combined: + combined += "\n" + combined += proc.stderr + + unused = extract_unused_vars(combined) + + if unused: + print("") + print( + "CMake reported unused manually-specified variables for preset '{}' :".format( + preset_name + ) + ) + for name in unused: + print(" {}".format(name)) + else: + print("") + print( + "No unused manually-specified CMake variables detected for preset '{}'.".format( + preset_name + ) + ) + + if proc.returncode != 0: + print("") + print("Note: 'cmake --preset {}' exited with code {}.".format( + preset_name, proc.returncode + )) + + +def check_workflow_for_preset(workflow_path: Path, preset_name: str): + """ + Best-effort check: does the given workflow YAML mention this preset/target + name anywhere? If not, print a warning so it is clear the CI matrix will + not build it. + """ + if not workflow_path.exists(): + print( + "Note: workflow YAML '{}' not found; skipping workflow check.".format( + workflow_path + ) + ) + return + + try: + text = workflow_path.read_text(encoding="utf-8") + except OSError as e: + print( + "Warning: could not read workflow YAML '{}': {}".format( + workflow_path, e + ), + file=sys.stderr, + ) + return + + if preset_name in text: + print( + "Workflow check: preset/target '{}' is referenced in '{}'.".format( + preset_name, workflow_path + ) + ) + else: + print( + "WARNING: preset/target '{}' was NOT found in workflow '{}'. " + "CI will not build this target until you add it to the matrix.".format( + preset_name, workflow_path + ), + file=sys.stderr, + ) + + def main(): ap = argparse.ArgumentParser(description="Generate or merge a CMakePresets.json from a .config file") - ap.add_argument("config", - help="Path to .config (relative to your current directory if not absolute)") - ap.add_argument("--toolchain", - default="cmake/toolchain_arm-none-eabi.cmake", - help="Path to toolchain file (relative to repo root if not absolute)") - ap.add_argument("--presets", - default="CMakePresets.json", - help="Path to CMakePresets.json to create/merge (relative to repo root if not absolute)") - ap.add_argument("--generator", - default="Ninja", - help="CMake generator") - ap.add_argument("--preset-name", - default=None, - help="Override preset name") - ap.add_argument("--binary-dir", - default=None, - help="Override binaryDir") - ap.add_argument("--display-name", - default=None, - help="Override displayName") + ap.add_argument( + "config", + help="Path to .config (relative to your current directory if not absolute)", + ) + ap.add_argument( + "--toolchain", + default="cmake/toolchain_arm-none-eabi.cmake", + help="Path to toolchain file as it should appear in CMAKE_TOOLCHAIN_FILE", + ) + ap.add_argument( + "--presets", + default="CMakePresets.json", + help="Path to CMakePresets.json to create/merge (relative to repo root if not absolute)", + ) + ap.add_argument( + "--generator", + default="Ninja", + help="CMake generator", + ) + ap.add_argument( + "--preset-name", + default=None, + help="Override preset name", + ) + ap.add_argument( + "--binary-dir", + default=None, + help="Override binaryDir", + ) + ap.add_argument( + "--display-name", + default=None, + help="Override displayName", + ) + ap.add_argument( + "--workflow", + default=".github/workflows/test-build-cmake-presets.yml", + help="Optional GitHub Actions workflow YAML to check for this preset name", + ) args = ap.parse_args() # Begin common dir init, for /tools/scripts script_path = Path(__file__).resolve() script_dir = script_path.parent.resolve() - # repo root is parent of tools/scripts → go up two levels + # repo root is parent of tools/scripts -- go up two levels repo_root = (script_dir / ".." / "..").resolve() caller_cwd = Path.cwd().resolve() @@ -161,50 +533,68 @@ def main(): # Resolve paths: # - config: relative to caller's CWD (so user can pass local relative paths naturally) - # - toolchain/presets: relative to repo root (we already chdir there) + # - presets: relative to repo root (we already chdir there) config_path = Path(args.config) if not config_path.is_absolute(): config_path = (caller_cwd / config_path).resolve() - toolchain_path = Path(args.toolchain) - if not toolchain_path.is_absolute(): - toolchain_path = (repo_root / toolchain_path).resolve() - presets_path = Path(args.presets) if not presets_path.is_absolute(): presets_path = (repo_root / presets_path).resolve() + # Pre-validate the existing presets JSON so we can show a clear message + # instead of crashing on a JSONDecodeError later. + validate_presets_json(presets_path) + kv = parse_config(config_path) if not kv: print(f"No settings parsed from .config: {config_path}", file=sys.stderr) sys.exit(2) + # Handle inherited values like SIGN/HASH before converting to cache vars + kv, inherited_messages = filter_inherited_values(kv) + target = choose_target(kv) cache = to_cache_vars(kv) - # Use forward slashes in JSON for better CMake portability - cache = ensure_base_vars(cache, toolchain_path.as_posix()) + + # Use the toolchain value exactly as passed on the command line, + # but normalize backslashes for JSON/CMake friendliness. + toolchain_value = args.toolchain.replace("\\", "/") + cache = ensure_base_vars(cache, toolchain_value) # Build preset objects source_dir = "${sourceDir}" # CMake variable; leave literal preset_name = args.preset_name or make_preset_name(target) binary_dir = args.binary_dir or make_binary_dir(source_dir, target) - display_name = args.display_name or f"Linux/WSL ARM ({target})" + display_name = args.display_name or f"{target}" - cfg_preset = OrderedDict([ - ("name", preset_name), - ("displayName", display_name), - ("inherits", "base"), - ("generator", args.generator), - ("binaryDir", binary_dir), - ("cacheVariables", cache), - ]) - bld_preset = OrderedDict([ - ("name", preset_name), - ("configurePreset", preset_name), - ("jobs", 4), - ("verbose", True), - ("targets", ["all"]), - ]) + # For STM32 boards, inherit like stm32g0: + # [ "base", "stm32", "sign_hash_config" ] + # For everything else, just inherit "base". + inherits_value = "base" + if isinstance(preset_name, str) and preset_name.startswith("stm32") and preset_name != "stm32": + inherits_value = ["base", "stm32", "sign_hash_config"] + + cfg_preset = OrderedDict( + [ + ("name", preset_name), + ("displayName", display_name), + ("inherits", inherits_value), + ("generator", args.generator), + ("binaryDir", binary_dir), + ("cacheVariables", cache), + ] + ) + + bld_preset = OrderedDict( + [ + ("name", preset_name), + ("configurePreset", preset_name), + ("jobs", 4), + ("verbose", True), + ("targets", ["all"]), + ] + ) # Ensure schema v3 unless existing file says otherwise doc = load_existing_presets(presets_path) @@ -222,6 +612,20 @@ def main(): print(f"Updated {presets_path} with preset '{preset_name}' targeting '{target}'") + # Best-effort: check if this preset/target name appears in the workflow matrix + workflow_path = Path(args.workflow) + if not workflow_path.is_absolute(): + workflow_path = (repo_root / workflow_path).resolve() + check_workflow_for_preset(workflow_path, preset_name) + if inherited_messages: + print("") + print("Inherited .config values summary:") + for msg in inherited_messages: + print(" " + msg) + + # After updating presets, run cmake and show any unused variables + run_cmake_and_report_unused(preset_name, repo_root) + if __name__ == "__main__": main() diff --git a/tools/scripts/wolfboot_cmake_full_build.sh b/tools/scripts/wolfboot_cmake_full_build.sh index 66c1b705..ffbf0b80 100644 --- a/tools/scripts/wolfboot_cmake_full_build.sh +++ b/tools/scripts/wolfboot_cmake_full_build.sh @@ -2,6 +2,8 @@ # wolfboot_cmake_full_build.sh # +# >>>>> EDIT WITH CAUTION: Used by ./github/workflow tests <<<<< +# # ./tools/scripts/wolfboot_cmake_full_build.sh --CLEAN [your target] # ./tools/scripts/wolfboot_cmake_full_build.sh --target [your target] # ./tools/scripts/wolfboot_cmake_full_build.sh --flash [your target] @@ -284,6 +286,40 @@ if [ $# -gt 0 ]; then fi exit "$status" fi + + if [ "$THIS_OPERATION" = "--flash-unsigned" ]; then + echo "Flash Target=$TARGET" + CLI="$(find_stm32_tool STM32_Programmer_CLI)" || { echo "CLI not found"; exit 1; } + if [ -f "$CLI" ]; then + echo "Found STM32 flasher: $CLI" + else + echo "CLI=$CLI" + echo "STM32_Programmer_CLI.exe not found, exiting" + exit 2 + fi + + # TODO Alternative preset inherited configs may write to build directories other than build-$TARGET. + # Currently works only with base_build_presetName preset path + APP_BIN="../build-$TARGET/app.bin" + echo Checking "APP_BIN=$APP_BIN" + if [ ! -f "$APP_BIN" ]; then + echo "Missing: $APP_BIN (build first: cmake --build --preset \"$TARGET\")" + exit 2 + fi + + BOOT_ADDR=0x0800A000 # your wolfBoot BOOT address + BOOT_ADDR=0x08000000 # your wolfBoot BOOT address + echo "BOOT_ADDR=$BOOT_ADDR" + # SWD via ST-LINK (Windows handles the USB) + "$CLI" -c port=SWD mode=UR freq=400 -w "$APP_BIN" "$BOOT_ADDR" -v -hardRst + status=$? + if [ "$status" -eq 0 ]; then + echo "OK: command succeeded" + else + echo "Failed: command exited with status $status" + fi + exit "$status" + fi fi if [ "$TARGET" = "" ]; then @@ -292,6 +328,7 @@ if [ "$TARGET" = "" ]; then echo " $0 --CLEAN [your target]" echo " $0 --target [your target]" echo " $0 --flash [your target]" + echo " $0 --flash-unsigned [your target]" echo "" cmake -S . -B build --list-presets=configure exit 1