diff --git a/bootloaders/riotboot/Makefile b/bootloaders/riotboot/Makefile index bd478e0a24e7..71b1949214a4 100644 --- a/bootloaders/riotboot/Makefile +++ b/bootloaders/riotboot/Makefile @@ -3,6 +3,7 @@ APPLICATION = riotboot # Include riotboot flash partition functionality USEMODULE += riotboot_slot +USEMODULE += riotboot_flashwrite # We don't want to re-configure any hardware CFLAGS += -DDISABLE_BOARD_INIT=1 diff --git a/bootloaders/riotboot/main.c b/bootloaders/riotboot/main.c index 2e41b073b4d9..71ff0594caba 100644 --- a/bootloaders/riotboot/main.c +++ b/bootloaders/riotboot/main.c @@ -20,41 +20,22 @@ * @} */ -#include "cpu.h" -#include "panic.h" +#include "riotboot/bootloader.h" #include "riotboot/slot.h" +#include "riotboot/wdt.h" void kernel_init(void) { - uint32_t version = 0; - int slot = -1; - - for (unsigned i = 0; i < riotboot_slot_numof; i++) { - const riotboot_hdr_t *riot_hdr = riotboot_slot_get_hdr(i); - if (riotboot_slot_validate(i)) { - /* skip slot if metadata broken */ - continue; - } - if (riot_hdr->start_addr != riotboot_slot_get_image_startaddr(i)) { - continue; - } - if (slot == -1 || riot_hdr->version > version) { - version = riot_hdr->version; - slot = i; - } - } - + riotboot_hdr_t riot_hdr; + int slot = riotboot_bootloader_get_slot(&riot_hdr); if (slot != -1) { +#if MODULE_RIOTBOOT_WDT + unsigned boot = riotboot_hdr_get_boot_count(&riot_hdr); + riotboot_wdt_start(CONFIG_RIOTBOOT_WDT_TIMEOUT_MSEC << boot); +#endif riotboot_slot_jump(slot); } /* serious trouble! nothing to boot */ while (1) {} } - -NORETURN void core_panic(core_panic_t crash_code, const char *message) -{ - (void)crash_code; - (void)message; - while (1) {} -} diff --git a/bootloaders/riotboot_common.mk b/bootloaders/riotboot_common.mk index b30dbdd5bd0b..157b3c49fb6b 100644 --- a/bootloaders/riotboot_common.mk +++ b/bootloaders/riotboot_common.mk @@ -17,6 +17,7 @@ DISABLE_MODULE += pm_layered # avoid using stdio USEMODULE += stdio_null +USEMODULE += riotboot_bootloader # RIOT codebase RIOTBASE ?= $(CURDIR)/../.. diff --git a/bootloaders/riotboot_dfu/main.c b/bootloaders/riotboot_dfu/main.c index 275213c4b6c7..c091dc4c1e09 100644 --- a/bootloaders/riotboot_dfu/main.c +++ b/bootloaders/riotboot_dfu/main.c @@ -22,8 +22,7 @@ * @} */ -#include "cpu.h" -#include "panic.h" +#include "riotboot/bootloader.h" #include "riotboot/slot.h" #include "riotboot/usb_dfu.h" #include "ztimer.h" @@ -56,23 +55,8 @@ static bool _bootloader_alternative_mode(void) void kernel_init(void) { - uint32_t version = 0; - int slot = -1; - - for (unsigned i = 0; i < riotboot_slot_numof; i++) { - const riotboot_hdr_t *riot_hdr = riotboot_slot_get_hdr(i); - if (riotboot_slot_validate(i)) { - /* skip slot if metadata broken */ - continue; - } - if (riot_hdr->start_addr != riotboot_slot_get_image_startaddr(i)) { - continue; - } - if (slot == -1 || riot_hdr->version > version) { - version = riot_hdr->version; - slot = i; - } - } + riotboot_hdr_t riot_hdr; + int slot = riotboot_bootloader_get_slot(&riot_hdr); /* Init ztimer before starting DFU mode */ ztimer_init(); @@ -87,10 +71,3 @@ void kernel_init(void) /* Nothing to boot, stay in DFU mode to flash a slot */ riotboot_usb_dfu_init(1); } - -NORETURN void core_panic(core_panic_t crash_code, const char *message) -{ - (void)crash_code; - (void)message; - while (1) {} -} diff --git a/bootloaders/riotboot_serial/main.c b/bootloaders/riotboot_serial/main.c index 5fce9408133d..b656ba76cee3 100644 --- a/bootloaders/riotboot_serial/main.c +++ b/bootloaders/riotboot_serial/main.c @@ -22,37 +22,10 @@ */ #include "cpu.h" -#include "panic.h" +#include "riotboot/bootloader.h" #include "riotboot/slot.h" #include "riotboot/serial.h" -static int _get_slot(void) -{ - uint32_t version = 0; - int slot = -1; - - if (!IS_USED(MODULE_RIOTBOOT_SLOT)) { - return -1; - } - - for (unsigned i = 0; i < riotboot_slot_numof; i++) { - const riotboot_hdr_t *riot_hdr = riotboot_slot_get_hdr(i); - if (riotboot_slot_validate(i)) { - /* skip slot if metadata broken */ - continue; - } - if (riot_hdr->start_addr != riotboot_slot_get_image_startaddr(i)) { - continue; - } - if (slot == -1 || riot_hdr->version > version) { - version = riot_hdr->version; - slot = i; - } - } - - return slot; -} - static void _boot_default(int slot) { if (!IS_USED(MODULE_RIOTBOOT_SLOT)) { @@ -62,7 +35,8 @@ static void _boot_default(int slot) } if (slot == -1) { - slot = _get_slot(); + riotboot_hdr_t riot_hdr; + slot = riotboot_bootloader_get_slot(&riot_hdr); } if (slot != -1) { @@ -84,10 +58,3 @@ void kernel_init(void) /* serious trouble! nothing to boot */ while (1) {} } - -NORETURN void core_panic(core_panic_t crash_code, const char *message) -{ - (void)crash_code; - (void)message; - while (1) {} -} diff --git a/bootloaders/riotboot_tinyusb_dfu/main.c b/bootloaders/riotboot_tinyusb_dfu/main.c index 275213c4b6c7..c091dc4c1e09 100644 --- a/bootloaders/riotboot_tinyusb_dfu/main.c +++ b/bootloaders/riotboot_tinyusb_dfu/main.c @@ -22,8 +22,7 @@ * @} */ -#include "cpu.h" -#include "panic.h" +#include "riotboot/bootloader.h" #include "riotboot/slot.h" #include "riotboot/usb_dfu.h" #include "ztimer.h" @@ -56,23 +55,8 @@ static bool _bootloader_alternative_mode(void) void kernel_init(void) { - uint32_t version = 0; - int slot = -1; - - for (unsigned i = 0; i < riotboot_slot_numof; i++) { - const riotboot_hdr_t *riot_hdr = riotboot_slot_get_hdr(i); - if (riotboot_slot_validate(i)) { - /* skip slot if metadata broken */ - continue; - } - if (riot_hdr->start_addr != riotboot_slot_get_image_startaddr(i)) { - continue; - } - if (slot == -1 || riot_hdr->version > version) { - version = riot_hdr->version; - slot = i; - } - } + riotboot_hdr_t riot_hdr; + int slot = riotboot_bootloader_get_slot(&riot_hdr); /* Init ztimer before starting DFU mode */ ztimer_init(); @@ -87,10 +71,3 @@ void kernel_init(void) /* Nothing to boot, stay in DFU mode to flash a slot */ riotboot_usb_dfu_init(1); } - -NORETURN void core_panic(core_panic_t crash_code, const char *message) -{ - (void)crash_code; - (void)message; - while (1) {} -} diff --git a/core/lib/init.c b/core/lib/init.c index e6e4c026a964..df84d6846e48 100644 --- a/core/lib/init.c +++ b/core/lib/init.c @@ -30,7 +30,10 @@ #include "periph/pm.h" #include "thread.h" #include "stdio_base.h" - +#if IS_USED(MODULE_RIOTBOOT) +# include "riotboot/slot.h" +# include "riotboot/wdt.h" +#endif #if IS_USED(MODULE_VFS) #include "vfs.h" #endif @@ -60,6 +63,14 @@ static void *main_trampoline(void *arg) LOG_INFO(CONFIG_BOOT_MSG_STRING "\n"); } +#if IS_USED(MODULE_RIOTBOOT) + if (IS_USED(MODULE_RIOTBOOT_HDR_AUTO_CONFIRM)) { + riotboot_slot_confirm(); + if (IS_USED(MODULE_RIOTBOOT_WDT)) { + riotboot_wdt_stop(); + } + } +#endif int res = main(); if (IS_USED(MODULE_TEST_UTILS_MAIN_EXIT_CB)) { diff --git a/dist/tools/riotboot_gen_hdr/genhdr.c b/dist/tools/riotboot_gen_hdr/genhdr.c index aebd819e9b60..92772a03aa71 100644 --- a/dist/tools/riotboot_gen_hdr/genhdr.c +++ b/dist/tools/riotboot_gen_hdr/genhdr.c @@ -28,23 +28,36 @@ */ #define HDR_ALIGN (256) -static void populate_hdr(riotboot_hdr_t *hdr, uint32_t ver, uint32_t addr) +static size_t populate_hdr_v1(riotboot_hdr_t *hdr, uint32_t ver, uint32_t addr) { /* ensure the buffer and header have 0's */ - memset(hdr, '\0', sizeof(riotboot_hdr_t)); - - /* Generate image header */ - hdr->magic_number = RIOTBOOT_MAGIC; - hdr->version = ver; - hdr->start_addr = addr; + memset(hdr, '\0', sizeof(*hdr)); + /* Generate image header v1 */ + hdr->v1.magic_number = RIOTBOOT_MAGIC_V1; + hdr->v1.version = ver; + hdr->v1.start_addr = addr; + /* calculate header checksum */ + hdr->v1.chksum = riotboot_hdr_checksum(hdr); + return sizeof(hdr->v1); +} +static size_t populate_hdr_v2(riotboot_hdr_t *hdr, uint32_t ver, uint32_t addr) +{ + /* ensure the buffer and header have 0's */ + memset(hdr, '\0', sizeof(*hdr)); + /* Generate image header v2 */ + hdr->v2.magic_number = RIOTBOOT_MAGIC_V2; + hdr->v2.version = ver; + hdr->v2.start_addr = addr; + hdr->v2.flags = 0xffffffff; /* calculate header checksum */ - hdr->chksum = riotboot_hdr_checksum(hdr); + hdr->v2.chksum = riotboot_hdr_checksum(hdr); + return sizeof(hdr->v2); } int genhdr(int argc, char *argv[]) { - const char generate_usage[] = " "; + const char generate_usage[] = " [-v [v1,v2]]"; /* riotboot_hdr buffer */ uint8_t *hdr_buf; @@ -93,6 +106,26 @@ int genhdr(int argc, char *argv[]) hdr_len = hdr_len_arg; } + /* generate only v1 by default */ + bool gen_v1 = true; + bool gen_v2 = false; + if (argc >= 8) { + gen_v1 = false; + if (!strcmp(argv[6], "-v")) { + for (int a = 7; a < argc; a++) { + if (!strcmp(argv[a], "v1")) { + gen_v1 = true; + } + else if (!strcmp(argv[a], "v2")) { + gen_v2 = true; + } + else { + fprintf(stderr, "Error: unknown version '%s'!\n", argv[a]); + return -1; + } + } + } + } /* prepare a 0 initialised buffer for riotboot_hdr_t */ hdr_buf = calloc(1, hdr_len); if (hdr_buf == NULL) { @@ -100,7 +133,14 @@ int genhdr(int argc, char *argv[]) return -1; } - populate_hdr((riotboot_hdr_t*)hdr_buf, app_ver, start_addr); + size_t gen_hdr_size = 0; + uint8_t *p_hdr = hdr_buf; + if (gen_v1) { + gen_hdr_size += populate_hdr_v1((riotboot_hdr_t *)(p_hdr + gen_hdr_size), app_ver, start_addr); + } + if (gen_v2) { + gen_hdr_size += populate_hdr_v2((riotboot_hdr_t *)(p_hdr + gen_hdr_size), app_ver, start_addr); + } /* Write the header */ if (!to_file(argv[5], hdr_buf, hdr_len)) { @@ -124,35 +164,43 @@ int updatehdr(int argc, char *argv[]) riotboot_hdr_t hdr = { 0 }; int res = from_file(file, &hdr, sizeof(hdr)); - if (res < (int)sizeof(hdr)) { + if (res < (int)sizeof(hdr.v1)) { fprintf(stderr, "Can't read header from %s\n", file); return -EIO; } - - if (hdr.magic_number != RIOTBOOT_MAGIC) { - fprintf(stderr, "Invalid magic: %x\n", hdr.magic_number); + uint32_t magic = riotboot_hdr_get_magic_number(&hdr); + if (magic == RIOTBOOT_MAGIC_V2) { + hdr.v2.magic_number = RIOTBOOT_MAGIC_V2; + hdr.v2.version = atoi(argv[2]); + hdr.v2.chksum = riotboot_hdr_checksum(&hdr); + to_file(file, &hdr, sizeof(hdr.v2)); + } + else if (magic == RIOTBOOT_MAGIC_V1) { + hdr.v1.magic_number = RIOTBOOT_MAGIC_V1; + hdr.v1.version = atoi(argv[2]); + hdr.v1.chksum = riotboot_hdr_checksum(&hdr); + to_file(file, &hdr, sizeof(hdr.v1)); + } + else { + fprintf(stderr, "Invalid magic: %x\n", magic); return -EIO; } - hdr.version = atoi(argv[2]); - hdr.chksum = riotboot_hdr_checksum(&hdr); - to_file(file, &hdr, sizeof(hdr)); - return 0; } static void print_hdr(const riotboot_hdr_t *hdr) { - printf("version: %u\n", hdr->version); - printf("address: 0x%x\n", hdr->start_addr); + printf("version: %u\n", riotboot_hdr_get_version(hdr)); + printf("address: 0x%x\n", riotboot_hdr_get_start_addr(hdr)); printf("checksum: %svalid\n", riotboot_hdr_validate(hdr) ? "in" : ""); } static void print_hdr_json(const riotboot_hdr_t *hdr) { printf("{\n"); - printf("\t\"version\": %u,\n", hdr->version); - printf("\t\"address\": %u,\n", hdr->start_addr); + printf("\t\"version\": %u,\n", riotboot_hdr_get_version(hdr)); + printf("\t\"address\": %u,\n", riotboot_hdr_get_start_addr(hdr)); printf("\t\"valid\": %s\n", riotboot_hdr_validate(hdr) ? "false" : "true"); printf("}\n"); } @@ -166,8 +214,8 @@ int readhdr(const char *file, bool json) return -EIO; } - if (hdr.magic_number != RIOTBOOT_MAGIC) { - fprintf(stderr, "Invalid magic: %x\n", hdr.magic_number); + if (riotboot_hdr_get_magic_number(&hdr) != RIOTBOOT_MAGIC) { + fprintf(stderr, "Invalid magic: %x\n", riotboot_hdr_get_magic_number(&hdr)); return -EIO; } diff --git a/examples/advanced/suit_update/main.c b/examples/advanced/suit_update/main.c index 84589c6e6a3a..e07cc3ac0f57 100644 --- a/examples/advanced/suit_update/main.c +++ b/examples/advanced/suit_update/main.c @@ -25,6 +25,7 @@ #include "suit/transport/coap.h" #ifdef MODULE_SUIT_STORAGE_FLASHWRITE +#include "periph/flashpage.h" #include "riotboot/slot.h" #endif @@ -69,19 +70,47 @@ static int cmd_print_riotboot_hdr(int argc, char **argv) (void)argc; (void)argv; - int current_slot = riotboot_slot_current(); - if (current_slot != -1) { + if (riotboot_slot_current() == -1) { + printf("[FAILED] You're not running riotboot\n"); + return -1; + } + if (argc == 1) { + printf("Usage: %s [ [activate|deactivate|confirm|dismiss]]\n", argv[0]); + return 0; + } + int slot = atoi(argv[1]); + if (argc == 2) { /* Sometimes, udhcp output messes up the following printfs. That - * confuses the test script. As a workaround, just disable interrupts - * for a while. - */ + * confuses the test script. As a workaround, just disable interrupts + * for a while. + */ unsigned state = irq_disable(); - riotboot_slot_print_hdr(current_slot); + riotboot_slot_print_hdr(slot); irq_restore(state); + return 0; } - else { - printf("[FAILED] You're not running riotboot\n"); + riotboot_hdr_t hdr = *riotboot_slot_get_hdr(slot); + if (riotboot_hdr_is_v2(&hdr)) { + if (!strcmp(argv[2], "activate")) { + riotboot_hdr_set_img_state(&hdr, RIOTBOOT_HDR_IMG_STATE_ACTIVATED); + } + else if (!strcmp(argv[2], "deactivate")) { + riotboot_hdr_set_img_state(&hdr, RIOTBOOT_HDR_IMG_STATE_DEACTIVATED); + } + else if (!strcmp(argv[2], "confirm")) { + riotboot_hdr_set_img_state(&hdr, RIOTBOOT_HDR_IMG_STATE_CONFIRMED); + } + else if (!strcmp(argv[2], "dismiss")) { + riotboot_hdr_set_img_state(&hdr, RIOTBOOT_HDR_IMG_STATE_DISMISSED); + } + else { + printf("Unknown command %s\n", argv[2]); + return -1; + } + flashpage_write(flashpage_addr(flashpage_page(riotboot_slot_get_hdr(slot))), + &hdr.v2, sizeof(hdr.v2)); } + return 0; } diff --git a/makefiles/boot/riotboot.mk b/makefiles/boot/riotboot.mk index 06a6b7dd22e7..040d789060c0 100644 --- a/makefiles/boot/riotboot.mk +++ b/makefiles/boot/riotboot.mk @@ -35,6 +35,14 @@ $(BINDIR_RIOTBOOT)/%.elf: $(BASELIBS) $(ARCHIVES) $(BINDIR_RIOTBOOT) $(Q)$(_LINK) -o $@ endif +RIOTBOOT_HDR_VERSION ?= -v +ifneq (,$(filter riotboot_hdr_v1, $(USEMODULE))) + RIOTBOOT_HDR_VERSION := $(RIOTBOOT_HDR_VERSION) v1 +endif +ifneq (,$(filter riotboot_hdr_v2, $(USEMODULE))) + RIOTBOOT_HDR_VERSION := $(RIOTBOOT_HDR_VERSION) v2 +endif + # Slot 0 and 1 firmware offset, after header SLOT0_IMAGE_OFFSET := $$(($(SLOT0_OFFSET) + $(RIOTBOOT_HDR_LEN))) SLOT1_IMAGE_OFFSET := $$(($(SLOT1_OFFSET) + $(RIOTBOOT_HDR_LEN))) @@ -70,7 +78,7 @@ $(HEADER_TOOL): FORCE # It must be always regenerated in case of any changes, so FORCE .PRECIOUS: %.bin %.hdr: $(HEADER_TOOL) %.bin FORCE - $(Q)$(HEADER_TOOL) generate $< $(APP_VER) $$(($(ROM_START_ADDR)+$(OFFSET))) $(RIOTBOOT_HDR_LEN) - > $@ + $(Q)$(HEADER_TOOL) generate $< $(APP_VER) $$(($(ROM_START_ADDR)+$(OFFSET))) $(RIOTBOOT_HDR_LEN) - $(RIOTBOOT_HDR_VERSION) > $@ $(BINDIR_RIOTBOOT)/slot0.hdr: OFFSET=$(SLOT0_IMAGE_OFFSET) $(BINDIR_RIOTBOOT)/slot1.hdr: OFFSET=$(SLOT1_IMAGE_OFFSET) diff --git a/sys/include/riotboot/bootloader.h b/sys/include/riotboot/bootloader.h new file mode 100644 index 000000000000..70f335cd4c6f --- /dev/null +++ b/sys/include/riotboot/bootloader.h @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2025 ML!PA Consulting GmbH + */ + +#pragma once + +/** + * @defgroup sys_riotboot_bootloader RIOT bootloader common functions + * @ingroup sys + * @{ + * + * @brief RIOT bootloader common functions + * + * @author Fabian Hüßler + * @} + */ + +#include "riotboot/hdr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Get the slot number of the bootable image + * + * @param[out] riot_hdr Copy of riotboot image header + * + * @retval Slot number of the bootable image + * @retval -1 if none found + */ +int riotboot_bootloader_get_slot(riotboot_hdr_t *riot_hdr); + +#ifdef __cplusplus +} +#endif diff --git a/sys/include/riotboot/flashwrite.h b/sys/include/riotboot/flashwrite.h index b9a63a558ae8..6bec0a24a901 100644 --- a/sys/include/riotboot/flashwrite.h +++ b/sys/include/riotboot/flashwrite.h @@ -150,7 +150,7 @@ int riotboot_flashwrite_init_raw(riotboot_flashwrite_t *state, int target_slot, * @brief Initialize firmware update (riotboot version) * * This function initializes a firmware update, skipping riotboot's magic - * number ("RIOT") by calling @ref riotboot_flashwrite_init_raw() with an + * number by calling @ref riotboot_flashwrite_init_raw() with an * offset of RIOTBOOT_FLASHWRITE_SKIPLEN (4). This ensures that riotboot will * ignore the slot until the magic number has been restored, e.g., through @ref * riotboot_flashwrite_finish(). @@ -163,7 +163,7 @@ int riotboot_flashwrite_init_raw(riotboot_flashwrite_t *state, int target_slot, static inline int riotboot_flashwrite_init(riotboot_flashwrite_t *state, int target_slot) { - /* initialize state, but skip "RIOT" */ + /* initialize state, but skip magic number */ return riotboot_flashwrite_init_raw(state, target_slot, RIOTBOOT_FLASHWRITE_SKIPLEN); } @@ -211,7 +211,7 @@ int riotboot_flashwrite_finish_raw(riotboot_flashwrite_t *state, * @brief Finish a firmware update (riotboot version) * * This function finishes a firmware update by re-writing the first header so - * it includes riotboot's magic number ("RIOT"). + * it includes riotboot's magic number. * * @param[in] state ptr to previously used state structure * @@ -219,7 +219,8 @@ int riotboot_flashwrite_finish_raw(riotboot_flashwrite_t *state, */ static inline int riotboot_flashwrite_finish(riotboot_flashwrite_t *state) { - return riotboot_flashwrite_finish_raw(state, (const uint8_t *)"RIOT", + uint32_t magic = RIOTBOOT_MAGIC; + return riotboot_flashwrite_finish_raw(state, (const uint8_t *)&magic, RIOTBOOT_FLASHWRITE_SKIPLEN); } diff --git a/sys/include/riotboot/hdr.h b/sys/include/riotboot/hdr.h index 1633ac215a0f..3802b616df1c 100644 --- a/sys/include/riotboot/hdr.h +++ b/sys/include/riotboot/hdr.h @@ -17,7 +17,7 @@ * * The header contains * - * - "RIOT" as magic number + * - the magic number * - the application version * - the address where the RIOT firmware is found * - the checksum of the three previous fields @@ -31,29 +31,151 @@ * @} */ +#include +#include +#include + #ifdef __cplusplus extern "C" { #endif -#include +/** + * @brief Magic number for riotboot_hdr version 1 + */ +#define RIOTBOOT_MAGIC_V1 0x544f4952 /* "RIOT" */ /** - * @brief Magic number for riotboot_hdr - * + * @brief Magic number for riotboot_hdr version 2 */ -#define RIOTBOOT_MAGIC 0x544f4952 /* "RIOT" */ +#define RIOTBOOT_MAGIC_V2 0x54304952 /* "RI0T" */ + +#ifndef DOXYGEN +#if defined(MODULE_RIOTBOOT_HDR_V2) +# define RIOTBOOT_MAGIC RIOTBOOT_MAGIC_V2 +#else +# define RIOTBOOT_MAGIC RIOTBOOT_MAGIC_V1 +#endif +#endif + +/** + * @brief Bit shift position of tentative boot counter + */ +#define RIOTBOOT_HDR_IMAGE_BOOT_COUNT_SHIFT 0u +/** + * @brief 4-bit mask of tentative boot counter + */ +#define RIOTBOOT_HDR_IMAGE_BOOT_COUNT_MASK (0x0000000f << RIOTBOOT_HDR_IMAGE_BOOT_COUNT_SHIFT) +/** + * @brief Bit shift position of image boot state + */ +#define RIOTBOOT_HDR_IMAGE_STATE_SHIFT 4u +/** + * @brief 4-bit mask of image boot state + */ +#define RIOTBOOT_HDR_IMAGE_STATE_MASK (0x0000000f << RIOTBOOT_HDR_IMAGE_STATE_SHIFT) +/** + * @brief Maximum number of attempts to boot a tentative image before reverting to the previous one + */ +#ifndef CONFIG_RIOTBOOT_MAX_ATTEMPTS +# define CONFIG_RIOTBOOT_MAX_ATTEMPTS 3u +#endif + +static_assert(CONFIG_RIOTBOOT_MAX_ATTEMPTS > 0, + "CONFIG_RIOTBOOT_MAX_ATTEMPTS must be at least 1"); + +static_assert(CONFIG_RIOTBOOT_MAX_ATTEMPTS <= 4, + "CONFIG_RIOTBOOT_MAX_ATTEMPTS must be <= 4"); + +/** + * @brief Image state enumeration + */ +typedef enum riotboot_hdr_img_state { + RIOTBOOT_HDR_IMG_STATE_INSTALLED = 0x0f, /**< Image is installed (1111) */ + RIOTBOOT_HDR_IMG_STATE_DEACTIVATED = 0x0e, /**< Image is deactivated (1110) */ + RIOTBOOT_HDR_IMG_STATE_ACTIVATED = 0x0c, /**< Image is activated (1100) */ + RIOTBOOT_HDR_IMG_STATE_CONFIRMED = 0x08, /**< Image is confirmed (1000) */ + RIOTBOOT_HDR_IMG_STATE_DISMISSED = 0x00, /**< Image is dismissed (0000) */ +} riotboot_hdr_img_state_t; /** * @brief Structure to store image header - All members are little endian - * @{ */ -typedef struct { - uint32_t magic_number; /**< Header magic number (always "RIOT") */ +struct riotboot_hdr_v1 { + uint32_t magic_number; /**< Header magic number */ uint32_t version; /**< Integer representing the partition version */ uint32_t start_addr; /**< Address after the allocated space for the header */ uint32_t chksum; /**< Checksum of riotboot_hdr */ +}; + +/** + * @brief Structure to store image header v2 - All members are little endian + */ +struct riotboot_hdr_v2 { + uint32_t magic_number; /**< Header magic number */ + uint32_t version; /**< Integer representing the partition version */ + uint32_t start_addr; /**< Address after the allocated space for the header */ + uint32_t chksum; /**< Checksum of riotboot_hdr */ + /** + * @brief General purpose flags for image state and boot count + * + * The flags are not covered by the checksum, because checksum recomputation + * would require flash page erase of the currently running program. + */ + uint32_t flags; +}; + +/** + * @brief Union to store riotboot headers in different versions + */ +typedef union riotboot_hdr { + struct riotboot_hdr_v1 v1; /**< Version 1 header */ + struct riotboot_hdr_v2 v2; /**< Version 2 header */ } riotboot_hdr_t; -/** @} */ + +/** + * @brief Getter for riotboot magic number + * + * @param[in] riotboot_hdr ptr to image header + * + * @return Magic number + */ +uint32_t riotboot_hdr_get_magic_number(const riotboot_hdr_t *riotboot_hdr); + +/** + * @brief Getter for riotboot version + * + * @param[in] riotboot_hdr ptr to image header + * + * @return Version number + */ +uint32_t riotboot_hdr_get_version(const riotboot_hdr_t *riotboot_hdr); + +/** + * @brief Getter for riotboot start address + * + * @param[in] riotboot_hdr ptr to image header + * + * @return Start address + */ +uint32_t riotboot_hdr_get_start_addr(const riotboot_hdr_t *riotboot_hdr); + +/** + * @brief Getter for riotboot flags + * + * @param[in] riotboot_hdr ptr to image header + * + * @return Flags + */ +uint32_t riotboot_hdr_get_flags(const riotboot_hdr_t *riotboot_hdr); + +/** + * @brief Getter for the header checksum + * + * @param[in] riotboot_hdr ptr to image header + * + * @return Checksum of the given riotboot_hdr + */ +uint32_t riotboot_hdr_get_checksum(const riotboot_hdr_t *riotboot_hdr); /** * @brief Print formatted riotboot_hdr_t to STDIO @@ -82,6 +204,51 @@ int riotboot_hdr_validate(const riotboot_hdr_t *riotboot_hdr); */ uint32_t riotboot_hdr_checksum(const riotboot_hdr_t *riotboot_hdr); +/** + * @brief Check if the header is version 2 + * + * @param[in] riotboot_hdr ptr to image header + * + * @return true if version is 2, false otherwise + */ +bool riotboot_hdr_is_v2(const riotboot_hdr_t *riotboot_hdr); + +/** + * @brief Get current image boot count + * + * @param[in] riotboot_hdr ptr to image header + * + * @return boot counter + */ +unsigned riotboot_hdr_get_boot_count(const riotboot_hdr_t *riotboot_hdr); + +/** + * @brief Set image boot count + * + * @param[in] riotboot_hdr ptr to image header + * @param[in] boot_count new boot count + */ +void riotboot_hdr_set_boot_count(riotboot_hdr_t *riotboot_hdr, + unsigned boot_count); + +/** + * @brief Set image state + * + * @param[in] riotboot_hdr ptr to image header + * @param[in] state new image state + * @return 0 on success, -1 on failure + */ +int riotboot_hdr_set_img_state(riotboot_hdr_t *riotboot_hdr, + riotboot_hdr_img_state_t state); + +/** + * @brief Get image state + * + * @param[in] riotboot_hdr ptr to image header + * @return image state + */ +int riotboot_hdr_get_img_state(const riotboot_hdr_t *riotboot_hdr); + #ifdef __cplusplus } #endif diff --git a/sys/include/riotboot/slot.h b/sys/include/riotboot/slot.h index 2f0ff116c7dd..81851c37e066 100644 --- a/sys/include/riotboot/slot.h +++ b/sys/include/riotboot/slot.h @@ -119,6 +119,27 @@ size_t riotboot_slot_offset(unsigned slot); */ void riotboot_slot_dump_addrs(void); +/** + * @brief Confirm the currently running image slot + * + * This function sets the image state to CONFIRMED in the header of the + * currently running image, if it is in ACTIVATED state. + * It must be called manually if not module `riotboot_hdr_auto_confirm` is used. + */ +void riotboot_slot_confirm(void); + +/** + * @brief Activate or deactivate the other slot + * + * This function sets the image state to ACTIVATED or DEACTIVATED in the header of the + * non-running image slot. + * + * A deactivated slot can be activated but an activated slot cannot be deactivated. + * + * @param[in] active if true, set to ACTIVATED, else set to INACTIVE + */ +void riotboot_slot_activate_other(bool active); + /** * @brief Get the size of a slot * diff --git a/sys/include/riotboot/wdt.h b/sys/include/riotboot/wdt.h new file mode 100644 index 000000000000..8214ea5bf753 --- /dev/null +++ b/sys/include/riotboot/wdt.h @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: 2025 ML!PA Consulting GmbH + */ + +#pragma once + +/** + * @defgroup sys_riotboot_wdt RIOT watchdog timer + * @ingroup sys + * @{ + * + * @brief RIOT bootloader watchdog timer + * + * @author Fabian Hüßler + * @} + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initial watchdog timeout in milliseconds to confirm a successful boot + * + * An initial watchdog timer is started to catch an infinite loop which prevents proper startup. + * The timeout is doubled on each unsuccessful attempt. + */ +#ifndef CONFIG_RIOTBOOT_WDT_TIMEOUT_MSEC +# define CONFIG_RIOTBOOT_WDT_TIMEOUT_MSEC 4000 +#endif + +static_assert(CONFIG_RIOTBOOT_WDT_TIMEOUT_MSEC > 0, + "CONFIG_RIOTBOOT_WDT_TIMEOUT_MSEC must be greater than 0"); + +/** + * @brief Start the watchdog timer to confirm a successful boot + * + * The watchdog must be stopped with @ref riotboot_wdt_stop() after a + * successful boot, otherwise it will reboot the system. + * + * @param[in] msec Initial timeout in milliseconds + */ +void riotboot_wdt_start(unsigned msec); + +/** + * @brief Stop the watchdog timer started with @ref riotboot_wdt_start() + */ +void riotboot_wdt_stop(void); + +#ifdef __cplusplus +} +#endif diff --git a/sys/riotboot/Makefile.dep b/sys/riotboot/Makefile.dep index bc72834547ff..0f36609cbd6d 100644 --- a/sys/riotboot/Makefile.dep +++ b/sys/riotboot/Makefile.dep @@ -22,6 +22,15 @@ ifneq (,$(filter riotboot_hdr, $(USEMODULE))) USEMODULE += checksum endif +ifneq (,$(filter riotboot_hdr_v2,$(USEMODULE))) + ifneq (,$(filter periph_wdt,$(FEATURES_PROVIDED))) + DEFAULT_MODULE += riotboot_wdt + endif + # should be explicitly disabled + DEFAULT_MODULE += riotboot_hdr_auto_activate + DEFAULT_MODULE += riotboot_hdr_auto_confirm +endif + ifneq (,$(filter riotboot_usb_dfu, $(USEMODULE))) USEMODULE += usbus_dfu USEMODULE += riotboot_flashwrite @@ -30,6 +39,10 @@ ifneq (,$(filter riotboot_usb_dfu, $(USEMODULE))) FEATURES_REQUIRED += periph_pm endif +ifneq (,$(filter riotboot_wdt,$(USEMODULE))) + FEATURES_REQUIRED += periph_wdt +endif + ifneq (,$(filter riotboot_tinyusb_dfu, $(USEMODULE))) USEPKG += tinyusb endif diff --git a/sys/riotboot/Makefile.include b/sys/riotboot/Makefile.include index d0182d3812a2..84bcb0eb9144 100644 --- a/sys/riotboot/Makefile.include +++ b/sys/riotboot/Makefile.include @@ -25,3 +25,12 @@ CFLAGS += -DSLOT0_OFFSET=$(SLOT0_OFFSET) CFLAGS += -DSLOT1_LEN=$(SLOT1_LEN) CFLAGS += -DSLOT1_OFFSET=$(SLOT1_OFFSET) CFLAGS += -DNUM_SLOTS=$(NUM_SLOTS) + +# riotboot header version 1 +PSEUDOMODULES += riotboot_hdr_v1 +# riotboot header version 2 +PSEUDOMODULES += riotboot_hdr_v2 +# User has to mark image as active manually +PSEUDOMODULES += riotboot_hdr_auto_activate +# Confirm image on first boot once main is reached +PSEUDOMODULES += riotboot_hdr_auto_confirm diff --git a/sys/riotboot/bootloader.c b/sys/riotboot/bootloader.c new file mode 100644 index 000000000000..de0f2ef7711b --- /dev/null +++ b/sys/riotboot/bootloader.c @@ -0,0 +1,114 @@ +/* + * SPDX-FileCopyrightText: 2025 ML!PA Consulting GmbH + */ + +/** + * @ingroup sys_riotboot_bootloader + * @{ + * + * @file + * @brief RIOT bootloader common functions + * + * Only contains functions shared between different bootloaders + * + * @author Fabian Hüßler + * @} + */ + +#include "compiler_hints.h" +#include "panic.h" +#if MODULE_PERIPH_FLASHPAGE +# include "periph/flashpage.h" +#endif +#include "riotboot/hdr.h" +#include "riotboot/slot.h" + +#include + +static int _get_slot(void) +{ + int slot = -1; + uint32_t revision = 0; + + for (unsigned i = 0; i < riotboot_slot_numof; i++) { + const riotboot_hdr_t *riot_hdr = riotboot_slot_get_hdr(i); + if (riotboot_slot_validate(i)) { + /* skip slot if metadata broken */ + continue; + } + if (riotboot_hdr_get_start_addr(riot_hdr) != + riotboot_slot_get_image_startaddr(i)) { + continue; + } + int state = riotboot_hdr_get_img_state(riot_hdr); + if (state == RIOTBOOT_HDR_IMG_STATE_DEACTIVATED || + state == RIOTBOOT_HDR_IMG_STATE_DISMISSED) { + /* skip image which previously failed to boot or is not yet activated */ + continue; + } + if (slot == -1 || riotboot_hdr_get_version(riot_hdr) > revision) { + revision = riotboot_hdr_get_version(riot_hdr); + slot = (int)i; + } + } + + return slot; +} + +static int _set_boot(int slot, riotboot_hdr_t *riot_hdr) +{ + int state; + unsigned boot = 0; + + *riot_hdr = *riotboot_slot_get_hdr(slot); + if (riotboot_hdr_is_v2(riot_hdr)) { + state = riotboot_hdr_get_img_state(riot_hdr); + boot = riotboot_hdr_get_boot_count(riot_hdr); + if (state == RIOTBOOT_HDR_IMG_STATE_ACTIVATED) { + /* not yet confirmed */ + if (boot == CONFIG_RIOTBOOT_MAX_ATTEMPTS) { + /* failed to boot for CONFIG_RIOTBOOT_MAX_ATTEMPTS attempts */ + state = RIOTBOOT_HDR_IMG_STATE_DISMISSED; + riotboot_hdr_set_img_state(riot_hdr, state); + } + else { + riotboot_hdr_set_boot_count(riot_hdr, boot + 1); + } +#if MODULE_PERIPH_FLASHPAGE + if (memcmp(&riot_hdr->v2, &riotboot_slot_get_hdr(slot)->v2, sizeof(riot_hdr->v2)) != 0) { + flashpage_write(flashpage_addr(flashpage_page(riotboot_slot_get_hdr(slot))), + &riot_hdr->v2, sizeof(riot_hdr->v2)); + } +#endif + } + if (state == RIOTBOOT_HDR_IMG_STATE_DISMISSED || + state == RIOTBOOT_HDR_IMG_STATE_DEACTIVATED) { + return -1; /* no valid slot to boot */ + } + } + + return (int)boot; +} + +int riotboot_bootloader_get_slot(riotboot_hdr_t *riot_hdr) +{ + int slot; + int boot; + + do { + slot = _get_slot(); + if (slot == -1) { + return -1; + } + boot = _set_boot(slot, riot_hdr); + } while (boot == -1); + + return slot; +} + +NORETURN void core_panic(core_panic_t crash_code, const char *message) +{ + (void)crash_code; + (void)message; + while (1) {} +} diff --git a/sys/riotboot/flashwrite.c b/sys/riotboot/flashwrite.c index 7f55df2e556d..3717d0b71770 100644 --- a/sys/riotboot/flashwrite.c +++ b/sys/riotboot/flashwrite.c @@ -24,6 +24,7 @@ #include #include "architecture.h" +#include "macros/utils.h" #include "riotboot/flashwrite.h" #include "riotboot/slot.h" #include "od.h" @@ -204,8 +205,8 @@ int riotboot_flashwrite_invalidate_latest(void) { int _slot_to_revert; - _slot_to_revert = (riotboot_slot_get_hdr(riotboot_slot_other())->version - > riotboot_slot_get_hdr(riotboot_slot_current())->version) + _slot_to_revert = (riotboot_hdr_get_version(riotboot_slot_get_hdr(riotboot_slot_other())) > + riotboot_hdr_get_version(riotboot_slot_get_hdr(riotboot_slot_current()))) ? riotboot_slot_other() : riotboot_slot_current(); return riotboot_flashwrite_invalidate(_slot_to_revert); } @@ -220,6 +221,7 @@ int riotboot_flashwrite_finish_raw(riotboot_flashwrite_t *state, uint8_t *slot_start = (uint8_t *)riotboot_slot_get_hdr(state->target_slot); #if IS_ACTIVE(CONFIG_RIOTBOOT_FLASHWRITE_RAW) + len = MIN(len, RIOTBOOT_FLASHPAGE_BUFFER_SIZE); memcpy(state->firstblock_buf, bytes, len); flashpage_write(slot_start, state->firstblock_buf, RIOTBOOT_FLASHPAGE_BUFFER_SIZE); diff --git a/sys/riotboot/flashwrite_verify_sha256.c b/sys/riotboot/flashwrite_verify_sha256.c index 7eaeed513b9f..5a9cde627710 100644 --- a/sys/riotboot/flashwrite_verify_sha256.c +++ b/sys/riotboot/flashwrite_verify_sha256.c @@ -20,6 +20,7 @@ * @} */ +#include "riotboot/hdr.h" #include #include @@ -49,7 +50,8 @@ int riotboot_flashwrite_verify_sha256(const uint8_t *sha256_digest, /* add RIOTBOOT_MAGIC since it isn't written into flash until * riotboot_flashwrite_finish()" */ - sha256_update(&sha256, "RIOT", 4); + uint32_t magic = RIOTBOOT_MAGIC; + sha256_update(&sha256, &magic, 4); /* account for injected RIOTBOOT_MAGIC by skipping RIOTBOOT_MAGIC_LEN */ sha256_update(&sha256, img_start + 4, img_len - 4); diff --git a/sys/riotboot/hdr.c b/sys/riotboot/hdr.c index c2ff4084f3c5..e8720f010f05 100644 --- a/sys/riotboot/hdr.c +++ b/sys/riotboot/hdr.c @@ -23,6 +23,7 @@ #include #include +#include #include #ifdef RIOT_VERSION @@ -38,28 +39,106 @@ #if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ # error \ - "This code is implementented in a way that it will only work for little-endian systems!" + "This code is implemented in a way that it will only work for little-endian systems!" #endif +static uint32_t *_magic_number(riotboot_hdr_t *riotboot_hdr) +{ + return &riotboot_hdr->v1.magic_number; +} + +static uint32_t *_version(riotboot_hdr_t *riotboot_hdr) +{ + switch (riotboot_hdr->v1.magic_number) { + case RIOTBOOT_MAGIC_V1: + return &riotboot_hdr->v1.version; + case RIOTBOOT_MAGIC_V2: + return &riotboot_hdr->v2.version; + default: + return NULL; + } +} + +static uint32_t *_start_addr(riotboot_hdr_t *riotboot_hdr) +{ + switch (riotboot_hdr->v1.magic_number) { + case RIOTBOOT_MAGIC_V1: + return &riotboot_hdr->v1.start_addr; + case RIOTBOOT_MAGIC_V2: + return &riotboot_hdr->v2.start_addr; + default: + return NULL; + } +} + +static uint32_t *_flags(riotboot_hdr_t *riotboot_hdr) +{ + if (riotboot_hdr->v1.magic_number == RIOTBOOT_MAGIC_V2) { + return &riotboot_hdr->v2.flags; + } + return NULL; +} + +static uint32_t *_checksum(riotboot_hdr_t *riotboot_hdr) +{ + switch (riotboot_hdr->v1.magic_number) { + case RIOTBOOT_MAGIC_V1: + return &riotboot_hdr->v1.chksum; + case RIOTBOOT_MAGIC_V2: + return &riotboot_hdr->v2.chksum; + default: + return NULL; + } +} + +uint32_t riotboot_hdr_get_magic_number(const riotboot_hdr_t *riotboot_hdr) +{ + return *_magic_number((riotboot_hdr_t *)riotboot_hdr); +} + +uint32_t riotboot_hdr_get_version(const riotboot_hdr_t *riotboot_hdr) +{ + return *_version((riotboot_hdr_t *)riotboot_hdr); +} + +uint32_t riotboot_hdr_get_start_addr(const riotboot_hdr_t *riotboot_hdr) +{ + return *_start_addr((riotboot_hdr_t *)riotboot_hdr); +} + +uint32_t riotboot_hdr_get_flags(const riotboot_hdr_t *riotboot_hdr) +{ + return *_flags((riotboot_hdr_t *)riotboot_hdr); +} + +uint32_t riotboot_hdr_get_checksum(const riotboot_hdr_t *riotboot_hdr) +{ + return *_checksum((riotboot_hdr_t *)riotboot_hdr); +} + void riotboot_hdr_print(const riotboot_hdr_t *riotboot_hdr) { - printf("Image magic_number: 0x%08x\n", - (unsigned)riotboot_hdr->magic_number); - printf("Image Version: 0x%08x\n", (unsigned)riotboot_hdr->version); - printf("Image start address: 0x%08x\n", (unsigned)riotboot_hdr->start_addr); - printf("Header chksum: 0x%08x\n", (unsigned)riotboot_hdr->chksum); + uint32_t magic = riotboot_hdr_get_magic_number(riotboot_hdr); + printf("Image magic_number: 0x%08x\n", (unsigned)magic); + printf("Image Version: 0x%08x\n", (unsigned)riotboot_hdr_get_version(riotboot_hdr)); + printf("Image start address: 0x%08x\n", (unsigned)riotboot_hdr_get_start_addr(riotboot_hdr)); + printf("Header chksum: 0x%08x\n", (unsigned)riotboot_hdr_get_checksum(riotboot_hdr)); + if (magic == RIOTBOOT_MAGIC_V2) { + printf("Image flags: 0x%08x\n", (unsigned)riotboot_hdr_get_flags(riotboot_hdr)); + } printf("\n"); } int riotboot_hdr_validate(const riotboot_hdr_t *riotboot_hdr) { - if (riotboot_hdr->magic_number != RIOTBOOT_MAGIC) { + uint32_t magic = riotboot_hdr_get_magic_number(riotboot_hdr); + if (magic != RIOTBOOT_MAGIC_V1 && magic != RIOTBOOT_MAGIC_V2) { LOG_DEBUG("%s: riotboot_hdr magic number invalid\n", __func__); return -1; } int res = riotboot_hdr_checksum(riotboot_hdr) == - riotboot_hdr->chksum ? 0 : -1; + riotboot_hdr_get_checksum(riotboot_hdr) ? 0 : -1; if (res) { LOG_DEBUG("%s: riotboot_hdr checksum invalid\n", __func__); @@ -70,7 +149,63 @@ int riotboot_hdr_validate(const riotboot_hdr_t *riotboot_hdr) uint32_t riotboot_hdr_checksum(const riotboot_hdr_t *riotboot_hdr) { - return fletcher32((uint16_t *)riotboot_hdr, offsetof(riotboot_hdr_t, - chksum) / + return fletcher32((uint16_t *)riotboot_hdr, + ((uintptr_t)_checksum((riotboot_hdr_t *)riotboot_hdr) - + (uintptr_t)riotboot_hdr) / sizeof(uint16_t)); } + +bool riotboot_hdr_is_v2(const riotboot_hdr_t *riotboot_hdr) +{ + return (riotboot_hdr_get_magic_number(riotboot_hdr) == RIOTBOOT_MAGIC_V2); +} + +unsigned riotboot_hdr_get_boot_count(const riotboot_hdr_t *riotboot_hdr) +{ + if (riotboot_hdr_is_v2(riotboot_hdr)) { + unsigned count = (riotboot_hdr->v2.flags & RIOTBOOT_HDR_IMAGE_BOOT_COUNT_MASK) >> + RIOTBOOT_HDR_IMAGE_BOOT_COUNT_SHIFT; + /* count trailing zeros */ + return !count ? 4 : __builtin_ctz(count); + } + return 0; +} + +void riotboot_hdr_set_boot_count(riotboot_hdr_t *riotboot_hdr, + unsigned boot_count) +{ + if (riotboot_hdr_is_v2(riotboot_hdr)) { + riotboot_hdr->v2.flags &= ~RIOTBOOT_HDR_IMAGE_BOOT_COUNT_MASK; + riotboot_hdr->v2.flags |= (((0x0f << boot_count) & 0x0f) + << RIOTBOOT_HDR_IMAGE_BOOT_COUNT_SHIFT); + } +} + +int riotboot_hdr_set_img_state(riotboot_hdr_t *riotboot_hdr, + riotboot_hdr_img_state_t state) +{ + if (riotboot_hdr_is_v2(riotboot_hdr)) { + int current = riotboot_hdr_get_img_state(riotboot_hdr); + if (current == RIOTBOOT_HDR_IMG_STATE_DEACTIVATED && + state != RIOTBOOT_HDR_IMG_STATE_ACTIVATED) { + return -1; + } + if (current == RIOTBOOT_HDR_IMG_STATE_ACTIVATED && + (state != RIOTBOOT_HDR_IMG_STATE_CONFIRMED && + state != RIOTBOOT_HDR_IMG_STATE_DISMISSED)) { + return -2; + } + riotboot_hdr->v2.flags &= ~RIOTBOOT_HDR_IMAGE_STATE_MASK; + riotboot_hdr->v2.flags |= state << RIOTBOOT_HDR_IMAGE_STATE_SHIFT; + } + return 0; +} + +int riotboot_hdr_get_img_state(const riotboot_hdr_t *riotboot_hdr) +{ + if (riotboot_hdr_is_v2(riotboot_hdr)) { + return (riotboot_hdr->v2.flags & RIOTBOOT_HDR_IMAGE_STATE_MASK) + >> RIOTBOOT_HDR_IMAGE_STATE_SHIFT; + } + return RIOTBOOT_HDR_IMG_STATE_CONFIRMED; +} diff --git a/sys/riotboot/slot.c b/sys/riotboot/slot.c index ce1cb066fd3f..de7aa45902ba 100644 --- a/sys/riotboot/slot.c +++ b/sys/riotboot/slot.c @@ -28,6 +28,9 @@ #include "container.h" #include "cpu.h" +#ifdef MODULE_PERIPH_FLASHPAGE +# include "periph/flashpage.h" +#endif #include "riotboot/slot.h" #include "riotboot/hdr.h" @@ -49,7 +52,7 @@ const unsigned riotboot_slot_numof = ARRAY_SIZE(riotboot_slots); static void _riotboot_slot_jump_to_image(const riotboot_hdr_t *hdr) { - cpu_jump_to_image(hdr->start_addr); + cpu_jump_to_image(riotboot_hdr_get_start_addr(hdr)); } int riotboot_slot_current(void) @@ -58,7 +61,7 @@ int riotboot_slot_current(void) for (unsigned i = 0; i < riotboot_slot_numof; i++) { const riotboot_hdr_t *hdr = riotboot_slot_get_hdr(i); - if (base_addr == hdr->start_addr) { + if (base_addr == riotboot_hdr_get_start_addr(hdr)) { return i; } } @@ -78,7 +81,7 @@ void riotboot_slot_jump(unsigned slot) uint32_t riotboot_slot_get_image_startaddr(unsigned slot) { - return riotboot_slot_get_hdr(slot)->start_addr; + return riotboot_hdr_get_start_addr(riotboot_slot_get_hdr(slot)); } void riotboot_slot_dump_addrs(void) @@ -88,7 +91,7 @@ void riotboot_slot_dump_addrs(void) printf("slot %u: metadata: %p image: 0x%08" PRIx32 "\n", slot, hdr, - hdr->start_addr); + riotboot_hdr_get_start_addr(hdr)); } } @@ -103,3 +106,35 @@ size_t riotboot_slot_offset(unsigned slot) { return (size_t)riotboot_slot_get_hdr(slot) - CPU_FLASH_BASE; } + +void riotboot_slot_confirm(void) +{ + int slot = riotboot_slot_current(); + riotboot_hdr_t riot_hdr = *riotboot_slot_get_hdr(slot); + if (riotboot_hdr_is_v2(&riot_hdr)) { + riotboot_hdr_set_img_state(&riot_hdr, RIOTBOOT_HDR_IMG_STATE_CONFIRMED); + if (memcmp(&riot_hdr, riotboot_slot_get_hdr(slot), sizeof(riot_hdr.v2))) { +#ifdef MODULE_PERIPH_FLASHPAGE + flashpage_write(flashpage_addr(flashpage_page(riotboot_slot_get_hdr(slot))), + &riot_hdr.v2, sizeof(riot_hdr.v2)); +#endif + } + } +} + +void riotboot_slot_activate_other(bool active) +{ + int slot = riotboot_slot_other(); + riotboot_hdr_t riot_hdr = *riotboot_slot_get_hdr(slot); + if (riotboot_hdr_is_v2(&riot_hdr)) { + riotboot_hdr_set_img_state(&riot_hdr, + active ? RIOTBOOT_HDR_IMG_STATE_ACTIVATED + : RIOTBOOT_HDR_IMG_STATE_DEACTIVATED); + if (memcmp(&riot_hdr, riotboot_slot_get_hdr(slot), sizeof(riot_hdr.v2))) { +#ifdef MODULE_PERIPH_FLASHPAGE + flashpage_write(flashpage_addr(flashpage_page(riotboot_slot_get_hdr(slot))), + &riot_hdr.v2, sizeof(riot_hdr.v2)); +#endif + } + } +} diff --git a/sys/riotboot/wdt.c b/sys/riotboot/wdt.c new file mode 100644 index 000000000000..9ebe5d19fe1e --- /dev/null +++ b/sys/riotboot/wdt.c @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2025 ML!PA Consulting GmbH + */ + +/** + * @ingroup sys_riotboot_wdt + * @{ + * + * @file + * @brief RIOT bootloader watchdog timer + * + * @author Fabian Hüßler + * @} + */ + +#include "periph/wdt.h" + +void riotboot_wdt_start(unsigned msec) +{ + if (WDT_HAS_INIT) { + wdt_init(); + } + wdt_setup_reboot(0, msec); + wdt_start(); +} + +void riotboot_wdt_stop(void) +{ + wdt_stop(); +} diff --git a/sys/shell/cmds/sys.c b/sys/shell/cmds/sys.c index 0c86ffa692be..3ae1437cc768 100644 --- a/sys/shell/cmds/sys.c +++ b/sys/shell/cmds/sys.c @@ -69,7 +69,8 @@ static int _version_handler(int argc, char **argv) int slot = riotboot_slot_current(); if (slot >= 0) { const riotboot_hdr_t *hdr = riotboot_slot_get_hdr(slot); - printf("%s v%"PRIu32", slot %u\n", RIOT_APPLICATION, hdr->version, slot); + printf("%s v%"PRIu32", slot %u\n", RIOT_APPLICATION, + riotboot_hdr_get_version(hdr), slot); } #endif #ifdef CONFIG_RIOT_VERSION_EXTRA diff --git a/sys/suit/storage/flashwrite.c b/sys/suit/storage/flashwrite.c index 1d4ce8ebe8fd..062feb4c85e5 100644 --- a/sys/suit/storage/flashwrite.c +++ b/sys/suit/storage/flashwrite.c @@ -101,7 +101,11 @@ static int _flashwrite_install(suit_storage_t *storage, (void)manifest; suit_storage_flashwrite_t *fw = _get_fw(storage); - return riotboot_flashwrite_finish(&fw->writer); + int fin = riotboot_flashwrite_finish(&fw->writer); + if (fin < 0) { + return SUIT_ERR_STORAGE; + } + return fin; } static int _flashwrite_read(suit_storage_t *storage, uint8_t *buf, @@ -109,17 +113,16 @@ static int _flashwrite_read(suit_storage_t *storage, uint8_t *buf, { suit_storage_flashwrite_t *fw = _get_fw(storage); - static const char _prefix[] = "RIOT"; - static const size_t _prefix_len = sizeof(_prefix) - 1; + const uint32_t magic = RIOTBOOT_MAGIC; int target_slot = riotboot_slot_other(); size_t slot_size = riotboot_slot_size(target_slot); - /* Insert the "RIOT" magic number */ - if (offset < (_prefix_len)) { - size_t prefix_to_copy = _prefix_len - offset; - memcpy(buf, _prefix + offset, prefix_to_copy); + /* Insert the magic number */ + if (offset < (sizeof(magic))) { + size_t prefix_to_copy = sizeof(magic) - offset; + memcpy(buf, &magic + offset, prefix_to_copy); len -= prefix_to_copy; - offset = _prefix_len; + offset = sizeof(magic); buf += prefix_to_copy; } @@ -130,7 +133,7 @@ static int _flashwrite_read(suit_storage_t *storage, uint8_t *buf, * contains the magic number already copied above. */ if (offset < RIOTBOOT_FLASHPAGE_BUFFER_SIZE) { const size_t chunk_remaining = - RIOTBOOT_FLASHPAGE_BUFFER_SIZE - _prefix_len; + RIOTBOOT_FLASHPAGE_BUFFER_SIZE - sizeof(magic); /* How much of the first page must be copied */ size_t firstpage_to_copy = len > chunk_remaining ? (chunk_remaining) : len; @@ -196,8 +199,8 @@ static int _flashwrite_get_seq_no(const suit_storage_t *storage, /* skip slot if metadata broken */ continue; } - if (!valid || riot_hdr->version > max_seq_no) { - max_seq_no = riot_hdr->version; + if (!valid || riotboot_hdr_get_version(riot_hdr) > max_seq_no) { + max_seq_no = riotboot_hdr_get_version(riot_hdr); valid = true; } } @@ -216,7 +219,7 @@ static int _flashwrite_set_seq_no(suit_storage_t *storage, int target_slot = riotboot_slot_other(); const riotboot_hdr_t *hdr = riotboot_slot_get_hdr(target_slot); - if (hdr->version == seq_no) { + if (riotboot_hdr_get_version(hdr) == seq_no) { return SUIT_OK; } diff --git a/sys/suit/transport/worker.c b/sys/suit/transport/worker.c index a56503167841..30b8777fef0a 100644 --- a/sys/suit/transport/worker.c +++ b/sys/suit/transport/worker.c @@ -140,9 +140,23 @@ int suit_handle_manifest_buf(const uint8_t *buffer, size_t size) riotboot_hdr_print(hdr); ztimer_sleep(ZTIMER_MSEC, 1 * MS_PER_SEC); - return riotboot_hdr_validate(hdr); + if ((res = riotboot_hdr_validate(hdr)) < 0) { + LOG_INFO("suit_worker: target slot header invalid\n"); + return -EINVAL; + } + if (riotboot_hdr_get_flags(hdr) != UINT32_MAX) { + LOG_INFO("suit_worker: target slot header flags invalid\n"); + return -EINVAL; + } + if (IS_USED(MODULE_RIOTBOOT_HDR_AUTO_ACTIVATE)) { + LOG_INFO("suit_worker: activating target slot\n"); + riotboot_slot_activate_other(true); + } + else { + LOG_INFO("suit_worker: deactivating target slot\n"); + riotboot_slot_activate_other(false); + } #endif - return res; } diff --git a/tests/riotboot_hdr/main.c b/tests/riotboot_hdr/main.c index 109dff56bbe7..53f70049320d 100644 --- a/tests/riotboot_hdr/main.c +++ b/tests/riotboot_hdr/main.c @@ -22,24 +22,30 @@ #include "embUnit.h" const riotboot_hdr_t riotboot_hdr_good = { - .magic_number = RIOTBOOT_MAGIC, - .version = 0x5bd19bff, - .start_addr = 0x00001100, - .chksum = 0x02eda672 + .v1 = { + .magic_number = RIOTBOOT_MAGIC_V1, + .version = 0x5bd19bff, + .start_addr = 0x00001100, + .chksum = 0x02eda672 + } }; const riotboot_hdr_t riotboot_hdr_bad_magic = { - .magic_number = 0x12345678, - .version = 0x5bd19bff, - .start_addr = 0x00001100, - .chksum = 0x02eda672 + .v1 = { + .magic_number = 0x12345678, + .version = 0x5bd19bff, + .start_addr = 0x00001100, + .chksum = 0x02eda672 + } }; const riotboot_hdr_t riotboot_hdr_bad_chksum = { - .magic_number = RIOTBOOT_MAGIC, - .version = 0x5bd19bff, - .start_addr = 0x00001100, - .chksum = 0x02000000 + .v1 = { + .magic_number = RIOTBOOT_MAGIC_V1, + .version = 0x5bd19bff, + .start_addr = 0x00001100, + .chksum = 0x02000000 + } }; static void test_riotboot_hdr_01(void)