Skip to content

Commit a8e879b

Browse files
authored
Merge pull request #49 from gustavonihei/feature/c3_mcuboot
ESP32-C3: Add support for booting from MCUboot bootloader
2 parents ca4f295 + 1ced513 commit a8e879b

File tree

11 files changed

+767
-97
lines changed

11 files changed

+767
-97
lines changed

esp-hal-common/src/lib.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,11 +157,8 @@ mod critical_section_impl {
157157
unsafe impl critical_section::Impl for super::CriticalSection {
158158
unsafe fn acquire() -> critical_section::RawRestoreState {
159159
let mut mstatus = 0u32;
160-
unsafe {
161-
core::arch::asm!("csrrci {0}, mstatus, 8", inout(reg) mstatus);
162-
}
160+
core::arch::asm!("csrrci {0}, mstatus, 8", inout(reg) mstatus);
163161
let interrupts_active = (mstatus & 0b1000) != 0;
164-
165162
#[cfg(multi_core)]
166163
{
167164
let guard = multicore::MULTICORE_LOCK.lock();

esp32c3-hal/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ categories = [
2525
]
2626

2727
[dependencies]
28+
cfg-if = "1.0"
2829
embedded-hal = { version = "0.2.7", features = ["unproven"] }
2930
embedded-hal-1 = { version = "=1.0.0-alpha.9", optional = true, package = "embedded-hal" }
3031
embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true }
@@ -43,6 +44,7 @@ ssd1306 = "0.7.1"
4344

4445
[features]
4546
default = ["rt", "vectored"]
47+
mcu-boot = []
4648
direct-boot = []
4749
eh1 = ["esp-hal-common/eh1", "dep:embedded-hal-1", "dep:embedded-hal-nb"]
4850
rt = ["riscv-rt"]
@@ -62,3 +64,6 @@ required-features = ["eh1"]
6264
[[example]]
6365
name = "spi_eh1_device_loopback"
6466
required-features = ["eh1"]
67+
68+
[profile.dev]
69+
opt-level = 1

esp32c3-hal/README.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,130 @@ The compilation target for this device is officially supported via the `stable`
2323
$ rustup target add riscv32imc-unknown-none-elf
2424
```
2525

26+
### Supported boot methods
27+
28+
#### IDF Bootloader
29+
30+
The [IDF second stage bootloader](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-guides/startup.html#second-stage-bootloader) is the default bootloader solution.
31+
32+
By default, [espflash](https://github.com/esp-rs/espflash) fetches the required binaries (Bootloader and Partition Table) and flashes them onto the target device together with the Rust-based application firmware image.
33+
34+
#### MCUboot Secure Bootloader
35+
36+
[MCUboot](https://github.com/mcu-tools/mcuboot) is a secure bootloader solution feature-wise equivalent to the [IDF Bootloader](#idf-bootloader).
37+
You may find more information on the documentation pages for MCUboot and the Espressif port:
38+
- https://docs.mcuboot.com/
39+
- https://docs.mcuboot.com/readme-espressif.html
40+
41+
##### Requirements
42+
43+
Booting from MCUboot secure bootloader requires the Rust application image to be built in a [MCUboot-specific image format](https://docs.mcuboot.com/design.html#image-format). You need to install the following dependencies:
44+
45+
```shell
46+
# Required for generating the object file in Intel HEX format
47+
cargo install cargo-binutils
48+
rustup component add llvm-tools-preview
49+
50+
# MCUboot's tool for image signing and key management
51+
pip install imgtool
52+
```
53+
54+
Currently, MCUboot is still not supported as a booting option in [espflash](https://github.com/esp-rs/espflash/issues/267), so you'll need to use the [esptool](https://github.com/espressif/esptool) utility for flashing both the MCUboot bootloader and the Rust application binaries:
55+
56+
```shell
57+
# Serial flasher utility for Espressif chips
58+
pip install esptool
59+
```
60+
61+
Download a prebuilt MCUboot bootloader image for the target device:
62+
63+
```shell
64+
# Prebuilt MCUboot bootloader binary
65+
curl -LO https://github.com/espressif/esp-nuttx-bootloader/releases/download/latest/mcuboot-esp32c3.bin
66+
```
67+
68+
##### Booting the Hello World example from MCUboot
69+
70+
Build the Hello World example with MCUboot support:
71+
72+
```shell
73+
cargo build --release --example hello_world --features mcu-boot
74+
```
75+
Then proceed to generating the application binary and flashing it onto the target device:
76+
77+
```shell
78+
# Generate the object file in Intel HEX format
79+
rust-objcopy -O ihex target/riscv32imc-unknown-none-elf/release/examples/hello_world app.hex
80+
81+
# Generate the application firmware image binary file in MCUboot-format
82+
imgtool sign --pad --align 4 -v 0 -s auto -H 32 --pad-header -S 0x100000 app.hex app.bin
83+
84+
# Flash the application firmware image binary onto the target device
85+
esptool.py -c esp32c3 -p /dev/ttyUSB0 -b 921600 --after no_reset write_flash -fs 4MB -fm dio -ff 40m 0x0 ./mcuboot-esp32c3.bin 0x110000 ./app.bin
86+
```
87+
Once the device is flashed, you may monitor the serial interface (e.g. with `picocom`):
88+
89+
```shell
90+
picocom -b 115200 /dev/ttyUSB0 --imap lfcrlf
91+
```
92+
93+
Reset the board and MCUboot should load the Hello World example:
94+
```shell
95+
ESP-ROM:esp32c3-api1-20210207
96+
Build:Feb 7 2021
97+
rst:0x1 (POWERON),boot:0xc (SPI_FAST_FLASH_BOOT)
98+
SPIWP:0xee
99+
mode:DIO, clock div:2
100+
load:0x3fcd8598,len:0x10cc
101+
load:0x403c8000,len:0x2b90
102+
load:0x403d0000,len:0x1364
103+
entry 0x403c804a
104+
[esp32c3] [INF] Enabling RNG early entropy source...
105+
[esp32c3] [INF] *** Booting MCUboot build v1.8.0-86-g14763b1 ***
106+
[esp32c3] [INF] Primary image: magic=good, swap_type=0x2, copy_done=0x1, image_ok=0x3
107+
[esp32c3] [INF] Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3
108+
[esp32c3] [INF] Boot source: none
109+
[esp32c3] [INF] Swap type: test
110+
[esp32c3] [INF] Disabling RNG early entropy source...
111+
[esp32c3] [INF] br_image_off = 0x10000
112+
[esp32c3] [INF] ih_hdr_size = 0x20
113+
[esp32c3] [INF] DRAM segment: start=0x3fcd0000, size=0x0, vaddr=0x3fcd0000
114+
[esp32c3] [INF] IRAM segment: start=0x1d00, size=0x170c, vaddr=0x40380000
115+
[esp32c3] [INF] start=0x40380004
116+
Hello world!
117+
Hello world!
118+
Hello world!
119+
```
120+
121+
#### Direct Boot
122+
123+
[Direct Boot](https://github.com/espressif/esp32c3-direct-boot-example#direct-boot-in-esp32-c3) allows an application stored in the External Flash to be executed directly, without being copied into Internal RAM.
124+
125+
##### Booting the Hello World example using Direct Boot
126+
127+
Build the Hello World example with support for Direct Boot:
128+
129+
```shell
130+
cargo build --release --example hello_world --features direct-boot
131+
```
132+
133+
Then proceed to generating the application binary and flashing it onto the target device:
134+
135+
```shell
136+
cargo espflash --release --format direct-boot --features direct-boot --example hello_world --monitor
137+
```
138+
139+
The ROM Bootloader will identify the firmware image built with Direct Boot support and load it appropriately from the External Flash:
140+
141+
```shell
142+
ESP-ROM:esp32c3-api1-20210207
143+
Build:Feb 7 2021
144+
rst:0x1 (POWERON),boot:0xc (SPI_FAST_FLASH_BOOT)
145+
Hello world!
146+
Hello world!
147+
Hello world!
148+
```
149+
26150
## License
27151

28152
Licensed under either of:

esp32c3-hal/build.rs

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
use std::{env, fs::File, io::Write, path::PathBuf, process::exit};
22

3+
// Thanks to kennytm and TheDan64 for the assert_used_features macro.
4+
// Source:
5+
// https://github.com/TheDan64/inkwell/blob/36c3b106e61b1b45295a35f94023d93d9328c76f/src/lib.rs#L81-L110
6+
macro_rules! assert_unique_features {
7+
() => {};
8+
($first:tt $(,$rest:tt)*) => {
9+
$(
10+
#[cfg(all(feature = $first, feature = $rest))]
11+
compile_error!(concat!("Features \"", $first, "\" and \"", $rest, "\" cannot be used together"));
12+
)*
13+
assert_unique_features!($($rest),*);
14+
}
15+
}
16+
17+
assert_unique_features! {"mcu-boot", "direct-boot"}
18+
319
#[cfg(feature = "direct-boot")]
420
fn main() {
521
check_opt_level();
@@ -36,7 +52,7 @@ fn main() {
3652
add_defaults();
3753
}
3854

39-
#[cfg(not(feature = "direct-boot"))]
55+
#[cfg(not(any(feature = "mcu-boot",feature = "direct-boot")))]
4056
fn main() {
4157
check_opt_level();
4258

@@ -66,13 +82,55 @@ fn main() {
6682
add_defaults();
6783
}
6884

85+
#[cfg(feature = "mcu-boot")]
86+
fn main() {
87+
check_opt_level();
88+
89+
// Put the linker script somewhere the linker can find it
90+
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
91+
92+
File::create(out.join("memory.x"))
93+
.unwrap()
94+
.write_all(include_bytes!("ld/mb-esp32c3-memory.x"))
95+
.unwrap();
96+
97+
File::create(out.join("esp32c3-link.x"))
98+
.unwrap()
99+
.write_all(include_bytes!("ld/mb-esp32c3-link.x"))
100+
.unwrap();
101+
102+
File::create(out.join("riscv-link.x"))
103+
.unwrap()
104+
.write_all(include_bytes!("ld/mb-riscv-link.x"))
105+
.unwrap();
106+
107+
File::create(out.join("linkall.x"))
108+
.unwrap()
109+
.write_all(include_bytes!("ld/mb-linkall.x"))
110+
.unwrap();
111+
112+
println!("cargo:rustc-link-search={}", out.display());
113+
114+
// Only re-run the build script when memory.x is changed,
115+
// instead of when any part of the source code changes.
116+
println!("cargo:rerun-if-changed=ld/memory.x");
117+
118+
add_defaults();
119+
}
120+
69121
fn add_defaults() {
70122
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
123+
71124
File::create(out.join("hal-defaults.x"))
72125
.unwrap()
73126
.write_all(include_bytes!("ld/hal-defaults.x"))
74127
.unwrap();
75128

129+
File::create(out.join("rom-functions.x"))
130+
.unwrap()
131+
.write_all(include_bytes!("ld/rom-functions.x"))
132+
.unwrap();
133+
76134
println!("cargo:rustc-link-search={}", out.display());
77135
}
78136

esp32c3-hal/ld/db-esp32c3-memory.x

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ MEMORY
3232
RTC_FAST : ORIGIN = 0x50000000, LENGTH = 0x2000 /*- ESP_BOOTLOADER_RESERVE_RTC*/
3333
}
3434

35-
3635
REGION_ALIAS("REGION_TEXT", IROM);
3736
REGION_ALIAS("REGION_RODATA", DROM);
3837

esp32c3-hal/ld/mb-esp32c3-link.x

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
INCLUDE memory.x
2+
3+
SECTIONS
4+
{
5+
.metadata :
6+
{
7+
/* Magic for load header */
8+
9+
LONG(0xace637d3)
10+
11+
/* Application entry point address */
12+
13+
KEEP(*(.entry_addr))
14+
15+
/* IRAM metadata:
16+
* - Destination address (VMA) for IRAM region
17+
* - Flash offset (LMA) for start of IRAM region
18+
* - Size of IRAM region
19+
*/
20+
21+
LONG(ADDR(.rwtext))
22+
LONG(LOADADDR(.rwtext))
23+
LONG(SIZEOF(.rwtext))
24+
25+
/* DRAM metadata:
26+
* - Destination address (VMA) for DRAM region
27+
* - Flash offset (LMA) for start of DRAM region
28+
* - Size of DRAM region
29+
*/
30+
31+
LONG(ADDR(.data))
32+
LONG(LOADADDR(.data))
33+
LONG(SIZEOF(.data))
34+
} > metadata
35+
}
36+
37+
INCLUDE riscv-link.x
38+
39+
_image_drom_vma = ADDR(.rodata);
40+
_image_drom_lma = LOADADDR(.rodata);
41+
_image_drom_size = LOADADDR(.rodata) + SIZEOF(.rodata) - _image_drom_lma;
42+
43+
_image_irom_vma = ADDR(.text);
44+
_image_irom_lma = LOADADDR(.text);
45+
_image_irom_size = LOADADDR(.text) + SIZEOF(.text) - _image_irom_lma;

esp32c3-hal/ld/mb-esp32c3-memory.x

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
MEMORY
2+
{
3+
/*
4+
https://github.com/espressif/esptool/blob/ed64d20b051d05f3f522bacc6a786098b562d4b8/esptool/targets/esp32c3.py#L78-L90
5+
MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"],
6+
[0x3C000000, 0x3C800000, "DROM"],
7+
[0x3FC80000, 0x3FCE0000, "DRAM"],
8+
[0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"],
9+
[0x3FF00000, 0x3FF20000, "DROM_MASK"],
10+
[0x40000000, 0x40060000, "IROM_MASK"],
11+
[0x42000000, 0x42800000, "IROM"],
12+
[0x4037C000, 0x403E0000, "IRAM"],
13+
[0x50000000, 0x50002000, "RTC_IRAM"],
14+
[0x50000000, 0x50002000, "RTC_DRAM"],
15+
[0x600FE000, 0x60100000, "MEM_INTERNAL2"]]
16+
*/
17+
18+
/* The origin values for "metadata" and "ROM" memory regions are the actual
19+
* load addresses.
20+
*
21+
* NOTE: The memory region starting from 0x0 with 0x20 length is reserved
22+
* for the MCUboot header, which will be prepended to the binary file by
23+
* the "imgtool" during the signing of firmware image.
24+
*/
25+
metadata : ORIGIN = 0x20, LENGTH = 0x20
26+
ROM : ORIGIN = 0x40, LENGTH = 0x400000 - 0x40
27+
28+
/* 400K of on soc RAM, 16K reserved for cache */
29+
ICACHE : ORIGIN = 0x4037C000, LENGTH = 0x4000
30+
/* Instruction RAM */
31+
IRAM : ORIGIN = 0x4037C000 + 0x4000, LENGTH = 400K - 0x4000
32+
/* Data RAM */
33+
DRAM : ORIGIN = 0x3FC80000, LENGTH = 0x50000
34+
35+
/* External flash */
36+
/* Instruction ROM */
37+
IROM : ORIGIN = 0x42000000, LENGTH = 0x400000
38+
/* Data ROM */
39+
/* The DROM segment origin is offset by 0x40 for mirroring the actual ROM
40+
* image layout:
41+
* 0x0 - 0x1F : MCUboot header
42+
* 0x20 - 0x3F : Application image metadata section
43+
* 0x40 onwards: ROM code and data
44+
* This is required to meet the following constraint from the external
45+
* flash MMU:
46+
* VMA % 64KB == LMA % 64KB
47+
* i.e. the lower 16 bits of both the virtual address (address seen by the
48+
* CPU) and the load address (physical address of the external flash) must
49+
* be equal.
50+
*/
51+
DROM : ORIGIN = 0x3C000000 + 0x40, LENGTH = 0x400000 - 0x40
52+
53+
/* RTC fast memory (executable). Persists over deep sleep. */
54+
RTC_FAST : ORIGIN = 0x50000000, LENGTH = 0x2000 /*- ESP_BOOTLOADER_RESERVE_RTC*/
55+
}
56+
57+
REGION_ALIAS("REGION_TEXT", IROM);
58+
REGION_ALIAS("REGION_RODATA", DROM);
59+
60+
REGION_ALIAS("REGION_DATA", DRAM);
61+
REGION_ALIAS("REGION_BSS", DRAM);
62+
REGION_ALIAS("REGION_HEAP", DRAM);
63+
REGION_ALIAS("REGION_STACK", DRAM);
64+
65+
REGION_ALIAS("REGION_RWTEXT", IRAM);
66+
REGION_ALIAS("REGION_RTC_FAST", RTC_FAST);

esp32c3-hal/ld/mb-linkall.x

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
INCLUDE "esp32c3-link.x"
2+
INCLUDE "hal-defaults.x"
3+
INCLUDE "rom-functions.x"

0 commit comments

Comments
 (0)