diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index 289608ab7bd..ced0edfa135 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -157,11 +157,8 @@ mod critical_section_impl { unsafe impl critical_section::Impl for super::CriticalSection { unsafe fn acquire() -> critical_section::RawRestoreState { let mut mstatus = 0u32; - unsafe { - core::arch::asm!("csrrci {0}, mstatus, 8", inout(reg) mstatus); - } + core::arch::asm!("csrrci {0}, mstatus, 8", inout(reg) mstatus); let interrupts_active = (mstatus & 0b1000) != 0; - #[cfg(multi_core)] { let guard = multicore::MULTICORE_LOCK.lock(); diff --git a/esp32c3-hal/Cargo.toml b/esp32c3-hal/Cargo.toml index eb6d7e50e01..bf525740f2f 100644 --- a/esp32c3-hal/Cargo.toml +++ b/esp32c3-hal/Cargo.toml @@ -25,6 +25,7 @@ categories = [ ] [dependencies] +cfg-if = "1.0" embedded-hal = { version = "0.2.7", features = ["unproven"] } embedded-hal-1 = { version = "=1.0.0-alpha.9", optional = true, package = "embedded-hal" } embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true } @@ -43,6 +44,7 @@ ssd1306 = "0.7.1" [features] default = ["rt", "vectored"] +mcu-boot = [] direct-boot = [] eh1 = ["esp-hal-common/eh1", "dep:embedded-hal-1", "dep:embedded-hal-nb"] rt = ["riscv-rt"] @@ -62,3 +64,6 @@ required-features = ["eh1"] [[example]] name = "spi_eh1_device_loopback" required-features = ["eh1"] + +[profile.dev] +opt-level = 1 diff --git a/esp32c3-hal/README.md b/esp32c3-hal/README.md index e9baa4ed8ed..2498251490a 100644 --- a/esp32c3-hal/README.md +++ b/esp32c3-hal/README.md @@ -23,6 +23,130 @@ The compilation target for this device is officially supported via the `stable` $ rustup target add riscv32imc-unknown-none-elf ``` +### Supported boot methods + +#### IDF Bootloader + +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. + +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. + +#### MCUboot Secure Bootloader + +[MCUboot](https://github.com/mcu-tools/mcuboot) is a secure bootloader solution feature-wise equivalent to the [IDF Bootloader](#idf-bootloader). +You may find more information on the documentation pages for MCUboot and the Espressif port: +- https://docs.mcuboot.com/ +- https://docs.mcuboot.com/readme-espressif.html + +##### Requirements + +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: + +```shell +# Required for generating the object file in Intel HEX format +cargo install cargo-binutils +rustup component add llvm-tools-preview + +# MCUboot's tool for image signing and key management +pip install imgtool +``` + +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: + +```shell +# Serial flasher utility for Espressif chips +pip install esptool +``` + +Download a prebuilt MCUboot bootloader image for the target device: + +```shell +# Prebuilt MCUboot bootloader binary +curl -LO https://github.com/espressif/esp-nuttx-bootloader/releases/download/latest/mcuboot-esp32c3.bin +``` + +##### Booting the Hello World example from MCUboot + +Build the Hello World example with MCUboot support: + +```shell +cargo build --release --example hello_world --features mcu-boot +``` +Then proceed to generating the application binary and flashing it onto the target device: + +```shell +# Generate the object file in Intel HEX format +rust-objcopy -O ihex target/riscv32imc-unknown-none-elf/release/examples/hello_world app.hex + +# Generate the application firmware image binary file in MCUboot-format +imgtool sign --pad --align 4 -v 0 -s auto -H 32 --pad-header -S 0x100000 app.hex app.bin + +# Flash the application firmware image binary onto the target device +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 +``` +Once the device is flashed, you may monitor the serial interface (e.g. with `picocom`): + +```shell +picocom -b 115200 /dev/ttyUSB0 --imap lfcrlf +``` + +Reset the board and MCUboot should load the Hello World example: +```shell +ESP-ROM:esp32c3-api1-20210207 +Build:Feb 7 2021 +rst:0x1 (POWERON),boot:0xc (SPI_FAST_FLASH_BOOT) +SPIWP:0xee +mode:DIO, clock div:2 +load:0x3fcd8598,len:0x10cc +load:0x403c8000,len:0x2b90 +load:0x403d0000,len:0x1364 +entry 0x403c804a +[esp32c3] [INF] Enabling RNG early entropy source... +[esp32c3] [INF] *** Booting MCUboot build v1.8.0-86-g14763b1 *** +[esp32c3] [INF] Primary image: magic=good, swap_type=0x2, copy_done=0x1, image_ok=0x3 +[esp32c3] [INF] Scratch: magic=unset, swap_type=0x1, copy_done=0x3, image_ok=0x3 +[esp32c3] [INF] Boot source: none +[esp32c3] [INF] Swap type: test +[esp32c3] [INF] Disabling RNG early entropy source... +[esp32c3] [INF] br_image_off = 0x10000 +[esp32c3] [INF] ih_hdr_size = 0x20 +[esp32c3] [INF] DRAM segment: start=0x3fcd0000, size=0x0, vaddr=0x3fcd0000 +[esp32c3] [INF] IRAM segment: start=0x1d00, size=0x170c, vaddr=0x40380000 +[esp32c3] [INF] start=0x40380004 +Hello world! +Hello world! +Hello world! +``` + +#### Direct Boot + +[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. + +##### Booting the Hello World example using Direct Boot + +Build the Hello World example with support for Direct Boot: + +```shell +cargo build --release --example hello_world --features direct-boot +``` + +Then proceed to generating the application binary and flashing it onto the target device: + +```shell +cargo espflash --release --format direct-boot --features direct-boot --example hello_world --monitor +``` + +The ROM Bootloader will identify the firmware image built with Direct Boot support and load it appropriately from the External Flash: + +```shell +ESP-ROM:esp32c3-api1-20210207 +Build:Feb 7 2021 +rst:0x1 (POWERON),boot:0xc (SPI_FAST_FLASH_BOOT) +Hello world! +Hello world! +Hello world! +``` + ## License Licensed under either of: diff --git a/esp32c3-hal/build.rs b/esp32c3-hal/build.rs index f4f7994d54e..8bfe0d5ee00 100644 --- a/esp32c3-hal/build.rs +++ b/esp32c3-hal/build.rs @@ -1,5 +1,21 @@ use std::{env, fs::File, io::Write, path::PathBuf, process::exit}; +// Thanks to kennytm and TheDan64 for the assert_used_features macro. +// Source: +// https://github.com/TheDan64/inkwell/blob/36c3b106e61b1b45295a35f94023d93d9328c76f/src/lib.rs#L81-L110 +macro_rules! assert_unique_features { + () => {}; + ($first:tt $(,$rest:tt)*) => { + $( + #[cfg(all(feature = $first, feature = $rest))] + compile_error!(concat!("Features \"", $first, "\" and \"", $rest, "\" cannot be used together")); + )* + assert_unique_features!($($rest),*); + } +} + +assert_unique_features! {"mcu-boot", "direct-boot"} + #[cfg(feature = "direct-boot")] fn main() { check_opt_level(); @@ -36,7 +52,7 @@ fn main() { add_defaults(); } -#[cfg(not(feature = "direct-boot"))] +#[cfg(not(any(feature = "mcu-boot",feature = "direct-boot")))] fn main() { check_opt_level(); @@ -66,13 +82,55 @@ fn main() { add_defaults(); } +#[cfg(feature = "mcu-boot")] +fn main() { + check_opt_level(); + + // Put the linker script somewhere the linker can find it + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("ld/mb-esp32c3-memory.x")) + .unwrap(); + + File::create(out.join("esp32c3-link.x")) + .unwrap() + .write_all(include_bytes!("ld/mb-esp32c3-link.x")) + .unwrap(); + + File::create(out.join("riscv-link.x")) + .unwrap() + .write_all(include_bytes!("ld/mb-riscv-link.x")) + .unwrap(); + + File::create(out.join("linkall.x")) + .unwrap() + .write_all(include_bytes!("ld/mb-linkall.x")) + .unwrap(); + + println!("cargo:rustc-link-search={}", out.display()); + + // Only re-run the build script when memory.x is changed, + // instead of when any part of the source code changes. + println!("cargo:rerun-if-changed=ld/memory.x"); + + add_defaults(); +} + fn add_defaults() { let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("hal-defaults.x")) .unwrap() .write_all(include_bytes!("ld/hal-defaults.x")) .unwrap(); + File::create(out.join("rom-functions.x")) + .unwrap() + .write_all(include_bytes!("ld/rom-functions.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); } diff --git a/esp32c3-hal/ld/db-esp32c3-memory.x b/esp32c3-hal/ld/db-esp32c3-memory.x index a3d0108689d..af43bd8183c 100644 --- a/esp32c3-hal/ld/db-esp32c3-memory.x +++ b/esp32c3-hal/ld/db-esp32c3-memory.x @@ -32,7 +32,6 @@ MEMORY RTC_FAST : ORIGIN = 0x50000000, LENGTH = 0x2000 /*- ESP_BOOTLOADER_RESERVE_RTC*/ } - REGION_ALIAS("REGION_TEXT", IROM); REGION_ALIAS("REGION_RODATA", DROM); diff --git a/esp32c3-hal/ld/mb-esp32c3-link.x b/esp32c3-hal/ld/mb-esp32c3-link.x new file mode 100644 index 00000000000..0b44b527f2c --- /dev/null +++ b/esp32c3-hal/ld/mb-esp32c3-link.x @@ -0,0 +1,45 @@ +INCLUDE memory.x + +SECTIONS +{ + .metadata : + { + /* Magic for load header */ + + LONG(0xace637d3) + + /* Application entry point address */ + + KEEP(*(.entry_addr)) + + /* IRAM metadata: + * - Destination address (VMA) for IRAM region + * - Flash offset (LMA) for start of IRAM region + * - Size of IRAM region + */ + + LONG(ADDR(.rwtext)) + LONG(LOADADDR(.rwtext)) + LONG(SIZEOF(.rwtext)) + + /* DRAM metadata: + * - Destination address (VMA) for DRAM region + * - Flash offset (LMA) for start of DRAM region + * - Size of DRAM region + */ + + LONG(ADDR(.data)) + LONG(LOADADDR(.data)) + LONG(SIZEOF(.data)) + } > metadata +} + +INCLUDE riscv-link.x + +_image_drom_vma = ADDR(.rodata); +_image_drom_lma = LOADADDR(.rodata); +_image_drom_size = LOADADDR(.rodata) + SIZEOF(.rodata) - _image_drom_lma; + +_image_irom_vma = ADDR(.text); +_image_irom_lma = LOADADDR(.text); +_image_irom_size = LOADADDR(.text) + SIZEOF(.text) - _image_irom_lma; diff --git a/esp32c3-hal/ld/mb-esp32c3-memory.x b/esp32c3-hal/ld/mb-esp32c3-memory.x new file mode 100644 index 00000000000..a7c59cff8ec --- /dev/null +++ b/esp32c3-hal/ld/mb-esp32c3-memory.x @@ -0,0 +1,66 @@ +MEMORY +{ + /* + https://github.com/espressif/esptool/blob/ed64d20b051d05f3f522bacc6a786098b562d4b8/esptool/targets/esp32c3.py#L78-L90 + MEMORY_MAP = [[0x00000000, 0x00010000, "PADDING"], + [0x3C000000, 0x3C800000, "DROM"], + [0x3FC80000, 0x3FCE0000, "DRAM"], + [0x3FC88000, 0x3FD00000, "BYTE_ACCESSIBLE"], + [0x3FF00000, 0x3FF20000, "DROM_MASK"], + [0x40000000, 0x40060000, "IROM_MASK"], + [0x42000000, 0x42800000, "IROM"], + [0x4037C000, 0x403E0000, "IRAM"], + [0x50000000, 0x50002000, "RTC_IRAM"], + [0x50000000, 0x50002000, "RTC_DRAM"], + [0x600FE000, 0x60100000, "MEM_INTERNAL2"]] + */ + + /* The origin values for "metadata" and "ROM" memory regions are the actual + * load addresses. + * + * NOTE: The memory region starting from 0x0 with 0x20 length is reserved + * for the MCUboot header, which will be prepended to the binary file by + * the "imgtool" during the signing of firmware image. + */ + metadata : ORIGIN = 0x20, LENGTH = 0x20 + ROM : ORIGIN = 0x40, LENGTH = 0x400000 - 0x40 + + /* 400K of on soc RAM, 16K reserved for cache */ + ICACHE : ORIGIN = 0x4037C000, LENGTH = 0x4000 + /* Instruction RAM */ + IRAM : ORIGIN = 0x4037C000 + 0x4000, LENGTH = 400K - 0x4000 + /* Data RAM */ + DRAM : ORIGIN = 0x3FC80000, LENGTH = 0x50000 + + /* External flash */ + /* Instruction ROM */ + IROM : ORIGIN = 0x42000000, LENGTH = 0x400000 + /* Data ROM */ + /* The DROM segment origin is offset by 0x40 for mirroring the actual ROM + * image layout: + * 0x0 - 0x1F : MCUboot header + * 0x20 - 0x3F : Application image metadata section + * 0x40 onwards: ROM code and data + * This is required to meet the following constraint from the external + * flash MMU: + * VMA % 64KB == LMA % 64KB + * i.e. the lower 16 bits of both the virtual address (address seen by the + * CPU) and the load address (physical address of the external flash) must + * be equal. + */ + DROM : ORIGIN = 0x3C000000 + 0x40, LENGTH = 0x400000 - 0x40 + + /* RTC fast memory (executable). Persists over deep sleep. */ + RTC_FAST : ORIGIN = 0x50000000, LENGTH = 0x2000 /*- ESP_BOOTLOADER_RESERVE_RTC*/ +} + +REGION_ALIAS("REGION_TEXT", IROM); +REGION_ALIAS("REGION_RODATA", DROM); + +REGION_ALIAS("REGION_DATA", DRAM); +REGION_ALIAS("REGION_BSS", DRAM); +REGION_ALIAS("REGION_HEAP", DRAM); +REGION_ALIAS("REGION_STACK", DRAM); + +REGION_ALIAS("REGION_RWTEXT", IRAM); +REGION_ALIAS("REGION_RTC_FAST", RTC_FAST); diff --git a/esp32c3-hal/ld/mb-linkall.x b/esp32c3-hal/ld/mb-linkall.x new file mode 100644 index 00000000000..02bc894cd57 --- /dev/null +++ b/esp32c3-hal/ld/mb-linkall.x @@ -0,0 +1,3 @@ +INCLUDE "esp32c3-link.x" +INCLUDE "hal-defaults.x" +INCLUDE "rom-functions.x" diff --git a/esp32c3-hal/ld/mb-riscv-link.x b/esp32c3-hal/ld/mb-riscv-link.x new file mode 100644 index 00000000000..1190c755a47 --- /dev/null +++ b/esp32c3-hal/ld/mb-riscv-link.x @@ -0,0 +1,250 @@ +ENTRY(_start_hal) +PROVIDE(_start_trap = _start_trap_hal); + +PROVIDE(_stack_start = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK)); +PROVIDE(_max_hart_id = 0); +PROVIDE(_hart_stack_size = 2K); +PROVIDE(_heap_size = 0); + +PROVIDE(UserSoft = DefaultHandler); +PROVIDE(SupervisorSoft = DefaultHandler); +PROVIDE(MachineSoft = DefaultHandler); +PROVIDE(UserTimer = DefaultHandler); +PROVIDE(SupervisorTimer = DefaultHandler); +PROVIDE(MachineTimer = DefaultHandler); +PROVIDE(UserExternal = DefaultHandler); +PROVIDE(SupervisorExternal = DefaultHandler); +PROVIDE(MachineExternal = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultInterruptHandler); +PROVIDE(ExceptionHandler = DefaultExceptionHandler); + +/* # Pre-initialization function */ +/* If the user overrides this using the `#[pre_init]` attribute or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = default_pre_init); + +/* A PAC/HAL defined routine that should initialize custom interrupt controller if needed. */ +PROVIDE(_setup_interrupts = default_setup_interrupts); + +/* # Multi-processing hook function + fn _mp_hook() -> bool; + + This function is called from all the harts and must return true only for one hart, + which will perform memory initialization. For other harts it must return false + and implement wake-up in platform-dependent way (e.g. after waiting for a user interrupt). +*/ +PROVIDE(_mp_hook = default_mp_hook); + +SECTIONS +{ + .rodata : + { + _srodata = .; + *(EXCLUDE_FILE (*libriscv-*.rlib:riscv.*) .rodata); + *(EXCLUDE_FILE (*libriscv-*.rlib:riscv.*) .rodata.*); + *(EXCLUDE_FILE (*libriscv_rt-*.rlib:riscv-rt.*) .rodata); + *(EXCLUDE_FILE (*libriscv_rt-*.rlib:riscv-rt.*) .rodata.*); + *(.srodata .srodata.*); + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + _erodata = .; + } > REGION_RODATA AT>ROM + + .rwtext : + { + _srwtext = .; + /* Put reset handler first in .rwtext section so it ends up as the entry */ + /* point of the program. */ + KEEP(*(.init)); + KEEP(*(.init.rust)); + . = ALIGN(4); + KEEP(*(.trap)); + KEEP(*(.trap.rust)); + + *libriscv-*.rlib:riscv.*(.literal .text .literal.* .text.*); + *libriscv_rt-*.rlib:riscv-rt.*(.literal .text .literal.* .text.*); + *(.rwtext); + . = ALIGN(4); + _erwtext = .; + } > REGION_RWTEXT AT>ROM + + .rwtext.dummy (NOLOAD): + { + /* This section is required to skip .rwtext area because REGION_RWTEXT + * and REGION_BSS reflect the same address space on different buses. + */ + + . = ORIGIN(REGION_BSS) + _erwtext - _srwtext; + } > REGION_BSS + + .bss (NOLOAD) : + { + _sbss = .; + *(.sbss .sbss.* .bss .bss.*); + . = ALIGN(4); + _ebss = .; + } > REGION_BSS + + /* fictitious region that represents the memory available for the heap */ + .heap (NOLOAD) : + { + _sheap = .; + . += _heap_size; + . = ALIGN(4); + _eheap = .; + } > REGION_HEAP + + /* fictitious region that represents the memory available for the stack */ + .stack (NOLOAD) : + { + _estack = .; + . = ABSOLUTE(_stack_start); + _sstack = .; + } > REGION_STACK + + .data : + { + _sdata = .; + /* Must be called __global_pointer$ for linker relaxations to work. */ + PROVIDE(__global_pointer$ = . + 0x800); + *(.sdata .sdata.* .sdata2 .sdata2.*); + *(.data .data.*); + *libriscv-*.rlib:riscv.*(.rodata .rodata.*); + *libriscv_rt-*.rlib:riscv-rt.*(.rodata .rodata.*); + . = ALIGN(4); + _edata = .; + } > REGION_DATA AT>ROM + + .rtc_fast.text : + { + _srtc_fast_text = .; + *(.rtc_fast.literal .rtc_fast.text .rtc_fast.literal.* .rtc_fast.text.*) + . = ALIGN(4); + _ertc_fast_text = .; + } > REGION_RTC_FAST AT>ROM + + .rtc_fast.data : + { + _rtc_fast_data_start = ABSOLUTE(.); + *(.rtc_fast.data .rtc_fast.data.*) + . = ALIGN(4); + _rtc_fast_data_end = ABSOLUTE(.); + } > REGION_RTC_FAST AT>ROM + + .rtc_fast.bss (NOLOAD) : ALIGN(4) + { + _rtc_fast_bss_start = ABSOLUTE(.); + *(.rtc_fast.bss .rtc_fast.bss.*) + . = ALIGN(4); + _rtc_fast_bss_end = ABSOLUTE(.); + } > REGION_RTC_FAST + + .rtc_fast.noinit (NOLOAD) : ALIGN(4) + { + *(.rtc_fast.noinit .rtc_fast.noinit.*) + } > REGION_RTC_FAST + + /* The alignment of the "text" output section is forced to + * 0x00010000 (64KB) to ensure that it will be allocated at the beginning + * of the next available Flash block. + * This is required to meet the following constraint from the external + * flash MMU: + * VMA % 64KB == LMA % 64KB + * i.e. the lower 16 bits of both the virtual address (address seen by the + * CPU) and the load address (physical address of the external flash) must + * be equal. + */ + + .text.dummy (NOLOAD) : ALIGN(0x10000) + { + /* This section is required to skip .rodata area because REGION_TEXT + * and REGION_RODATA reflect the same address space on different buses. + */ + + . += SIZEOF(.rodata); + } > REGION_TEXT + + .text : ALIGN(0x10000) + { + _stext = .; + *(EXCLUDE_FILE (*libriscv-*.rlib:riscv.*) .text) + *(EXCLUDE_FILE (*libriscv-*.rlib:riscv.*) .text.*) + *(EXCLUDE_FILE (*libriscv_rt-*.rlib:riscv-rt.*) .text) + *(EXCLUDE_FILE (*libriscv_rt-*.rlib:riscv-rt.*) .text.*) + *(.text .text.*); + _etext = .; + } > REGION_TEXT AT>ROM + + /* fake output .got section */ + /* Dynamic relocations are unsupported. This section is only used to detect + relocatable code in the input files and raise an error if relocatable code + is found */ + .got (INFO) : + { + KEEP(*(.got .got.*)); + } + + .eh_frame (INFO) : { KEEP(*(.eh_frame)) } + .eh_frame_hdr (INFO) : { *(.eh_frame_hdr) } +} + +PROVIDE(_sidata = _erodata + 8); +PROVIDE(_irwtext = ORIGIN(DROM) + _text_size + _rodata_size + _data_size); +PROVIDE(_irtc_fast_text = ORIGIN(DROM) + _text_size + _rodata_size + _data_size + _rwtext_size); +PROVIDE(_irtc_fast_data = ORIGIN(DROM) + _text_size + _rodata_size + _data_size + _rwtext_size + _fast_text_size); + +/* Do not exceed this mark in the error messages above | */ +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_RODATA) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_RODATA must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_DATA) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_DATA must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_HEAP) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_HEAP must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_TEXT) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_TEXT must be 4-byte aligned"); + +ASSERT(ORIGIN(REGION_STACK) % 4 == 0, " +ERROR(riscv-rt): the start of the REGION_STACK must be 4-byte aligned"); + +ASSERT(_stext % 4 == 0, " +ERROR(riscv-rt): `_stext` must be 4-byte aligned"); + +ASSERT(_sdata % 4 == 0 && _edata % 4 == 0, " +BUG(riscv-rt): .data is not 4-byte aligned"); + +ASSERT(_sidata % 4 == 0, " +BUG(riscv-rt): the LMA of .data is not 4-byte aligned"); + +ASSERT(_sbss % 4 == 0 && _ebss % 4 == 0, " +BUG(riscv-rt): .bss is not 4-byte aligned"); + +ASSERT(_sheap % 4 == 0, " +BUG(riscv-rt): start of .heap is not 4-byte aligned"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT), " +ERROR(riscv-rt): The .text section must be placed inside the REGION_TEXT region. +Set _stext to an address smaller than 'ORIGIN(REGION_TEXT) + LENGTH(REGION_TEXT)'"); + +ASSERT(SIZEOF(.stack) > (_max_hart_id + 1) * _hart_stack_size, " +ERROR(riscv-rt): .stack section is too small for allocating stacks for all the harts. +Consider changing `_max_hart_id` or `_hart_stack_size`."); + +ASSERT(SIZEOF(.got) == 0, " +.got section detected in the input files. Dynamic relocations are not +supported. If you are linking to C code compiled using the `gcc` crate +then modify your build script to compile the C code _without_ the +-fPIC flag. See the documentation of the `gcc::Config.fpic` method for +details."); + +/* Do not exceed this mark in the error messages above | */ diff --git a/esp32c3-hal/ld/rom-functions.x b/esp32c3-hal/ld/rom-functions.x new file mode 100644 index 00000000000..34431f0a21b --- /dev/null +++ b/esp32c3-hal/ld/rom-functions.x @@ -0,0 +1,7 @@ +ets_printf = 0x40000040; +PROVIDE(esp_rom_printf = ets_printf); +PROVIDE(cache_invalidate_icache_all = 0x400004d8); +PROVIDE(cache_suspend_icache = 0x40000524); +PROVIDE(cache_resume_icache = 0x40000528); +PROVIDE(cache_ibus_mmu_set = 0x40000560); +PROVIDE(cache_dbus_mmu_set = 0x40000564); diff --git a/esp32c3-hal/src/lib.rs b/esp32c3-hal/src/lib.rs index 9efac024a54..53a2b118038 100644 --- a/esp32c3-hal/src/lib.rs +++ b/esp32c3-hal/src/lib.rs @@ -1,6 +1,9 @@ #![no_std] -use core::arch::global_asm; +use core::arch::{asm, global_asm}; + +#[cfg(feature = "mcu-boot")] +use core::mem::size_of; pub use embedded_hal as ehal; pub use esp_hal_common::{ @@ -31,6 +34,7 @@ pub use esp_hal_common::{ Serial, UsbSerialJtag, }; + #[cfg(feature = "direct-boot")] use riscv_rt::pre_init; @@ -45,6 +49,49 @@ pub mod analog { } extern "C" { + cfg_if::cfg_if! { + if #[cfg(feature = "mcu-boot")] { + // Functions from internal ROM + fn cache_suspend_icache() -> u32; + fn cache_resume_icache(val: u32); + fn cache_invalidate_icache_all(); + fn cache_dbus_mmu_set( + ext_ram: u32, + vaddr: u32, + paddr: u32, + psize: u32, + num: u32, + fixed: u32, + ) -> i32; + fn cache_ibus_mmu_set( + ext_ram: u32, + vaddr: u32, + paddr: u32, + psize: u32, + num: u32, + fixed: u32, + ) -> i32; + + /* IROM metadata: + * - Destination address (VMA) for IROM region + * - Flash offset (LMA) for start of IROM region + * - Size of IROM region + */ + static mut _image_irom_vma: u32; + static mut _image_irom_lma: u32; + static mut _image_irom_size: u32; + + /* DROM metadata: + * - Destination address (VMA) for DROM region + * - Flash offset (LMA) for start of DROM region + * - Size of DROM region + */ + static mut _image_drom_vma: u32; + static mut _image_drom_lma: u32; + static mut _image_drom_size: u32; + } + } + // Boundaries of the .iram section static mut _srwtext: u32; static mut _erwtext: u32; @@ -190,97 +237,91 @@ _start_trap_hal: "# ); -global_asm!( - r#" -.section .init, "ax" -.global _start_hal - -_start_hal: - /* Jump to the absolute address defined by the linker script. */ - lui ra, %hi(_abs_start_hal) - jr %lo(_abs_start_hal)(ra) -"# -); - -global_asm!( - r#" -.section .text - -_abs_start_hal: - .option norelax - .cfi_startproc - .cfi_undefined ra - - // unsupported on ESP32C3 - // csrw mie, 0 - // csrw mip, 0 - - li x1, 0 - li x2, 0 - li x3, 0 - li x4, 0 - li x5, 0 - li x6, 0 - li x7, 0 - li x8, 0 - li x9, 0 - li x10,0 - li x11,0 - li x12,0 - li x13,0 - li x14,0 - li x15,0 - li x16,0 - li x17,0 - li x18,0 - li x19,0 - li x20,0 - li x21,0 - li x22,0 - li x23,0 - li x24,0 - li x25,0 - li x26,0 - li x27,0 - li x28,0 - li x29,0 - li x30,0 - li x31,0 +#[cfg(feature = "mcu-boot")] +#[link_section = ".entry_addr"] +#[no_mangle] +#[used] +// Entry point address for the MCUboot image header +static ENTRY_POINT: unsafe fn() -> ! = start_hal; + +#[link_section = ".init"] +#[export_name = "_start_hal"] +unsafe fn start_hal() -> ! { + asm!( + r#" + .option norelax + + // unsupported on ESP32-C3 + // csrw mie, 0 + // csrw mip, 0 + + li x1, 0 + li x2, 0 + li x3, 0 + li x4, 0 + li x5, 0 + li x6, 0 + li x7, 0 + li x8, 0 + li x9, 0 + li x10,0 + li x11,0 + li x12,0 + li x13,0 + li x14,0 + li x15,0 + li x16,0 + li x17,0 + li x18,0 + li x19,0 + li x20,0 + li x21,0 + li x22,0 + li x23,0 + li x24,0 + li x25,0 + li x26,0 + li x27,0 + li x28,0 + li x29,0 + li x30,0 + li x31,0 + + .option push + .option norelax + la gp, __global_pointer$ + .option pop + + // Check hart id + csrr a2, mhartid + lui t0, %hi(_max_hart_id) + add t0, t0, %lo(_max_hart_id) + bgtu a2, t0, abort_hal + + // Allocate stacks + la sp, _stack_start + lui t0, %hi(_hart_stack_size) + add t0, t0, %lo(_hart_stack_size) + + beqz a2, 2f // Jump if single-hart + mv t1, a2 + mv t2, t0 + 1: + add t0, t0, t2 + addi t1, t1, -1 + bnez t1, 1b + 2: + sub sp, sp, t0 + + // Set frame pointer + add s0, sp, zero + + jal zero, _start_rust + "# + ); - .option push - .option norelax - la gp, __global_pointer$ - .option pop - - // Check hart id - csrr a2, mhartid - lui t0, %hi(_max_hart_id) - add t0, t0, %lo(_max_hart_id) - bgtu a2, t0, abort_hal - - // Allocate stacks - la sp, _stack_start - lui t0, %hi(_hart_stack_size) - add t0, t0, %lo(_hart_stack_size) - - beqz a2, 2f // Jump if single-hart - mv t1, a2 - mv t2, t0 -1: - add t0, t0, t2 - addi t1, t1, -1 - bnez t1, 1b -2: - sub sp, sp, t0 - - // Set frame pointer - add s0, sp, zero - - jal zero, _start_rust - - .cfi_endproc -"# -); + unreachable!() +} global_asm!( r#" @@ -306,10 +347,85 @@ unsafe fn init() { r0::init_data(&mut _srtc_fast_text, &mut _ertc_fast_text, &_irtc_fast_text); } +#[cfg(feature = "mcu-boot")] +#[link_section = ".rwtext"] +unsafe fn configure_mmu() { + const PARTITION_OFFSET: u32 = 0x10000; + let app_irom_lma = PARTITION_OFFSET + ((&_image_irom_lma as *const u32) as u32); + let app_irom_size = (&_image_irom_size as *const u32) as u32; + let app_irom_vma = (&_image_irom_vma as *const u32) as u32; + let app_drom_lma = PARTITION_OFFSET + ((&_image_drom_lma as *const u32) as u32); + let app_drom_size = (&_image_drom_size as *const u32) as u32; + let app_drom_vma = (&_image_drom_vma as *const u32) as u32; + + let autoload = cache_suspend_icache(); + cache_invalidate_icache_all(); + + /* Clear the MMU entries that are already set up, so the new app only has + * the mappings it creates. + */ + + const FLASH_MMU_TABLE: *mut u32 = 0x600c_5000 as *mut u32; + const ICACHE_MMU_SIZE: usize = 0x200; + const FLASH_MMU_TABLE_SIZE: usize = ICACHE_MMU_SIZE / size_of::(); + const MMU_TABLE_INVALID_VAL: u32 = 0x100; + + for i in 0..FLASH_MMU_TABLE_SIZE { + FLASH_MMU_TABLE.add(i).write_volatile(MMU_TABLE_INVALID_VAL); + } + + const MMU_BLOCK_SIZE: u32 = 0x0001_0000; + const MMU_FLASH_MASK: u32 = !(MMU_BLOCK_SIZE - 1); + + let calc_mmu_pages = |size, vaddr| { + (size + (vaddr - (vaddr & MMU_FLASH_MASK)) + MMU_BLOCK_SIZE - 1) / MMU_BLOCK_SIZE + }; + + let drom_lma_aligned = app_drom_lma & MMU_FLASH_MASK; + let drom_vma_aligned = app_drom_vma & MMU_FLASH_MASK; + let drom_page_count = calc_mmu_pages(app_drom_size, app_drom_vma); + cache_dbus_mmu_set( + 0, + drom_vma_aligned, + drom_lma_aligned, + 64, + drom_page_count, + 0, + ); + + let irom_lma_aligned = app_irom_lma & MMU_FLASH_MASK; + let irom_vma_aligned = app_irom_vma & MMU_FLASH_MASK; + let irom_page_count = calc_mmu_pages(app_irom_size, app_irom_vma); + cache_ibus_mmu_set( + 0, + irom_vma_aligned, + irom_lma_aligned, + 64, + irom_page_count, + 0, + ); + + let peripherals = pac::Peripherals::steal(); + peripherals.EXTMEM.icache_ctrl1.modify(|_, w| { + w.icache_shut_ibus() + .clear_bit() + .icache_shut_dbus() + .clear_bit() + }); + + cache_resume_icache(autoload); +} + #[allow(unreachable_code)] #[export_name = "_mp_hook"] #[doc(hidden)] +#[cfg_attr(feature = "mcu-boot", link_section = ".rwtext")] pub fn mp_hook() -> bool { + #[cfg(feature = "mcu-boot")] + unsafe { + configure_mmu(); + } + unsafe { r0::zero_bss(&mut _rtc_fast_bss_start, &mut _rtc_fast_bss_end); }