From d8b90c0e6d074d0c08f5a2ba705a2409b4713110 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 8 Dec 2023 13:11:56 +0000 Subject: [PATCH 1/3] Now builds on stable. Tested working with probe-rs CLI. Slightly larger binary, but I can live with that for not having to use nightly rust. --- .cargo/config | 5 ----- .github/workflows/ci.yml | 5 ----- README.md | 16 ++++++++++------ rust-toolchain | 6 +++++- src/algo.rs | 5 ----- src/main.rs | 24 +++++++----------------- 6 files changed, 22 insertions(+), 39 deletions(-) diff --git a/.cargo/config b/.cargo/config index 2253c32..8b2fc36 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,7 +1,3 @@ -[unstable] -build-std = ["core"] -build-std-features = ["panic_immediate_abort"] - [target.'cfg(all(target_arch = "arm", target_os = "none"))'] rustflags = [ @@ -9,7 +5,6 @@ rustflags = [ "-C", "link-arg=-Tlink.x", # Code-size optimizations. - "-Z", "trap-unreachable=no", "-C", "inline-threshold=5", "-C", "no-vectorize-loops", "-C", "force-frame-pointers=no", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63a3527..20fc9e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,17 +11,12 @@ env: jobs: build: - runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 - name: Install Dependencies run: | - sudo apt update - rustup component add rust-src cargo install cargo-binutils - rustup component add llvm-tools-preview - name: Build run: | ./build.sh diff --git a/README.md b/README.md index 2587111..ecdea3e 100644 --- a/README.md +++ b/README.md @@ -5,23 +5,27 @@ It implements the CMSIS-Pack ABI, so it's compatible with any tools that use it, ## Dependencies -Run the following requirements: +Run the following to install the requirements: + ```bash -cargo install cargo-binutils && rustup component add llvm-tools-preview rust-src +cargo install cargo-binutils ``` -## Building -Building requires nightly Rust. +The `rust-toolchain` file will get you the targets and components you need. + +## Building Just run `build.sh`. It spits out the flash algo in the probe-rs YAML format: - flash-algo$ ./build.sh +```console +flash-algo$ ./build.sh instructions: sLUUIACIGUoBRguI...wRwAgcEc= pc_init: 0x00000000 pc_uninit: 0x0000007c pc_program_page: 0x00000088 pc_erase_sector: 0x00000084 pc_erase_all: 0x00000080 +``` ## Hacking @@ -30,7 +34,7 @@ the glue functions for a given struct implementing it. This is generic for all c `main.rs` has the actual implementation for RP2040. -# License +## License This thingy is licensed under either of diff --git a/rust-toolchain b/rust-toolchain index bf867e0..2861012 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1,5 @@ -nightly +[toolchain] +channel = "stable" +components = [ "llvm-tools" ] +profile = "minimal" +targets = ["thumbv6m-none-eabi"] diff --git a/src/algo.rs b/src/algo.rs index bf548bb..7ebee54 100644 --- a/src/algo.rs +++ b/src/algo.rs @@ -1,6 +1,5 @@ #![macro_use] - use core::arch::asm; use core::num::NonZeroU32; @@ -12,10 +11,6 @@ fn panic(_info: &core::panic::PanicInfo) -> ! { } } -pub const FUNCTION_ERASE: u32 = 1; -pub const FUNCTION_PROGRAM: u32 = 2; -pub const FUNCTION_VERIFY: u32 = 3; - pub type ErrorCode = NonZeroU32; pub trait FlashAlgo: Sized + 'static { diff --git a/src/main.rs b/src/main.rs index 0f88c89..94b57c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,28 +3,19 @@ mod algo; -use core::mem; use core::mem::MaybeUninit; use self::algo::*; fn find_func(tag: [u8; 2]) -> T { - let tag = u16::from_le_bytes(tag); - + let tag = u16::from_le_bytes(tag) as u32; + type RomTableLookupFn = unsafe extern "C" fn(table: *const u16, code: u32) -> usize; unsafe { - let mut entry = *(0x00000014 as *const u16) as *const u16; - loop { - let entry_tag = entry.read(); - if entry_tag == 0 { - panic!("Func not found"); - } - entry = entry.add(1); - let entry_addr = entry.read(); - entry = entry.add(1); - if entry_tag == tag { - return mem::transmute_copy(&(entry_addr as u32)); - } - } + let lookup_func = core::ptr::read(0x0000_0018 as *const u16) as usize; + let lookup_func: RomTableLookupFn = core::mem::transmute(lookup_func); + let table = core::ptr::read(0x0000_00014 as *const u16) as usize; + let result = lookup_func(table as *const u16, tag); + core::mem::transmute_copy(&result) } } @@ -58,7 +49,6 @@ algo!(RP2040Algo); const BLOCK_SIZE: u32 = 65536; const SECTOR_SIZE: u32 = 4096; -const PAGE_SIZE: u32 = 256; const BLOCK_ERASE_CMD: u8 = 0xd8; const FLASH_BASE: u32 = 0x1000_0000; From 35fd9793e247f0597c7f1397a7dd4b053780f2ff Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sat, 9 Dec 2023 14:57:15 +0000 Subject: [PATCH 2/3] Fail if ROM funcs are missing. Belt-and-braces in case we ever get a different RP2040 ROM with missing functions. Also use constants for the ROM addresses. --- src/main.rs | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index 94b57c8..d181a73 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,15 +7,23 @@ use core::mem::MaybeUninit; use self::algo::*; -fn find_func(tag: [u8; 2]) -> T { +fn find_func(tag: [u8; 2]) -> Option { let tag = u16::from_le_bytes(tag) as u32; type RomTableLookupFn = unsafe extern "C" fn(table: *const u16, code: u32) -> usize; + /// This location in flash holds a 16-bit truncated pointer for the ROM lookup function + const ROM_TABLE_LOOKUP_PTR: *const u16 = 0x0000_0018 as _; + /// This location in flash holds a 16-bit truncated pointer for the ROM function table + /// (there's also a ROM data table which we don't need) + const FUNC_TABLE: *const u16 = 0x0000_0014 as _; unsafe { - let lookup_func = core::ptr::read(0x0000_0018 as *const u16) as usize; + let lookup_func = ROM_TABLE_LOOKUP_PTR.read() as usize; let lookup_func: RomTableLookupFn = core::mem::transmute(lookup_func); - let table = core::ptr::read(0x0000_00014 as *const u16) as usize; + let table = FUNC_TABLE.read() as usize; let result = lookup_func(table as *const u16, tag); - core::mem::transmute_copy(&result) + if result == 0 { + return None; + } + Some(core::mem::transmute_copy(&result)) } } @@ -29,15 +37,15 @@ struct ROMFuncs { } impl ROMFuncs { - fn load() -> Self { - ROMFuncs { - connect_internal_flash: find_func(*b"IF"), - flash_exit_xip: find_func(*b"EX"), - flash_range_erase: find_func(*b"RE"), - flash_range_program: find_func(*b"RP"), - flash_flush_cache: find_func(*b"FC"), - flash_enter_cmd_xip: find_func(*b"CX"), - } + fn load() -> Option { + Some(ROMFuncs { + connect_internal_flash: find_func(*b"IF")?, + flash_exit_xip: find_func(*b"EX")?, + flash_range_erase: find_func(*b"RE")?, + flash_range_program: find_func(*b"RP")?, + flash_flush_cache: find_func(*b"FC")?, + flash_enter_cmd_xip: find_func(*b"CX")?, + }) } } @@ -54,8 +62,9 @@ const FLASH_BASE: u32 = 0x1000_0000; impl FlashAlgo for RP2040Algo { fn new(_address: u32, _clock: u32, _function: u32) -> Result { - let funcs = ROMFuncs::load(); - + let Some(funcs) = ROMFuncs::load() else { + return Err(ErrorCode::new(1).unwrap()); + }; (funcs.connect_internal_flash)(); (funcs.flash_exit_xip)(); Ok(Self { funcs }) From 1d215befbcd0cc027b3461f0b60e6ba6192d62d1 Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Sat, 9 Dec 2023 14:57:55 +0000 Subject: [PATCH 3/3] Add docs so clippy is happy. --- src/algo.rs | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/algo.rs b/src/algo.rs index 7ebee54..19d8b4e 100644 --- a/src/algo.rs +++ b/src/algo.rs @@ -33,6 +33,12 @@ macro_rules! algo { static mut _IS_INIT: bool = false; static mut _ALGO_INSTANCE: MaybeUninit<$type> = MaybeUninit::uninit(); + /// Initialise the Flash Algorithm + /// + /// # Safety + /// + /// Will disable execution from Flash. Ensure you are running from SRAM + /// and do not call any flash-based based functions. #[no_mangle] #[link_section = ".entry"] pub unsafe extern "C" fn Init(addr: u32, clock: u32, function: u32) -> u32 { @@ -48,16 +54,25 @@ macro_rules! algo { Err(e) => e.get(), } } + /// Uninitialise the Flash Algorithm #[no_mangle] #[link_section = ".entry"] - pub unsafe extern "C" fn UnInit() -> u32 { - if !_IS_INIT { - return 1; + pub extern "C" fn UnInit() -> u32 { + unsafe { + if !_IS_INIT { + return 1; + } + _ALGO_INSTANCE.as_mut_ptr().drop_in_place(); + _IS_INIT = false; } - _ALGO_INSTANCE.as_mut_ptr().drop_in_place(); - _IS_INIT = false; 0 } + /// Erase the flash chip. + /// + /// # Safety + /// + /// Will erase the flash chip. Ensure you really want to erase the + /// flash chip. #[no_mangle] #[link_section = ".entry"] pub unsafe extern "C" fn EraseChip() -> u32 { @@ -70,6 +85,11 @@ macro_rules! algo { Err(e) => e.get(), } } + /// Erase the a sector on the flash chip. + /// + /// # Safety + /// + /// Will erase the given sector. Pass a valid sector address. #[no_mangle] #[link_section = ".entry"] pub unsafe extern "C" fn EraseSector(addr: u32) -> u32 { @@ -82,6 +102,12 @@ macro_rules! algo { Err(e) => e.get(), } } + /// Write to a page on the flash chip. + /// + /// # Safety + /// + /// Will program the given page. Pass a valid page address, and a + /// valid pointer to at least `size` bytes of data. #[no_mangle] #[link_section = ".entry"] pub unsafe extern "C" fn ProgramPage(addr: u32, size: u32, data: *const u8) -> u32 {