diff options
| author | Ulf Lilleengen <[email protected]> | 2022-04-26 18:33:09 +0200 |
|---|---|---|
| committer | Ulf Lilleengen <[email protected]> | 2022-04-27 15:17:18 +0200 |
| commit | da61611f8f57410a87106961efd24d80e6a8f63e (patch) | |
| tree | 81bf5f96a052be8cc74fa4f513592adf1f4bb1db /embassy-boot | |
| parent | 484e0acc638c27366e19275c32db9c8487ea8fba (diff) | |
Add bootloader to CI
Diffstat (limited to 'embassy-boot')
| -rw-r--r-- | embassy-boot/boot/Cargo.toml | 5 | ||||
| -rw-r--r-- | embassy-boot/boot/src/lib.rs | 57 | ||||
| -rw-r--r-- | embassy-boot/nrf/Cargo.toml | 2 | ||||
| -rw-r--r-- | embassy-boot/nrf/src/lib.rs | 2 | ||||
| -rw-r--r-- | embassy-boot/nrf/src/main.rs | 5 | ||||
| -rw-r--r-- | embassy-boot/stm32/Cargo.toml | 4 | ||||
| -rw-r--r-- | embassy-boot/stm32/memory.x | 12 | ||||
| -rw-r--r-- | embassy-boot/stm32/src/lib.rs | 8 | ||||
| -rw-r--r-- | embassy-boot/stm32/src/main.rs | 18 |
9 files changed, 40 insertions, 73 deletions
diff --git a/embassy-boot/boot/Cargo.toml b/embassy-boot/boot/Cargo.toml index 04deab30b..0a3006ffc 100644 --- a/embassy-boot/boot/Cargo.toml +++ b/embassy-boot/boot/Cargo.toml | |||
| @@ -21,8 +21,3 @@ log = "0.4" | |||
| 21 | env_logger = "0.9" | 21 | env_logger = "0.9" |
| 22 | rand = "0.8" | 22 | rand = "0.8" |
| 23 | futures = { version = "0.3", features = ["executor"] } | 23 | futures = { version = "0.3", features = ["executor"] } |
| 24 | |||
| 25 | [features] | ||
| 26 | write-4 = [] | ||
| 27 | write-8 = [] | ||
| 28 | invert-erase = [] | ||
diff --git a/embassy-boot/boot/src/lib.rs b/embassy-boot/boot/src/lib.rs index 080ea2426..554709250 100644 --- a/embassy-boot/boot/src/lib.rs +++ b/embassy-boot/boot/src/lib.rs | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | #![feature(type_alias_impl_trait)] | 1 | #![feature(type_alias_impl_trait)] |
| 2 | #![feature(generic_associated_types)] | 2 | #![feature(generic_associated_types)] |
| 3 | #![feature(generic_const_exprs)] | ||
| 4 | #![allow(incomplete_features)] | ||
| 3 | #![no_std] | 5 | #![no_std] |
| 4 | ///! embassy-boot is a bootloader and firmware updater for embedded devices with flash | 6 | ///! embassy-boot is a bootloader and firmware updater for embedded devices with flash |
| 5 | ///! storage implemented using embedded-storage | 7 | ///! storage implemented using embedded-storage |
| @@ -17,24 +19,9 @@ mod fmt; | |||
| 17 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; | 19 | use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; |
| 18 | use embedded_storage_async::nor_flash::AsyncNorFlash; | 20 | use embedded_storage_async::nor_flash::AsyncNorFlash; |
| 19 | 21 | ||
| 20 | #[cfg(not(any(feature = "write-4", feature = "write-8",)))] | ||
| 21 | compile_error!("No write size/alignment specified. Must specify exactly one of the following features: write-4, write-8"); | ||
| 22 | |||
| 23 | const BOOT_MAGIC: u8 = 0xD0; | 22 | const BOOT_MAGIC: u8 = 0xD0; |
| 24 | const SWAP_MAGIC: u8 = 0xF0; | 23 | const SWAP_MAGIC: u8 = 0xF0; |
| 25 | 24 | ||
| 26 | #[cfg(feature = "write-4")] | ||
| 27 | const WRITE_SIZE: usize = 4; | ||
| 28 | |||
| 29 | #[cfg(feature = "write-8")] | ||
| 30 | const WRITE_SIZE: usize = 8; | ||
| 31 | |||
| 32 | #[cfg(feature = "invert-erase")] | ||
| 33 | const ERASE_VALUE: u8 = 0x00; | ||
| 34 | |||
| 35 | #[cfg(not(feature = "invert-erase"))] | ||
| 36 | const ERASE_VALUE: u8 = 0xFF; | ||
| 37 | |||
| 38 | #[derive(Copy, Clone, Debug)] | 25 | #[derive(Copy, Clone, Debug)] |
| 39 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 26 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 40 | pub struct Partition { | 27 | pub struct Partition { |
| @@ -96,7 +83,7 @@ pub trait FlashProvider { | |||
| 96 | 83 | ||
| 97 | /// BootLoader works with any flash implementing embedded_storage and can also work with | 84 | /// BootLoader works with any flash implementing embedded_storage and can also work with |
| 98 | /// different page sizes and flash write sizes. | 85 | /// different page sizes and flash write sizes. |
| 99 | pub struct BootLoader<const PAGE_SIZE: usize> { | 86 | pub struct BootLoader<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8> { |
| 100 | // Page with current state of bootloader. The state partition has the following format: | 87 | // Page with current state of bootloader. The state partition has the following format: |
| 101 | // | Range | Description | | 88 | // | Range | Description | |
| 102 | // | 0 - WRITE_SIZE | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | | 89 | // | 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> { | |||
| 108 | dfu: Partition, | 95 | dfu: Partition, |
| 109 | } | 96 | } |
| 110 | 97 | ||
| 111 | impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> { | 98 | impl<const PAGE_SIZE: usize, const WRITE_SIZE: usize, const ERASE_VALUE: u8> |
| 99 | BootLoader<PAGE_SIZE, WRITE_SIZE, ERASE_VALUE> | ||
| 100 | { | ||
| 112 | pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { | 101 | pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self { |
| 113 | assert_eq!(active.len() % PAGE_SIZE, 0); | 102 | assert_eq!(active.len() % PAGE_SIZE, 0); |
| 114 | assert_eq!(dfu.len() % PAGE_SIZE, 0); | 103 | assert_eq!(dfu.len() % PAGE_SIZE, 0); |
| @@ -352,8 +341,6 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> { | |||
| 352 | self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?; | 341 | self.copy_page_once_to_active(page * 2 + 1, dfu_page, active_page, p)?; |
| 353 | } | 342 | } |
| 354 | 343 | ||
| 355 | info!("DONE COPYING"); | ||
| 356 | |||
| 357 | Ok(()) | 344 | Ok(()) |
| 358 | } | 345 | } |
| 359 | 346 | ||
| @@ -379,7 +366,6 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> { | |||
| 379 | let flash = p.flash(); | 366 | let flash = p.flash(); |
| 380 | flash.read(self.state.from as u32, &mut magic)?; | 367 | flash.read(self.state.from as u32, &mut magic)?; |
| 381 | 368 | ||
| 382 | info!("Read magic: {:x}", magic); | ||
| 383 | if magic == [SWAP_MAGIC; WRITE_SIZE] { | 369 | if magic == [SWAP_MAGIC; WRITE_SIZE] { |
| 384 | Ok(State::Swap) | 370 | Ok(State::Swap) |
| 385 | } else { | 371 | } else { |
| @@ -451,13 +437,9 @@ pub struct FirmwareUpdater { | |||
| 451 | dfu: Partition, | 437 | dfu: Partition, |
| 452 | } | 438 | } |
| 453 | 439 | ||
| 454 | #[cfg(feature = "write-4")] | 440 | // NOTE: Aligned to the largest write size supported by flash |
| 455 | #[repr(align(4))] | 441 | #[repr(align(32))] |
| 456 | pub struct Aligned([u8; 4]); | 442 | pub struct Aligned<const N: usize>([u8; N]); |
| 457 | |||
| 458 | #[cfg(feature = "write-8")] | ||
| 459 | #[repr(align(8))] | ||
| 460 | pub struct Aligned([u8; 8]); | ||
| 461 | 443 | ||
| 462 | impl Default for FirmwareUpdater { | 444 | impl Default for FirmwareUpdater { |
| 463 | fn default() -> Self { | 445 | fn default() -> Self { |
| @@ -480,6 +462,9 @@ impl Default for FirmwareUpdater { | |||
| 480 | &__bootloader_state_end as *const u32 as usize, | 462 | &__bootloader_state_end as *const u32 as usize, |
| 481 | ) | 463 | ) |
| 482 | }; | 464 | }; |
| 465 | |||
| 466 | trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to); | ||
| 467 | trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to); | ||
| 483 | FirmwareUpdater::new(dfu, state) | 468 | FirmwareUpdater::new(dfu, state) |
| 484 | } | 469 | } |
| 485 | } | 470 | } |
| @@ -496,14 +481,20 @@ impl FirmwareUpdater { | |||
| 496 | 481 | ||
| 497 | /// Instruct bootloader that DFU should commence at next boot. | 482 | /// Instruct bootloader that DFU should commence at next boot. |
| 498 | /// Must be provided with an aligned buffer to use for reading and writing magic; | 483 | /// Must be provided with an aligned buffer to use for reading and writing magic; |
| 499 | pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> { | 484 | pub async fn mark_update<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> |
| 500 | let mut aligned = Aligned([0; WRITE_SIZE]); | 485 | where |
| 486 | [(); F::WRITE_SIZE]:, | ||
| 487 | { | ||
| 488 | let mut aligned = Aligned([0; { F::WRITE_SIZE }]); | ||
| 501 | self.set_magic(&mut aligned.0, SWAP_MAGIC, flash).await | 489 | self.set_magic(&mut aligned.0, SWAP_MAGIC, flash).await |
| 502 | } | 490 | } |
| 503 | 491 | ||
| 504 | /// Mark firmware boot successfully | 492 | /// Mark firmware boot successfully |
| 505 | pub async fn mark_booted<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> { | 493 | pub async fn mark_booted<F: AsyncNorFlash>(&mut self, flash: &mut F) -> Result<(), F::Error> |
| 506 | let mut aligned = Aligned([0; WRITE_SIZE]); | 494 | where |
| 495 | [(); F::WRITE_SIZE]:, | ||
| 496 | { | ||
| 497 | let mut aligned = Aligned([0; { F::WRITE_SIZE }]); | ||
| 507 | self.set_magic(&mut aligned.0, BOOT_MAGIC, flash).await | 498 | self.set_magic(&mut aligned.0, BOOT_MAGIC, flash).await |
| 508 | } | 499 | } |
| 509 | 500 | ||
| @@ -626,7 +617,7 @@ mod tests { | |||
| 626 | flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); | 617 | flash.0[0..4].copy_from_slice(&[BOOT_MAGIC; 4]); |
| 627 | let mut flash = SingleFlashProvider::new(&mut flash); | 618 | let mut flash = SingleFlashProvider::new(&mut flash); |
| 628 | 619 | ||
| 629 | let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); | 620 | let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE); |
| 630 | 621 | ||
| 631 | assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap()); | 622 | assert_eq!(State::Boot, bootloader.prepare_boot(&mut flash).unwrap()); |
| 632 | } | 623 | } |
| @@ -643,7 +634,7 @@ mod tests { | |||
| 643 | flash.0[i] = original[i - ACTIVE.from]; | 634 | flash.0[i] = original[i - ACTIVE.from]; |
| 644 | } | 635 | } |
| 645 | 636 | ||
| 646 | let mut bootloader = BootLoader::<4096>::new(ACTIVE, DFU, STATE); | 637 | let mut bootloader = BootLoader::<4096, 4, 0xFF>::new(ACTIVE, DFU, STATE); |
| 647 | let mut updater = FirmwareUpdater::new(DFU, STATE); | 638 | let mut updater = FirmwareUpdater::new(DFU, STATE); |
| 648 | let mut offset = 0; | 639 | let mut offset = 0; |
| 649 | for chunk in update.chunks(4096) { | 640 | for chunk in update.chunks(4096) { |
diff --git a/embassy-boot/nrf/Cargo.toml b/embassy-boot/nrf/Cargo.toml index 78157d246..97207ac29 100644 --- a/embassy-boot/nrf/Cargo.toml +++ b/embassy-boot/nrf/Cargo.toml | |||
| @@ -13,7 +13,7 @@ defmt-rtt = { version = "0.3", optional = true } | |||
| 13 | 13 | ||
| 14 | embassy = { path = "../../embassy", default-features = false } | 14 | embassy = { path = "../../embassy", default-features = false } |
| 15 | embassy-nrf = { path = "../../embassy-nrf", default-features = false, features = ["nightly"] } | 15 | embassy-nrf = { path = "../../embassy-nrf", default-features = false, features = ["nightly"] } |
| 16 | embassy-boot = { path = "../boot", default-features = false, features = ["write-4"] } | 16 | embassy-boot = { path = "../boot", default-features = false } |
| 17 | cortex-m = { version = "0.7" } | 17 | cortex-m = { version = "0.7" } |
| 18 | cortex-m-rt = { version = "0.7" } | 18 | cortex-m-rt = { version = "0.7" } |
| 19 | embedded-storage = "0.3.0" | 19 | embedded-storage = "0.3.0" |
diff --git a/embassy-boot/nrf/src/lib.rs b/embassy-boot/nrf/src/lib.rs index 500cae500..c12899d77 100644 --- a/embassy-boot/nrf/src/lib.rs +++ b/embassy-boot/nrf/src/lib.rs | |||
| @@ -13,7 +13,7 @@ use embassy_nrf::{ | |||
| 13 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; | 13 | use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; |
| 14 | 14 | ||
| 15 | pub struct BootLoader { | 15 | pub struct BootLoader { |
| 16 | boot: embassy_boot::BootLoader<PAGE_SIZE>, | 16 | boot: embassy_boot::BootLoader<PAGE_SIZE, 4, 0xFF>, |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | impl BootLoader { | 19 | impl BootLoader { |
diff --git a/embassy-boot/nrf/src/main.rs b/embassy-boot/nrf/src/main.rs index 63de7c869..0ccd3774d 100644 --- a/embassy-boot/nrf/src/main.rs +++ b/embassy-boot/nrf/src/main.rs | |||
| @@ -46,8 +46,5 @@ unsafe fn DefaultHandler(_: i16) -> ! { | |||
| 46 | 46 | ||
| 47 | #[panic_handler] | 47 | #[panic_handler] |
| 48 | fn panic(_info: &core::panic::PanicInfo) -> ! { | 48 | fn panic(_info: &core::panic::PanicInfo) -> ! { |
| 49 | unsafe { | 49 | cortex_m::asm::udf(); |
| 50 | cortex_m::asm::udf(); | ||
| 51 | core::hint::unreachable_unchecked(); | ||
| 52 | } | ||
| 53 | } | 50 | } |
diff --git a/embassy-boot/stm32/Cargo.toml b/embassy-boot/stm32/Cargo.toml index 76bc480bf..a706e4c06 100644 --- a/embassy-boot/stm32/Cargo.toml +++ b/embassy-boot/stm32/Cargo.toml | |||
| @@ -27,10 +27,6 @@ defmt = [ | |||
| 27 | "embassy-stm32/defmt", | 27 | "embassy-stm32/defmt", |
| 28 | ] | 28 | ] |
| 29 | debug = ["defmt-rtt"] | 29 | debug = ["defmt-rtt"] |
| 30 | flash-2k = ["embassy-boot/write-8"] | ||
| 31 | flash-128 = ["embassy-boot/write-4"] | ||
| 32 | flash-256 = ["embassy-boot/write-4"] | ||
| 33 | invert-erase = ["embassy-boot/invert-erase"] | ||
| 34 | thumbv6 = [] | 30 | thumbv6 = [] |
| 35 | 31 | ||
| 36 | [profile.dev] | 32 | [profile.dev] |
diff --git a/embassy-boot/stm32/memory.x b/embassy-boot/stm32/memory.x index c5356ed37..110c23259 100644 --- a/embassy-boot/stm32/memory.x +++ b/embassy-boot/stm32/memory.x | |||
| @@ -8,11 +8,11 @@ MEMORY | |||
| 8 | RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 16K | 8 | RAM (rwx) : ORIGIN = 0x20000008, LENGTH = 16K |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE); | 11 | __bootloader_state_start = ORIGIN(BOOTLOADER_STATE) - ORIGIN(FLASH); |
| 12 | __bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE); | 12 | __bootloader_state_end = ORIGIN(BOOTLOADER_STATE) + LENGTH(BOOTLOADER_STATE) - ORIGIN(FLASH); |
| 13 | 13 | ||
| 14 | __bootloader_active_start = ORIGIN(ACTIVE); | 14 | __bootloader_active_start = ORIGIN(ACTIVE) - ORIGIN(FLASH); |
| 15 | __bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE); | 15 | __bootloader_active_end = ORIGIN(ACTIVE) + LENGTH(ACTIVE) - ORIGIN(FLASH); |
| 16 | 16 | ||
| 17 | __bootloader_dfu_start = ORIGIN(DFU); | 17 | __bootloader_dfu_start = ORIGIN(DFU) - ORIGIN(FLASH); |
| 18 | __bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU); | 18 | __bootloader_dfu_end = ORIGIN(DFU) + LENGTH(DFU) - ORIGIN(FLASH); |
diff --git a/embassy-boot/stm32/src/lib.rs b/embassy-boot/stm32/src/lib.rs index d1754a310..68220780c 100644 --- a/embassy-boot/stm32/src/lib.rs +++ b/embassy-boot/stm32/src/lib.rs | |||
| @@ -5,12 +5,13 @@ | |||
| 5 | mod fmt; | 5 | mod fmt; |
| 6 | 6 | ||
| 7 | pub use embassy_boot::{FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider, State}; | 7 | pub use embassy_boot::{FirmwareUpdater, FlashProvider, Partition, SingleFlashProvider, State}; |
| 8 | use embassy_stm32::flash::{ERASE_SIZE, ERASE_VALUE, WRITE_SIZE}; | ||
| 8 | 9 | ||
| 9 | pub struct BootLoader<const PAGE_SIZE: usize> { | 10 | pub struct BootLoader { |
| 10 | boot: embassy_boot::BootLoader<PAGE_SIZE>, | 11 | boot: embassy_boot::BootLoader<ERASE_SIZE, WRITE_SIZE, ERASE_VALUE>, |
| 11 | } | 12 | } |
| 12 | 13 | ||
| 13 | impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> { | 14 | impl BootLoader { |
| 14 | /// Create a new bootloader instance using parameters from linker script | 15 | /// Create a new bootloader instance using parameters from linker script |
| 15 | pub fn default() -> Self { | 16 | pub fn default() -> Self { |
| 16 | extern "C" { | 17 | extern "C" { |
| @@ -65,6 +66,7 @@ impl<const PAGE_SIZE: usize> BootLoader<PAGE_SIZE> { | |||
| 65 | 66 | ||
| 66 | pub unsafe fn load(&mut self, start: usize) -> ! { | 67 | pub unsafe fn load(&mut self, start: usize) -> ! { |
| 67 | trace!("Loading app at 0x{:x}", start); | 68 | trace!("Loading app at 0x{:x}", start); |
| 69 | #[allow(unused_mut)] | ||
| 68 | let mut p = cortex_m::Peripherals::steal(); | 70 | let mut p = cortex_m::Peripherals::steal(); |
| 69 | #[cfg(not(feature = "thumbv6"))] | 71 | #[cfg(not(feature = "thumbv6"))] |
| 70 | p.SCB.invalidate_icache(); | 72 | p.SCB.invalidate_icache(); |
diff --git a/embassy-boot/stm32/src/main.rs b/embassy-boot/stm32/src/main.rs index 6fe0fb66d..563bc55d3 100644 --- a/embassy-boot/stm32/src/main.rs +++ b/embassy-boot/stm32/src/main.rs | |||
| @@ -9,9 +9,6 @@ use defmt_rtt as _; | |||
| 9 | use embassy_boot_stm32::*; | 9 | use embassy_boot_stm32::*; |
| 10 | use embassy_stm32::flash::Flash; | 10 | use embassy_stm32::flash::Flash; |
| 11 | 11 | ||
| 12 | #[cfg(not(any(feature = "flash-2k", feature = "flash-256", feature = "flash-128")))] | ||
| 13 | compile_error!("No flash size specified. Must specify exactly one of the following features: flash-2k, flash-256, flash-128"); | ||
| 14 | |||
| 15 | #[entry] | 12 | #[entry] |
| 16 | fn main() -> ! { | 13 | fn main() -> ! { |
| 17 | let p = embassy_stm32::init(Default::default()); | 14 | let p = embassy_stm32::init(Default::default()); |
| @@ -24,15 +21,7 @@ fn main() -> ! { | |||
| 24 | } | 21 | } |
| 25 | */ | 22 | */ |
| 26 | 23 | ||
| 27 | #[cfg(feature = "flash-2k")] | 24 | let mut bl = BootLoader::default(); |
| 28 | let mut bl: BootLoader<2048> = BootLoader::default(); | ||
| 29 | |||
| 30 | #[cfg(feature = "flash-256")] | ||
| 31 | let mut bl: BootLoader<256> = BootLoader::default(); | ||
| 32 | |||
| 33 | #[cfg(feature = "flash-128")] | ||
| 34 | let mut bl: BootLoader<128> = BootLoader::default(); | ||
| 35 | |||
| 36 | let mut flash = Flash::unlock(p.FLASH); | 25 | let mut flash = Flash::unlock(p.FLASH); |
| 37 | let start = bl.prepare(&mut SingleFlashProvider::new(&mut flash)); | 26 | let start = bl.prepare(&mut SingleFlashProvider::new(&mut flash)); |
| 38 | core::mem::drop(flash); | 27 | core::mem::drop(flash); |
| @@ -55,8 +44,5 @@ unsafe fn DefaultHandler(_: i16) -> ! { | |||
| 55 | 44 | ||
| 56 | #[panic_handler] | 45 | #[panic_handler] |
| 57 | fn panic(_info: &core::panic::PanicInfo) -> ! { | 46 | fn panic(_info: &core::panic::PanicInfo) -> ! { |
| 58 | unsafe { | 47 | cortex_m::asm::udf(); |
| 59 | cortex_m::asm::udf(); | ||
| 60 | core::hint::unreachable_unchecked(); | ||
| 61 | } | ||
| 62 | } | 48 | } |
