diff --git a/.gitignore b/.gitignore index cf6614e..eee521e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .vscode/ build*/ +mcuboot/ diff --git a/boards/overlays/nucleo_g431rb.overlay b/boards/overlays/nucleo_g431rb.overlay index e69de29..f992f67 100644 --- a/boards/overlays/nucleo_g431rb.overlay +++ b/boards/overlays/nucleo_g431rb.overlay @@ -0,0 +1,29 @@ +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x00020000>; /* 128KB */ + read-only; + }; + + slot0_partition: partition@20000 { + label = "image-0"; + reg = <0x00020000 0x00060000>; /* 384KB */ + }; + + slot1_partition: partition@80000 { + label = "image-1"; + reg = <0x00080000 0x00060000>; /* 384KB */ + }; + }; +}; + +/ { + chosen { + zephyr,code-partition = &boot_partition; + }; +}; \ No newline at end of file diff --git a/drivers/bootloader/CMakeLists.txt b/drivers/bootloader/CMakeLists.txt new file mode 100644 index 0000000..373dfb0 --- /dev/null +++ b/drivers/bootloader/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.20.0) + +set(MCUBOOT_DIR ${CMAKE_CURRENT_LIST_DIR}/../../bootloader/mcuboot/boot/zephyr) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(mcuboot_bootloader) + +add_subdirectory(${MCUBOOT_DIR} mcuboot) diff --git a/drivers/bootloader/prj.conf b/drivers/bootloader/prj.conf new file mode 100644 index 0000000..df9f307 --- /dev/null +++ b/drivers/bootloader/prj.conf @@ -0,0 +1,36 @@ +CONFIG_SIZE_OPTIMIZATIONS=y + +# Size required - inside /bootloader/mcuboot/boot/zephyr/prj.conf +# CONFIG_MAIN_STACK_SIZE=10240 + +# min tested stack size that works with/without debugging +CONFIG_MAIN_STACK_SIZE=2048 + +# Signature of bootloader +CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y +CONFIG_BOOT_SIGNATURE_KEY_FILE="root-ec-p256.pem" + +# Flash support +CONFIG_FLASH=y + +# Boot mode +CONFIG_BOOT_SWAP_USING_MOVE=y + +# Reduce features to fit in 12KB +CONFIG_BOOT_BOOTSTRAP=n +CONFIG_MCUBOOT_SERIAL=n + +# Disable for debugging +# CONFIG_GPIO=n +# CONFIG_CONSOLE=n +# CONFIG_UART_CONSOLE=n +# CONFIG_SERIAL=n +# CONFIG_LOG=n + +# Enable for debugging +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y +CONFIG_SERIAL=y +CONFIG_LOG=y +CONFIG_LOG_MODE_MINIMAL=y +CONFIG_BOOT_BANNER=y \ No newline at end of file diff --git a/drivers/mcuboot_demo/image1/CMakeLists.txt b/drivers/mcuboot_demo/image1/CMakeLists.txt new file mode 100644 index 0000000..220f6ef --- /dev/null +++ b/drivers/mcuboot_demo/image1/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2025 The FINCH CubeSat Project Flight Software Contributors +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(obc) + +target_sources(app PRIVATE src/blinky1.c) +# target_sources(app PRIVATE src/blinky2.c) diff --git a/drivers/mcuboot_demo/image1/app.overlay b/drivers/mcuboot_demo/image1/app.overlay new file mode 100644 index 0000000..f992f67 --- /dev/null +++ b/drivers/mcuboot_demo/image1/app.overlay @@ -0,0 +1,29 @@ +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 0x00020000>; /* 128KB */ + read-only; + }; + + slot0_partition: partition@20000 { + label = "image-0"; + reg = <0x00020000 0x00060000>; /* 384KB */ + }; + + slot1_partition: partition@80000 { + label = "image-1"; + reg = <0x00080000 0x00060000>; /* 384KB */ + }; + }; +}; + +/ { + chosen { + zephyr,code-partition = &boot_partition; + }; +}; \ No newline at end of file diff --git a/drivers/mcuboot_demo/image1/prj.conf b/drivers/mcuboot_demo/image1/prj.conf new file mode 100644 index 0000000..7d97fed --- /dev/null +++ b/drivers/mcuboot_demo/image1/prj.conf @@ -0,0 +1,27 @@ +# Enable STREAM_FLASH first (required dependency for IMG_MANAGER) +CONFIG_STREAM_FLASH=y + +# Enable IMG_MANAGER (parent config) +CONFIG_IMG_MANAGER=y + +# Then enable MCUboot variant (this is selected automatically as default, but be explicit) +CONFIG_MCUBOOT_IMG_MANAGER=y + +# Required dependencies +CONFIG_BOOTLOADER_MCUBOOT=y +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y + +# Reboot support +CONFIG_REBOOT=y + +# Signature types: none, rsa, ecdsa +# CONFIG_MCUBOOT_GENERATE_UNSIGNED_IMAGE=y +# CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="/bootloader/mcuboot/root-rsa-2048.pem" +CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="/bootloader/mcuboot/root-ec-p256.pem" + +# Logging (optional) +CONFIG_LOG=y +CONFIG_PRINTK=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/drivers/mcuboot_demo/image1/src/blinky1.c b/drivers/mcuboot_demo/image1/src/blinky1.c new file mode 100644 index 0000000..a5cbda2 --- /dev/null +++ b/drivers/mcuboot_demo/image1/src/blinky1.c @@ -0,0 +1,57 @@ +/* Copyright (c) 2025 The FINCH CubeSat Project Flight Software Contributors*/ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include +#include + +#define LED_NODE DT_ALIAS(led0) +static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET_OR(LED_NODE, gpios, {0}); + +void main(void) +{ + // 1. Initialization + // If this image just booted successfully, mark it as "Good" so we don't revert. + if (!boot_is_img_confirmed()) { + printk("Confirming image...\n"); + boot_write_img_confirmed(); + } + + gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); + + printk("Running FAST blinky (Image 1).\n"); + printk("System will auto-swap in approx 10 seconds...\n"); + + int blink_count = 0; + int trigger_threshold = 50; // 50 * 200ms = 10 seconds + + // 2. The Main Loop + while (1) { + gpio_pin_toggle_dt(&led); + k_msleep(200); // Fast blink + + blink_count++; + + // 3. The Automatic Trigger Check + if (blink_count == trigger_threshold) { + printk("Time's up! Requesting upgrade to slow blinky...\n"); + + // A. Request the swap (Test Mode) + // 'BOOT_UPGRADE_TEST' ensures that if the new image crashes, + // the bootloader will swap BACK to this one automatically. + int ret = boot_request_upgrade(BOOT_UPGRADE_TEST); + + if (ret == 0) { + printk("Upgrade requested successfully. Rebooting now!\n"); + // Give the serial port a moment to flush the text + k_msleep(100); + + // B. Software Reset (Simulates the Black Button) + sys_reboot(SYS_REBOOT_COLD); + } else { + printk("Failed to request upgrade: %d\n", ret); + } + } + } +} diff --git a/drivers/mcuboot_demo/image2/CMakeLists.txt b/drivers/mcuboot_demo/image2/CMakeLists.txt new file mode 100644 index 0000000..6ac0b93 --- /dev/null +++ b/drivers/mcuboot_demo/image2/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2025 The FINCH CubeSat Project Flight Software Contributors +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(obc) + +# target_sources(app PRIVATE src/blinky1.c) +target_sources(app PRIVATE src/blinky2.c) diff --git a/drivers/mcuboot_demo/image2/prj.conf b/drivers/mcuboot_demo/image2/prj.conf new file mode 100644 index 0000000..7d97fed --- /dev/null +++ b/drivers/mcuboot_demo/image2/prj.conf @@ -0,0 +1,27 @@ +# Enable STREAM_FLASH first (required dependency for IMG_MANAGER) +CONFIG_STREAM_FLASH=y + +# Enable IMG_MANAGER (parent config) +CONFIG_IMG_MANAGER=y + +# Then enable MCUboot variant (this is selected automatically as default, but be explicit) +CONFIG_MCUBOOT_IMG_MANAGER=y + +# Required dependencies +CONFIG_BOOTLOADER_MCUBOOT=y +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y + +# Reboot support +CONFIG_REBOOT=y + +# Signature types: none, rsa, ecdsa +# CONFIG_MCUBOOT_GENERATE_UNSIGNED_IMAGE=y +# CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="/bootloader/mcuboot/root-rsa-2048.pem" +CONFIG_MCUBOOT_SIGNATURE_KEY_FILE="/bootloader/mcuboot/root-ec-p256.pem" + +# Logging (optional) +CONFIG_LOG=y +CONFIG_PRINTK=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y diff --git a/drivers/mcuboot_demo/image2/src/blinky2.c b/drivers/mcuboot_demo/image2/src/blinky2.c new file mode 100644 index 0000000..a20a420 --- /dev/null +++ b/drivers/mcuboot_demo/image2/src/blinky2.c @@ -0,0 +1,51 @@ +/* Copyright (c) 2025 The FINCH CubeSat Project Flight Software Contributors*/ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include + +#define LED_NODE DT_ALIAS(led0) +#define SW0_NODE DT_ALIAS(sw0) + +static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET_OR(LED_NODE, gpios, {0}); +static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0}); +static struct gpio_callback button_cb; + +void button_pressed(const struct device *dev, struct gpio_callback *cb, uint32_t pins) +{ + printk("Button pressed! Requesting upgrade to fast blinky...\n"); + + int ret = boot_request_upgrade(BOOT_UPGRADE_TEST); + if (ret) { + printk("Failed to request upgrade: %d\n", ret); + return; + } + + printk("Upgrade requested, rebooting in next reset...\n"); + + // could command a system reboot on button press or hold off until next reset + + // k_msleep(100); + // sys_reboot(SYS_REBOOT_COLD); +} + +void main(void) +{ + if (!boot_is_img_confirmed()) { + printk("Confirming image...\n"); + boot_write_img_confirmed(); + } + + gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); + + gpio_pin_configure_dt(&button, GPIO_INPUT); + gpio_pin_interrupt_configure_dt(&button, GPIO_INT_EDGE_TO_ACTIVE); + gpio_init_callback(&button_cb, button_pressed, BIT(button.pin)); + gpio_add_callback(button.port, &button_cb); + + printk("Running slow blinky. Press button to swap.\n"); + + while (1) { + gpio_pin_toggle_dt(&led); + k_msleep(1000); // Slow blink: 1 sec + } +} diff --git a/scripts/build_mcu.sh b/scripts/build_mcu.sh new file mode 100755 index 0000000..2a7d0cd --- /dev/null +++ b/scripts/build_mcu.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +west build -p always -b nucleo_g431rb -d build_mcuboot /bootloader/mcuboot/boot/zephyr -- \ + -DCONF_FILE=/workspace/drivers/bootloader/prj.conf + +# blinky1.c in mcuboot_demo CMake +west build -p always -b nucleo_g431rb -d build_fast drivers/mcuboot_demo/image1 + +# blinky2.c in mcuboot_demo CMake +west build -p always -b nucleo_g431rb -d build_slow drivers/mcuboot_demo/image2 + +# build own key for production - key used for signing images +# python3 /bootloader/mcuboot/scripts/imgtool.py keygen -k production-key-ec-p256.pem -t ecdsa-p256 diff --git a/scripts/flash_mcuboot.sh b/scripts/flash_mcuboot.sh new file mode 100755 index 0000000..3ed7dee --- /dev/null +++ b/scripts/flash_mcuboot.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +echo "=== Flashing MCUboot Setup ===" + +echo "1. Flashing MCUboot bootloader..." +pyocd flash -t stm32g431rbtx build_mcuboot/zephyr/zephyr.hex + +echo "2. Flashing fast_blinky to slot 0 (0x08008800)..." +pyocd flash -t stm32g431rbtx --base-address 0x08008800 build_fast/zephyr/zephyr.signed.bin + +echo "3. Flashing slow_blinky to slot 1 (0x08014800)..." +pyocd flash -t stm32g431rbtx --base-address 0x08014800 build_slow/zephyr/zephyr.signed.bin + +echo "=== Done! Reset board to boot ===" diff --git a/west.yml b/west.yml index 67c15cf..f1a551e 100644 --- a/west.yml +++ b/west.yml @@ -14,3 +14,7 @@ manifest: url: https://github.com/libcsp/libcsp.git revision: f8c366e5dfd9879990747d815ff5b2c2dda4dfe3 path: modules/lib/libcsp + - name: mcuboot + url: https://github.com/mcu-tools/mcuboot + revision: v2.1.0 + path: bootloader/mcuboot