aboutsummaryrefslogtreecommitdiff
path: root/embassy-boot
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2022-12-01 18:26:22 +0100
committerUlf Lilleengen <[email protected]>2022-12-02 11:28:33 +0100
commitbb89a2341cca1aad79bc6d5f3532008541c9e428 (patch)
tree63ff838ef0192f492ac2aae59b0b8a445b8200d6 /embassy-boot
parenteb010fbe33c2b99bdeaa68a2045b8a8e220cf0aa (diff)
feat: embassy-boot for rp2040
Add embassy-boot support for RP2040, with examples for the Raspberry Pi Pico. Co-authored-by: Mathias Koch <[email protected]>
Diffstat (limited to 'embassy-boot')
-rw-r--r--embassy-boot/rp/Cargo.toml71
-rw-r--r--embassy-boot/rp/README.md26
-rw-r--r--embassy-boot/rp/build.rs8
-rw-r--r--embassy-boot/rp/src/fmt.rs225
-rw-r--r--embassy-boot/rp/src/lib.rs90
5 files changed, 420 insertions, 0 deletions
diff --git a/embassy-boot/rp/Cargo.toml b/embassy-boot/rp/Cargo.toml
new file mode 100644
index 000000000..93099b233
--- /dev/null
+++ b/embassy-boot/rp/Cargo.toml
@@ -0,0 +1,71 @@
1[package]
2edition = "2021"
3name = "embassy-boot-rp"
4version = "0.1.0"
5description = "Bootloader lib for RP2040 chips"
6license = "MIT OR Apache-2.0"
7
8[package.metadata.embassy_docs]
9src_base = "https://github.com/embassy-rs/embassy/blob/embassy-boot-rp-v$VERSION/src/"
10src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-boot/rp/src/"
11target = "thumbv6m-none-eabi"
12
13[lib]
14
15[dependencies]
16defmt = { version = "0.3", optional = true }
17defmt-rtt = { version = "0.4", optional = true }
18log = { version = "0.4", optional = true }
19
20embassy-sync = { path = "../../embassy-sync" }
21embassy-rp = { path = "../../embassy-rp", default-features = false, features = ["nightly"] }
22embassy-boot = { path = "../boot", default-features = false }
23cortex-m = { version = "0.7.6" }
24cortex-m-rt = { version = "0.7" }
25embedded-storage = "0.3.0"
26embedded-storage-async = "0.3.0"
27cfg-if = "1.0.0"
28
29[features]
30defmt = [
31 "dep:defmt",
32 "embassy-boot/defmt",
33 "embassy-rp/defmt",
34]
35log = [
36 "dep:log",
37 "embassy-boot/log",
38 "embassy-rp/log",
39]
40debug = ["defmt-rtt"]
41
42[profile.dev]
43debug = 2
44debug-assertions = true
45incremental = false
46opt-level = 'z'
47overflow-checks = true
48
49[profile.release]
50codegen-units = 1
51debug = 2
52debug-assertions = false
53incremental = false
54lto = 'fat'
55opt-level = 'z'
56overflow-checks = false
57
58# do not optimize proc-macro crates = faster builds from scratch
59[profile.dev.build-override]
60codegen-units = 8
61debug = false
62debug-assertions = false
63opt-level = 0
64overflow-checks = false
65
66[profile.release.build-override]
67codegen-units = 8
68debug = false
69debug-assertions = false
70opt-level = 0
71overflow-checks = false
diff --git a/embassy-boot/rp/README.md b/embassy-boot/rp/README.md
new file mode 100644
index 000000000..c0c2d85fa
--- /dev/null
+++ b/embassy-boot/rp/README.md
@@ -0,0 +1,26 @@
1# embassy-boot-rp
2
3An [Embassy](https://embassy.dev) project.
4
5An adaptation of `embassy-boot` for RP2040.
6
7NOTE: The applications using this bootloader should not link with the `link-rp.x` linker script.
8
9## Features
10
11* Configure bootloader partitions based on linker script.
12* Load applications from active partition.
13
14## Minimum supported Rust version (MSRV)
15
16`embassy-boot-rp` requires Rust nightly to compile as it relies on async traits for interacting with the flash peripherals.
17
18## License
19
20This work is licensed under either of
21
22- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
23 <http://www.apache.org/licenses/LICENSE-2.0>)
24- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
25
26at your option.
diff --git a/embassy-boot/rp/build.rs b/embassy-boot/rp/build.rs
new file mode 100644
index 000000000..2cbc7ef5e
--- /dev/null
+++ b/embassy-boot/rp/build.rs
@@ -0,0 +1,8 @@
1use std::env;
2
3fn main() {
4 let target = env::var("TARGET").unwrap();
5 if target.starts_with("thumbv6m-") {
6 println!("cargo:rustc-cfg=armv6m");
7 }
8}
diff --git a/embassy-boot/rp/src/fmt.rs b/embassy-boot/rp/src/fmt.rs
new file mode 100644
index 000000000..066970813
--- /dev/null
+++ b/embassy-boot/rp/src/fmt.rs
@@ -0,0 +1,225 @@
1#![macro_use]
2#![allow(unused_macros)]
3
4#[cfg(all(feature = "defmt", feature = "log"))]
5compile_error!("You may not enable both `defmt` and `log` features.");
6
7macro_rules! assert {
8 ($($x:tt)*) => {
9 {
10 #[cfg(not(feature = "defmt"))]
11 ::core::assert!($($x)*);
12 #[cfg(feature = "defmt")]
13 ::defmt::assert!($($x)*);
14 }
15 };
16}
17
18macro_rules! assert_eq {
19 ($($x:tt)*) => {
20 {
21 #[cfg(not(feature = "defmt"))]
22 ::core::assert_eq!($($x)*);
23 #[cfg(feature = "defmt")]
24 ::defmt::assert_eq!($($x)*);
25 }
26 };
27}
28
29macro_rules! assert_ne {
30 ($($x:tt)*) => {
31 {
32 #[cfg(not(feature = "defmt"))]
33 ::core::assert_ne!($($x)*);
34 #[cfg(feature = "defmt")]
35 ::defmt::assert_ne!($($x)*);
36 }
37 };
38}
39
40macro_rules! debug_assert {
41 ($($x:tt)*) => {
42 {
43 #[cfg(not(feature = "defmt"))]
44 ::core::debug_assert!($($x)*);
45 #[cfg(feature = "defmt")]
46 ::defmt::debug_assert!($($x)*);
47 }
48 };
49}
50
51macro_rules! debug_assert_eq {
52 ($($x:tt)*) => {
53 {
54 #[cfg(not(feature = "defmt"))]
55 ::core::debug_assert_eq!($($x)*);
56 #[cfg(feature = "defmt")]
57 ::defmt::debug_assert_eq!($($x)*);
58 }
59 };
60}
61
62macro_rules! debug_assert_ne {
63 ($($x:tt)*) => {
64 {
65 #[cfg(not(feature = "defmt"))]
66 ::core::debug_assert_ne!($($x)*);
67 #[cfg(feature = "defmt")]
68 ::defmt::debug_assert_ne!($($x)*);
69 }
70 };
71}
72
73macro_rules! todo {
74 ($($x:tt)*) => {
75 {
76 #[cfg(not(feature = "defmt"))]
77 ::core::todo!($($x)*);
78 #[cfg(feature = "defmt")]
79 ::defmt::todo!($($x)*);
80 }
81 };
82}
83
84macro_rules! unreachable {
85 ($($x:tt)*) => {
86 {
87 #[cfg(not(feature = "defmt"))]
88 ::core::unreachable!($($x)*);
89 #[cfg(feature = "defmt")]
90 ::defmt::unreachable!($($x)*);
91 }
92 };
93}
94
95macro_rules! panic {
96 ($($x:tt)*) => {
97 {
98 #[cfg(not(feature = "defmt"))]
99 ::core::panic!($($x)*);
100 #[cfg(feature = "defmt")]
101 ::defmt::panic!($($x)*);
102 }
103 };
104}
105
106macro_rules! trace {
107 ($s:literal $(, $x:expr)* $(,)?) => {
108 {
109 #[cfg(feature = "log")]
110 ::log::trace!($s $(, $x)*);
111 #[cfg(feature = "defmt")]
112 ::defmt::trace!($s $(, $x)*);
113 #[cfg(not(any(feature = "log", feature="defmt")))]
114 let _ = ($( & $x ),*);
115 }
116 };
117}
118
119macro_rules! debug {
120 ($s:literal $(, $x:expr)* $(,)?) => {
121 {
122 #[cfg(feature = "log")]
123 ::log::debug!($s $(, $x)*);
124 #[cfg(feature = "defmt")]
125 ::defmt::debug!($s $(, $x)*);
126 #[cfg(not(any(feature = "log", feature="defmt")))]
127 let _ = ($( & $x ),*);
128 }
129 };
130}
131
132macro_rules! info {
133 ($s:literal $(, $x:expr)* $(,)?) => {
134 {
135 #[cfg(feature = "log")]
136 ::log::info!($s $(, $x)*);
137 #[cfg(feature = "defmt")]
138 ::defmt::info!($s $(, $x)*);
139 #[cfg(not(any(feature = "log", feature="defmt")))]
140 let _ = ($( & $x ),*);
141 }
142 };
143}
144
145macro_rules! warn {
146 ($s:literal $(, $x:expr)* $(,)?) => {
147 {
148 #[cfg(feature = "log")]
149 ::log::warn!($s $(, $x)*);
150 #[cfg(feature = "defmt")]
151 ::defmt::warn!($s $(, $x)*);
152 #[cfg(not(any(feature = "log", feature="defmt")))]
153 let _ = ($( & $x ),*);
154 }
155 };
156}
157
158macro_rules! error {
159 ($s:literal $(, $x:expr)* $(,)?) => {
160 {
161 #[cfg(feature = "log")]
162 ::log::error!($s $(, $x)*);
163 #[cfg(feature = "defmt")]
164 ::defmt::error!($s $(, $x)*);
165 #[cfg(not(any(feature = "log", feature="defmt")))]
166 let _ = ($( & $x ),*);
167 }
168 };
169}
170
171#[cfg(feature = "defmt")]
172macro_rules! unwrap {
173 ($($x:tt)*) => {
174 ::defmt::unwrap!($($x)*)
175 };
176}
177
178#[cfg(not(feature = "defmt"))]
179macro_rules! unwrap {
180 ($arg:expr) => {
181 match $crate::fmt::Try::into_result($arg) {
182 ::core::result::Result::Ok(t) => t,
183 ::core::result::Result::Err(e) => {
184 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
185 }
186 }
187 };
188 ($arg:expr, $($msg:expr),+ $(,)? ) => {
189 match $crate::fmt::Try::into_result($arg) {
190 ::core::result::Result::Ok(t) => t,
191 ::core::result::Result::Err(e) => {
192 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
193 }
194 }
195 }
196}
197
198#[derive(Debug, Copy, Clone, Eq, PartialEq)]
199pub struct NoneError;
200
201pub trait Try {
202 type Ok;
203 type Error;
204 fn into_result(self) -> Result<Self::Ok, Self::Error>;
205}
206
207impl<T> Try for Option<T> {
208 type Ok = T;
209 type Error = NoneError;
210
211 #[inline]
212 fn into_result(self) -> Result<T, NoneError> {
213 self.ok_or(NoneError)
214 }
215}
216
217impl<T, E> Try for Result<T, E> {
218 type Ok = T;
219 type Error = E;
220
221 #[inline]
222 fn into_result(self) -> Self {
223 self
224 }
225}
diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs
new file mode 100644
index 000000000..85fc81827
--- /dev/null
+++ b/embassy-boot/rp/src/lib.rs
@@ -0,0 +1,90 @@
1#![no_std]
2#![feature(type_alias_impl_trait)]
3#![warn(missing_docs)]
4#![doc = include_str!("../README.md")]
5mod fmt;
6
7pub use embassy_boot::{AlignedBuffer, BootFlash, FirmwareUpdater, FlashConfig, Partition, SingleFlashConfig, State};
8use embassy_rp::flash::{ERASE_SIZE, WRITE_SIZE};
9
10/// A bootloader for RP2040 devices.
11pub struct BootLoader {
12 boot: embassy_boot::BootLoader,
13 magic: AlignedBuffer<WRITE_SIZE>,
14 page: AlignedBuffer<ERASE_SIZE>,
15}
16
17impl BootLoader {
18 /// Create a new bootloader instance using the supplied partitions for active, dfu and state.
19 pub fn new(active: Partition, dfu: Partition, state: Partition) -> Self {
20 Self {
21 boot: embassy_boot::BootLoader::new(active, dfu, state),
22 magic: AlignedBuffer([0; WRITE_SIZE]),
23 page: AlignedBuffer([0; ERASE_SIZE]),
24 }
25 }
26
27 /// Inspect the bootloader state and perform actions required before booting, such as swapping
28 /// firmware.
29 pub fn prepare<F: FlashConfig>(&mut self, flash: &mut F) -> usize {
30 match self.boot.prepare_boot(flash, self.magic.as_mut(), self.page.as_mut()) {
31 Ok(_) => embassy_rp::flash::FLASH_BASE + self.boot.boot_address(),
32 Err(_) => panic!("boot prepare error!"),
33 }
34 }
35
36 /// Boots the application.
37 ///
38 /// # Safety
39 ///
40 /// This modifies the stack pointer and reset vector and will run code placed in the active partition.
41 pub unsafe fn load(&mut self, start: usize) -> ! {
42 trace!("Loading app at 0x{:x}", start);
43 #[allow(unused_mut)]
44 let mut p = cortex_m::Peripherals::steal();
45 #[cfg(not(armv6m))]
46 p.SCB.invalidate_icache();
47 p.SCB.vtor.write(start as u32);
48
49 cortex_m::asm::bootload(start as *const u32)
50 }
51}
52
53impl Default for BootLoader {
54 /// Create a new bootloader instance using parameters from linker script
55 fn default() -> Self {
56 extern "C" {
57 static __bootloader_state_start: u32;
58 static __bootloader_state_end: u32;
59 static __bootloader_active_start: u32;
60 static __bootloader_active_end: u32;
61 static __bootloader_dfu_start: u32;
62 static __bootloader_dfu_end: u32;
63 }
64
65 let active = unsafe {
66 Partition::new(
67 &__bootloader_active_start as *const u32 as usize,
68 &__bootloader_active_end as *const u32 as usize,
69 )
70 };
71 let dfu = unsafe {
72 Partition::new(
73 &__bootloader_dfu_start as *const u32 as usize,
74 &__bootloader_dfu_end as *const u32 as usize,
75 )
76 };
77 let state = unsafe {
78 Partition::new(
79 &__bootloader_state_start as *const u32 as usize,
80 &__bootloader_state_end as *const u32 as usize,
81 )
82 };
83
84 trace!("ACTIVE: 0x{:x} - 0x{:x}", active.from, active.to);
85 trace!("DFU: 0x{:x} - 0x{:x}", dfu.from, dfu.to);
86 trace!("STATE: 0x{:x} - 0x{:x}", state.from, state.to);
87
88 Self::new(active, dfu, state)
89 }
90}