Add bootloader to CI
This commit is contained in:
@ -21,8 +21,3 @@ log = "0.4"
|
||||
env_logger = "0.9"
|
||||
rand = "0.8"
|
||||
futures = { version = "0.3", features = ["executor"] }
|
||||
|
||||
[features]
|
||||
write-4 = []
|
||||
write-8 = []
|
||||
invert-erase = []
|
||||
|
@ -1,5 +1,7 @@
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(generic_const_exprs)]
|
||||
#![allow(incomplete_features)]
|
||||
#![no_std]
|
||||
///! embassy-boot is a bootloader and firmware updater for embedded devices with flash
|
||||
///! storage implemented using embedded-storage
|
||||
@ -17,24 +19,9 @@ mod fmt;
|
||||
use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash};
|
||||
use embedded_storage_async::nor_flash::AsyncNorFlash;
|
||||
|
||||
#[cfg(not(any(feature = "write-4", feature = "write-8",)))]
|
||||
compile_error!("No write size/alignment specified. Must specify exactly one of the following features: write-4, write-8");
|
||||
|
||||
const BOOT_MAGIC: u8 = 0xD0;
|
||||
const SWAP_MAGIC: u8 = 0xF0;
|
||||
|
||||
#[cfg(feature = "write-4")]
|
||||
const WRITE_SIZE: usize = 4;
|
||||
|
||||
#[cfg(feature = "write-8")]
|
||||
const WRITE_SIZE: usize = 8;
|
||||
|
||||
#[cfg(feature = "invert-erase")]
|
||||
const ERASE_VALUE: u8 = 0x00;
|
||||
|
||||
#[cfg(not(feature = "invert-erase"))]
|
||||
const ERASE_VALUE: u8 = 0xFF;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Partition {
|
||||
@ -96,7 +83,7 @@ pub trait FlashProvider {
|
||||
|
||||
/// BootLoader works with any flash implementing embedded_storage and can also work with
|
||||
/// different page sizes and flash write sizes.
|
||||
pub struct BootLoader<const PAGE_SIZE: usize> {
|
||||
pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8> {
|
||||
// Page with current state of bootloader. The state partition has the following format:
|
||||
// | Range | Description |
|
||||
// | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. |
|
||||
@ -108,7 +95,9 @@ pub struct BootLoader<const PAGE_SIZE: usize> {
|
||||
dfu: Partition,
|
||||
}
|
||||
|
||||
impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
|
||||
impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8>
|
||||
BootLoader<PAGE_SIZE, WRITE_SIZE, ERASE_VALUE>
|
||||
{
|
||||
pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
|
||||
assert_eq!(active.len() % PAGE_SIZE, 0);
|
||||
assert_eq!(dfu.len() % PAGE_SIZE, 0);
|
||||
@ -352,8 +341,6 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
|
||||
self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?;
|
||||
}
|
||||
|
||||
info!("DONE COPYING");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -379,7 +366,6 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
|
||||
let flash = p.flash();
|
||||
flash.read(self.state.from as u32, &mut magic)?;
|
||||
|
||||
info!("Read magic: {:x}", magic);
|
||||
if magic == [SWAP_MAGIC; WRITE_SIZE] {
|
||||
Ok(State::Swap)
|
||||
} else {
|
||||
@ -451,13 +437,9 @@ pub struct FirmwareUpdater {
|
||||
dfu: Partition,
|
||||
}
|
||||
|
||||
#[cfg(feature = "write-4")]
|
||||
#[repr(align(4))]
|
||||
pub struct Aligned([u8; 4]);
|
||||
|
||||
#[cfg(feature = "write-8")]
|
||||
#[repr(align(8))]
|
||||
pub struct Aligned([u8; 8]);
|
||||
// NOTE: Aligned to the largest write size supported by flash
|
||||
#[repr(align(32))]
|
||||
pub struct Aligned<const N: usize>([u8; N]);
|
||||
|
||||
impl Default for FirmwareUpdater {
|
||||
fn default() -> Self {
|
||||
@ -480,6 +462,9 @@ impl Default for FirmwareUpdater {
|
||||
&__bootloader_state_end as *const u32 as usize,
|
||||
)
|
||||
};
|
||||
|
||||
trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
|
||||
trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
|
||||
FirmwareUpdater::new(dfu, state)
|
||||
}
|
||||
}
|
||||
@ -496,14 +481,20 @@ impl FirmwareUpdater {
|
||||
|
||||
/// Instruct bootloader that DFU should commence at next boot.
|
||||
/// Must be provided with an aligned buffer to use for reading and writing magic;
|
||||
pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> {
|
||||
let mut aligned = Aligned([0; WRITE_SIZE]);
|
||||
pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error>
|
||||
where
|
||||
[(); F::WRITE_SIZE]:,
|
||||
{
|
||||
let mut aligned = Aligned([0; { F::WRITE_SIZE }]);
|
||||
self.set_magic(&mut aligned.0, SWAP_MAGIC, flash).await
|
||||
}
|
||||
|
||||
/// Mark firmware boot successfully
|
||||
pub async fn mark_booted<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> {
|
||||
let mut aligned = Aligned([0; WRITE_SIZE]);
|
||||
pub async fn mark_booted<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error>
|
||||
where
|
||||
[(); F::WRITE_SIZE]:,
|
||||
{
|
||||
let mut aligned = Aligned([0; { F::WRITE_SIZE }]);
|
||||
self.set_magic(&mut aligned.0, BOOT_MAGIC, flash).await
|
||||
}
|
||||
|
||||
@ -626,7 +617,7 @@ mod tests {
|
||||
flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]);
|
||||
let mut flash = SingleFlashProvider::new(&mut flash);
|
||||
|
||||
let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE);
|
||||
let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE);
|
||||
|
||||
assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap());
|
||||
}
|
||||
@ -643,7 +634,7 @@ mod tests {
|
||||
flash.0[i] = original[i - ACTIVE.from];
|
||||
}
|
||||
|
||||
let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE);
|
||||
let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE);
|
||||
let mut updater = FirmwareUpdater::new(DFU, STATE);
|
||||
let mut offset = 0;
|
||||
for chunk in update.chunks(4096) {
|
||||
|
@ -13,7 +13,7 @@ defmt-rtt = { version = "0.3", optional = true }
|
||||
|
||||
embassy = { path = "../../embassy", default-features = false }
|
||||
embassy-nrf = { path = "../../embassy-nrf", default-features = false, features = ["nightly"] }
|
||||
embassy-boot = { path = "../boot", default-features = false, features = ["write-4"] }
|
||||
embassy-boot = { path = "../boot", default-features = false }
|
||||
cortex-m = { version = "0.7" }
|
||||
cortex-m-rt = { version = "0.7" }
|
||||
embedded-storage = "0.3.0"
|
||||
|
@ -13,7 +13,7 @@ use embassy_nrf::{
|
||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
||||
|
||||
pub struct BootLoader {
|
||||
boot: embassy_boot::BootLoader<PAGE_SIZE>,
|
||||
boot: embassy_boot::BootLoader<PAGE_SIZE, 4, 0xFF>,
|
||||
}
|
||||
|
||||
impl BootLoader {
|
||||
|
@ -46,8 +46,5 @@ unsafe fn DefaultHandler(_: i16) -> ! {
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
cortex_m::asm::udf();
|
||||
core::hint::unreachable_unchecked();
|
||||
}
|
||||
cortex_m::asm::udf();
|
||||
}
|
||||
|
@ -27,10 +27,6 @@ defmt = [
|
||||
"embassy-stm32/defmt",
|
||||
]
|
||||
debug = ["defmt-rtt"]
|
||||
flash-2k = ["embassy-boot/write-8"]
|
||||
flash-128 = ["embassy-boot/write-4"]
|
||||
flash-256 = ["embassy-boot/write-4"]
|
||||
invert-erase = ["embassy-boot/invert-erase"]
|
||||
thumbv6 = []
|
||||
|
||||
[profile.dev]
|
||||
|
@ -8,11 +8,11 @@ MEMORY
|
||||
RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 16K
|
||||
}
|
||||
|
||||
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE);
|
||||
__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE);
|
||||
__bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH);
|
||||
__bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH);
|
||||
|
||||
__bootloader_active_start = ORIGIN(ACTIVE);
|
||||
__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE);
|
||||
__bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH);
|
||||
__bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH);
|
||||
|
||||
__bootloader_dfu_start = ORIGIN(DFU);
|
||||
__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU);
|
||||
__bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH);
|
||||
__bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH);
|
||||
|
@ -5,12 +5,13 @@
|
||||
mod fmt;
|
||||
|
||||
pub use embassy_boot::{FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider, State};
|
||||
use embassy_stm32::flash::{ERASE_SIZE, ERASE_VALUE, WRITE_SIZE};
|
||||
|
||||
pub struct BootLoader<const PAGE_SIZE: usize> {
|
||||
boot: embassy_boot::BootLoader<PAGE_SIZE>,
|
||||
pub struct BootLoader {
|
||||
boot: embassy_boot::BootLoader<ERASE_SIZE, WRITE_SIZE, ERASE_VALUE>,
|
||||
}
|
||||
|
||||
impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
|
||||
impl BootLoader {
|
||||
/// Create a new bootloader instance using parameters from linker script
|
||||
pub fn default() -> Self {
|
||||
extern "C" {
|
||||
@ -65,6 +66,7 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> {
|
||||
|
||||
pub unsafe fn load(&mut self, start: usize) -> ! {
|
||||
trace!("Loading app at 0x{:x}", start);
|
||||
#[allow(unused_mut)]
|
||||
let mut p = cortex_m::Peripherals::steal();
|
||||
#[cfg(not(feature = "thumbv6"))]
|
||||
p.SCB.invalidate_icache();
|
||||
|
@ -9,9 +9,6 @@ use defmt_rtt as _;
|
||||
use embassy_boot_stm32::*;
|
||||
use embassy_stm32::flash::Flash;
|
||||
|
||||
#[cfg(not(any(feature = "flash-2k", feature = "flash-256", feature = "flash-128")))]
|
||||
compile_error!("No flash size specified. Must specify exactly one of the following features: flash-2k, flash-256, flash-128");
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let p = embassy_stm32::init(Default::default());
|
||||
@ -24,15 +21,7 @@ fn main() -> ! {
|
||||
}
|
||||
*/
|
||||
|
||||
#[cfg(feature = "flash-2k")]
|
||||
let mut bl: BootLoader<2048> = BootLoader::default();
|
||||
|
||||
#[cfg(feature = "flash-256")]
|
||||
let mut bl: BootLoader<256> = BootLoader::default();
|
||||
|
||||
#[cfg(feature = "flash-128")]
|
||||
let mut bl: BootLoader<128> = BootLoader::default();
|
||||
|
||||
let mut bl = BootLoader::default();
|
||||
let mut flash = Flash::unlock(p.FLASH);
|
||||
let start = bl.prepare(&mut SingleFlashProvider::new(&mut flash));
|
||||
core::mem::drop(flash);
|
||||
@ -55,8 +44,5 @@ unsafe fn DefaultHandler(_: i16) -> ! {
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
unsafe {
|
||||
cortex_m::asm::udf();
|
||||
core::hint::unreachable_unchecked();
|
||||
}
|
||||
cortex_m::asm::udf();
|
||||
}
|
||||
|
Reference in New Issue
Block a user