From 9c40a0a125edbf2be601466243477767a68b1011 Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Tue, 14 Oct 2025 16:05:10 +0200 Subject: [PATCH 1/7] Implement filesystem-based partition state access and CLI tools - Add filesystem access implementation in HAL (`hal/filesystem.c`) - Introduce new library_fs target and build rules for filesystem-backed partition management - Provide example configuration for library_fs - Add CLI application (`hal/library_fs.c`) to query and manage partitions - Update documentation with usage instructions and build steps for the new CLI tool - Update .gitignore --- .gitignore | 5 + Makefile | 10 ++ arch.mk | 13 ++ config/examples/library_fs.config | 24 +++ config/examples/zynqmp.config | 2 + docs/lib.md | 97 +++++++++++- hal/filesystem.c | 139 +++++++++++++++++ hal/library.c | 3 +- hal/library_fs.c | 210 ++++++++++++++++++++++++++ include/target.h.in | 2 +- include/user_settings.h | 2 + options.mk | 4 + src/image.c | 6 +- tools/scripts/boot_status.py | 140 +++++++++++++++++ tools/scripts/build_lib_fs_example.sh | 37 +++++ 15 files changed, 689 insertions(+), 5 deletions(-) create mode 100644 config/examples/library_fs.config create mode 100644 hal/filesystem.c create mode 100644 hal/library_fs.c create mode 100755 tools/scripts/boot_status.py create mode 100755 tools/scripts/build_lib_fs_example.sh diff --git a/.gitignore b/.gitignore index 3589261973..aebf0353ab 100644 --- a/.gitignore +++ b/.gitignore @@ -118,6 +118,7 @@ tools/tpm/policy_create tools/tpm/policy_sign config/*.ld test-lib +lib-fs # Elf preprocessing tools tools/squashelf/** @@ -275,3 +276,7 @@ language.settings.xml /**/build /**/build-** +# Eclipse +.cproject +.project +.settings/ diff --git a/Makefile b/Makefile index 534e7dba29..5357a90f28 100644 --- a/Makefile +++ b/Makefile @@ -164,6 +164,10 @@ ifeq ($(TARGET),library) MAIN_TARGET:=libwolfboot.a endif +ifeq ($(TARGET),library_fs) + MAIN_TARGET:=libwolfboot.a +endif + ifeq ($(TARGET),raspi3) MAIN_TARGET:=wolfboot.bin endif @@ -214,6 +218,10 @@ test-lib: libwolfboot.a hal/library.o @echo "\t[BIN] $@" $(Q)$(CC) $(CFLAGS) -o $@ hal/library.o libwolfboot.a +lib-fs: libwolfboot.a hal/library_fs.o hal/filesystem.o + @echo "\t[BIN] $@" + $(Q)$(CC) $(CFLAGS) -o $@ hal/library_fs.o hal/filesystem.o libwolfboot.a + wolfboot.efi: wolfboot.elf @echo "\t[BIN] $@" $(Q)$(OBJCOPY) -j .rodata -j .text -j .sdata -j .data \ @@ -440,6 +448,8 @@ clean: $(Q)rm -f tools/keytools/otp/otp-keystore-gen $(Q)rm -f .stack_usage $(Q)rm -f $(WH_NVM_BIN) $(WH_NVM_HEX) + $(Q)rm -f test-lib + $(Q)rm -f lib-fs $(Q)$(MAKE) -C test-app clean V=$(V) $(Q)$(MAKE) -C tools/check_config -s clean $(Q)$(MAKE) -C stage1 -s clean diff --git a/arch.mk b/arch.mk index 76d3e7da4d..48a3416412 100644 --- a/arch.mk +++ b/arch.mk @@ -1109,8 +1109,13 @@ ifeq ($(ARCH),sim) USE_GCC_HEADLESS=0 LD = gcc ifneq ($(TARGET),library) + ifneq ($(TARGET),library_fs) UPDATE_OBJS:=src/update_flash.o endif + endif + ifeq ($(TARGET),library_fs) + UPDATE_OBJS += hal/filesystem.o + endif LD_START_GROUP= LD_END_GROUP= BOOT_IMG=test-app/image.elf @@ -1326,6 +1331,14 @@ ifeq ($(TARGET),library) NO_LOADER=1 endif +ifeq ($(TARGET),library_fs) + EXT_FLASH=1 + # Force all partitions to be marked as external + NO_XIP=1 + NO_SWAP_EXT= +endif + + ## Set default update object ifneq ($(WOLFBOOT_NO_PARTITIONS),1) ifeq ($(UPDATE_OBJS),) diff --git a/config/examples/library_fs.config b/config/examples/library_fs.config new file mode 100644 index 0000000000..d0c055ae9e --- /dev/null +++ b/config/examples/library_fs.config @@ -0,0 +1,24 @@ +ARCH=sim +TARGET=library_fs + +SIGN?=ED25519 +HASH?=SHA256 +IMAGE_HEADER_SIZE?=256 +DEBUG=0 +SPMATH?=0 +SPMATHALL?=0 + +# Flash Partition Filename +WOLFBOOT_PARTITION_FILENAME=\"internal_flash.dd\" +EXT_FLASH=1 + +# Flash Sector Size +WOLFBOOT_SECTOR_SIZE=0x1000 +# Application Partition Size +WOLFBOOT_PARTITION_SIZE=0x40000 +# Location in flash for boot partition +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x80000 +# Location in flash for update partition +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x100000 +# Location in flash for swap +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x180000 diff --git a/config/examples/zynqmp.config b/config/examples/zynqmp.config index d04a233fc6..70dc8cb241 100644 --- a/config/examples/zynqmp.config +++ b/config/examples/zynqmp.config @@ -63,6 +63,8 @@ WOLFBOOT_SECTOR_SIZE=0x20000 # Application Partition Size WOLFBOOT_PARTITION_SIZE=0x2A00000 # Location in Flash for wolfBoot +WOLFBOOT_ORIGIN=0x0 +# Location in Flash for Primary Boot Partition WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x800000 # Load Partition to RAM Address WOLFBOOT_LOAD_ADDRESS?=0x10000000 diff --git a/docs/lib.md b/docs/lib.md index a86e9010cd..7827191562 100644 --- a/docs/lib.md +++ b/docs/lib.md @@ -21,7 +21,7 @@ of the manifest header. On success, zero is returned. If the image does not contain a valid 'magic number' at the beginning -of the manifest, or if the size of the image is bigger than `WOLFBOOT_PARTITION_SIZE`, -1 is returned. +of the manifest, or if the size of the image is bigger than `WOLFBOOT_PARTITION_SIZE`, -1 is returned. If the `open_image_address` operation is successful, two other functions can be invoked: @@ -123,3 +123,98 @@ Firmware Valid booting 0x5609e3526590(actually exiting) ``` +## Library mode: Partition Manager CLI Example + +An example application using filesystem access is provided in `hal/library_fs.c`. + +The CLI application `lib-fs` allow querying partition states, triggering updates, and marking the boot partition as successful. + +### Building the lib-fs example + +To generate and verify a signed boot partition using simulation and library_fs targets, follow these steps. +You can run these steps using the provided script at `tools/scripts/build_lib_fs_example.sh`: + +``` +./tools/scripts/build_lib_fs_example.sh +``` + +Alternatively, you can perform the steps manually as described below: + +Step 1: Copy the configuration for simulation and build the signed boot partition: +``` +cp config/examples/sim.config .config +make +``` +This will generate a file with a signed boot partition named `internal_flash.dd`. + +Step 2: Change the target back to `library_fs`: +``` +cp config/examples/library_fs.config .config +``` + +Step 3: Ensure that the partition layout in `sim.config` matches the layout in `library_fs.config`. + +Step 4: Clean previous build artifacts and build the CLI application: +``` +make clean +make lib-fs +``` +This will produce the `lib-fs` executable. + +Step 5: Mark the BOOT partition as successfully loaded: +``` +./lib-fs success +``` + +Step 6: Verify the integrity and authenticity of the BOOT partition: +``` +./lib-fs verify-boot +``` + +### Using the Partition Manager CLI + +The example configuration points the binary to access `/dev/mtd0` for partition data. You can simulate this file path with `modprobe mtdram total_size=16384 erase_size=128`. You may need to adjust the file permissions to allow read/write access. + +Run the application with one of the supported commands: + +``` +./lib-fs +``` + +Available commands: + +- `status` : Show state of all partitions +- `get-boot` : Get BOOT partition state +- `get-update` : Get UPDATE partition state +- `update-trigger` : Trigger an update (sets UPDATE partition to UPDATING) +- `success` : Mark BOOT partition as SUCCESS +- `verify-boot` : Verify integrity and authenticity of BOOT partition +- `verify-update` : Verify integrity and authenticity of UPDATE partition +- `help` : Show usage information + +#### Example usage + +Show all partition states: +``` +./lib-fs status +``` + +Trigger an update: +``` +./lib-fs update-trigger +``` + +Mark the boot partition as successful: +``` +./lib-fs success +``` + +Verify BOOT partition: +``` +./lib-fs verify-boot +``` + +Verify UPDATE partition: +``` +./lib-fs verify-update +``` diff --git a/hal/filesystem.c b/hal/filesystem.c new file mode 100644 index 0000000000..5a2cb78ca8 --- /dev/null +++ b/hal/filesystem.c @@ -0,0 +1,139 @@ +/* filesystem.c + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + + +#include "hal.h" +#include "wolfboot/wolfboot.h" +#include "printf.h" + +#ifndef WOLFBOOT_PARTITION_FILENAME +#error "WOLFBOOT_PARTITION_FILENAME needs to be defined for filesystem HAL" +#endif + +#ifndef MIN + #define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +/* HAL Stubs */ +void hal_init(void) +{ + return; +} +int hal_flash_write(uintptr_t address, const uint8_t *data, int len) +{ + (void)address; + (void)data; + (void)len; + return -1; +} +int hal_flash_erase(uintptr_t address, int len) +{ + (void)address; + (void)len; + return -1; +} +void hal_flash_unlock(void) +{ + return; +} +void hal_flash_lock(void) +{ + return; +} +void hal_prepare_boot(void) +{ + return; +} +void do_boot(const uint32_t *app_offset) +{ + (void)app_offset; +} + +/* filesystem access */ +static XFILE fp = XBADFILE; +static byte fp_write = 0; +static long fp_size = 0; + +static int setup_file(byte read_only) +{ + if (fp != XBADFILE) { + if (!read_only && !fp_write) { + /* Need to reopen to allow writing */ + XFCLOSE(fp); + fp = XBADFILE; + } + } + if (fp == XBADFILE) { + fp = XFOPEN(WOLFBOOT_PARTITION_FILENAME, read_only ? "rb" : "r+b"); + if (fp != XBADFILE) { + fp_write = !read_only; + if (XFSEEK(fp, 0, XSEEK_END) < 0 || (fp_size = XFTELL(fp)) < 0 || + XFSEEK(fp, 0, XSEEK_SET) < 0) { + /* Failed to get the file size */ + XFCLOSE(fp); + fp = XBADFILE; + } + } + } + return fp != XBADFILE ? 0 : -1; +} + +int ext_flash_write(uintptr_t address, const uint8_t *data, int len) +{ + if (setup_file(0) != 0) + return -1; + if (address + len > (uintptr_t)fp_size) + return -1; /* Don't allow writing past the file size */ + if (XFSEEK(fp, address, XSEEK_SET) < 0) + return -1; + if ((int)XFWRITE(data, 1, len, fp) != len) + return -1; + if (XFFLUSH(fp) != 0) + return -1; + return 0; +} + +int ext_flash_read(uintptr_t address, uint8_t *data, int len) +{ + if (setup_file(1) != 0) + return -1; + if (XFSEEK(fp, address, XSEEK_SET) < 0) + return -1; + return (int)XFREAD(data, 1, len, fp); +} +int ext_flash_erase(uintptr_t address, int len) +{ + byte zeros[256]; + XMEMSET(zeros, 0, sizeof(zeros)); + for (; len > 0; len -= sizeof(zeros), address += sizeof(zeros)) { + if (ext_flash_write(address, zeros, (int)MIN((int)sizeof(zeros), len)) != 0) + return -1; + } + return 0; +} +void ext_flash_lock(void) +{ + return; +} +void ext_flash_unlock(void) +{ + return; +} diff --git a/hal/library.c b/hal/library.c index 82e5a8b55c..ca7f83827d 100644 --- a/hal/library.c +++ b/hal/library.c @@ -142,7 +142,8 @@ int wolfBoot_start(void) exit: if (ret < 0) { wolfBoot_printf("Failure %d: Hdr %d, Hash %d, Sig %d\n", ret, - os_image.hdr_ok, os_image.sha_ok, os_image.signature_ok); + (int)os_image.hdr_ok, (int)os_image.sha_ok, + (int)os_image.signature_ok); } return 0; diff --git a/hal/library_fs.c b/hal/library_fs.c new file mode 100644 index 0000000000..c71535c5d1 --- /dev/null +++ b/hal/library_fs.c @@ -0,0 +1,210 @@ +/* library_fs.c + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfBoot. + * + * wolfBoot is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfBoot is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include +#include +#include + +#include "image.h" +#include "printf.h" +#include "wolfboot/wolfboot.h" + +/* Helper function to convert partition ID to string */ +static const char* partition_name(uint8_t part) +{ + switch (part) { + case PART_BOOT: + return "BOOT"; + case PART_UPDATE: + return "UPDATE"; + default: + return "UNKNOWN"; + } +} + +/* Helper function to convert state value to string */ +static const char* state_name(uint8_t state) +{ + switch (state) { + case IMG_STATE_NEW: + return "NEW"; + case IMG_STATE_UPDATING: + return "UPDATING"; + case IMG_STATE_SUCCESS: + return "SUCCESS"; + default: + return "UNKNOWN"; + } +} + +/* Print partition state */ +static int cmd_get_state(uint8_t part) +{ + uint8_t state; + int ret; + + ret = wolfBoot_get_partition_state(part, &state); + if (ret != 0) { + wolfBoot_printf("Error: Failed to get state for %s partition (error: %d)\n", + partition_name(part), ret); + return -1; + } + + wolfBoot_printf("%s partition state: %s (0x%02X)\n", + partition_name(part), state_name(state), state); + return 0; +} + +/* Print all partition states */ +static int cmd_get_all_states(void) +{ + int ret = 0; + + wolfBoot_printf("=== Partition States ===\n"); + + if (cmd_get_state(PART_BOOT) != 0) + ret = -1; + + if (cmd_get_state(PART_UPDATE) != 0) + ret = -1; + + return ret; +} + +/* Trigger an update */ +static int cmd_update_trigger(void) +{ + wolfBoot_printf("Triggering update...\n"); + wolfBoot_update_trigger(); + wolfBoot_printf("Update triggered successfully. UPDATE partition set to UPDATING state.\n"); + return 0; +} + +/* Mark current boot as successful */ +static int cmd_success(void) +{ + wolfBoot_printf("Marking BOOT partition as SUCCESS...\n"); + wolfBoot_success(); + wolfBoot_printf("BOOT partition marked as SUCCESS.\n"); + return 0; +} + +/* Print usage information */ +static void print_usage(const char* prog_name) +{ + wolfBoot_printf("wolfBoot Partition Manager CLI\n"); + wolfBoot_printf("\nUsage: %s [options]\n\n", prog_name); + wolfBoot_printf("Commands:\n"); + wolfBoot_printf(" status - Show state of all partitions\n"); + wolfBoot_printf(" get-boot - Get BOOT partition state\n"); + wolfBoot_printf(" get-update - Get UPDATE partition state\n"); + wolfBoot_printf(" update-trigger - Trigger an update (sets UPDATE partition to UPDATING)\n"); + wolfBoot_printf(" success - Mark BOOT partition as SUCCESS\n"); + wolfBoot_printf(" verify-boot - Verify integrity and authenticity of BOOT partition\n"); + wolfBoot_printf(" verify-update - Verify integrity and authenticity of UPDATE partition\n"); + wolfBoot_printf(" help - Show this help message\n"); + wolfBoot_printf("\nPartitions:\n"); + wolfBoot_printf(" BOOT - Currently running firmware partition\n"); + wolfBoot_printf(" UPDATE - Staging partition for new firmware\n"); + wolfBoot_printf("\nExamples:\n"); + wolfBoot_printf(" %s status - Display all partition states\n", prog_name); + wolfBoot_printf(" %s update-trigger - Stage an update for next boot\n", prog_name); + wolfBoot_printf(" %s success - Confirm current firmware is working\n", prog_name); + wolfBoot_printf("\n"); +} + +/* Verify integrity and authenticity of a partition */ +static int cmd_verify(uint8_t part) +{ + struct wolfBoot_image img; + int ret; + size_t img_size = 0; + + ret = wolfBoot_open_image(&img, part); + if (ret < 0) { + wolfBoot_printf("Error: Failed to open image header for %s partition (error: %d)\n", + partition_name(part), ret); + return -1; + } + + ret = wolfBoot_verify_integrity(&img); + if (ret < 0) { + wolfBoot_printf("Integrity check failed for %s partition\n", partition_name(part)); + return -1; + } + + ret = wolfBoot_verify_authenticity(&img); + if (ret < 0) { + wolfBoot_printf("Authenticity check failed for %s partition\n", partition_name(part)); + return -1; + } + + wolfBoot_printf("%s partition: Integrity and authenticity verified.\n", partition_name(part)); + return ret; +} + +int main(int argc, const char* argv[]) +{ + int ret = 0; + + /* Check for argument count */ + if (argc != 2) { + print_usage(argv[0]); + return 1; + } + + const char* command = argv[1]; + + /* Process commands */ + if (strcmp(command, "status") == 0) { + ret = cmd_get_all_states(); + } + else if (strcmp(command, "get-boot") == 0) { + ret = cmd_get_state(PART_BOOT); + } + else if (strcmp(command, "get-update") == 0) { + ret = cmd_get_state(PART_UPDATE); + } + else if (strcmp(command, "update-trigger") == 0) { + ret = cmd_update_trigger(); + } + else if (strcmp(command, "success") == 0) { + ret = cmd_success(); + } + else if (strcmp(command, "verify-boot") == 0) { + ret = cmd_verify(PART_BOOT); + } + else if (strcmp(command, "verify-update") == 0) { + ret = cmd_verify(PART_UPDATE); + } + else if (strcmp(command, "help") == 0 || strcmp(command, "--help") == 0 || + strcmp(command, "-h") == 0) { + print_usage(argv[0]); + ret = 0; + } + else { + wolfBoot_printf("Error: Unknown command '%s'\n\n", command); + print_usage(argv[0]); + ret = 1; + } + + return ret; +} diff --git a/include/target.h.in b/include/target.h.in index 7fce6ff97d..7d5f8c3af3 100644 --- a/include/target.h.in +++ b/include/target.h.in @@ -36,7 +36,7 @@ #ifdef WOLFBOOT_FIXED_PARTITIONS -#ifdef ARCH_SIM +#if defined(ARCH_SIM) && !defined(WOLFBOOT_PARTITION_FILENAME) #include /* use runtime ram base for simulator */ extern uint8_t *sim_ram_base; diff --git a/include/user_settings.h b/include/user_settings.h index 379488ee78..0c6d56ab1d 100644 --- a/include/user_settings.h +++ b/include/user_settings.h @@ -472,7 +472,9 @@ extern int tolower(int c); #define NO_HC128 #define NO_DES3 #define NO_WRITEV +#ifndef WOLFBOOT_PARTITION_FILENAME #define NO_FILESYSTEM +#endif #define NO_MAIN_DRIVER #define NO_OLD_RNGNAME #define NO_WOLFSSL_DIR diff --git a/options.mk b/options.mk index 9e4bf77433..119d335370 100644 --- a/options.mk +++ b/options.mk @@ -1037,3 +1037,7 @@ endif ifeq ($(USE_UART3),1) CFLAGS += -DUSE_UART3=1 endif + +ifneq ($(WOLFBOOT_PARTITION_FILENAME),) + CFLAGS += -DWOLFBOOT_PARTITION_FILENAME=$(WOLFBOOT_PARTITION_FILENAME) +endif diff --git a/src/image.c b/src/image.c index 688825b67b..16312dcc8a 100644 --- a/src/image.c +++ b/src/image.c @@ -882,8 +882,10 @@ static int hdr_cpy_done = 0; static uint8_t *fetch_hdr_cpy(struct wolfBoot_image *img) { if (!hdr_cpy_done) { - ext_flash_check_read((uintptr_t)img->hdr, hdr_cpy, IMAGE_HEADER_SIZE); - hdr_cpy_done = 1; + memset(hdr_cpy, 0, sizeof(hdr_cpy)); + if (ext_flash_check_read((uintptr_t)img->hdr, hdr_cpy, + IMAGE_HEADER_SIZE) == IMAGE_HEADER_SIZE) + hdr_cpy_done = 1; } return hdr_cpy; } diff --git a/tools/scripts/boot_status.py b/tools/scripts/boot_status.py new file mode 100755 index 0000000000..e72bd29c22 --- /dev/null +++ b/tools/scripts/boot_status.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 + +import argparse +import sys + +# Declare config_vars as a global variable +config_vars: dict[str, int] = {} + +def set_status(status_file: str, partition: str, value: str) -> None: + with open(status_file, "r+b") as f: + if partition == "BOOT": + addr = config_vars["WOLFBOOT_PARTITION_BOOT_ADDRESS"] + elif partition == "UPDATE": + addr = config_vars["WOLFBOOT_PARTITION_UPDATE_ADDRESS"] + else: + print(f"Error: Invalid partition: {partition}") + sys.exit(1) + f.seek(addr + config_vars["WOLFBOOT_PARTITION_SIZE"] - 4) + magic: bytes = f.read(4) + if magic != b"BOOT": + f.seek(addr + config_vars["WOLFBOOT_PARTITION_SIZE"] - 4) + f.write(b"BOOT") + if value == "NEW": + status_byte = b"\xFF" + elif value == "UPDATING": + status_byte = b"\x70" + elif value == "SUCCESS": + status_byte = b"\x00" + else: + print(f"Error: Invalid value: {value}") + sys.exit(1) + # Write status byte at correct address + f.seek(addr + config_vars["WOLFBOOT_PARTITION_SIZE"] - 5) + f.write(status_byte) + +def get_status(status_file: str, partition: str) -> None: + with open(status_file, "rb") as f: + if partition == "BOOT": + addr = config_vars["WOLFBOOT_PARTITION_BOOT_ADDRESS"] + elif partition == "UPDATE": + addr = config_vars["WOLFBOOT_PARTITION_UPDATE_ADDRESS"] + else: + print(f"Error: Invalid partition: {partition}") + sys.exit(1) + f.seek(addr + config_vars["WOLFBOOT_PARTITION_SIZE"] - 4) + magic: bytes = f.read(4) + if magic != b"BOOT": + print(f"Error: Missing magic at expected address {hex(addr)}") + sys.exit(1) + f.seek(addr + config_vars["WOLFBOOT_PARTITION_SIZE"] - 5) + status_byte: bytes = f.read(1) + if status_byte == b"\xFF": + print("NEW") + elif status_byte == b"\x70": + print("UPDATING") + elif status_byte == b"\x00": + print("SUCCESS") + else: + print("INVALID") + +def read_config(config_path: str) -> dict[str, str]: + """ + Reads a config file and returns a dictionary of variables. + Supports lines of the form KEY=VALUE, KEY:=VALUE, KEY::=VALUE, KEY:::=VALUE, and KEY?=VALUE. + Ignores comments and blank lines. + """ + config: dict[str, str] = {} + assignment_ops = [":::= ", "::=", ":=", "="] + + with open(config_path, "r") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#"): + continue + + for op in assignment_ops: + if op in line: + parts = line.split(op, 1) + if len(parts) == 2: + key = parts[0].rstrip("?").strip() + value = parts[1].strip() + config[key] = value + break # Stop after first matching operator + return config + +def main() -> None: + parser = argparse.ArgumentParser(description="Manage boot status") + parser.add_argument( + "file", + type=str, + help="Path to the boot status file" + ) + parser.add_argument( + "config", + type=str, + help="Path to the .config file" + ) + + subparsers = parser.add_subparsers(dest="command", required=True) + + partitions = ["BOOT", "UPDATE"] + states = ["SUCCESS", "UPDATING", "NEW"] + + set_parser = subparsers.add_parser("set") + set_parser.add_argument("slot", choices=partitions, type=str) + set_parser.add_argument("value", choices=states, type=str) + + get_parser = subparsers.add_parser("get") + get_parser.add_argument("slot", choices=partitions, type=str) + + args = parser.parse_args() + + read_vars = read_config(args.config) + + # Check required config variables using a for loop + required_vars = [ + "WOLFBOOT_PARTITION_SIZE", + "WOLFBOOT_PARTITION_BOOT_ADDRESS", + "WOLFBOOT_PARTITION_UPDATE_ADDRESS", + ] + for var in required_vars: + if var not in read_vars: + print(f"Error: Missing required config variable: {var}") + sys.exit(1) + try: + config_vars[var] = int(read_vars[var], 16) + except ValueError: + print(f"Error: Config variable {var} value '{read_vars[var]}' is not a valid hex number") + sys.exit(1) + + command: str = str(args.command) + if command == "set": + set_status(str(args.file), str(args.slot), str(args.value)) + elif command == "get": + get_status(str(args.file), str(args.slot)) + else: + parser.print_help() + +if __name__ == "__main__": + main() diff --git a/tools/scripts/build_lib_fs_example.sh b/tools/scripts/build_lib_fs_example.sh new file mode 100755 index 0000000000..e9e6b1dd75 --- /dev/null +++ b/tools/scripts/build_lib_fs_example.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +# build_lib_fs_example.sh +# Automates the build and verification steps for the wolfBoot lib-fs example. +# Run this script from the root of your wolfBoot project. + +set -e + +echo "Step 1: Setting simulation config and building signed boot partition..." +cp config/examples/sim.config .config +make clean +make + +if [ ! -f internal_flash.dd ]; then + echo "Error: internal_flash.dd not found. Build may have failed." + exit 1 +fi + +echo "Step 2: Switching to library_fs config..." +cp config/examples/library_fs.config .config + +echo "Step 3: Cleaning previous build artifacts and building lib-fs..." +make clean +make lib-fs + +if [ ! -f lib-fs ]; then + echo "Error: lib-fs executable not found. Build may have failed." + exit 1 +fi + +echo "Step 4: Marking BOOT partition as SUCCESS..." +./lib-fs success + +echo "Step 5: Verifying BOOT partition integrity and authenticity..." +./lib-fs verify-boot + +echo "Build and verification steps complete." From dfba19522360846bdb0ed11bb9ec26c9b09ac592 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 22 Oct 2025 12:59:22 -0700 Subject: [PATCH 2/7] Fix build error with MMU defined. --- hal/filesystem.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hal/filesystem.c b/hal/filesystem.c index 5a2cb78ca8..01dc1a7a7c 100644 --- a/hal/filesystem.c +++ b/hal/filesystem.c @@ -62,7 +62,12 @@ void hal_prepare_boot(void) { return; } + +#ifdef MMU +void do_boot(const uint32_t *app_offset, const uint32_t* dts_offset) +#else void do_boot(const uint32_t *app_offset) +#endif { (void)app_offset; } From 95876202deb7a8f27e28bb3739e7ada53605a252 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 22 Oct 2025 14:36:06 -0700 Subject: [PATCH 3/7] Fixes for build warnings --- hal/filesystem.c | 5 +++-- hal/library_fs.c | 32 +++++++++++++++++++------------- include/printf.h | 2 +- src/update_ram.c | 4 ++-- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/hal/filesystem.c b/hal/filesystem.c index 01dc1a7a7c..edefe20141 100644 --- a/hal/filesystem.c +++ b/hal/filesystem.c @@ -37,14 +37,15 @@ void hal_init(void) { return; } -int hal_flash_write(uintptr_t address, const uint8_t *data, int len) + +int hal_flash_write(haladdr_t address, const uint8_t *data, int len) { (void)address; (void)data; (void)len; return -1; } -int hal_flash_erase(uintptr_t address, int len) +int hal_flash_erase(haladdr_t address, int len) { (void)address; (void)len; diff --git a/hal/library_fs.c b/hal/library_fs.c index c71535c5d1..def0f7076a 100644 --- a/hal/library_fs.c +++ b/hal/library_fs.c @@ -60,14 +60,14 @@ static int cmd_get_state(uint8_t part) { uint8_t state; int ret; - + ret = wolfBoot_get_partition_state(part, &state); if (ret != 0) { wolfBoot_printf("Error: Failed to get state for %s partition (error: %d)\n", partition_name(part), ret); return -1; } - + wolfBoot_printf("%s partition state: %s (0x%02X)\n", partition_name(part), state_name(state), state); return 0; @@ -77,15 +77,15 @@ static int cmd_get_state(uint8_t part) static int cmd_get_all_states(void) { int ret = 0; - + wolfBoot_printf("=== Partition States ===\n"); - + if (cmd_get_state(PART_BOOT) != 0) ret = -1; - + if (cmd_get_state(PART_UPDATE) != 0) ret = -1; - + return ret; } @@ -164,15 +164,21 @@ static int cmd_verify(uint8_t part) int main(int argc, const char* argv[]) { int ret = 0; - + const char* prog_name = "lib-fs"; + const char* command; + + if (argc >= 1) { + prog_name = argv[0]; + } + /* Check for argument count */ if (argc != 2) { - print_usage(argv[0]); + print_usage(prog_name); return 1; } - - const char* command = argv[1]; - + + command = argv[1]; + /* Process commands */ if (strcmp(command, "status") == 0) { ret = cmd_get_all_states(); @@ -195,7 +201,7 @@ int main(int argc, const char* argv[]) else if (strcmp(command, "verify-update") == 0) { ret = cmd_verify(PART_UPDATE); } - else if (strcmp(command, "help") == 0 || strcmp(command, "--help") == 0 || + else if (strcmp(command, "help") == 0 || strcmp(command, "--help") == 0 || strcmp(command, "-h") == 0) { print_usage(argv[0]); ret = 0; @@ -205,6 +211,6 @@ int main(int argc, const char* argv[]) print_usage(argv[0]); ret = 1; } - + return ret; } diff --git a/include/printf.h b/include/printf.h index 9a92b24d99..860774d355 100644 --- a/include/printf.h +++ b/include/printf.h @@ -30,7 +30,7 @@ #if defined(WOLFBOOT_DEBUG_EFI) && !defined(PRINTF_ENABLED) # define PRINTF_ENABLED #endif -#if defined(ARCH_SIM) && !defined(PRINTF_ENABLED) +#if (defined(ARCH_SIM) || defined(__linux__)) && !defined(PRINTF_ENABLED) # define PRINTF_ENABLED #endif diff --git a/src/update_ram.c b/src/update_ram.c index 696ed15193..36e2491a81 100644 --- a/src/update_ram.c +++ b/src/update_ram.c @@ -312,7 +312,7 @@ void RAMFUNCTION wolfBoot_start(void) dts_size = (uint32_t)os_image.fw_size; wolfBoot_printf("Loading DTS (size %lu) to RAM at %08lx\n", - dts_size, dts_addr); + (long unsigned int)dts_size, (long unsigned int)dts_addr); ext_flash_check_read((uintptr_t)os_image.fw_base, (uint8_t*)dts_addr, dts_size); } @@ -331,7 +331,7 @@ void RAMFUNCTION wolfBoot_start(void) uint8_t* dts_dst = (uint8_t*)WOLFBOOT_LOAD_DTS_ADDRESS; dts_size = (uint32_t)ret; wolfBoot_printf("Loading DTB (size %d) from %p to RAM at %p\n", - dts_size, dts_addr, WOLFBOOT_LOAD_DTS_ADDRESS); + dts_size, dts_addr, (void*)WOLFBOOT_LOAD_DTS_ADDRESS); memcpy(dts_dst, dts_addr, dts_size); dts_addr = dts_dst; } From 3c77ab6bdf8e976f278413a4e8b851a50e3c0168 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 22 Oct 2025 14:56:39 -0700 Subject: [PATCH 4/7] Fix for P1021 stage 1 --- stage1/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stage1/Makefile b/stage1/Makefile index 003b90ec8f..2267e2cf78 100644 --- a/stage1/Makefile +++ b/stage1/Makefile @@ -101,7 +101,7 @@ CFLAGS+=\ # For printf support (disable NO_PRINTF_UART) and increase WOLFBOOT_STAGE1_SIZE ifeq ($(ARCH),PPC) - CFLAGS+=-DNO_PRINTF_UART + CFLAGS+=-DNO_PRINTF_UART -DWOLFBOOT_NO_PRINTF ifeq ($(NO_XIP),1) # Use PIC (Position Independent Code) for first stage loader CFLAGS+=-fPIC From f5854901d2e095c34cfe0692a4df9fca1d536b80 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 23 Oct 2025 09:27:20 -0700 Subject: [PATCH 5/7] Patch to fix unit-test missing `wolfBoot_get_blob_version` --- tools/unit-tests/unit-image.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/unit-tests/unit-image.c b/tools/unit-tests/unit-image.c index 81f5b2d02c..9a16f47738 100644 --- a/tools/unit-tests/unit-image.c +++ b/tools/unit-tests/unit-image.c @@ -76,6 +76,13 @@ static const unsigned char pubkey_digest[SHA256_DIGEST_SIZE] = { }; +uint32_t wolfBoot_get_blob_version(uint8_t *blob) +{ + (void)blob; + return 1; +} + + static unsigned char test_img_v200000000_signed_bin[] = { 0x57, 0x4f, 0x4c, 0x46, 0x13, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0xc2, 0xeb, 0x0b, 0xff, 0xff, 0xff, 0xff, 0x02, 0x00, 0x08, 0x00, From e48ba6e95aeb0fab51b0f57ab4adb04cb1c718ae Mon Sep 17 00:00:00 2001 From: Juliusz Sosinowicz Date: Fri, 24 Oct 2025 12:04:47 +0200 Subject: [PATCH 6/7] Add CI --- .github/workflows/test-filesystem.yml | 46 +++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/test-filesystem.yml diff --git a/.github/workflows/test-filesystem.yml b/.github/workflows/test-filesystem.yml new file mode 100644 index 0000000000..3cd1ae25db --- /dev/null +++ b/.github/workflows/test-filesystem.yml @@ -0,0 +1,46 @@ +name: Test Filesystem + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +jobs: + build-lib-fs-example: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: true + + - name: Install build dependencies + run: sudo apt-get update && sudo apt-get install -y build-essential + + - name: Set simulation config and build signed boot partition + run: | + cp config/examples/sim.config .config + make clean + make + + - name: Check for internal_flash.dd + run: | + if [ ! -f internal_flash.dd ]; then + echo "Error: internal_flash.dd not found. Build may have failed." + exit 1 + fi + + - name: Switch to library_fs config + run: cp config/examples/library_fs.config .config + + - name: Clean and build lib-fs + run: | + make clean + make lib-fs + + - name: Mark BOOT partition as SUCCESS + run: ./lib-fs success + + - name: Verify BOOT partition integrity and authenticity + run: ./lib-fs verify-boot From 0700599ba74e4b64c65d86b71a506372235de9b0 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 29 Oct 2025 07:54:35 -0700 Subject: [PATCH 7/7] Improvements to lib-fs status and added keystore. Added some useful logging/debugging --- arch.mk | 3 + config/examples/library_fs.config | 3 + hal/filesystem.c | 29 ++++++-- hal/library_fs.c | 111 +++++++++++++++++++++--------- 4 files changed, 108 insertions(+), 38 deletions(-) diff --git a/arch.mk b/arch.mk index 48a3416412..ed36c2ae1f 100644 --- a/arch.mk +++ b/arch.mk @@ -1336,6 +1336,9 @@ ifeq ($(TARGET),library_fs) # Force all partitions to be marked as external NO_XIP=1 NO_SWAP_EXT= + NO_LOADER=1 + USE_GCC_HEADLESS=0 + CFLAGS+=-DWOLFBOOT_USE_STDLIBC endif diff --git a/config/examples/library_fs.config b/config/examples/library_fs.config index d0c055ae9e..efae442de9 100644 --- a/config/examples/library_fs.config +++ b/config/examples/library_fs.config @@ -22,3 +22,6 @@ WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x80000 WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x100000 # Location in flash for swap WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x180000 + +# Optional debugging of flash read/write/erase calls +# CFLAGS_EXTRA+=-DDEBUG_EXT_FLASH \ No newline at end of file diff --git a/hal/filesystem.c b/hal/filesystem.c index edefe20141..ba8d9c1fb3 100644 --- a/hal/filesystem.c +++ b/hal/filesystem.c @@ -98,12 +98,20 @@ static int setup_file(byte read_only) fp = XBADFILE; } } + else { + wolfBoot_printf("Failed to open file %s\n", + WOLFBOOT_PARTITION_FILENAME); + } } return fp != XBADFILE ? 0 : -1; } int ext_flash_write(uintptr_t address, const uint8_t *data, int len) { +#ifdef DEBUG_EXT_FLASH + wolfBoot_printf("ext_flash_write: addr %p data %p len %d\n", + (void*)address, data, len); +#endif if (setup_file(0) != 0) return -1; if (address + len > (uintptr_t)fp_size) @@ -119,19 +127,32 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len) int ext_flash_read(uintptr_t address, uint8_t *data, int len) { +#ifdef DEBUG_EXT_FLASH + wolfBoot_printf("ext_flash_read: addr %p data %p len %d\n", + (void*)address, data, len); +#endif if (setup_file(1) != 0) return -1; if (XFSEEK(fp, address, XSEEK_SET) < 0) return -1; return (int)XFREAD(data, 1, len, fp); } + int ext_flash_erase(uintptr_t address, int len) { - byte zeros[256]; - XMEMSET(zeros, 0, sizeof(zeros)); - for (; len > 0; len -= sizeof(zeros), address += sizeof(zeros)) { - if (ext_flash_write(address, zeros, (int)MIN((int)sizeof(zeros), len)) != 0) + byte erase_data[256]; +#ifdef DEBUG_EXT_FLASH + wolfBoot_printf("ext_flash_erase: addr %p len %d\n", + (void*)address, len); +#endif + XMEMSET(erase_data, 0xFF, sizeof(erase_data)); + while (len > 0) { + int erase_len = (int)MIN((int)sizeof(erase_data), len); + if (ext_flash_write(address, erase_data, erase_len) != 0) { return -1; + } + len -= erase_len; + address += erase_len; } return 0; } diff --git a/hal/library_fs.c b/hal/library_fs.c index def0f7076a..d5fb01e2c6 100644 --- a/hal/library_fs.c +++ b/hal/library_fs.c @@ -27,6 +27,7 @@ #include "printf.h" #include "wolfboot/wolfboot.h" + /* Helper function to convert partition ID to string */ static const char* partition_name(uint8_t part) { @@ -41,13 +42,17 @@ static const char* partition_name(uint8_t part) } /* Helper function to convert state value to string */ -static const char* state_name(uint8_t state) +static const char* partition_state_name(uint8_t state) { switch (state) { case IMG_STATE_NEW: return "NEW"; case IMG_STATE_UPDATING: return "UPDATING"; + case IMG_STATE_FINAL_FLAGS: + return "FFLAGS"; + case IMG_STATE_TESTING: + return "TESTING"; case IMG_STATE_SUCCESS: return "SUCCESS"; default: @@ -55,38 +60,77 @@ static const char* state_name(uint8_t state) } } -/* Print partition state */ -static int cmd_get_state(uint8_t part) +/* Print all partition states */ +static int cmd_get_all_states(void) { - uint8_t state; - int ret; + uint32_t cur_fw_version, update_fw_version; + uint16_t hdrSz; + uint8_t boot_part_state = IMG_STATE_NEW, update_part_state = IMG_STATE_NEW; - ret = wolfBoot_get_partition_state(part, &state); - if (ret != 0) { - wolfBoot_printf("Error: Failed to get state for %s partition (error: %d)\n", - partition_name(part), ret); - return -1; + cur_fw_version = wolfBoot_current_firmware_version(); + update_fw_version = wolfBoot_update_firmware_version(); + + wolfBoot_get_partition_state(PART_BOOT, &boot_part_state); + wolfBoot_get_partition_state(PART_UPDATE, &update_part_state); + + wolfBoot_printf("\n"); + wolfBoot_printf("System information\n"); + wolfBoot_printf("====================================\n"); + wolfBoot_printf("Firmware version : 0x%lx\n", + (unsigned long)wolfBoot_current_firmware_version()); + wolfBoot_printf("Current firmware state: %s\n", + partition_state_name(boot_part_state)); + if (update_fw_version != 0) { + if (update_part_state == IMG_STATE_UPDATING) { + wolfBoot_printf("Candidate firmware version : 0x%lx\n", + (unsigned long)update_fw_version); + } else { + wolfBoot_printf("Backup firmware version : 0x%lx\n", + (unsigned long)update_fw_version); + } + wolfBoot_printf("Update state: %s\n", + partition_state_name(update_part_state)); + if (update_fw_version > cur_fw_version) { + wolfBoot_printf("'reboot' to initiate update.\n"); + } else { + wolfBoot_printf("Update image older than current.\n"); + } + } else { + wolfBoot_printf("No image in update partition.\n"); } - wolfBoot_printf("%s partition state: %s (0x%02X)\n", - partition_name(part), state_name(state), state); return 0; } -/* Print all partition states */ -static int cmd_get_all_states(void) +static int cmd_get_keystore(void) { - int ret = 0; - - wolfBoot_printf("=== Partition States ===\n"); + int i, j; + uint32_t n_keys; - if (cmd_get_state(PART_BOOT) != 0) - ret = -1; - - if (cmd_get_state(PART_UPDATE) != 0) - ret = -1; + wolfBoot_printf("\n"); + wolfBoot_printf("Bootloader keystore information\n"); + wolfBoot_printf("====================================\n"); + n_keys = keystore_num_pubkeys(); + wolfBoot_printf("Number of public keys: %lu\n", (unsigned long)n_keys); + for (i = 0; i < (int)n_keys; i++) { + uint32_t size = keystore_get_size(i); + uint32_t type = keystore_get_key_type(i); + uint32_t mask = keystore_get_mask(i); + uint8_t *keybuf = keystore_get_buffer(i); - return ret; + wolfBoot_printf("\n"); + wolfBoot_printf(" Public Key #%d: size %lu, type %lx, mask %08lx\n", i, + (unsigned long)size, (unsigned long)type, (unsigned long)mask); + wolfBoot_printf(" ====================================\n "); + for (j = 0; j < (int)size; j++) { + wolfBoot_printf("%02X ", keybuf[j]); + if (j % 16 == 15) { + wolfBoot_printf("\n "); + } + } + wolfBoot_printf("\n"); + } + return 0; } /* Trigger an update */ @@ -114,8 +158,7 @@ static void print_usage(const char* prog_name) wolfBoot_printf("\nUsage: %s [options]\n\n", prog_name); wolfBoot_printf("Commands:\n"); wolfBoot_printf(" status - Show state of all partitions\n"); - wolfBoot_printf(" get-boot - Get BOOT partition state\n"); - wolfBoot_printf(" get-update - Get UPDATE partition state\n"); + wolfBoot_printf(" keystore - Show keystore information\n"); wolfBoot_printf(" update-trigger - Trigger an update (sets UPDATE partition to UPDATING)\n"); wolfBoot_printf(" success - Mark BOOT partition as SUCCESS\n"); wolfBoot_printf(" verify-boot - Verify integrity and authenticity of BOOT partition\n"); @@ -140,24 +183,27 @@ static int cmd_verify(uint8_t part) ret = wolfBoot_open_image(&img, part); if (ret < 0) { - wolfBoot_printf("Error: Failed to open image header for %s partition (error: %d)\n", + wolfBoot_printf("Failed to open image header for %s partition (error: %d)\n", partition_name(part), ret); return -1; } ret = wolfBoot_verify_integrity(&img); if (ret < 0) { - wolfBoot_printf("Integrity check failed for %s partition\n", partition_name(part)); + wolfBoot_printf("Integrity check failed for %s partition\n", + partition_name(part)); return -1; } ret = wolfBoot_verify_authenticity(&img); if (ret < 0) { - wolfBoot_printf("Authenticity check failed for %s partition\n", partition_name(part)); + wolfBoot_printf("Authenticity check failed for %s partition\n", + partition_name(part)); return -1; } - wolfBoot_printf("%s partition: Integrity and authenticity verified.\n", partition_name(part)); + wolfBoot_printf("%s partition: Integrity and authenticity verified.\n", + partition_name(part)); return ret; } @@ -183,11 +229,8 @@ int main(int argc, const char* argv[]) if (strcmp(command, "status") == 0) { ret = cmd_get_all_states(); } - else if (strcmp(command, "get-boot") == 0) { - ret = cmd_get_state(PART_BOOT); - } - else if (strcmp(command, "get-update") == 0) { - ret = cmd_get_state(PART_UPDATE); + else if (strcmp(command, "keystore") == 0) { + ret = cmd_get_keystore(); } else if (strcmp(command, "update-trigger") == 0) { ret = cmd_update_trigger();