diff --git a/.cargo/config.toml b/.cargo/config.toml index 9e68c152..3ac78d2d 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,7 @@ [alias] xtask = 'run -p xtask --' nrf52840 = 'run -p xtask --features nrf52840 -- nrf52840' +nrf9160 = 'run -p xtask --features nrf9160 -- nrf9160' stm32f411 = 'run -p xtask --features stm32f411 -- stm32f411' stm32f446 = 'run -p xtask --features stm32f446 -- stm32f446' stm32f469 = 'run -p xtask --features stm32f469 -- stm32f469' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17c7a312..4901c4b1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,7 @@ jobs: cargo +nightly test --package rustBoot --lib --features stm32f746 -- parser::tests --nocapture cargo +nightly test --package rustBoot --lib --features stm32f334 -- parser::tests --nocapture cargo +nightly test --package rustBoot --lib --features rp2040 -- parser::tests --nocapture + cargo +nightly test --package rustBoot --lib --features nrf9160 -- parser::tests --nocapture builds: runs-on: ${{ matrix.os }} @@ -72,6 +73,13 @@ jobs: use-cross: false command: run args: -p xtask --features nrf52840 -- nrf52840 build rustBoot-only + - name: nrf9160 + if: matrix.target == 'thumbv8m.main-none-eabihf' + uses: actions-rs/cargo@v1 + with: + use-cross: false + command: run + args: -p xtask --features nrf9160 -- nrf9160 build rustBoot-only - name: stm32f411 if: matrix.target == 'thumbv7em-none-eabihf' uses: actions-rs/cargo@v1 diff --git a/DCO.md b/DCO.md new file mode 100644 index 00000000..f437b1ce --- /dev/null +++ b/DCO.md @@ -0,0 +1,85 @@ +# Developer Certificate of Origin (DCO) + +rustBoot enforces the Developer Certificate of Origin (DCO). It requires all commit messages to contain the `Signed-off-by` line with an email address that matches the commit author and the name on your GitHub account. + +The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the DCO, reformatted for readability: + +```text +By making a contribution to this project, I certify that: + +The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or + +The contribution is based upon previous work that, to the best of my knowledge, is covered under an > appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or + +The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. + +I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. +``` + +Contributors sign-off that they adhere to these requirements by adding a `Signed-off-by` line to commit messages. + +## How to sign-off + +The project requires a sign-off message in the following format appear on each commit in the pull request: + +```text +feat: new feature + +Signed-off-by: John Smith +``` + +The text can either be manually added to your commit body, or you can add either `-s` or `--signoff` to your usual git commit commands. + +#### Creating your signoff + +Git has a `-s | --signoff` command-line option to append this automatically to your commit message: + +```bash +git commit --signoff --message 'This is my commit message' +``` + +```bash +git commit -s -m "This is my commit message" +``` + +This will use your default git configuration which is found in `.git/config` and usually, it is the `username systemaddress` of the machine which you are using. + +To change this, you can use the following commands (Note these only change the current repo settings, you will need to add `--global` for these commands to change the installation default). + +Your name: + +```bash +git config user.name "FIRST_NAME LAST_NAME" +``` + +Your email: + +```bash +git config user.email "MY_NAME@example.com" +``` + +#### How to amend a sign-off + +If you have authored a commit that is missing the signed-off-by line, you can amend your commits and push them to GitHub + +```bash +git commit --amend --signoff +``` + +If you've pushed your changes to GitHub already you'll need to force push your branch after this with `git push -f`. + +## DCO Failures + +The project uses a DCO bot for all GitHub pulls to verify that each commit is signed off. When you create your pull request, it will automatically be verified by this bot. An example of what to expect is below. + +![DCO Bot image](docs/images/dco.png) + +If your Pull Request fails the DCO check, it's necessary to fix the entire commit history in the PR. Although this is a situation we'd like to avoid the best practice is to squash the commit history to a single commit, append the DCO sign-off as described above or interactively in the rebase comment editing process, and force push. For example, if you have 2 commits in your history (Note the ~2): + +```bash +git rebase --interactive HEAD~2 +(interactive squash + DCO append) +git push origin --force +``` + +> Note, that in general rewriting history in this way is something that can cause issues with the review process and this should only be done to correct a DCO mistake. \ No newline at end of file diff --git a/README.md b/README.md index 35926a77..761cba60 100644 --- a/README.md +++ b/README.md @@ -51,4 +51,4 @@ rustBoot is licensed under * MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) ## Contributing: -Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the MIT license, shall be licensed as above, without any additional terms or conditions. \ No newline at end of file +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the MIT license, shall be licensed as above, without any additional terms or conditions.We enforce [developer certificate of origin (DCO) commit signing](./DCO.md). \ No newline at end of file diff --git a/boards/bootloaders/nrf9160/.cargo/config.toml b/boards/bootloaders/nrf9160/.cargo/config.toml new file mode 100644 index 00000000..20c9eb49 --- /dev/null +++ b/boards/bootloaders/nrf9160/.cargo/config.toml @@ -0,0 +1,17 @@ +# ============================================================================= +# Build configuration options for Cortex-M +# ============================================================================= + +[build] +target = "thumbv8m.main-none-eabihf" + +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-run --chip nRF9160_xxAA" # runner specific to nrf52840. Replace this with probe-run option for your board. +rustflags = [ + "-C", "linker=flip-link", + "-C", "link-arg=-Tlink.x", + # "-C", "link-arg=-Tdefmt.x", + # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x + # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + "-C", "link-arg=--nmagic", +] \ No newline at end of file diff --git a/boards/bootloaders/nrf9160/Cargo.toml b/boards/bootloaders/nrf9160/Cargo.toml new file mode 100644 index 00000000..7da19a60 --- /dev/null +++ b/boards/bootloaders/nrf9160/Cargo.toml @@ -0,0 +1,31 @@ +[package] +build = "build.rs" +edition = "2018" +name = "nrf9160" +version = "0.1.0" + +# makes `cargo check --all-targets` work +[[bin]] +bench = false +doctest = false +name = "nrf9160" +test = false + +[dependencies] +cortex-m-rt = "0.7" +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +rustBoot-hal = {path = "../../hal", features = ["nrf9160", "nrf"]} + +#trustzone-m-macros = { path = "../../../tools/trustzone-m-tools/macros" } +#trustzone-m-secure-rt = {path = "../../tools/trustzone-m-tools/secure-rt", features = ["nrf9160"]} + +nrf9160-pac = "0.12.2" +spin = "0.5" + +rustBoot-update = {path = "../../update", features = ["nrf9160"]} +defmt = {version = "0.3.1", optional = true} +defmt-rtt = {version = "0.3.2", optional = true} + +[features] +default = ["defmt", "defmt-rtt"] + diff --git a/boards/bootloaders/nrf9160/README.md b/boards/bootloaders/nrf9160/README.md new file mode 100644 index 00000000..ccf18e76 --- /dev/null +++ b/boards/bootloaders/nrf9160/README.md @@ -0,0 +1,28 @@ +`rustBoot` support for [nrf9160](https://www.nordicsemi.com/Products/Development-hardware/nrf9160-dk) development board, we have one example. It has 4 leds. If you're using a different version of the board, you'll probably need to edit `firmware and hal implementations` to accomodate for differences. Just make sure you **dont change** the name of files/folders or the folder structure, as `cargo xtask` looks for these file/folder names. + +- In order to test this example you'll need a couple of things - `wolfcrypt, probe-run, python3, nrf-connect Programmer installed` +- If you've managed to install all of them, you can use below commands to build and sign all 3 packages (i.e. bootloader + bootfw + updatefw) onto the board. + - Command for build rustBoot + `cargo nrf9160 build rustBoot-only` + + - Command for build packages + `cargo nrf9160 build pkgs-for` + + - Command for sign packages + `cargo nrf9160 sign pkgs-for` + +- In order to flash all 3 binarise (i.e. bootloader + bootfw + updatefw) I've used `probe-rs-cli` and `probe-rs-cli`. + - To flash bootloader use this command + `probe-run < bootloader file name > --chip NRF9160_XXAA` + - To flash bootfw + updatefw use following command + 'probe-rs-cli download --format Bin --base-address {boot_part_addr} --chip nRF9160_xxAA nrf9160_bootfw_v_signed.bin' + +- In order to confirm that its working, I've configured the `bootfw to turn ON LED1 and blink LED2` for a few seconds, trigger an update and then reset. Upon reset, the bootloader verifies the update and swaps the contents of boot and update partitions. If everything checks out, it boots into the update, `turn ON LED3 and blink LED4` and finally sets the confirmation flag to indicate that the update was successful. + +Here's the [command line output](/boards/bootloaders/stm32h723/debug.md). + +## Blinky(s): + +**blinks green before image verification and swap, after trigger an update, blinks red after image verification and swap:** + +[![bootfw_and_updtfw](https://user-images.githubusercontent.com/92363511/173661166-bad18bd5-8e35-4429-8852-93ea29b46ed9.png)](https://user-images.githubusercontent.com/92363511/173660773-4f4d7cbd-6d43-4418-b5b5-099619054aff.mov) \ No newline at end of file diff --git a/boards/bootloaders/nrf9160/build.rs b/boards/bootloaders/nrf9160/build.rs new file mode 100644 index 00000000..65b0b7a4 --- /dev/null +++ b/boards/bootloaders/nrf9160/build.rs @@ -0,0 +1,42 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // 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!("memory.x")) + .unwrap(); + + let mut linker_scripts = vec![( + &include_bytes!("trustzone_memory.x.in")[..], + "trustzone_memory.x", + )]; + + if cfg!(feature = "_nrf") { + linker_scripts.push(( + &include_bytes!("nrf_region_asserts.x.in")[..], + "region_asserts.x", + )); + } else { + linker_scripts.push(( + &include_bytes!("no_region_asserts.x.in")[..], + "region_asserts.x", + )); + } + + for (script_bytes, script_name) in linker_scripts { + let mut f = File::create(out.join(script_name)).unwrap(); + f.write_all(script_bytes).unwrap(); + + println!("cargo:rerun-if-changed={script_name}.in"); + } + + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=memory.x"); + +} diff --git a/boards/bootloaders/nrf9160/memory.x b/boards/bootloaders/nrf9160/memory.x new file mode 100644 index 00000000..df2c7a40 --- /dev/null +++ b/boards/bootloaders/nrf9160/memory.x @@ -0,0 +1,12 @@ +MEMORY +{ + FLASH : ORIGIN = 0x00000000, LENGTH = 188K + NSC_FLASH : ORIGIN = 0x0002F000, LENGTH = 4K + NS_FLASH : ORIGIN = 0x00030000, LENGTH = 832K + + RAM : ORIGIN = 0x20000000, LENGTH = 128K + NS_RAM : ORIGIN = 0x20020000, LENGTH = 128K +} + +INCLUDE trustzone_memory.x + diff --git a/boards/bootloaders/nrf9160/no_region_asserts.x.in b/boards/bootloaders/nrf9160/no_region_asserts.x.in new file mode 100644 index 00000000..0890a9d0 --- /dev/null +++ b/boards/bootloaders/nrf9160/no_region_asserts.x.in @@ -0,0 +1 @@ +/* Purposefully left empty */ \ No newline at end of file diff --git a/boards/bootloaders/nrf9160/nrf_region_asserts.x.in b/boards/bootloaders/nrf9160/nrf_region_asserts.x.in new file mode 100644 index 00000000..aec3efcf --- /dev/null +++ b/boards/bootloaders/nrf9160/nrf_region_asserts.x.in @@ -0,0 +1,5 @@ +ASSERT(LENGTH(NSC_FLASH) <= 4096, "ERROR(trustzone): The NSC flash region cannot be bigger than 4096 bytes"); +ASSERT(LENGTH(NSC_FLASH) >= 32, "ERROR(trustzone): The NSC flash region cannot be smaller than 32 bytes"); +ASSERT((LENGTH(NSC_FLASH) & (LENGTH(NSC_FLASH) - 1)) == 0, "ERROR(trustzone): The NSC flash region must have a length that is a power of 2"); + +ASSERT(_s_flash_end == _nsc_flash_start, "ERROR(trustzone): The NSC flash region must come right after the S flash region"); diff --git a/boards/bootloaders/nrf9160/src/main.rs b/boards/bootloaders/nrf9160/src/main.rs new file mode 100644 index 00000000..6e551f17 --- /dev/null +++ b/boards/bootloaders/nrf9160/src/main.rs @@ -0,0 +1,102 @@ +#![no_std] +#![no_main] +#![feature(abi_c_cmse_nonsecure_call)] +#![feature(cmse_nonsecure_entry)] +#![feature(type_alias_impl_trait)] + +#[cfg(feature = "defmt")] +use defmt_rtt as _; // global logger + +use rustBoot_hal::nrf::nrf9160::{FlashWriterEraser, initialize}; +use rustBoot_update::update::{update_flash::FlashUpdater, UpdateInterface}; + +use cortex_m_rt::entry; + +#[entry] +fn main() -> ! { + let dp = nrf9160_pac::Peripherals::take().unwrap(); + + unsafe { + (*cortex_m::peripheral::SCB::PTR) + .shcsr + .write((1 << 19) | (1 << 18) | (1 << 17) | (1 << 16)) + }; + + initialize( + [ + (dp.SPIM0_S, dp.SPIS0_S, dp.TWIM0_S, dp.TWIS0_S, dp.UARTE0_S).into(), + (dp.SPIM1_S, dp.SPIS1_S, dp.TWIM1_S, dp.TWIS1_S, dp.UARTE1_S).into(), + (dp.SPIM2_S, dp.SPIS2_S, dp.TWIM2_S, dp.TWIS2_S, dp.UARTE2_S).into(), + (dp.SPIM3_S, dp.SPIS3_S, dp.TWIM3_S, dp.TWIS3_S, dp.UARTE3_S).into(), + (&dp.P0_S).into(), + (&dp.KMU_S, &dp.NVMC_S).into(), + (dp.CLOCK_S, dp.POWER_S).into(), + (dp.RTC0_S).into(), + (dp.RTC1_S).into(), + ], + [ + (0, 0), + (0, 1), + (0, 2), + (0, 3), + (0, 4), + (0, 5), + (0, 6), + (0, 7), + (0, 8), + (0, 9), + (0, 10), + (0, 11), + (0, 12), + (0, 13), + (0, 14), + (0, 15), + (0, 16), + (0, 17), + (0, 18), + (0, 19), + (0, 20), + (0, 21), + (0, 22), + (0, 23), + (0, 24), + (0, 25), + (0, 26), + (0, 27), + // (0, 28), + // (0, 29), + (0, 30), + // (0, 31), + ], + [ + (0, 0), + (0, 1), + (0, 2), + (0, 3), + (0, 4), + (0, 5), + (0, 6), + (0, 7), + (0, 8), + (0, 9), + (0, 10), + (0, 11), + (0, 12), + (0, 13), + (0, 14), + (0, 15), + ], + ); + + // defmt::println!("Non secure memory initialized"); + + let updater = FlashUpdater::new(FlashWriterEraser::new(dp.NVMC_S, dp.NVMC_NS, true)); + updater.rustboot_start() +} + +#[panic_handler] // panicking behavior +fn panic(_: &core::panic::PanicInfo) -> ! { + loop { + cortex_m::asm::bkpt(); + } +} diff --git a/boards/bootloaders/nrf9160/trustzone_memory.x.in b/boards/bootloaders/nrf9160/trustzone_memory.x.in new file mode 100644 index 00000000..4376c6ad --- /dev/null +++ b/boards/bootloaders/nrf9160/trustzone_memory.x.in @@ -0,0 +1,31 @@ +_NS_VENEERS = ORIGIN(NS_FLASH); +_NSC_VENEERS = ORIGIN(NSC_FLASH); + +_s_flash_start = ORIGIN(FLASH); +_s_flash_end = _s_flash_start + LENGTH(FLASH); + +_nsc_flash_start = ORIGIN(NSC_FLASH); +_nsc_flash_end = _nsc_flash_start + LENGTH(NSC_FLASH); + +_ns_flash_start = ORIGIN(NS_FLASH); +_ns_flash_end = _ns_flash_start + LENGTH(NS_FLASH); + +_s_ram_start = ORIGIN(RAM); +_s_ram_end = _s_ram_start + LENGTH(RAM); + +_ns_ram_start = ORIGIN(NS_RAM); +_ns_ram_end = _ns_ram_start + LENGTH(NS_RAM); + +SECTIONS +{ + /* ### .ns_vectors */ + .nsc_vectors ORIGIN(NSC_FLASH) : + { + KEEP(*(.nsc_veneers.searcher)); + KEEP(*(.nsc_veneers)); + . = . + 12; /* Add an empty veneer at the end that should end up as 0's to indicate that we've reached the end */ + . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */ + } > NSC_FLASH = 0 +} + +INCLUDE region_asserts.x diff --git a/boards/firmware/nrf9160/boot_fw_blinky_green/.cargo/config.toml b/boards/firmware/nrf9160/boot_fw_blinky_green/.cargo/config.toml new file mode 100644 index 00000000..59a9efd0 --- /dev/null +++ b/boards/firmware/nrf9160/boot_fw_blinky_green/.cargo/config.toml @@ -0,0 +1,17 @@ +# ============================================================================= +# Build configuration options for Cortex-M +# ============================================================================= + +[build] +target = "thumbv8m.main-none-eabihf" + +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-run --chip nRF9160_xxAA --no-flash" # runner specific to nrf52840. Replace this with probe-run option for your board. +rustflags = [ + "-C", "linker=flip-link", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x + # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + "-C", "link-arg=--nmagic", +] \ No newline at end of file diff --git a/boards/firmware/nrf9160/boot_fw_blinky_green/Cargo.toml b/boards/firmware/nrf9160/boot_fw_blinky_green/Cargo.toml new file mode 100644 index 00000000..85113ff6 --- /dev/null +++ b/boards/firmware/nrf9160/boot_fw_blinky_green/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "nrf9160_bootfw" +version = "0.1.0" +edition = "2018" +build = "build.rs" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +# makes `cargo check --all-targets` work +[[bin]] +name = "nrf9160_bootfw" +bench = false +doctest = false +test = false + +[dependencies] +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7" +nrf9160-pac = "0.12.2" +#embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", rev = "c53614f", features = ["nightly"] } +#embassy-nrf = { git = "https://github.com/embassy-rs/embassy.git", rev = "c53614f", features = ["nightly", "nrf9160-ns", "unstable-pac", "unstable-traits"] } +#embassy-sync = "0.1.0" +nrf9160-hal = { version = "0.16.0", default-features = false } +defmt = {version = "0.3.2", optional = true} +defmt-rtt = {version = "0.3.2", optional = true} +#trustzone-m-macros = { path = "../../../tools/trustzone-m-tools/macros" } +rustBoot-hal = {path = "../../../hal", default-features = false, features = ["nrf9160", "nrf"]} +rustBoot-update = {path = "../../../update", features = ["nrf9160"]} +panic-probe = { version = "0.3.0" } +spin = "0.9.4" + +#[build-dependencies] + + +[features] +default = ["defmt", "defmt-rtt"] \ No newline at end of file diff --git a/boards/firmware/nrf9160/boot_fw_blinky_green/build.rs b/boards/firmware/nrf9160/boot_fw_blinky_green/build.rs new file mode 100644 index 00000000..c057547b --- /dev/null +++ b/boards/firmware/nrf9160/boot_fw_blinky_green/build.rs @@ -0,0 +1,17 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // 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!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=memory.x"); +} diff --git a/boards/firmware/nrf9160/boot_fw_blinky_green/memory.x b/boards/firmware/nrf9160/boot_fw_blinky_green/memory.x new file mode 100644 index 00000000..f1526d46 --- /dev/null +++ b/boards/firmware/nrf9160/boot_fw_blinky_green/memory.x @@ -0,0 +1,35 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* TODO Adjust these memory regions to match your device memory layout */ + /* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */ + /* We'll need prepend a 256-byte rustBoot header. So add an offset - 0x100 */ + FLASH (rx) : ORIGIN = 0x00040100, LENGTH = 768K + RAM (rwx) : ORIGIN = 0x20020000, LENGTH = 128K +} + +/* This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* You may want to use this variable to locate the call stack and static + variables in different memory regions. Below is shown the default value */ +/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ + +/* You can use this symbol to customize the location of the .text section */ +/* If omitted the .text section will be placed right after the .vector_table + section */ +/* This is required only on microcontrollers that store some configuration right + after the vector table */ +/* _stext = ORIGIN(FLASH) + 0x400; */ + +/* Example of putting non-initialized variables into custom RAM locations. */ +/* This assumes you have defined a region RAM2 above, and in the Rust + sources added the attribute `#[link_section = ".ram2bss"]` to the data + you want to place there. */ +/* Note that the section will not be zero-initialized by the runtime! */ +/* SECTIONS { + .ram2bss (NOLOAD) : ALIGN(4) { + *(.ram2bss); + . = ALIGN(4); + } > RAM2 + } INSERT AFTER .bss; +*/ \ No newline at end of file diff --git a/boards/firmware/nrf9160/boot_fw_blinky_green/src/main.rs b/boards/firmware/nrf9160/boot_fw_blinky_green/src/main.rs new file mode 100644 index 00000000..f0eb6111 --- /dev/null +++ b/boards/firmware/nrf9160/boot_fw_blinky_green/src/main.rs @@ -0,0 +1,138 @@ +#![no_std] +#![no_main] +#![allow(non_snake_case)] +#![feature(cmse_nonsecure_entry)] +#![feature(abi_c_cmse_nonsecure_call)] +#![feature(once_cell)] +#![feature(type_alias_impl_trait)] + +use nrf9160_hal::{prelude::OutputPin, gpio, uarte, Uarte}; + +use cortex_m_rt::{exception, entry}; +use rustBoot_hal::{nrf::nrf9160::FlashWriterEraser, FlashInterface}; //nrf::nrf9160::GLOBAL_UART}; +use rustBoot_update::update::{update_flash::FlashUpdater, UpdateInterface}; +use core::{fmt::Write, cell::OnceCell, panic::PanicInfo}; +// use defmt::*; +use nrf9160_pac::{UARTE0_NS, P0_NS}; + +// SCB Application Interrupt and Reset Control Register Definitions +const SCB_AIRCR_VECTKEY_POS: u32 = 16; // SCB AIRCR: VECTKEY Position +const SCB_AIRCR_PRIGROUP_POS: u32 = 8; // SCB AIRCR: PRIGROUP Position +const SCB_AIRCR_PRIGROUP_MSK: u32 = 7u32 << SCB_AIRCR_PRIGROUP_POS; // SCB AIRCR: PRIGROUP Mask +const SCB_AIRCR_SYSRESETREQ_POS: u32 = 2; // SCB AIRCR: SYSRESETREQ Position +const SCB_AIRCR_SYSRESETREQ_MSK: u32 = 1u32 << SCB_AIRCR_SYSRESETREQ_POS; // SCB AIRCR: SYSRESETREQ Mask + +#[cfg(feature = "defmt")] +use defmt_rtt as _; // global logger +// use panic_probe as _; + +/// System Reset +/// +/// Initiates a system reset request to reset the MCU. +#[inline] +pub fn nvic_systemreset() -> ! { + let core_peripherals = cortex_m::Peripherals::take().unwrap(); + let scb = core_peripherals.SCB; + cortex_m::asm::dsb(); + unsafe { + scb.aircr.write( + (0x5FA << SCB_AIRCR_VECTKEY_POS) + | (scb.aircr.read() & SCB_AIRCR_PRIGROUP_MSK) + | SCB_AIRCR_SYSRESETREQ_MSK, + ); + } + cortex_m::asm::dsb(); + loop {} +} + +// A UART we can access from anywhere (with run-time lock checking). +// static GLOBAL_UART: OnceCell>>> = +// OnceCell::new(); + +// struct Printer; +// impl Write for Printer { +// fn write_str(&mut self, s: &str) -> core::fmt::Result { +// UART_OUT.lock(|uart| { +// uart.borrow_mut() +// .as_mut() +// .unwrap() +// .blocking_write(s.as_bytes()) +// .unwrap() +// }); +// Ok(()) +// } +// } + +#[entry] +fn main()-> ! { + let p = nrf9160_pac::Peripherals::take().unwrap();//unsafe { nrf9160_pac::Peripherals::steal() }; + + let p0: P0_NS = unsafe { core::mem::transmute(()) }; + let p0 = gpio::p0::Parts::new(p0); + let mut led2 = p0.p0_03.into_push_pull_output(gpio::Level::Low).degrade(); + let mut led1 = p0.p0_02.into_push_pull_output(gpio::Level::High).degrade(); + led1.set_high().unwrap(); + let mut count = 0; + + let uarte0: UARTE0_NS = p.UARTE0_NS; + + let pins = uarte::Pins { + txd: p0.p0_01.into_push_pull_output(gpio::Level::High).degrade(), + rxd: p0.p0_00.into_floating_input().degrade(), + cts: None, + rts: None, + }; + + let mut i = 0; + let flash_writer = FlashWriterEraser::new(p.NVMC_S, p.NVMC_NS, false);//{nvmc_s:p.NVMC_S, nvmc_ns: p.NVMC_NS, secure: false}; + + let updater = FlashUpdater::new(flash_writer); + while i < 5 { + led2.set_low(); + cortex_m::asm::delay(5000000); + led2.set_high(); + cortex_m::asm::delay(5000000); + i += 1; + } + + match updater.update_trigger() { + Ok(_v) => {} + Err(e) => { + panic!("couldnt trigger update: {:?}", e); + } + } + + nvic_systemreset(); +} + + + +#[exception] +unsafe fn HardFault(frame: &cortex_m_rt::ExceptionFrame) -> ! { + // defmt::println!("{:?}", frame); + let sau = &*cortex_m::peripheral::SAU::PTR; + defmt::println!("Secure ctrl: {:X}", sau.ctrl.read().0); + defmt::println!("Secure fault status register: {:X}", sau.sfsr.read().0); + defmt::println!("Secure fault address register: {:X}", sau.sfar.read().0); + + let scb = &*cortex_m::peripheral::SCB::PTR; + defmt::println!("Configurable Fault Status Register: {:X}", scb.cfsr.read()); + + cortex_m::asm::delay(u32::MAX); + + cortex_m::peripheral::SCB::sys_reset(); +} + + +#[exception] +unsafe fn DefaultHandler(irqn: i16) { + panic!("DefaultHandler IRQn = {}", irqn); +} + +// Called when our code panics. +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + cortex_m::interrupt::disable(); + defmt::println!("Panic occured"); + cortex_m::asm::udf(); +} \ No newline at end of file diff --git a/boards/firmware/nrf9160/updt_fw_blinky_red/.cargo/config.toml b/boards/firmware/nrf9160/updt_fw_blinky_red/.cargo/config.toml new file mode 100644 index 00000000..a5b5e45b --- /dev/null +++ b/boards/firmware/nrf9160/updt_fw_blinky_red/.cargo/config.toml @@ -0,0 +1,17 @@ +# ============================================================================= +# Build configuration options for Cortex-M +# ============================================================================= + +[build] +target = "thumbv8m.main-none-eabihf" + +# [target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# runner = "probe-run --chip nRF52840_xxAA" # runner specific to nrf52840. Replace this with probe-run option for your board. +rustflags = [ + "-C", "linker=flip-link", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + # This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x + # See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + "-C", "link-arg=--nmagic", +] \ No newline at end of file diff --git a/boards/firmware/nrf9160/updt_fw_blinky_red/Cargo.toml b/boards/firmware/nrf9160/updt_fw_blinky_red/Cargo.toml new file mode 100644 index 00000000..5f6ef7a1 --- /dev/null +++ b/boards/firmware/nrf9160/updt_fw_blinky_red/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "nrf9160_updtfw" +version = "0.1.0" +edition = "2018" +build = "build.rs" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +# makes `cargo check --all-targets` work +[[bin]] +name = "nrf9160_updtfw" +bench = false +doctest = false +test = false + +[dependencies] +cortex-m = "0.7" +cortex-m-rt = "0.7" +nrf9160-pac = "0.12.2" +nrf9160-hal = { version = "0.16.0", default-features = false } +defmt = {version = "0.3.2", optional = true} +defmt-rtt = {version = "0.3.2", optional = true} + +rustBoot-hal = {path = "../../../hal", default-features = false, features = ["nrf9160", "nrf"]} +rustBoot-update = {path = "../../../update", features = ["nrf9160"]} + +#[build-dependencies] + + +[features] +default = ["defmt", "defmt-rtt"] diff --git a/boards/firmware/nrf9160/updt_fw_blinky_red/build.rs b/boards/firmware/nrf9160/updt_fw_blinky_red/build.rs new file mode 100644 index 00000000..db4dd873 --- /dev/null +++ b/boards/firmware/nrf9160/updt_fw_blinky_red/build.rs @@ -0,0 +1,16 @@ +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // 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!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=memory.x"); +} diff --git a/boards/firmware/nrf9160/updt_fw_blinky_red/memory.x b/boards/firmware/nrf9160/updt_fw_blinky_red/memory.x new file mode 100644 index 00000000..20d304e0 --- /dev/null +++ b/boards/firmware/nrf9160/updt_fw_blinky_red/memory.x @@ -0,0 +1,35 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* TODO Adjust these memory regions to match your device memory layout */ + /* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */ + /* We'll need prepend a 256-byte rustBoot header. So add an offset - 0x100 */ + FLASH (rx) : ORIGIN = 0x00040100, LENGTH = 768K + RAM (rwx) : ORIGIN = 0x20020000, LENGTH = 128K +} + +/* This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* You may want to use this variable to locate the call stack and static + variables in different memory regions. Below is shown the default value */ +/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ + +/* You can use this symbol to customize the location of the .text section */ +/* If omitted the .text section will be placed right after the .vector_table + section */ +/* This is required only on microcontrollers that store some configuration right + after the vector table */ +/* _stext = ORIGIN(FLASH) + 0x400; */ + +/* Example of putting non-initialized variables into custom RAM locations. */ +/* This assumes you have defined a region RAM2 above, and in the Rust + sources added the attribute `#[link_section = ".ram2bss"]` to the data + you want to place there. */ +/* Note that the section will not be zero-initialized by the runtime! */ +/* SECTIONS { + .ram2bss (NOLOAD) : ALIGN(4) { + *(.ram2bss); + . = ALIGN(4); + } > RAM2 + } INSERT AFTER .bss; +*/ \ No newline at end of file diff --git a/boards/firmware/nrf9160/updt_fw_blinky_red/src/main.rs b/boards/firmware/nrf9160/updt_fw_blinky_red/src/main.rs new file mode 100644 index 00000000..38fe1ab1 --- /dev/null +++ b/boards/firmware/nrf9160/updt_fw_blinky_red/src/main.rs @@ -0,0 +1,48 @@ +#![no_std] +#![no_main] +#![allow(non_snake_case)] +#![feature(cmse_nonsecure_entry)] +#![feature(abi_c_cmse_nonsecure_call)] + +use nrf9160_hal::{gpio, Uarte, uarte, prelude::OutputPin}; +use nrf9160_pac::{P0_NS, UARTE0_NS}; +use cortex_m_rt::entry; + +use rustBoot_hal::nrf::nrf9160::FlashWriterEraser; +use rustBoot_update::update::{update_flash::FlashUpdater, UpdateInterface}; + +#[cfg(feature = "defmt")] +use defmt_rtt as _; // global logger + +#[entry] +fn main()->! { + + let p = nrf9160_pac::Peripherals::take().unwrap(); + let p0: P0_NS = unsafe { core::mem::transmute(()) }; + let p0 = gpio::p0::Parts::new(p0); + let mut led3 = p0.p0_04.into_push_pull_output(gpio::Level::Low).degrade(); + let mut led4 = p0.p0_05.into_push_pull_output(gpio::Level::High).degrade(); + led3.set_high().unwrap(); + + let flash_writer = FlashWriterEraser::new(p.NVMC_S, p.NVMC_NS, false); + let updater = FlashUpdater::new(flash_writer); + match updater.update_success() { + Ok(_v) => {} + Err(e) => panic!("failed to confirm update: {}", e), + }; + + loop{ + led4.set_low().unwrap(); + cortex_m::asm::delay(5000000); + led4.set_high().unwrap(); + cortex_m::asm::delay(5000000); + } + +} + +/// Called when our code panics. +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + cortex_m::asm::udf(); +} + diff --git a/boards/hal/Cargo.toml b/boards/hal/Cargo.toml index c7d22ec6..cc9f5ba3 100644 --- a/boards/hal/Cargo.toml +++ b/boards/hal/Cargo.toml @@ -35,6 +35,8 @@ rustBoot = {path = "../../rustBoot", default-features = true, optional = true} tock-registers = {version = "0.8.x", default-features = false, features = ["register_types"], optional = true} # platform specific dependencies for nrf52840 nrf52840-hal = {version = "0.16.0", optional = true} +# platform specific dependencies for nrf9160 +nrf9160-pac = {version = "0.12.2", optional = true} # platform specific dependencies for stm32h7 series stm32h7xx-hal = {version = "0.12.2", features = ["stm32h735", "rt"], optional = true} # platform specific dependencies for stm32f7 series @@ -56,6 +58,7 @@ log = [] # board-specific features nrf = [] nrf52840 = ["nrf", "nrf52840-hal"] +nrf9160 = ["nrf", "nrf9160-pac"] rpi = [] rpi4 = ["rpi", "tock-registers", "cortex-a", "rustBoot"] nxp = [] diff --git a/boards/hal/src/lib.rs b/boards/hal/src/lib.rs index 4ed48dd0..e7b44b7f 100644 --- a/boards/hal/src/lib.rs +++ b/boards/hal/src/lib.rs @@ -7,6 +7,7 @@ #![feature(asm_const)] #![allow(warnings)] #![feature(core_intrinsics)] +#![feature(abi_c_cmse_nonsecure_call)] #[cfg(feature = "nrf")] pub mod nrf; #[cfg(feature = "rpi")] diff --git a/boards/hal/src/nrf/mod.rs b/boards/hal/src/nrf/mod.rs index 74a0d5be..fe382a1a 100644 --- a/boards/hal/src/nrf/mod.rs +++ b/boards/hal/src/nrf/mod.rs @@ -1,2 +1,4 @@ #[cfg(feature = "nrf52840")] pub mod nrf52840; +#[cfg(feature = "nrf9160")] +pub mod nrf9160; diff --git a/boards/hal/src/nrf/nrf9160.rs b/boards/hal/src/nrf/nrf9160.rs new file mode 100644 index 00000000..66501a1c --- /dev/null +++ b/boards/hal/src/nrf/nrf9160.rs @@ -0,0 +1,581 @@ +//! NVMC (i.e. flash) driver for the nrf52840 board, written in pure-rust. + +use core::{ + ops::{Add, Sub}, + usize, cell::{OnceCell}, +}; + +use nrf9160_pac as pac; + +use crate::FlashInterface; +use pac::{Peripherals, NVMC_S, NVMC_NS}; +use nrf9160_constants::*; +pub use pac::SPU_S as SPU; +pub const FLASH_REGION_SIZE: u32 = 32 * 1024; +pub const RAM_REGION_SIZE: u32 = 8 * 1024; +use cortex_m; + +// #[cfg(feature = "defmt")] +// use defmt_rtt as _; // global logger + +#[rustfmt::skip] +mod nrf9160_constants { + pub const FLASH_PAGE_SIZE : u32 = 4096; + pub const STACK_LOW : u32 = 0x20_000_000; + pub const STACK_UP : u32 = 0x20_040_000; + pub const RB_HDR_SIZE : u32 = 0x100; + pub const BASE_ADDR : u32 = 0x40000; + pub const VTR_TABLE_SIZE : u32 = 0x100; + pub const FW_RESET_VTR : u32 = BASE_ADDR + RB_HDR_SIZE + VTR_TABLE_SIZE + 1; +} +// include!(concat!(env!("OUT_DIR"), "/trustzone_bindings.rs")); +// extern crate trustzone_m_nonsecure_rt; + +pub struct FlashWriterEraser { + pub nvmc_s: NVMC_S, + pub nvmc_ns: NVMC_NS, + pub secure: bool +} + +impl FlashWriterEraser +{ + pub fn new(nvmc_s: NVMC_S, nvmc_ns:NVMC_NS, secure: bool) -> Self { + FlashWriterEraser { + nvmc_s: nvmc_s, + nvmc_ns: nvmc_ns, + secure: secure + } + } +} + +impl FlashInterface for FlashWriterEraser { + /// Write data at the specified address + /// + /// Arguments: + /// - address: It holds the address of flash where data has to be written + /// - data: u8 pointer holding the holding data. + /// - len : number of bytes + /// + /// Return: + /// - NONE + fn hal_flash_write(&self, address: usize, data: *const u8, len: usize) { + let address = address as u32; + let len = len as u32; + let mut idx = 0u32; + let mut src = data as *mut u32; + let mut dst = address as *mut u32; + // defmt::println!("writting {:?} at address {:?} len {:?}", data.clone() ,address.clone(), len.clone() ); + + while idx < len { + let data_ptr = (data as *const u32) as u32; + // defmt::println!("hal flash write 1"); + if self.secure { + // Check if the following holds true and do a full word write i.e. 4-byte write + // - if `len - idx` is greater than 3 (i.e. 4 bytes). + // - if the address is aligned on a word (i.e. 4-byte) boundary. + // - if the data_ptr is aligned on a word (i.e. 4-byte) boundary. + if ((len - idx > 3) + && ((((address + idx) & 0x03) == 0) && ((data_ptr + idx) & 0x03) == 0)) + { + // // defmt::println!("hal flash write 2"); + // Enable NVM writes + + self.nvmc_s.configns.write(|w| w.wen().wen()); + // // defmt::println!("Config nvmc enabled"); + while self.nvmc_s.readynext.read().readynext().is_busy() { + // // defmt::println!("waiting for busy bit"); + } + // // defmt::println!("NVMC enabled"); + unsafe { + *dst = *src; // 4-byte write + }; + // // defmt::println!("hal flash write 3"); + // Wait until writing is done + while self.nvmc_s.ready.read().ready().is_busy() {} + // // defmt::println!("writting to memory done "); + src = ((src as u32) + 4) as *mut u32; // increment pointer by 4 + dst = ((dst as u32) + 4) as *mut u32; // increment pointer by 4 + idx += 4; + } else { + // // defmt::println!("hal flash write 4"); + // else do a single byte write i.e. 1-byte write + let mut val = 0u32; + let val_bytes = ((&mut val) as *mut u32) as *mut u8; + let offset = (address + idx) - (((address + idx) >> 2) << 2); // offset from nearest word aligned address + dst = ((dst as u32) - offset) as *mut u32; // subtract offset from dst addr + unsafe { + val = *dst; // assign current val at dst to val + // store data byte at idx to `val`. `val_bytes` is a byte-pointer to val. + *val_bytes.add(offset as usize) = *data.add(idx as usize); + } + // // defmt::println!("hal flash write 5"); + // Enable NVM writes + self.nvmc_s.configns.write(|w| w.wen().wen()); + while self.nvmc_s.readynext.read().readynext().is_busy() {} + // // defmt::println!("hal flash write 6"); + unsafe { + *dst = val; // Technically this is a 1-byte write ONLY + // but only full 32-bit words can be written to Flash using the NVMC interface + }; + // Wait until writing is done + while self.nvmc_s.ready.read().ready().is_busy() {} + // defmt::println!("hal flash write 7"); + src = ((src as u32) + 1) as *mut u32; // increment pointer by 1 + dst = ((dst as u32) + 1) as *mut u32; // increment pointer by 1 + idx += 1; + // defmt::println!("doing single byte write"); + } + } + else { + // Check if the following holds true and do a full word write i.e. 4-byte write + // - if `len - idx` is greater than 3 (i.e. 4 bytes). + // - if the address is aligned on a word (i.e. 4-byte) boundary. + // - if the data_ptr is aligned on a word (i.e. 4-byte) boundary. + if ((len - idx > 3) + && ((((address + idx) & 0x03) == 0) && ((data_ptr + idx) & 0x03) == 0)) + { + // // defmt::println!("hal flash write 2"); + // Enable NVM writes + + self.nvmc_ns.configns.write(|w| w.wen().wen()); + // // defmt::println!("Config nvmc enabled"); + while self.nvmc_ns.readynext.read().readynext().is_busy() { + // // defmt::println!("waiting for busy bit"); + } + // // defmt::println!("NVMC enabled"); + unsafe { + *dst = *src; // 4-byte write + }; + // // defmt::println!("hal flash write 3"); + // Wait until writing is done + while self.nvmc_ns.ready.read().ready().is_busy() {} + // // defmt::println!("writting to memory done "); + src = ((src as u32) + 4) as *mut u32; // increment pointer by 4 + dst = ((dst as u32) + 4) as *mut u32; // increment pointer by 4 + idx += 4; + } else { + // // defmt::println!("hal flash write 4"); + // else do a single byte write i.e. 1-byte write + let mut val = 0u32; + let val_bytes = ((&mut val) as *mut u32) as *mut u8; + let offset = (address + idx) - (((address + idx) >> 2) << 2); // offset from nearest word aligned address + dst = ((dst as u32) - offset) as *mut u32; // subtract offset from dst addr + unsafe { + val = *dst; // assign current val at dst to val + // store data byte at idx to `val`. `val_bytes` is a byte-pointer to val. + *val_bytes.add(offset as usize) = *data.add(idx as usize); + } + // // defmt::println!("hal flash write 5"); + // Enable NVM writes + self.nvmc_ns.configns.write(|w| w.wen().wen()); + while self.nvmc_ns.readynext.read().readynext().is_busy() {} + // // defmt::println!("hal flash write 6"); + unsafe { + *dst = val; // Technically this is a 1-byte write ONLY + // but only full 32-bit words can be written to Flash using the NVMC interface + }; + // Wait until writing is done + while self.nvmc_ns.ready.read().ready().is_busy() {} + // defmt::println!("hal flash write 7"); + src = ((src as u32) + 1) as *mut u32; // increment pointer by 1 + dst = ((dst as u32) + 1) as *mut u32; // increment pointer by 1 + idx += 1; + // defmt::println!("doing single byte write"); + } + } + } + } + + /// Erase the sector of a given address + /// + /// Arguments: + /// - addr: Address where data has to be erased + /// - len : number of bytes to be erased + /// + /// Return: + /// - NONE + fn hal_flash_erase(&self, addr: usize, len: usize) { + let starting_page = (addr/0x1000) as u32; + let ending_page = ((addr + len)/0x1000) as u32; + + // let address = starting_page * 0x1000; + // defmt::info!("starting_page={}, ending_page={}, len={}", starting_page, ending_page, len); + for start_addr in (starting_page..ending_page) { + // Enable erasing + if self.secure{ + // Enable the erase functionality of the flash + self.nvmc_s.configns.modify(|_, w| w.wen().een()); + // Start the erase process by writing a u32 word containing all 1's to the first word of the page + // This is safe because the flash slice is page aligned, so a pointer to the first byte is valid as a pointer to a u32. + unsafe { + let first_word = (start_addr * 0x1000) as *mut u32; + first_word.write_volatile(0xFFFFFFFF); + } + // Wait for the erase to be done + while self.nvmc_s.ready.read().ready().is_busy() {} + + self.nvmc_s.configns.modify(|_, w| w.wen().ren()); + } + else { + // Enable the erase functionality of the flash + self.nvmc_ns.configns.modify(|_, w| w.wen().een()); + // Start the erase process by writing a u32 word containing all 1's to the first word of the page + // This is safe because the flash slice is page aligned, so a pointer to the first byte is valid as a pointer to a u32. + unsafe { + let first_word = (start_addr * 0x1000) as *mut u32; + first_word.write_volatile(0xFFFFFFFF); + } + // Wait for the erase to be done + while self.nvmc_ns.ready.read().ready().is_busy() {} + + self.nvmc_ns.configns.modify(|_, w| w.wen().ren()); + } + } + // Synchronize the changes + cortex_m::asm::dsb(); + cortex_m::asm::isb(); + + } + + fn hal_init() {} + fn hal_flash_lock(&self) {} + fn hal_flash_unlock(&self) {} +} + +pub fn preboot() {} + +struct RefinedUsize(u32); + +impl RefinedUsize { + /// This method is used to check the address bound of stack pointer + /// + /// Method arguments: + /// - i : starting address of stack + /// Returns: + /// - It returns u32 address of stack pointer + pub fn bounded_int(i: u32) -> Self { + assert!(i >= MIN && i <= MAX); + RefinedUsize(i) + } + /// This method is used to check the address of reset pointer + /// + /// Method arguments: + /// - i : starting address of reset + /// Returns: + /// - It returns u32 address of reset pointer + pub fn single_valued_int(i: u32) -> Self { + // defmt::println!("i {:?} == VAL {:?}", i, VAL); + assert!(i == VAL); + RefinedUsize(i) + } +} + +/// This method is used to boot the firmware from a particular address +/// +/// Method arguments: +/// - fw_base_address : address of the firmware +/// Returns: +/// - NONE +#[rustfmt::skip] +pub fn boot_from(fw_base_address: usize) -> ! { + unsafe { + let ns_vector_table_addr = fw_base_address as u32; + // Write the Non-Secure Main Stack Pointer before switching state. Its value is the first + // entry of the Non Secure Vector Table. + cortex_m::register::msp::write_ns(*(ns_vector_table_addr as *const u32)); + // Create a Non-Secure function pointer to the address of the second entry of the Non + // Secure Vector Table. + let ns_reset_vector: extern "C-cmse-nonsecure-call" fn() -> ! = + core::mem::transmute::(ns_vector_table_addr + 4); + ns_reset_vector() + } +} + +/// This method is used to initialize the trustzone memory region, Peripheral, Pins, +/// DPPI to Secure/Non-Secure/Non-SecureCallable accordingly. The method checks the provided inpupts range +/// with the one provided in memory.x file and accordingly initializes the trustzone. +/// The method uses SPM register interface to initialize the trustZone. +/// +/// Method arguments: +/// - Non-Secure Peripherals : Array of peripherals which are to initialized as Non-secure +/// - Non-Secure Pins : Array of pins which are to initialized as Non-secure +/// - Non-Secure DPPI : Array of Dppi which are to initialized as Non-secure +/// Returns: +/// - NONE +pub fn initialize( + nonsecure_peripherals: [NonSecurePeripheral; PERIPHERALS_LEN], + nonsecure_pins: [(usize, u32); PINS_LEN], + nonsecure_dppi: [(usize, u32); DPPI_LEN], +) { + extern "C" { + static _s_flash_start: u32; + static _s_flash_end: u32; + + static _nsc_flash_start: u32; + static _nsc_flash_end: u32; + + static _ns_flash_start: u32; + static _ns_flash_end: u32; + + static _s_ram_start: u32; + static _s_ram_end: u32; + + static _ns_ram_start: u32; + static _ns_ram_end: u32; + } + + let s_flash_start = unsafe { core::ptr::addr_of!(_s_flash_start) as u32 }; + let s_flash_end = unsafe { core::ptr::addr_of!(_s_flash_end) as u32 }; + let s_flash = s_flash_start..s_flash_end; + + let nsc_flash_start = unsafe { core::ptr::addr_of!(_nsc_flash_start) as u32 }; + let nsc_flash_end = unsafe { core::ptr::addr_of!(_nsc_flash_end) as u32 }; + let nsc_flash = nsc_flash_start..nsc_flash_end; + #[cfg(feature = "memory_region_assertions")] + assert_eq!(s_flash_start % FLASH_REGION_SIZE, 0, "The start of the flash region must be on a region boundary: val % {FLASH_REGION_SIZE:#X} must be 0"); + #[cfg(feature = "memory_region_assertions")] + assert_eq!(nsc_flash_end % FLASH_REGION_SIZE, 0, "The end of the nsc_flash region must be on a region boundary: val % {FLASH_REGION_SIZE:#X} must be 0"); + // defmt::println!("in init function"); + + let ns_flash_start = unsafe { core::ptr::addr_of!(_ns_flash_start) as u32 }; + let ns_flash_end = unsafe { core::ptr::addr_of!(_ns_flash_end) as u32 }; + // defmt::println!("ns_flash_start: {:?}", ns_flash_start); + // defmt::println!("ns_flash_end: {:?}", ns_flash_end); + let ns_flash = ns_flash_start..ns_flash_end; + #[cfg(feature = "memory_region_assertions")] + assert_eq!(ns_flash_start % FLASH_REGION_SIZE, 0, "The start of the ns flash region must be on a region boundary: val % {FLASH_REGION_SIZE:#X} must be 0"); + #[cfg(feature = "memory_region_assertions")] + assert_eq!(ns_flash_end % FLASH_REGION_SIZE, 0, "The end of the ns flash region must be on a region boundary: val % {FLASH_REGION_SIZE:#X} must be 0"); + + let s_ram_start = unsafe { core::ptr::addr_of!(_s_ram_start) as u32 }; + let s_ram_end = unsafe { core::ptr::addr_of!(_s_ram_end) as u32 }; + let s_ram = s_ram_start..s_ram_end; + + // #[cfg(feature = "memory_region_assertions")] + // assert_eq!(s_ram_start % RAM_REGION_SIZE, 0, "The start of the ram region must be on a region boundary: val % {RAM_REGION_SIZE:#X} must be 0"); + #[cfg(feature = "memory_region_assertions")] + assert_eq!(s_ram_end % RAM_REGION_SIZE, 0, "The end of the ram region must be on a region boundary: val % {RAM_REGION_SIZE:#X} must be 0"); + + let ns_ram_start = unsafe { core::ptr::addr_of!(_ns_ram_start) as u32 }; + let ns_ram_end = unsafe { core::ptr::addr_of!(_ns_ram_end) as u32 }; + let ns_ram = ns_ram_start..ns_ram_end; + #[cfg(feature = "memory_region_assertions")] + assert_eq!(ns_ram_start % RAM_REGION_SIZE, 0, "The start of the ns ram region must be on a region boundary: val % {RAM_REGION_SIZE:#X} must be 0"); + #[cfg(feature = "memory_region_assertions")] + assert_eq!(ns_ram_end % RAM_REGION_SIZE, 0, "The end of the ns ram region must be on a region boundary: val % {RAM_REGION_SIZE:#X} must be 0"); + + let spu = unsafe { core::mem::transmute::<_, SPU>(()) }; + // // defmt::println!("in init function 5"); + for (address, region) in spu + .flashregion + .iter() + .enumerate() + .map(|(index, region)| (index as u32 * FLASH_REGION_SIZE, region)) + { + if s_flash.contains(&address) || nsc_flash.contains(&address) { + region.perm.write(|w| { + w.execute() + .enable() + .read() + .enable() + .write() + .enable() + .secattr() + .secure() + }); + } + else if ns_flash.contains(&address) { + region.perm.write(|w| { + w.execute() + .enable() + .read() + .enable() + .write() + .enable() + .secattr() + .non_secure() + }); + } + } + + set_nsc_region(&spu, nsc_flash_start..nsc_flash_end); + + for (address, region) in spu + .ramregion + .iter() + .enumerate() + .map(|(index, region)| (0x20000000 + index as u32 * RAM_REGION_SIZE, region)) + { + if s_ram.contains(&address) { + region.perm.write(|w| { + w.execute() + .enable() + .read() + .enable() + .write() + .enable() + .secattr() + .secure() + }); + } + else if ns_ram.contains(&address) { + region.perm.write(|w| { + w.execute() + .enable() + .read() + .enable() + .write() + .enable() + .secattr() + .non_secure() + }); + } + } + // defmt::println!("ns ram initialized"); + // Set all given peripherals to nonsecure + for peripheral in nonsecure_peripherals { + spu.periphid[peripheral.id] + .perm + .write(|w| w.secattr().non_secure().dmasec().non_secure()); + } + + // Set all given pins to nonsecure + for (pin_port, pin) in nonsecure_pins { + spu.gpioport[pin_port] + .perm + .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << pin)) }) + } + + // Set all given dppi channels to nonsecure + for (port, channel) in nonsecure_dppi { + spu.dppi[port] + .perm + .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << channel)) }) + } + // We're using Nordic's SPU instead of the default SAU. To do that we must disable the SAU and + // set the ALLNS (All Non-secure) bit. + let sau = unsafe { core::mem::transmute::<_, cortex_m::peripheral::SAU>(()) }; + unsafe { + sau.ctrl.modify(|mut ctrl| { + ctrl.0 = 0b10; + ctrl + }); + + // Also set the stack pointer of nonsecure + cortex_m::register::msp::write_ns(ns_ram_end); + } +} + +/// This method is used to set the Non-SecureCallable region of memory. +/// +/// Method arguments: +/// - &SPU : Reference of Secure partition unit. +/// - region : Memory region which is to be set ans Non-SecureCallable +/// Returns: +/// - NONE +fn set_nsc_region(spu: &SPU, region: core::ops::Range) { + let sg_start = region.start; + let nsc_size = FLASH_REGION_SIZE - (sg_start % FLASH_REGION_SIZE); + let size_reg = (31 - nsc_size.leading_zeros()) - 4; + let region_reg = (sg_start as u32 / FLASH_REGION_SIZE) & 0x3F; // x << SPU_FLASHNSC_REGION_REGION_Pos & SPU_FLASHNSC_REGION_REGION_Msk + spu.flashnsc[0].size.write(|w| { + unsafe { + w.bits(size_reg); + } + w + }); + spu.flashnsc[0].region.write(|w| { + unsafe { + w.bits(region_reg); + } + w + }); +} + +pub struct NonSecurePeripheral { + id: usize, +} + +macro_rules! impl_ns_peripheral { + ($peripheral:ty, $id:expr) => { + impl From<$peripheral> for NonSecurePeripheral { + fn from(_: $peripheral) -> Self { + Self { id: $id } + } + } + }; +} + +#[cfg(feature = "nrf9160")] +mod nrf9160_peripheral_impl { + use super::*; + + impl_ns_peripheral!(nrf9160_pac::REGULATORS_S, 4); + impl_ns_peripheral!((nrf9160_pac::CLOCK_S, nrf9160_pac::POWER_S), 5); + impl_ns_peripheral!( + ( + nrf9160_pac::SPIM0_S, + nrf9160_pac::SPIS0_S, + nrf9160_pac::TWIM0_S, + nrf9160_pac::TWIS0_S, + nrf9160_pac::UARTE0_S + ), + 8 + ); + impl_ns_peripheral!( + ( + nrf9160_pac::SPIM1_S, + nrf9160_pac::SPIS1_S, + nrf9160_pac::TWIM1_S, + nrf9160_pac::TWIS1_S, + nrf9160_pac::UARTE1_S + ), + 9 + ); + impl_ns_peripheral!( + ( + nrf9160_pac::SPIM2_S, + nrf9160_pac::SPIS2_S, + nrf9160_pac::TWIM2_S, + nrf9160_pac::TWIS2_S, + nrf9160_pac::UARTE2_S + ), + 10 + ); + impl_ns_peripheral!( + ( + nrf9160_pac::SPIM3_S, + nrf9160_pac::SPIS3_S, + nrf9160_pac::TWIM3_S, + nrf9160_pac::TWIS3_S, + nrf9160_pac::UARTE3_S + ), + 11 + ); + impl_ns_peripheral!(nrf9160_pac::SAADC_S, 14); + impl_ns_peripheral!(nrf9160_pac::TIMER0_S, 15); + impl_ns_peripheral!(nrf9160_pac::TIMER1_S, 16); + impl_ns_peripheral!(nrf9160_pac::TIMER2_S, 17); + impl_ns_peripheral!(nrf9160_pac::RTC0_S, 20); + impl_ns_peripheral!(nrf9160_pac::RTC1_S, 21); + impl_ns_peripheral!(&nrf9160_pac::DPPIC_S, 23); + impl_ns_peripheral!(nrf9160_pac::WDT_S, 24); + impl_ns_peripheral!(nrf9160_pac::EGU0_S, 27); + impl_ns_peripheral!(nrf9160_pac::EGU1_S, 28); + impl_ns_peripheral!(nrf9160_pac::EGU2_S, 29); + impl_ns_peripheral!(nrf9160_pac::EGU3_S, 30); + impl_ns_peripheral!(nrf9160_pac::EGU4_S, 31); + impl_ns_peripheral!(nrf9160_pac::EGU5_S, 32); + impl_ns_peripheral!(nrf9160_pac::PWM0_S, 34); + impl_ns_peripheral!(nrf9160_pac::PWM1_S, 35); + impl_ns_peripheral!(nrf9160_pac::PWM2_S, 36); + impl_ns_peripheral!(nrf9160_pac::PDM_S, 38); + impl_ns_peripheral!(nrf9160_pac::I2S_S, 40); + impl_ns_peripheral!(nrf9160_pac::IPC_S, 42); + impl_ns_peripheral!(nrf9160_pac::FPU_S, 44); + impl_ns_peripheral!((&nrf9160_pac::KMU_S, + &nrf9160_pac::NVMC_S), 57); + impl_ns_peripheral!(nrf9160_pac::VMC_S, 58); + impl_ns_peripheral!(&nrf9160_pac::P0_S, 66); +} + diff --git a/boards/update/Cargo.toml b/boards/update/Cargo.toml index 1cef0403..07ebed1a 100644 --- a/boards/update/Cargo.toml +++ b/boards/update/Cargo.toml @@ -31,6 +31,7 @@ rustBoot-hal = {path = "../hal"} [features] default = [] nrf52840 = ["rustBoot/nrf52840"] +nrf9160 = ["rustBoot/nrf9160"] stm32f411 = ["rustBoot/stm32f411"] stm32f446 = ["rustBoot/stm32f446"] stm32f469 = ["rustBoot/stm32f469"] diff --git a/docs/images/dco.png b/docs/images/dco.png new file mode 100644 index 00000000..8cec0f83 Binary files /dev/null and b/docs/images/dco.png differ diff --git a/rustBoot/Cargo.toml b/rustBoot/Cargo.toml index 2d91f517..0557d437 100644 --- a/rustBoot/Cargo.toml +++ b/rustBoot/Cargo.toml @@ -53,7 +53,8 @@ sha256 = [] sha384 = [] # boards specific features mcu = [] -nrf52840 = ["mcu"] +nrf52840 = ["mcu"] +nrf9160 = ["mcu"] stm32f411 = ["mcu"] stm32f446 = ["mcu"] stm32f469 = ["mcu"] diff --git a/rustBoot/src/constants.rs b/rustBoot/src/constants.rs index 1eec3e62..748af573 100644 --- a/rustBoot/src/constants.rs +++ b/rustBoot/src/constants.rs @@ -13,6 +13,17 @@ pub const SWAP_PARTITION_ADDRESS: usize = 0x57000; #[cfg(feature = "nrf52840")] pub const UPDATE_PARTITION_ADDRESS: usize = 0x58000; +#[cfg(feature = "nrf9160")] +pub const SECTOR_SIZE: usize = 0x1000; +#[cfg(feature = "nrf9160")] +pub const PARTITION_SIZE: usize = 0x40000; +#[cfg(feature = "nrf9160")] +pub const BOOT_PARTITION_ADDRESS: usize = 0x40000; +#[cfg(feature = "nrf9160")] +pub const SWAP_PARTITION_ADDRESS: usize = 0xC0000; +#[cfg(feature = "nrf9160")] +pub const UPDATE_PARTITION_ADDRESS: usize = 0x80000; + #[cfg(feature = "stm32f411")] pub const SECTOR_SIZE: usize = 0x20000; #[cfg(feature = "stm32f411")] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 5de44f20..3917562a 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -12,6 +12,7 @@ xshell = "0.1.9" [features] nrf52840 = ["mcu", "rustBoot/nrf52840"] +nrf9160 = ["mcu", "rustBoot/nrf9160"] stm32f411 = ["mcu", "rustBoot/stm32f411"] stm32f446 = ["mcu", "rustBoot/stm32f446"] stm32f469 = ["mcu", "rustBoot/stm32f469"] diff --git a/xtask/src/main.rs b/xtask/src/main.rs index eadbbb7a..acc1242f 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -65,6 +65,9 @@ fn build_rustBoot_only(target: &&str) -> Result<(), anyhow::Error> { &"nrf52840" => { cmd!("cargo build --release").run()?; } + &"nrf9160" => { + cmd!("cargo build --release").run()?; + } &"stm32f411" => { cmd!("cargo build --release").run()?; } @@ -151,6 +154,16 @@ fn sign_packages(target: &&str, boot_ver: &&str, updt_ver: &&str) -> Result<(), cmd!("cargo run mcu-image ../boards/sign_images/signed_images/nrf52840_updtfw.bin nistp256 ../boards/sign_images/keygen/ecc256.der {updt_ver}").run()?; Ok(()) } + "nrf9160" => { + let _p = xshell::pushd(root_dir().join("boards/sign_images/signed_images"))?; + cmd!("rust-objcopy -I elf32-littlearm ../../target/thumbv8m.main-none-eabihf/release/nrf9160_bootfw -O binary nrf9160_bootfw.bin").run()?; + cmd!("rust-objcopy -I elf32-littlearm ../../target/thumbv8m.main-none-eabihf/release/nrf9160_updtfw -O binary nrf9160_updtfw.bin").run()?; + + let _p = xshell::pushd(root_dir().join("rbsigner"))?; + cmd!("cargo run mcu-image ../boards/sign_images/signed_images/nrf9160_bootfw.bin nistp256 ../boards/sign_images/keygen/ecc256.der {boot_ver}").run()?; + cmd!("cargo run mcu-image ../boards/sign_images/signed_images/nrf9160_updtfw.bin nistp256 ../boards/sign_images/keygen/ecc256.der {updt_ver}").run()?; + Ok(()) + } "stm32f411" => { let _p = xshell::pushd(root_dir().join("boards/sign_images/signed_images"))?; cmd!("rust-objcopy -I elf32-littlearm ../../target/thumbv7em-none-eabihf/release/stm32f411_bootfw -O binary stm32f411_bootfw.bin").run()?; @@ -238,6 +251,15 @@ fn flash_signed_fwimages(target: &&str, boot_ver: &&str, updt_ver: &&str) -> Res cmd!("probe-rs-cli download --format Bin --base-address {updt_part_addr} --chip nRF52840_xxAA nrf52840_updtfw_v{updt_ver}_signed.bin").run()?; Ok(()) } + "nrf9160" => { + let _p = xshell::pushd(root_dir().join("boards/sign_images/signed_images"))?; + let boot_part_addr = format!("0x{:x}", BOOT_PARTITION_ADDRESS); + cmd!("probe-rs-cli download --format Bin --base-address {boot_part_addr} --chip nRF9160_xxAA nrf9160_bootfw_v{boot_ver}_signed.bin").run()?; + + let updt_part_addr = format!("0x{:x}", UPDATE_PARTITION_ADDRESS); + cmd!("probe-rs-cli download --format Bin --base-address {updt_part_addr} --chip nRF9160_xxAA nrf9160_updtfw_v{updt_ver}_signed.bin").run()?; + Ok(()) + } "stm32f411" => { let _p = xshell::pushd(root_dir().join("boards/sign_images/signed_images"))?; let boot_part_addr = format!("0x{:x}", BOOT_PARTITION_ADDRESS); @@ -312,6 +334,11 @@ fn flash_rustBoot(target: &&str) -> Result<(), anyhow::Error> { cmd!("cargo flash --chip nRF52840_xxAA --release").run()?; Ok(()) } + "nrf9160" => { + let _p = xshell::pushd(root_dir().join("boards/bootloaders").join(target))?; + cmd!("cargo flash --chip nRF9160_xxAA --release").run()?; + Ok(()) + } "stm32f411" => { let _p = xshell::pushd(root_dir().join("boards/bootloaders").join(target))?; cmd!("cargo flash --chip stm32f411vetx --release").run()?; @@ -362,6 +389,14 @@ fn full_image_flash(target: &&str, boot_ver: &&str, updt_ver: &&str) -> Result<( flash_rustBoot(target)?; Ok(()) } + "nrf9160" => { + build_rustBoot(target)?; + sign_packages(target, boot_ver, updt_ver)?; + cmd!("probe-rs-cli erase --chip nRF9160_xxAA").run()?; + flash_signed_fwimages(target, boot_ver, updt_ver)?; + flash_rustBoot(target)?; + Ok(()) + } "stm32f411" => { build_rustBoot(target)?; sign_packages(target, boot_ver, updt_ver)?;