Merge pull request #3297 from CBJamo/rp2350_flash

rp: 2350 flash write and OTP
This commit is contained in:
Dario Nieuwenhuis 2024-09-10 21:36:49 +00:00 committed by GitHub
commit 0bf9a2591b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 999 additions and 36 deletions

View File

@ -17,9 +17,13 @@ use crate::peripherals::FLASH;
/// Flash base address. /// Flash base address.
pub const FLASH_BASE: *const u32 = 0x10000000 as _; pub const FLASH_BASE: *const u32 = 0x10000000 as _;
/// Address for xip setup function set up by the 235x bootrom.
#[cfg(feature = "_rp235x")]
pub const BOOTROM_BASE: *const u32 = 0x400e0000 as _;
/// If running from RAM, we might have no boot2. Use bootrom `flash_enter_cmd_xip` instead. /// If running from RAM, we might have no boot2. Use bootrom `flash_enter_cmd_xip` instead.
// TODO: when run-from-ram is set, completely skip the "pause cores and jumpp to RAM" dance. // TODO: when run-from-ram is set, completely skip the "pause cores and jumpp to RAM" dance.
pub const USE_BOOT2: bool = !cfg!(feature = "run-from-ram"); pub const USE_BOOT2: bool = !cfg!(feature = "run-from-ram") | cfg!(feature = "_rp235x");
// **NOTE**: // **NOTE**:
// //
@ -97,7 +101,10 @@ impl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Drop for BackgroundRead<'a, '
// Errata RP2040-E8: Perform an uncached read to make sure there's not a transfer in // Errata RP2040-E8: Perform an uncached read to make sure there's not a transfer in
// flight that might effect an address written to start a new transfer. This stalls // flight that might effect an address written to start a new transfer. This stalls
// until after any transfer is complete, so the address will not change anymore. // until after any transfer is complete, so the address will not change anymore.
#[cfg(feature = "rp2040")]
const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x13000000 as *const _; const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x13000000 as *const _;
#[cfg(feature = "_rp235x")]
const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x14000000 as *const _;
unsafe { unsafe {
core::ptr::read_volatile(XIP_NOCACHE_NOALLOC_BASE); core::ptr::read_volatile(XIP_NOCACHE_NOALLOC_BASE);
} }
@ -225,12 +232,14 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
} }
/// Read SPI flash unique ID /// Read SPI flash unique ID
#[cfg(feature = "rp2040")]
pub fn blocking_unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { pub fn blocking_unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> {
unsafe { in_ram(|| ram_helpers::flash_unique_id(uid))? }; unsafe { in_ram(|| ram_helpers::flash_unique_id(uid))? };
Ok(()) Ok(())
} }
/// Read SPI flash JEDEC ID /// Read SPI flash JEDEC ID
#[cfg(feature = "rp2040")]
pub fn blocking_jedec_id(&mut self) -> Result<u32, Error> { pub fn blocking_jedec_id(&mut self) -> Result<u32, Error> {
let mut jedec = None; let mut jedec = None;
unsafe { unsafe {
@ -301,7 +310,10 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> {
// Use the XIP AUX bus port, rather than the FIFO register access (e.x. // Use the XIP AUX bus port, rather than the FIFO register access (e.x.
// pac::XIP_CTRL.stream_fifo().as_ptr()) to avoid DMA stalling on // pac::XIP_CTRL.stream_fifo().as_ptr()) to avoid DMA stalling on
// general XIP access. // general XIP access.
#[cfg(feature = "rp2040")]
const XIP_AUX_BASE: *const u32 = 0x50400000 as *const _; const XIP_AUX_BASE: *const u32 = 0x50400000 as *const _;
#[cfg(feature = "_rp235x")]
const XIP_AUX_BASE: *const u32 = 0x50500000 as *const _;
let transfer = unsafe { let transfer = unsafe {
crate::dma::read( crate::dma::read(
self.dma.as_mut().unwrap(), self.dma.as_mut().unwrap(),
@ -512,7 +524,10 @@ mod ram_helpers {
pub unsafe fn flash_range_erase(addr: u32, len: u32) { pub unsafe fn flash_range_erase(addr: u32, len: u32) {
let mut boot2 = [0u32; 256 / 4]; let mut boot2 = [0u32; 256 / 4];
let ptrs = if USE_BOOT2 { let ptrs = if USE_BOOT2 {
#[cfg(feature = "rp2040")]
rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
#[cfg(feature = "_rp235x")]
core::ptr::copy_nonoverlapping(BOOTROM_BASE as *const u8, boot2.as_mut_ptr() as *mut u8, 256);
flash_function_pointers_with_boot2(true, false, &boot2) flash_function_pointers_with_boot2(true, false, &boot2)
} else { } else {
flash_function_pointers(true, false) flash_function_pointers(true, false)
@ -542,7 +557,10 @@ mod ram_helpers {
pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8]) { pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8]) {
let mut boot2 = [0u32; 256 / 4]; let mut boot2 = [0u32; 256 / 4];
let ptrs = if USE_BOOT2 { let ptrs = if USE_BOOT2 {
#[cfg(feature = "rp2040")]
rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
#[cfg(feature = "_rp235x")]
core::ptr::copy_nonoverlapping(BOOTROM_BASE as *const u8, (boot2).as_mut_ptr() as *mut u8, 256);
flash_function_pointers_with_boot2(true, true, &boot2) flash_function_pointers_with_boot2(true, true, &boot2)
} else { } else {
flash_function_pointers(true, true) flash_function_pointers(true, true)
@ -577,7 +595,10 @@ mod ram_helpers {
pub unsafe fn flash_range_program(addr: u32, data: &[u8]) { pub unsafe fn flash_range_program(addr: u32, data: &[u8]) {
let mut boot2 = [0u32; 256 / 4]; let mut boot2 = [0u32; 256 / 4];
let ptrs = if USE_BOOT2 { let ptrs = if USE_BOOT2 {
#[cfg(feature = "rp2040")]
rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
#[cfg(feature = "_rp235x")]
core::ptr::copy_nonoverlapping(BOOTROM_BASE as *const u8, boot2.as_mut_ptr() as *mut u8, 256);
flash_function_pointers_with_boot2(false, true, &boot2) flash_function_pointers_with_boot2(false, true, &boot2)
} else { } else {
flash_function_pointers(false, true) flash_function_pointers(false, true)
@ -606,15 +627,6 @@ mod ram_helpers {
#[link_section = ".data.ram_func"] #[link_section = ".data.ram_func"]
#[cfg(feature = "rp2040")] #[cfg(feature = "rp2040")]
unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) { unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {
/*
Should be equivalent to:
rom_data::connect_internal_flash();
rom_data::flash_exit_xip();
rom_data::flash_range_erase(addr, len, 1 << 31, 0); // if selected
rom_data::flash_range_program(addr, data as *const _, len); // if selected
rom_data::flash_flush_cache();
rom_data::flash_enter_cmd_xip();
*/
#[cfg(target_arch = "arm")] #[cfg(target_arch = "arm")]
core::arch::asm!( core::arch::asm!(
"mov r8, r0", "mov r8, r0",
@ -667,11 +679,30 @@ mod ram_helpers {
); );
} }
/// # Safety
///
/// Nothing must access flash while this is running.
/// Usually this means:
/// - interrupts must be disabled
/// - 2nd core must be running code from RAM or ROM with interrupts disabled
/// - DMA must not access flash memory
/// Length of data must be a multiple of 4096
/// addr must be aligned to 4096
#[inline(never)] #[inline(never)]
#[link_section = ".data.ram_func"] #[link_section = ".data.ram_func"]
#[cfg(feature = "_rp235x")] #[cfg(feature = "_rp235x")]
unsafe fn write_flash_inner(_addr: u32, _len: u32, _data: Option<&[u8]>, _ptrs: *const FlashFunctionPointers) { unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {
todo!(); let data = data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null());
((*ptrs).connect_internal_flash)();
((*ptrs).flash_exit_xip)();
if (*ptrs).flash_range_erase.is_some() {
((*ptrs).flash_range_erase.unwrap())(addr, len as usize, 1 << 31, 0);
}
if (*ptrs).flash_range_program.is_some() {
((*ptrs).flash_range_program.unwrap())(addr, data as *const _, len as usize);
}
((*ptrs).flash_flush_cache)();
((*ptrs).flash_enter_cmd_xip)();
} }
#[repr(C)] #[repr(C)]
@ -707,6 +738,7 @@ mod ram_helpers {
/// - DMA must not access flash memory /// - DMA must not access flash memory
/// ///
/// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)
#[cfg(feature = "rp2040")]
pub unsafe fn flash_unique_id(out: &mut [u8]) { pub unsafe fn flash_unique_id(out: &mut [u8]) {
let mut boot2 = [0u32; 256 / 4]; let mut boot2 = [0u32; 256 / 4];
let ptrs = if USE_BOOT2 { let ptrs = if USE_BOOT2 {
@ -715,6 +747,7 @@ mod ram_helpers {
} else { } else {
flash_function_pointers(false, false) flash_function_pointers(false, false)
}; };
// 4B - read unique ID // 4B - read unique ID
let cmd = [0x4B]; let cmd = [0x4B];
read_flash(&cmd[..], 4, out, &ptrs as *const FlashFunctionPointers); read_flash(&cmd[..], 4, out, &ptrs as *const FlashFunctionPointers);
@ -735,6 +768,7 @@ mod ram_helpers {
/// - DMA must not access flash memory /// - DMA must not access flash memory
/// ///
/// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)
#[cfg(feature = "rp2040")]
pub unsafe fn flash_jedec_id() -> u32 { pub unsafe fn flash_jedec_id() -> u32 {
let mut boot2 = [0u32; 256 / 4]; let mut boot2 = [0u32; 256 / 4];
let ptrs = if USE_BOOT2 { let ptrs = if USE_BOOT2 {
@ -743,6 +777,7 @@ mod ram_helpers {
} else { } else {
flash_function_pointers(false, false) flash_function_pointers(false, false)
}; };
let mut id = [0u8; 4]; let mut id = [0u8; 4];
// 9F - read JEDEC ID // 9F - read JEDEC ID
let cmd = [0x9F]; let cmd = [0x9F];
@ -750,6 +785,7 @@ mod ram_helpers {
u32::from_be_bytes(id) u32::from_be_bytes(id)
} }
#[cfg(feature = "rp2040")]
unsafe fn read_flash(cmd_addr: &[u8], dummy_len: u32, out: &mut [u8], ptrs: *const FlashFunctionPointers) { unsafe fn read_flash(cmd_addr: &[u8], dummy_len: u32, out: &mut [u8], ptrs: *const FlashFunctionPointers) {
read_flash_inner( read_flash_inner(
FlashCommand { FlashCommand {
@ -890,13 +926,6 @@ mod ram_helpers {
clobber_abi("C"), clobber_abi("C"),
); );
} }
#[inline(never)]
#[link_section = ".data.ram_func"]
#[cfg(feature = "_rp235x")]
unsafe fn read_flash_inner(_cmd: FlashCommand, _ptrs: *const FlashFunctionPointers) {
todo!();
}
} }
/// Make sure to uphold the contract points with rp2040-flash. /// Make sure to uphold the contract points with rp2040-flash.

View File

@ -15,6 +15,7 @@ pub use rp_binary_info as binary_info;
#[cfg(feature = "critical-section-impl")] #[cfg(feature = "critical-section-impl")]
mod critical_section_impl; mod critical_section_impl;
#[cfg(feature = "rp2040")]
mod intrinsics; mod intrinsics;
pub mod adc; pub mod adc;
@ -31,6 +32,8 @@ pub mod gpio;
pub mod i2c; pub mod i2c;
pub mod i2c_slave; pub mod i2c_slave;
pub mod multicore; pub mod multicore;
#[cfg(feature = "_rp235x")]
pub mod otp;
pub mod pwm; pub mod pwm;
mod reset; mod reset;
pub mod rom_data; pub mod rom_data;
@ -401,7 +404,7 @@ embassy_hal_internal::peripherals! {
BOOTSEL, BOOTSEL,
} }
#[cfg(not(feature = "boot2-none"))] #[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))]
macro_rules! select_bootloader { macro_rules! select_bootloader {
( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => { ( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => {
$( $(
@ -418,7 +421,7 @@ macro_rules! select_bootloader {
} }
} }
#[cfg(not(feature = "boot2-none"))] #[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))]
select_bootloader! { select_bootloader! {
"boot2-at25sf128a" => BOOT_LOADER_AT25SF128A, "boot2-at25sf128a" => BOOT_LOADER_AT25SF128A,
"boot2-gd25q64cs" => BOOT_LOADER_GD25Q64CS, "boot2-gd25q64cs" => BOOT_LOADER_GD25Q64CS,

108
embassy-rp/src/otp.rs Normal file
View File

@ -0,0 +1,108 @@
//! Interface to the RP2350's One Time Programmable Memory
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
// https://github.com/rp-rs/rp-hal/blob/main/rp235x-hal/src/rom_data.rs
/// The ways in which we can fail to read OTP
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
/// The user passed an invalid index to a function.
InvalidIndex,
/// The hardware refused to let us read this word, probably due to
/// read lock set earlier in the boot process.
InvalidPermissions,
}
/// OTP read address, using automatic Error Correction.
///
/// A 32-bit read returns the ECC-corrected data for two neighbouring rows, or
/// all-ones on permission failure. Only the first 8 KiB is populated.
pub const OTP_DATA_BASE: *const u32 = 0x4013_0000 as *const u32;
/// OTP read address, without using any automatic Error Correction.
///
/// A 32-bit read returns 24-bits of raw data from the OTP word.
pub const OTP_DATA_RAW_BASE: *const u32 = 0x4013_4000 as *const u32;
/// How many pages in OTP (post error-correction)
pub const NUM_PAGES: usize = 64;
/// How many rows in one page in OTP (post error-correction)
pub const NUM_ROWS_PER_PAGE: usize = 64;
/// How many rows in OTP (post error-correction)
pub const NUM_ROWS: usize = NUM_PAGES * NUM_ROWS_PER_PAGE;
/// Read one ECC protected word from the OTP
pub fn read_ecc_word(row: usize) -> Result<u16, Error> {
if row >= NUM_ROWS {
return Err(Error::InvalidIndex);
}
// First do a raw read to check permissions
let _ = read_raw_word(row)?;
// One 32-bit read gets us two rows
let offset = row >> 1;
// # Safety
//
// We checked this offset was in range already.
let value = unsafe { OTP_DATA_BASE.add(offset).read() };
if (row & 1) == 0 {
Ok(value as u16)
} else {
Ok((value >> 16) as u16)
}
}
/// Read one raw word from the OTP
///
/// You get the 24-bit raw value in the lower part of the 32-bit result.
pub fn read_raw_word(row: usize) -> Result<u32, Error> {
if row >= NUM_ROWS {
return Err(Error::InvalidIndex);
}
// One 32-bit read gets us one row
// # Safety
//
// We checked this offset was in range already.
let value = unsafe { OTP_DATA_RAW_BASE.add(row).read() };
if value == 0xFFFF_FFFF {
Err(Error::InvalidPermissions)
} else {
Ok(value)
}
}
/// Get the random 64bit chipid from rows 0x0-0x3.
pub fn get_chipid() -> Result<u64, Error> {
let w0 = read_ecc_word(0x000)?.to_be_bytes();
let w1 = read_ecc_word(0x001)?.to_be_bytes();
let w2 = read_ecc_word(0x002)?.to_be_bytes();
let w3 = read_ecc_word(0x003)?.to_be_bytes();
Ok(u64::from_be_bytes([
w3[0], w3[1], w2[0], w2[1], w1[0], w1[1], w0[0], w0[1],
]))
}
/// Get the 128bit private random number from rows 0x4-0xb.
///
/// This ID is not exposed through the USB PICOBOOT GET_INFO command
/// or the ROM get_sys_info() API. However note that the USB PICOBOOT OTP
/// access point can read the entirety of page 0, so this value is not
/// meaningfully private unless the USB PICOBOOT interface is disabled via the
//// DISABLE_BOOTSEL_USB_PICOBOOT_IFC flag in BOOT_FLAGS0
pub fn get_private_random_number() -> Result<u128, Error> {
let w0 = read_ecc_word(0x004)?.to_be_bytes();
let w1 = read_ecc_word(0x005)?.to_be_bytes();
let w2 = read_ecc_word(0x006)?.to_be_bytes();
let w3 = read_ecc_word(0x007)?.to_be_bytes();
let w4 = read_ecc_word(0x008)?.to_be_bytes();
let w5 = read_ecc_word(0x009)?.to_be_bytes();
let w6 = read_ecc_word(0x00a)?.to_be_bytes();
let w7 = read_ecc_word(0x00b)?.to_be_bytes();
Ok(u128::from_be_bytes([
w7[0], w7[1], w6[0], w6[1], w5[0], w5[1], w4[0], w4[1], w3[0], w3[1], w2[0], w2[1], w1[0], w1[1], w0[0], w0[1],
]))
}

View File

@ -0,0 +1,33 @@
#![cfg_attr(
feature = "rp2040",
doc = r"
//! Functions and data from the RPI Bootrom.
//!
//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1:
//!
//! > The Bootrom contains a number of public functions that provide useful
//! > RP2040 functionality that might be needed in the absence of any other code
//! > on the device, as well as highly optimized versions of certain key
//! > functionality that would otherwise have to take up space in most user
//! > binaries.
"
)]
#![cfg_attr(
feature = "_rp235x",
doc = r"
//! Functions and data from the RPI Bootrom.
//!
//! From [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the
//! RP2350 datasheet:
//!
//! > Whilst some ROM space is dedicated to the implementation of the boot
//! > sequence and USB/UART boot interfaces, the bootrom also contains public
//! > functions that provide useful RP2350 functionality that may be useful for
//! > any code or runtime running on the device
"
)]
#[cfg_attr(feature = "rp2040", path = "rp2040.rs")]
#[cfg_attr(feature = "_rp235x", path = "rp235x.rs")]
mod inner;
pub use inner::*;

View File

@ -189,7 +189,7 @@ macro_rules! rom_functions {
declare_rom_function! { declare_rom_function! {
$(#[$outer])* $(#[$outer])*
fn $name( $($argname: $ty),* ) -> $ret { fn $name( $($argname: $ty),* ) -> $ret {
$crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) $crate::rom_data::inner::rom_table_lookup($crate::rom_data::inner::FUNC_TABLE, *$c)
} }
} }
@ -205,7 +205,7 @@ macro_rules! rom_functions {
declare_rom_function! { declare_rom_function! {
$(#[$outer])* $(#[$outer])*
unsafe fn $name( $($argname: $ty),* ) -> $ret { unsafe fn $name( $($argname: $ty),* ) -> $ret {
$crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) $crate::rom_data::inner::rom_table_lookup($crate::rom_data::inner::FUNC_TABLE, *$c)
} }
} }

View File

@ -0,0 +1,752 @@
//! Functions and data from the RPI Bootrom.
//!
//! From [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the
//! RP2350 datasheet:
//!
//! > Whilst some ROM space is dedicated to the implementation of the boot
//! > sequence and USB/UART boot interfaces, the bootrom also contains public
//! > functions that provide useful RP2350 functionality that may be useful for
//! > any code or runtime running on the device
// Credit: taken from `rp-hal` (also licensed Apache+MIT)
// https://github.com/rp-rs/rp-hal/blob/main/rp235x-hal/src/rom_data.rs
/// A bootrom function table code.
pub type RomFnTableCode = [u8; 2];
/// This function searches for the tag which matches the mask.
type RomTableLookupFn = unsafe extern "C" fn(code: u32, mask: u32) -> usize;
/// Pointer to the value lookup function supplied by the ROM.
///
/// This address is described at `5.5.1. Locating the API Functions`
#[cfg(all(target_arch = "arm", target_os = "none"))]
const ROM_TABLE_LOOKUP_A2: *const u16 = 0x0000_0016 as _;
/// Pointer to the value lookup function supplied by the ROM.
///
/// This address is described at `5.5.1. Locating the API Functions`
#[cfg(all(target_arch = "arm", target_os = "none"))]
const ROM_TABLE_LOOKUP_A1: *const u32 = 0x0000_0018 as _;
/// Pointer to the data lookup function supplied by the ROM.
///
/// On Arm, the same function is used to look up code and data.
#[cfg(all(target_arch = "arm", target_os = "none"))]
const ROM_DATA_LOOKUP_A2: *const u16 = ROM_TABLE_LOOKUP_A2;
/// Pointer to the data lookup function supplied by the ROM.
///
/// On Arm, the same function is used to look up code and data.
#[cfg(all(target_arch = "arm", target_os = "none"))]
const ROM_DATA_LOOKUP_A1: *const u32 = ROM_TABLE_LOOKUP_A1;
/// Pointer to the value lookup function supplied by the ROM.
///
/// This address is described at `5.5.1. Locating the API Functions`
#[cfg(not(all(target_arch = "arm", target_os = "none")))]
const ROM_TABLE_LOOKUP_A2: *const u16 = 0x0000_7DFA as _;
/// Pointer to the value lookup function supplied by the ROM.
///
/// This address is described at `5.5.1. Locating the API Functions`
#[cfg(not(all(target_arch = "arm", target_os = "none")))]
const ROM_TABLE_LOOKUP_A1: *const u32 = 0x0000_7DF8 as _;
/// Pointer to the data lookup function supplied by the ROM.
///
/// On RISC-V, a different function is used to look up data.
#[cfg(not(all(target_arch = "arm", target_os = "none")))]
const ROM_DATA_LOOKUP_A2: *const u16 = 0x0000_7DF8 as _;
/// Pointer to the data lookup function supplied by the ROM.
///
/// On RISC-V, a different function is used to look up data.
#[cfg(not(all(target_arch = "arm", target_os = "none")))]
const ROM_DATA_LOOKUP_A1: *const u32 = 0x0000_7DF4 as _;
/// Address of the version number of the ROM.
const VERSION_NUMBER: *const u8 = 0x0000_0013 as _;
#[allow(unused)]
mod rt_flags {
pub const FUNC_RISCV: u32 = 0x0001;
pub const FUNC_RISCV_FAR: u32 = 0x0003;
pub const FUNC_ARM_SEC: u32 = 0x0004;
// reserved for 32-bit pointer: 0x0008
pub const FUNC_ARM_NONSEC: u32 = 0x0010;
// reserved for 32-bit pointer: 0x0020
pub const DATA: u32 = 0x0040;
// reserved for 32-bit pointer: 0x0080
#[cfg(all(target_arch = "arm", target_os = "none"))]
pub const FUNC_ARM_SEC_RISCV: u32 = FUNC_ARM_SEC;
#[cfg(not(all(target_arch = "arm", target_os = "none")))]
pub const FUNC_ARM_SEC_RISCV: u32 = FUNC_RISCV;
}
/// Retrieve rom content from a table using a code.
pub fn rom_table_lookup(tag: RomFnTableCode, mask: u32) -> usize {
let tag = u16::from_le_bytes(tag) as u32;
unsafe {
let lookup_func = if rom_version_number() == 1 {
ROM_TABLE_LOOKUP_A1.read() as usize
} else {
ROM_TABLE_LOOKUP_A2.read() as usize
};
let lookup_func: RomTableLookupFn = core::mem::transmute(lookup_func);
lookup_func(tag, mask)
}
}
/// Retrieve rom data content from a table using a code.
pub fn rom_data_lookup(tag: RomFnTableCode, mask: u32) -> usize {
let tag = u16::from_le_bytes(tag) as u32;
unsafe {
let lookup_func = if rom_version_number() == 1 {
ROM_DATA_LOOKUP_A1.read() as usize
} else {
ROM_DATA_LOOKUP_A2.read() as usize
};
let lookup_func: RomTableLookupFn = core::mem::transmute(lookup_func);
lookup_func(tag, mask)
}
}
macro_rules! declare_rom_function {
(
$(#[$outer:meta])*
fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
$lookup:block
) => {
#[doc = r"Additional access for the `"]
#[doc = stringify!($name)]
#[doc = r"` ROM function."]
pub mod $name {
/// Retrieve a function pointer.
#[cfg(not(feature = "rom-func-cache"))]
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
let p: usize = $lookup;
unsafe {
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
/// Retrieve a function pointer.
#[cfg(feature = "rom-func-cache")]
pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
use core::sync::atomic::{AtomicU16, Ordering};
// All pointers in the ROM fit in 16 bits, so we don't need a
// full width word to store the cached value.
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
// This is safe because the lookup will always resolve
// to the same value. So even if an interrupt or another
// core starts at the same time, it just repeats some
// work and eventually writes back the correct value.
let p: usize = match CACHED_PTR.load(Ordering::Relaxed) {
0 => {
let raw: usize = $lookup;
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
raw
},
val => val as usize,
};
unsafe {
let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
}
$(#[$outer])*
pub extern "C" fn $name( $($argname: $ty),* ) -> $ret {
$name::ptr()($($argname),*)
}
};
(
$(#[$outer:meta])*
unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
$lookup:block
) => {
#[doc = r"Additional access for the `"]
#[doc = stringify!($name)]
#[doc = r"` ROM function."]
pub mod $name {
/// Retrieve a function pointer.
#[cfg(not(feature = "rom-func-cache"))]
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
let p: usize = $lookup;
unsafe {
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
/// Retrieve a function pointer.
#[cfg(feature = "rom-func-cache")]
pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
use core::sync::atomic::{AtomicU16, Ordering};
// All pointers in the ROM fit in 16 bits, so we don't need a
// full width word to store the cached value.
static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
// This is safe because the lookup will always resolve
// to the same value. So even if an interrupt or another
// core starts at the same time, it just repeats some
// work and eventually writes back the correct value.
let p: usize = match CACHED_PTR.load(Ordering::Relaxed) {
0 => {
let raw: usize = $lookup;
CACHED_PTR.store(raw as u16, Ordering::Relaxed);
raw
},
val => val as usize,
};
unsafe {
let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
func
}
}
}
$(#[$outer])*
/// # Safety
///
/// This is a low-level C function. It may be difficult to call safely from
/// Rust. If in doubt, check the rp235x datasheet for details and do your own
/// safety evaluation.
pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret {
$name::ptr()($($argname),*)
}
};
}
// **************** 5.5.7 Low-level Flash Commands ****************
declare_rom_function! {
/// Restore all QSPI pad controls to their default state, and connect the
/// QMI peripheral to the QSPI pads.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn connect_internal_flash() -> () {
crate::rom_data::rom_table_lookup(*b"IF", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Initialise the QMI for serial operations (direct mode)
///
/// Also initialise a basic XIP mode, where the QMI will perform 03h serial
/// read commands at low speed (CLKDIV=12) in response to XIP reads.
///
/// Then, issue a sequence to the QSPI device on chip select 0, designed to
/// return it from continuous read mode ("XIP mode") and/or QPI mode to a
/// state where it will accept serial commands. This is necessary after
/// system reset to restore the QSPI device to a known state, because
/// resetting RP2350 does not reset attached QSPI devices. It is also
/// necessary when user code, having already performed some
/// continuous-read-mode or QPI-mode accesses, wishes to return the QSPI
/// device to a state where it will accept the serial erase and programming
/// commands issued by the bootroms flash access functions.
///
/// If a GPIO for the secondary chip select is configured via FLASH_DEVINFO,
/// then the XIP exit sequence is also issued to chip select 1.
///
/// The QSPI device should be accessible for XIP reads after calling this
/// function; the name flash_exit_xip refers to returning the QSPI device
/// from its XIP state to a serial command state.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn flash_exit_xip() -> () {
crate::rom_data::rom_table_lookup(*b"EX", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Erase count bytes, starting at addr (offset from start of flash).
///
/// Optionally, pass a block erase command e.g. D8h block erase, and the
/// size of the block erased by this command — this function will use the
/// larger block erase where possible, for much higher erase speed. addr
/// must be aligned to a 4096-byte sector, and count must be a multiple of
/// 4096 bytes.
///
/// This is a low-level flash API, and no validation of the arguments is
/// performed. See flash_op() for a higher-level API which checks alignment,
/// flash bounds and partition permissions, and can transparently apply a
/// runtime-to-storage address translation.
///
/// The QSPI device must be in a serial command state before calling this
/// API, which can be achieved by calling connect_internal_flash() followed
/// by flash_exit_xip(). After the erase, the flash cache should be flushed
/// via flash_flush_cache() to ensure the modified flash data is visible to
/// cached XIP accesses.
///
/// Finally, the original XIP mode should be restored by copying the saved
/// XIP setup function from bootram into SRAM, and executing it: the bootrom
/// provides a default function which restores the flash mode/clkdiv
/// discovered during flash scanning, and user programs can override this
/// with their own XIP setup function.
///
/// For the duration of the erase operation, QMI is in direct mode (Section
/// 12.14.5) and attempting to access XIP from DMA, the debugger or the
/// other core will return a bus fault. XIP becomes accessible again once
/// the function returns.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> () {
crate::rom_data::rom_table_lookup(*b"RE", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Program data to a range of flash storage addresses starting at addr
/// (offset from the start of flash) and count bytes in size.
///
/// `addr` must be aligned to a 256-byte boundary, and count must be a
/// multiple of 256.
///
/// This is a low-level flash API, and no validation of the arguments is
/// performed. See flash_op() for a higher-level API which checks alignment,
/// flash bounds and partition permissions, and can transparently apply a
/// runtime-to-storage address translation.
///
/// The QSPI device must be in a serial command state before calling this
/// API — see notes on flash_range_erase().
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> () {
crate::rom_data::rom_table_lookup(*b"RP", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Flush the entire XIP cache, by issuing an invalidate by set/way
/// maintenance operation to every cache line (Section 4.4.1).
///
/// This ensures that flash program/erase operations are visible to
/// subsequent cached XIP reads.
///
/// Note that this unpins pinned cache lines, which may interfere with
/// cache-as-SRAM use of the XIP cache.
///
/// No other operations are performed.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn flash_flush_cache() -> () {
crate::rom_data::rom_table_lookup(*b"FC", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Configure the QMI to generate a standard 03h serial read command, with
/// 24 address bits, upon each XIP access.
///
/// This is a slow XIP configuration, but is widely supported. CLKDIV is set
/// to 12. The debugger may call this function to ensure that flash is
/// readable following a program/erase operation.
///
/// Note that the same setup is performed by flash_exit_xip(), and the
/// RP2350 flash program/erase functions do not leave XIP in an inaccessible
/// state, so calls to this function are largely redundant. It is provided
/// for compatibility with RP2040.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn flash_enter_cmd_xip() -> () {
crate::rom_data::rom_table_lookup(*b"CX", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Configure QMI for one of a small menu of XIP read modes supported by the
/// bootrom. This mode is configured for both memory windows (both chip
/// selects), and the clock divisor is also applied to direct mode.
///
/// The available modes are:
///
/// * 0: `03h` serial read: serial address, serial data, no wait cycles
/// * 1: `0Bh` serial read: serial address, serial data, 8 wait cycles
/// * 2: `BBh` dual-IO read: dual address, dual data, 4 wait cycles
/// (including MODE bits, which are driven to 0)
/// * 3: `EBh` quad-IO read: quad address, quad data, 6 wait cycles
/// (including MODE bits, which are driven to 0)
///
/// The XIP write command/format are not configured by this function. When
/// booting from flash, the bootrom tries each of these modes in turn, from
/// 3 down to 0. The first mode that is found to work is remembered, and a
/// default XIP setup function is written into bootram that calls this
/// function (flash_select_xip_read_mode) with the parameters discovered
/// during flash scanning. This can be called at any time to restore the
/// flash parameters discovered during flash boot.
///
/// All XIP modes configured by the bootrom have an 8-bit serial command
/// prefix, so that the flash can remain in a serial command state, meaning
/// XIP accesses can be mixed more freely with program/erase serial
/// operations. This has a performance penalty, so users can perform their
/// own flash setup after flash boot using continuous read mode or QPI mode
/// to avoid or alleviate the command prefix cost.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn flash_select_xip_read_mode(bootrom_xip_mode: u8, clkdiv: u8) -> () {
crate::rom_data::rom_table_lookup(*b"XM", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Restore the QMI address translation registers, ATRANS0 through ATRANS7,
/// to their reset state. This makes the runtime- to-storage address map an
/// identity map, i.e. the mapped and unmapped address are equal, and the
/// entire space is fully mapped.
///
/// See [Section 12.14.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the RP2350
/// datasheet.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn flash_reset_address_trans() -> () {
crate::rom_data::rom_table_lookup(*b"RA", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
// **************** High-level Flash Commands ****************
declare_rom_function! {
/// Applies the address translation currently configured by QMI address
/// translation registers, ATRANS0 through ATRANS7.
///
/// See [Section 12.14.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the RP2350
/// datasheet.
///
/// Translating an address outside of the XIP runtime address window, or
/// beyond the bounds of an ATRANSx_SIZE field, returns
/// BOOTROM_ERROR_INVALID_ADDRESS, which is not a valid flash storage
/// address. Otherwise, return the storage address which QMI would access
/// when presented with the runtime address addr. This is effectively a
/// virtual-to-physical address translation for QMI.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn flash_runtime_to_storage_addr(addr: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"FA", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Non-secure version of [flash_runtime_to_storage_addr()]
///
/// Supported architectures: ARM-NS
#[cfg(all(target_arch = "arm", target_os = "none"))]
unsafe fn flash_runtime_to_storage_addr_ns(addr: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"FA", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)
}
}
declare_rom_function! {
/// Perform a flash read, erase, or program operation.
///
/// Erase operations must be sector-aligned (4096 bytes) and sector-
/// multiple-sized, and program operations must be page-aligned (256 bytes)
/// and page-multiple-sized; misaligned erase and program operations will
/// return BOOTROM_ERROR_BAD_ALIGNMENT. The operation — erase, read, program
/// — is selected by the CFLASH_OP_BITS bitfield of the flags argument.
///
/// See datasheet section 5.5.8.2 for more details.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn flash_op(flags: u32, addr: u32, size_bytes: u32, buffer: *mut u8) -> i32 {
crate::rom_data::rom_table_lookup(*b"FO", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Non-secure version of [flash_op()]
///
/// Supported architectures: ARM-NS
#[cfg(all(target_arch = "arm", target_os = "none"))]
unsafe fn flash_op_ns(flags: u32, addr: u32, size_bytes: u32, buffer: *mut u8) -> i32 {
crate::rom_data::rom_table_lookup(*b"FO", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)
}
}
// **************** Security Related Functions ****************
declare_rom_function! {
/// Allow or disallow the specific NS API (note all NS APIs default to
/// disabled).
///
/// See datasheet section 5.5.9.1 for more details.
///
/// Supported architectures: ARM-S
#[cfg(all(target_arch = "arm", target_os = "none"))]
unsafe fn set_ns_api_permission(ns_api_num: u32, allowed: u8) -> i32 {
crate::rom_data::rom_table_lookup(*b"SP", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC)
}
}
declare_rom_function! {
/// Utility method that can be used by secure ARM code to validate a buffer
/// passed to it from Non-secure code.
///
/// See datasheet section 5.5.9.2 for more details.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn validate_ns_buffer() -> () {
crate::rom_data::rom_table_lookup(*b"VB", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
// **************** Miscellaneous Functions ****************
declare_rom_function! {
/// Resets the RP2350 and uses the watchdog facility to restart.
///
/// See datasheet section 5.5.10.1 for more details.
///
/// Supported architectures: ARM-S, RISC-V
fn reboot(flags: u32, delay_ms: u32, p0: u32, p1: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"RB", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Non-secure version of [reboot()]
///
/// Supported architectures: ARM-NS
#[cfg(all(target_arch = "arm", target_os = "none"))]
fn reboot_ns(flags: u32, delay_ms: u32, p0: u32, p1: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"RB", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)
}
}
declare_rom_function! {
/// Resets internal bootrom state.
///
/// See datasheet section 5.5.10.2 for more details.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn bootrom_state_reset(flags: u32) -> () {
crate::rom_data::rom_table_lookup(*b"SR", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Set a boot ROM callback.
///
/// The only supported callback_number is 0 which sets the callback used for
/// the secure_call API.
///
/// See datasheet section 5.5.10.3 for more details.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn set_rom_callback(callback_number: i32, callback_fn: *const ()) -> i32 {
crate::rom_data::rom_table_lookup(*b"RC", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
// **************** System Information Functions ****************
declare_rom_function! {
/// Fills a buffer with various system information.
///
/// Note that this API is also used to return information over the PICOBOOT
/// interface.
///
/// See datasheet section 5.5.11.1 for more details.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn get_sys_info(out_buffer: *mut u32, out_buffer_word_size: usize, flags: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"GS", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Non-secure version of [get_sys_info()]
///
/// Supported architectures: ARM-NS
#[cfg(all(target_arch = "arm", target_os = "none"))]
unsafe fn get_sys_info_ns(out_buffer: *mut u32, out_buffer_word_size: usize, flags: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"GS", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)
}
}
declare_rom_function! {
/// Fills a buffer with information from the partition table.
///
/// Note that this API is also used to return information over the PICOBOOT
/// interface.
///
/// See datasheet section 5.5.11.2 for more details.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn get_partition_table_info(out_buffer: *mut u32, out_buffer_word_size: usize, flags_and_partition: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"GP", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Non-secure version of [get_partition_table_info()]
///
/// Supported architectures: ARM-NS
#[cfg(all(target_arch = "arm", target_os = "none"))]
unsafe fn get_partition_table_info_ns(out_buffer: *mut u32, out_buffer_word_size: usize, flags_and_partition: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"GP", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)
}
}
declare_rom_function! {
/// Loads the current partition table from flash, if present.
///
/// See datasheet section 5.5.11.3 for more details.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn load_partition_table(workarea_base: *mut u8, workarea_size: usize, force_reload: bool) -> i32 {
crate::rom_data::rom_table_lookup(*b"LP", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Writes data from a buffer into OTP, or reads data from OTP into a buffer.
///
/// See datasheet section 5.5.11.4 for more details.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn otp_access(buf: *mut u8, buf_len: usize, row_and_flags: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"OA", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Non-secure version of [otp_access()]
///
/// Supported architectures: ARM-NS
#[cfg(all(target_arch = "arm", target_os = "none"))]
unsafe fn otp_access_ns(buf: *mut u8, buf_len: usize, row_and_flags: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"OA", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)
}
}
// **************** Boot Related Functions ****************
declare_rom_function! {
/// Determines which of the partitions has the "better" IMAGE_DEF. In the
/// case of executable images, this is the one that would be booted.
///
/// See datasheet section 5.5.12.1 for more details.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn pick_ab_parition(workarea_base: *mut u8, workarea_size: usize, partition_a_num: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"AB", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Searches a memory region for a launchable image, and executes it if
/// possible.
///
/// See datasheet section 5.5.12.2 for more details.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn chain_image(workarea_base: *mut u8, workarea_size: usize, region_base: i32, region_size: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"CI", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Perform an "explicit" buy of an executable launched via an IMAGE_DEF
/// which was "explicit buy" flagged.
///
/// See datasheet section 5.5.12.3 for more details.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn explicit_buy(buffer: *mut u8, buffer_size: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"EB", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Not yet documented.
///
/// See datasheet section 5.5.12.4 for more details.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn get_uf2_target_partition(workarea_base: *mut u8, workarea_size: usize, family_id: u32, partition_out: *mut u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"GU", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
declare_rom_function! {
/// Returns: The index of the B partition of partition A if a partition
/// table is present and loaded, and there is a partition A with a B
/// partition; otherwise returns BOOTROM_ERROR_NOT_FOUND.
///
/// See datasheet section 5.5.12.5 for more details.
///
/// Supported architectures: ARM-S, RISC-V
unsafe fn get_b_partition(partition_a: u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"GB", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
}
}
// **************** Non-secure-specific Functions ****************
// NB: The "secure_call" function should be here, but it doesn't have a fixed
// function signature as it is designed to let you bounce into any secure
// function from non-secure mode.
// **************** RISC-V Functions ****************
declare_rom_function! {
/// Set stack for RISC-V bootrom functions to use.
///
/// See datasheet section 5.5.14.1 for more details.
///
/// Supported architectures: RISC-V
#[cfg(not(all(target_arch = "arm", target_os = "none")))]
unsafe fn set_bootrom_stack(base_size: *mut u32) -> i32 {
crate::rom_data::rom_table_lookup(*b"SS", crate::rom_data::inner::rt_flags::FUNC_RISCV)
}
}
/// The version number of the rom.
pub fn rom_version_number() -> u8 {
unsafe { *VERSION_NUMBER }
}
/// The 8 most significant hex digits of the Bootrom git revision.
pub fn git_revision() -> u32 {
let ptr = rom_data_lookup(*b"GR", rt_flags::DATA) as *const u32;
unsafe { ptr.read() }
}
/// A pointer to the resident partition table info.
///
/// The resident partition table is the subset of the full partition table that
/// is kept in memory, and used for flash permissions.
pub fn partition_table_pointer() -> *const u32 {
let ptr = rom_data_lookup(*b"PT", rt_flags::DATA) as *const *const u32;
unsafe { ptr.read() }
}
/// Determine if we are in secure mode
///
/// Returns `true` if we are in secure mode and `false` if we are in non-secure
/// mode.
#[cfg(all(target_arch = "arm", target_os = "none"))]
pub fn is_secure_mode() -> bool {
// Look at the start of ROM, which is always readable
#[allow(clippy::zero_ptr)]
let rom_base: *mut u32 = 0x0000_0000 as *mut u32;
// Use the 'tt' instruction to check the permissions for that address
let tt = cortex_m::asm::tt(rom_base);
// Is the secure bit set? => secure mode
(tt & (1 << 22)) != 0
}
/// Determine if we are in secure mode
///
/// Always returns `false` on RISC-V as it is impossible to determine if
/// you are in Machine Mode or User Mode by design.
#[cfg(not(all(target_arch = "arm", target_os = "none")))]
pub fn is_secure_mode() -> bool {
false
}

View File

@ -1,6 +1,7 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))'] [target.'cfg(all(target_arch = "arm", target_os = "none"))']
#runner = "probe-rs run --chip RP2040" #runner = "probe-rs run --chip RP2040"
runner = "elf2uf2-rs -d" #runner = "elf2uf2-rs -d"
runner = "picotool load -u -v -x -t elf"
[build] [build]
target = "thumbv8m.main-none-eabihf" target = "thumbv8m.main-none-eabihf"

View File

@ -21,7 +21,7 @@ pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
embassy_rp::binary_info::rp_program_name!(c"example"), embassy_rp::binary_info::rp_program_name!(c"example"),
embassy_rp::binary_info::rp_cargo_version!(), embassy_rp::binary_info::rp_cargo_version!(),
embassy_rp::binary_info::rp_program_description!(c"Blinky"), embassy_rp::binary_info::rp_program_description!(c"Flash"),
embassy_rp::binary_info::rp_program_build_attribute!(), embassy_rp::binary_info::rp_program_build_attribute!(),
]; ];
@ -41,22 +41,13 @@ async fn main(_spawner: Spawner) {
let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0); let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0);
// Get JEDEC id
let jedec = flash.blocking_jedec_id().unwrap();
info!("jedec id: 0x{:x}", jedec);
// Get unique id
let mut uid = [0; 8];
flash.blocking_unique_id(&mut uid).unwrap();
info!("unique id: {:?}", uid);
erase_write_sector(&mut flash, 0x00); erase_write_sector(&mut flash, 0x00);
multiwrite_bytes(&mut flash, ERASE_SIZE as u32); multiwrite_bytes(&mut flash, ERASE_SIZE as u32);
background_read(&mut flash, (ERASE_SIZE * 2) as u32).await; background_read(&mut flash, (ERASE_SIZE * 2) as u32).await;
loop {} info!("Flash Works!");
} }
fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) {
@ -82,7 +73,7 @@ fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH
defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf)); defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
info!("Contents after write starts with {=[u8]}", read_buf[0..4]); info!("Contents after write starts with {=[u8]}", read_buf[0..4]);
if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] { if read_buf[0..4] != [0x01, 0x02, 0x03, 0x04] {
defmt::panic!("unexpected"); defmt::panic!("unexpected");
} }
} }

View File

@ -0,0 +1,46 @@
//! This example shows reading the OTP constants on the RP235x.
#![no_std]
#![no_main]
use defmt::*;
use embassy_executor::Spawner;
use embassy_rp::block::ImageDef;
use embassy_rp::otp;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};
#[link_section = ".start_block"]
#[used]
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
// Program metadata for `picotool info`
#[link_section = ".bi_entries"]
#[used]
pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
embassy_rp::binary_info::rp_program_name!(c"OTP Read Example"),
embassy_rp::binary_info::rp_cargo_version!(),
embassy_rp::binary_info::rp_program_description!(c"OTP Read Example"),
embassy_rp::binary_info::rp_program_build_attribute!(),
];
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let _ = embassy_rp::init(Default::default());
//
// add some delay to give an attached debug probe time to parse the
// defmt RTT header. Reading that header might touch flash memory, which
// interferes with flash write operations.
// https://github.com/knurling-rs/defmt/pull/683
Timer::after_millis(10).await;
let chip_id = unwrap!(otp::get_chipid());
info!("Unique id:{:X}", chip_id);
let private_rand = unwrap!(otp::get_private_random_number());
info!("Private Rand:{:X}", private_rand);
loop {
Timer::after_secs(1).await;
}
}