aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-09-10 21:36:49 +0000
committerGitHub <[email protected]>2024-09-10 21:36:49 +0000
commit0bf9a2591b062f2f8409fe27c590e1783b139e38 (patch)
treec738996dd476da1fde72f23ef5eb05620498a8d4
parentaff1e7486210f6fa80627b92da5b24a1c8bf6017 (diff)
parente75903138a92e6c749454cee516c812df618bbfa (diff)
Merge pull request #3297 from CBJamo/rp2350_flash
rp: 2350 flash write and OTP
-rw-r--r--embassy-rp/src/flash.rs67
-rw-r--r--embassy-rp/src/lib.rs7
-rw-r--r--embassy-rp/src/otp.rs108
-rw-r--r--embassy-rp/src/rom_data/mod.rs33
-rw-r--r--embassy-rp/src/rom_data/rp2040.rs (renamed from embassy-rp/src/rom_data.rs)4
-rw-r--r--embassy-rp/src/rom_data/rp235x.rs752
-rw-r--r--examples/rp23/.cargo/config.toml3
-rw-r--r--examples/rp23/src/bin/flash.rs15
-rw-r--r--examples/rp23/src/bin/otp.rs46
9 files changed, 999 insertions, 36 deletions
diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs
index a68493932..fbc8b35ec 100644
--- a/embassy-rp/src/flash.rs
+++ b/embassy-rp/src/flash.rs
@@ -17,9 +17,13 @@ use crate::peripherals::FLASH;
17/// Flash base address. 17/// Flash base address.
18pub const FLASH_BASE: *const u32 = 0x10000000 as _; 18pub const FLASH_BASE: *const u32 = 0x10000000 as _;
19 19
20/// Address for xip setup function set up by the 235x bootrom.
21#[cfg(feature = "_rp235x")]
22pub const BOOTROM_BASE: *const u32 = 0x400e0000 as _;
23
20/// If running from RAM, we might have no boot2. Use bootrom `flash_enter_cmd_xip` instead. 24/// If running from RAM, we might have no boot2. Use bootrom `flash_enter_cmd_xip` instead.
21// TODO: when run-from-ram is set, completely skip the "pause cores and jumpp to RAM" dance. 25// TODO: when run-from-ram is set, completely skip the "pause cores and jumpp to RAM" dance.
22pub const USE_BOOT2: bool = !cfg!(feature = "run-from-ram"); 26pub const USE_BOOT2: bool = !cfg!(feature = "run-from-ram") | cfg!(feature = "_rp235x");
23 27
24// **NOTE**: 28// **NOTE**:
25// 29//
@@ -97,7 +101,10 @@ impl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Drop for BackgroundRead<'a, '
97 // Errata RP2040-E8: Perform an uncached read to make sure there's not a transfer in 101 // Errata RP2040-E8: Perform an uncached read to make sure there's not a transfer in
98 // flight that might effect an address written to start a new transfer. This stalls 102 // flight that might effect an address written to start a new transfer. This stalls
99 // until after any transfer is complete, so the address will not change anymore. 103 // until after any transfer is complete, so the address will not change anymore.
104 #[cfg(feature = "rp2040")]
100 const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x13000000 as *const _; 105 const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x13000000 as *const _;
106 #[cfg(feature = "_rp235x")]
107 const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x14000000 as *const _;
101 unsafe { 108 unsafe {
102 core::ptr::read_volatile(XIP_NOCACHE_NOALLOC_BASE); 109 core::ptr::read_volatile(XIP_NOCACHE_NOALLOC_BASE);
103 } 110 }
@@ -225,12 +232,14 @@ impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SI
225 } 232 }
226 233
227 /// Read SPI flash unique ID 234 /// Read SPI flash unique ID
235 #[cfg(feature = "rp2040")]
228 pub fn blocking_unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> { 236 pub fn blocking_unique_id(&mut self, uid: &mut [u8]) -> Result<(), Error> {
229 unsafe { in_ram(|| ram_helpers::flash_unique_id(uid))? }; 237 unsafe { in_ram(|| ram_helpers::flash_unique_id(uid))? };
230 Ok(()) 238 Ok(())
231 } 239 }
232 240
233 /// Read SPI flash JEDEC ID 241 /// Read SPI flash JEDEC ID
242 #[cfg(feature = "rp2040")]
234 pub fn blocking_jedec_id(&mut self) -> Result<u32, Error> { 243 pub fn blocking_jedec_id(&mut self) -> Result<u32, Error> {
235 let mut jedec = None; 244 let mut jedec = None;
236 unsafe { 245 unsafe {
@@ -301,7 +310,10 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> {
301 // Use the XIP AUX bus port, rather than the FIFO register access (e.x. 310 // Use the XIP AUX bus port, rather than the FIFO register access (e.x.
302 // pac::XIP_CTRL.stream_fifo().as_ptr()) to avoid DMA stalling on 311 // pac::XIP_CTRL.stream_fifo().as_ptr()) to avoid DMA stalling on
303 // general XIP access. 312 // general XIP access.
313 #[cfg(feature = "rp2040")]
304 const XIP_AUX_BASE: *const u32 = 0x50400000 as *const _; 314 const XIP_AUX_BASE: *const u32 = 0x50400000 as *const _;
315 #[cfg(feature = "_rp235x")]
316 const XIP_AUX_BASE: *const u32 = 0x50500000 as *const _;
305 let transfer = unsafe { 317 let transfer = unsafe {
306 crate::dma::read( 318 crate::dma::read(
307 self.dma.as_mut().unwrap(), 319 self.dma.as_mut().unwrap(),
@@ -512,7 +524,10 @@ mod ram_helpers {
512 pub unsafe fn flash_range_erase(addr: u32, len: u32) { 524 pub unsafe fn flash_range_erase(addr: u32, len: u32) {
513 let mut boot2 = [0u32; 256 / 4]; 525 let mut boot2 = [0u32; 256 / 4];
514 let ptrs = if USE_BOOT2 { 526 let ptrs = if USE_BOOT2 {
527 #[cfg(feature = "rp2040")]
515 rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); 528 rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
529 #[cfg(feature = "_rp235x")]
530 core::ptr::copy_nonoverlapping(BOOTROM_BASE as *const u8, boot2.as_mut_ptr() as *mut u8, 256);
516 flash_function_pointers_with_boot2(true, false, &boot2) 531 flash_function_pointers_with_boot2(true, false, &boot2)
517 } else { 532 } else {
518 flash_function_pointers(true, false) 533 flash_function_pointers(true, false)
@@ -542,7 +557,10 @@ mod ram_helpers {
542 pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8]) { 557 pub unsafe fn flash_range_erase_and_program(addr: u32, data: &[u8]) {
543 let mut boot2 = [0u32; 256 / 4]; 558 let mut boot2 = [0u32; 256 / 4];
544 let ptrs = if USE_BOOT2 { 559 let ptrs = if USE_BOOT2 {
560 #[cfg(feature = "rp2040")]
545 rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); 561 rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
562 #[cfg(feature = "_rp235x")]
563 core::ptr::copy_nonoverlapping(BOOTROM_BASE as *const u8, (boot2).as_mut_ptr() as *mut u8, 256);
546 flash_function_pointers_with_boot2(true, true, &boot2) 564 flash_function_pointers_with_boot2(true, true, &boot2)
547 } else { 565 } else {
548 flash_function_pointers(true, true) 566 flash_function_pointers(true, true)
@@ -577,7 +595,10 @@ mod ram_helpers {
577 pub unsafe fn flash_range_program(addr: u32, data: &[u8]) { 595 pub unsafe fn flash_range_program(addr: u32, data: &[u8]) {
578 let mut boot2 = [0u32; 256 / 4]; 596 let mut boot2 = [0u32; 256 / 4];
579 let ptrs = if USE_BOOT2 { 597 let ptrs = if USE_BOOT2 {
598 #[cfg(feature = "rp2040")]
580 rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256); 599 rom_data::memcpy44(&mut boot2 as *mut _, FLASH_BASE, 256);
600 #[cfg(feature = "_rp235x")]
601 core::ptr::copy_nonoverlapping(BOOTROM_BASE as *const u8, boot2.as_mut_ptr() as *mut u8, 256);
581 flash_function_pointers_with_boot2(false, true, &boot2) 602 flash_function_pointers_with_boot2(false, true, &boot2)
582 } else { 603 } else {
583 flash_function_pointers(false, true) 604 flash_function_pointers(false, true)
@@ -606,15 +627,6 @@ mod ram_helpers {
606 #[link_section = ".data.ram_func"] 627 #[link_section = ".data.ram_func"]
607 #[cfg(feature = "rp2040")] 628 #[cfg(feature = "rp2040")]
608 unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) { 629 unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {
609 /*
610 Should be equivalent to:
611 rom_data::connect_internal_flash();
612 rom_data::flash_exit_xip();
613 rom_data::flash_range_erase(addr, len, 1 << 31, 0); // if selected
614 rom_data::flash_range_program(addr, data as *const _, len); // if selected
615 rom_data::flash_flush_cache();
616 rom_data::flash_enter_cmd_xip();
617 */
618 #[cfg(target_arch = "arm")] 630 #[cfg(target_arch = "arm")]
619 core::arch::asm!( 631 core::arch::asm!(
620 "mov r8, r0", 632 "mov r8, r0",
@@ -667,11 +679,30 @@ mod ram_helpers {
667 ); 679 );
668 } 680 }
669 681
682 /// # Safety
683 ///
684 /// Nothing must access flash while this is running.
685 /// Usually this means:
686 /// - interrupts must be disabled
687 /// - 2nd core must be running code from RAM or ROM with interrupts disabled
688 /// - DMA must not access flash memory
689 /// Length of data must be a multiple of 4096
690 /// addr must be aligned to 4096
670 #[inline(never)] 691 #[inline(never)]
671 #[link_section = ".data.ram_func"] 692 #[link_section = ".data.ram_func"]
672 #[cfg(feature = "_rp235x")] 693 #[cfg(feature = "_rp235x")]
673 unsafe fn write_flash_inner(_addr: u32, _len: u32, _data: Option<&[u8]>, _ptrs: *const FlashFunctionPointers) { 694 unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) {
674 todo!(); 695 let data = data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null());
696 ((*ptrs).connect_internal_flash)();
697 ((*ptrs).flash_exit_xip)();
698 if (*ptrs).flash_range_erase.is_some() {
699 ((*ptrs).flash_range_erase.unwrap())(addr, len as usize, 1 << 31, 0);
700 }
701 if (*ptrs).flash_range_program.is_some() {
702 ((*ptrs).flash_range_program.unwrap())(addr, data as *const _, len as usize);
703 }
704 ((*ptrs).flash_flush_cache)();
705 ((*ptrs).flash_enter_cmd_xip)();
675 } 706 }
676 707
677 #[repr(C)] 708 #[repr(C)]
@@ -707,6 +738,7 @@ mod ram_helpers {
707 /// - DMA must not access flash memory 738 /// - DMA must not access flash memory
708 /// 739 ///
709 /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) 740 /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)
741 #[cfg(feature = "rp2040")]
710 pub unsafe fn flash_unique_id(out: &mut [u8]) { 742 pub unsafe fn flash_unique_id(out: &mut [u8]) {
711 let mut boot2 = [0u32; 256 / 4]; 743 let mut boot2 = [0u32; 256 / 4];
712 let ptrs = if USE_BOOT2 { 744 let ptrs = if USE_BOOT2 {
@@ -715,6 +747,7 @@ mod ram_helpers {
715 } else { 747 } else {
716 flash_function_pointers(false, false) 748 flash_function_pointers(false, false)
717 }; 749 };
750
718 // 4B - read unique ID 751 // 4B - read unique ID
719 let cmd = [0x4B]; 752 let cmd = [0x4B];
720 read_flash(&cmd[..], 4, out, &ptrs as *const FlashFunctionPointers); 753 read_flash(&cmd[..], 4, out, &ptrs as *const FlashFunctionPointers);
@@ -735,6 +768,7 @@ mod ram_helpers {
735 /// - DMA must not access flash memory 768 /// - DMA must not access flash memory
736 /// 769 ///
737 /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) 770 /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT)
771 #[cfg(feature = "rp2040")]
738 pub unsafe fn flash_jedec_id() -> u32 { 772 pub unsafe fn flash_jedec_id() -> u32 {
739 let mut boot2 = [0u32; 256 / 4]; 773 let mut boot2 = [0u32; 256 / 4];
740 let ptrs = if USE_BOOT2 { 774 let ptrs = if USE_BOOT2 {
@@ -743,6 +777,7 @@ mod ram_helpers {
743 } else { 777 } else {
744 flash_function_pointers(false, false) 778 flash_function_pointers(false, false)
745 }; 779 };
780
746 let mut id = [0u8; 4]; 781 let mut id = [0u8; 4];
747 // 9F - read JEDEC ID 782 // 9F - read JEDEC ID
748 let cmd = [0x9F]; 783 let cmd = [0x9F];
@@ -750,6 +785,7 @@ mod ram_helpers {
750 u32::from_be_bytes(id) 785 u32::from_be_bytes(id)
751 } 786 }
752 787
788 #[cfg(feature = "rp2040")]
753 unsafe fn read_flash(cmd_addr: &[u8], dummy_len: u32, out: &mut [u8], ptrs: *const FlashFunctionPointers) { 789 unsafe fn read_flash(cmd_addr: &[u8], dummy_len: u32, out: &mut [u8], ptrs: *const FlashFunctionPointers) {
754 read_flash_inner( 790 read_flash_inner(
755 FlashCommand { 791 FlashCommand {
@@ -890,13 +926,6 @@ mod ram_helpers {
890 clobber_abi("C"), 926 clobber_abi("C"),
891 ); 927 );
892 } 928 }
893
894 #[inline(never)]
895 #[link_section = ".data.ram_func"]
896 #[cfg(feature = "_rp235x")]
897 unsafe fn read_flash_inner(_cmd: FlashCommand, _ptrs: *const FlashFunctionPointers) {
898 todo!();
899 }
900} 929}
901 930
902/// Make sure to uphold the contract points with rp2040-flash. 931/// Make sure to uphold the contract points with rp2040-flash.
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index 21f0771de..c357c14c2 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -15,6 +15,7 @@ pub use rp_binary_info as binary_info;
15#[cfg(feature = "critical-section-impl")] 15#[cfg(feature = "critical-section-impl")]
16mod critical_section_impl; 16mod critical_section_impl;
17 17
18#[cfg(feature = "rp2040")]
18mod intrinsics; 19mod intrinsics;
19 20
20pub mod adc; 21pub mod adc;
@@ -31,6 +32,8 @@ pub mod gpio;
31pub mod i2c; 32pub mod i2c;
32pub mod i2c_slave; 33pub mod i2c_slave;
33pub mod multicore; 34pub mod multicore;
35#[cfg(feature = "_rp235x")]
36pub mod otp;
34pub mod pwm; 37pub mod pwm;
35mod reset; 38mod reset;
36pub mod rom_data; 39pub mod rom_data;
@@ -401,7 +404,7 @@ embassy_hal_internal::peripherals! {
401 BOOTSEL, 404 BOOTSEL,
402} 405}
403 406
404#[cfg(not(feature = "boot2-none"))] 407#[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))]
405macro_rules! select_bootloader { 408macro_rules! select_bootloader {
406 ( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => { 409 ( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => {
407 $( 410 $(
@@ -418,7 +421,7 @@ macro_rules! select_bootloader {
418 } 421 }
419} 422}
420 423
421#[cfg(not(feature = "boot2-none"))] 424#[cfg(all(not(feature = "boot2-none"), feature = "rp2040"))]
422select_bootloader! { 425select_bootloader! {
423 "boot2-at25sf128a" => BOOT_LOADER_AT25SF128A, 426 "boot2-at25sf128a" => BOOT_LOADER_AT25SF128A,
424 "boot2-gd25q64cs" => BOOT_LOADER_GD25Q64CS, 427 "boot2-gd25q64cs" => BOOT_LOADER_GD25Q64CS,
diff --git a/embassy-rp/src/otp.rs b/embassy-rp/src/otp.rs
new file mode 100644
index 000000000..cdaf5bded
--- /dev/null
+++ b/embassy-rp/src/otp.rs
@@ -0,0 +1,108 @@
1//! Interface to the RP2350's One Time Programmable Memory
2
3// Credit: taken from `rp-hal` (also licensed Apache+MIT)
4// https://github.com/rp-rs/rp-hal/blob/main/rp235x-hal/src/rom_data.rs
5
6/// The ways in which we can fail to read OTP
7#[derive(Debug, Clone)]
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9pub enum Error {
10 /// The user passed an invalid index to a function.
11 InvalidIndex,
12 /// The hardware refused to let us read this word, probably due to
13 /// read lock set earlier in the boot process.
14 InvalidPermissions,
15}
16
17/// OTP read address, using automatic Error Correction.
18///
19/// A 32-bit read returns the ECC-corrected data for two neighbouring rows, or
20/// all-ones on permission failure. Only the first 8 KiB is populated.
21pub const OTP_DATA_BASE: *const u32 = 0x4013_0000 as *const u32;
22
23/// OTP read address, without using any automatic Error Correction.
24///
25/// A 32-bit read returns 24-bits of raw data from the OTP word.
26pub const OTP_DATA_RAW_BASE: *const u32 = 0x4013_4000 as *const u32;
27
28/// How many pages in OTP (post error-correction)
29pub const NUM_PAGES: usize = 64;
30
31/// How many rows in one page in OTP (post error-correction)
32pub const NUM_ROWS_PER_PAGE: usize = 64;
33
34/// How many rows in OTP (post error-correction)
35pub const NUM_ROWS: usize = NUM_PAGES * NUM_ROWS_PER_PAGE;
36
37/// Read one ECC protected word from the OTP
38pub fn read_ecc_word(row: usize) -> Result<u16, Error> {
39 if row >= NUM_ROWS {
40 return Err(Error::InvalidIndex);
41 }
42 // First do a raw read to check permissions
43 let _ = read_raw_word(row)?;
44 // One 32-bit read gets us two rows
45 let offset = row >> 1;
46 // # Safety
47 //
48 // We checked this offset was in range already.
49 let value = unsafe { OTP_DATA_BASE.add(offset).read() };
50 if (row & 1) == 0 {
51 Ok(value as u16)
52 } else {
53 Ok((value >> 16) as u16)
54 }
55}
56
57/// Read one raw word from the OTP
58///
59/// You get the 24-bit raw value in the lower part of the 32-bit result.
60pub fn read_raw_word(row: usize) -> Result<u32, Error> {
61 if row >= NUM_ROWS {
62 return Err(Error::InvalidIndex);
63 }
64 // One 32-bit read gets us one row
65 // # Safety
66 //
67 // We checked this offset was in range already.
68 let value = unsafe { OTP_DATA_RAW_BASE.add(row).read() };
69 if value == 0xFFFF_FFFF {
70 Err(Error::InvalidPermissions)
71 } else {
72 Ok(value)
73 }
74}
75
76/// Get the random 64bit chipid from rows 0x0-0x3.
77pub fn get_chipid() -> Result<u64, Error> {
78 let w0 = read_ecc_word(0x000)?.to_be_bytes();
79 let w1 = read_ecc_word(0x001)?.to_be_bytes();
80 let w2 = read_ecc_word(0x002)?.to_be_bytes();
81 let w3 = read_ecc_word(0x003)?.to_be_bytes();
82
83 Ok(u64::from_be_bytes([
84 w3[0], w3[1], w2[0], w2[1], w1[0], w1[1], w0[0], w0[1],
85 ]))
86}
87
88/// Get the 128bit private random number from rows 0x4-0xb.
89///
90/// This ID is not exposed through the USB PICOBOOT GET_INFO command
91/// or the ROM get_sys_info() API. However note that the USB PICOBOOT OTP
92/// access point can read the entirety of page 0, so this value is not
93/// meaningfully private unless the USB PICOBOOT interface is disabled via the
94//// DISABLE_BOOTSEL_USB_PICOBOOT_IFC flag in BOOT_FLAGS0
95pub fn get_private_random_number() -> Result<u128, Error> {
96 let w0 = read_ecc_word(0x004)?.to_be_bytes();
97 let w1 = read_ecc_word(0x005)?.to_be_bytes();
98 let w2 = read_ecc_word(0x006)?.to_be_bytes();
99 let w3 = read_ecc_word(0x007)?.to_be_bytes();
100 let w4 = read_ecc_word(0x008)?.to_be_bytes();
101 let w5 = read_ecc_word(0x009)?.to_be_bytes();
102 let w6 = read_ecc_word(0x00a)?.to_be_bytes();
103 let w7 = read_ecc_word(0x00b)?.to_be_bytes();
104
105 Ok(u128::from_be_bytes([
106 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],
107 ]))
108}
diff --git a/embassy-rp/src/rom_data/mod.rs b/embassy-rp/src/rom_data/mod.rs
new file mode 100644
index 000000000..e5fcf8e3c
--- /dev/null
+++ b/embassy-rp/src/rom_data/mod.rs
@@ -0,0 +1,33 @@
1#![cfg_attr(
2 feature = "rp2040",
3 doc = r"
4//! Functions and data from the RPI Bootrom.
5//!
6//! From the [RP2040 datasheet](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf), Section 2.8.2.1:
7//!
8//! > The Bootrom contains a number of public functions that provide useful
9//! > RP2040 functionality that might be needed in the absence of any other code
10//! > on the device, as well as highly optimized versions of certain key
11//! > functionality that would otherwise have to take up space in most user
12//! > binaries.
13"
14)]
15#![cfg_attr(
16 feature = "_rp235x",
17 doc = r"
18//! Functions and data from the RPI Bootrom.
19//!
20//! From [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the
21//! RP2350 datasheet:
22//!
23//! > Whilst some ROM space is dedicated to the implementation of the boot
24//! > sequence and USB/UART boot interfaces, the bootrom also contains public
25//! > functions that provide useful RP2350 functionality that may be useful for
26//! > any code or runtime running on the device
27"
28)]
29
30#[cfg_attr(feature = "rp2040", path = "rp2040.rs")]
31#[cfg_attr(feature = "_rp235x", path = "rp235x.rs")]
32mod inner;
33pub use inner::*;
diff --git a/embassy-rp/src/rom_data.rs b/embassy-rp/src/rom_data/rp2040.rs
index baebe5b6c..5a74eddd6 100644
--- a/embassy-rp/src/rom_data.rs
+++ b/embassy-rp/src/rom_data/rp2040.rs
@@ -189,7 +189,7 @@ macro_rules! rom_functions {
189 declare_rom_function! { 189 declare_rom_function! {
190 $(#[$outer])* 190 $(#[$outer])*
191 fn $name( $($argname: $ty),* ) -> $ret { 191 fn $name( $($argname: $ty),* ) -> $ret {
192 $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) 192 $crate::rom_data::inner::rom_table_lookup($crate::rom_data::inner::FUNC_TABLE, *$c)
193 } 193 }
194 } 194 }
195 195
@@ -205,7 +205,7 @@ macro_rules! rom_functions {
205 declare_rom_function! { 205 declare_rom_function! {
206 $(#[$outer])* 206 $(#[$outer])*
207 unsafe fn $name( $($argname: $ty),* ) -> $ret { 207 unsafe fn $name( $($argname: $ty),* ) -> $ret {
208 $crate::rom_data::rom_table_lookup($crate::rom_data::FUNC_TABLE, *$c) 208 $crate::rom_data::inner::rom_table_lookup($crate::rom_data::inner::FUNC_TABLE, *$c)
209 } 209 }
210 } 210 }
211 211
diff --git a/embassy-rp/src/rom_data/rp235x.rs b/embassy-rp/src/rom_data/rp235x.rs
new file mode 100644
index 000000000..b16fee8f7
--- /dev/null
+++ b/embassy-rp/src/rom_data/rp235x.rs
@@ -0,0 +1,752 @@
1//! Functions and data from the RPI Bootrom.
2//!
3//! From [Section 5.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the
4//! RP2350 datasheet:
5//!
6//! > Whilst some ROM space is dedicated to the implementation of the boot
7//! > sequence and USB/UART boot interfaces, the bootrom also contains public
8//! > functions that provide useful RP2350 functionality that may be useful for
9//! > any code or runtime running on the device
10
11// Credit: taken from `rp-hal` (also licensed Apache+MIT)
12// https://github.com/rp-rs/rp-hal/blob/main/rp235x-hal/src/rom_data.rs
13
14/// A bootrom function table code.
15pub type RomFnTableCode = [u8; 2];
16
17/// This function searches for the tag which matches the mask.
18type RomTableLookupFn = unsafe extern "C" fn(code: u32, mask: u32) -> usize;
19
20/// Pointer to the value lookup function supplied by the ROM.
21///
22/// This address is described at `5.5.1. Locating the API Functions`
23#[cfg(all(target_arch = "arm", target_os = "none"))]
24const ROM_TABLE_LOOKUP_A2: *const u16 = 0x0000_0016 as _;
25
26/// Pointer to the value lookup function supplied by the ROM.
27///
28/// This address is described at `5.5.1. Locating the API Functions`
29#[cfg(all(target_arch = "arm", target_os = "none"))]
30const ROM_TABLE_LOOKUP_A1: *const u32 = 0x0000_0018 as _;
31
32/// Pointer to the data lookup function supplied by the ROM.
33///
34/// On Arm, the same function is used to look up code and data.
35#[cfg(all(target_arch = "arm", target_os = "none"))]
36const ROM_DATA_LOOKUP_A2: *const u16 = ROM_TABLE_LOOKUP_A2;
37
38/// Pointer to the data lookup function supplied by the ROM.
39///
40/// On Arm, the same function is used to look up code and data.
41#[cfg(all(target_arch = "arm", target_os = "none"))]
42const ROM_DATA_LOOKUP_A1: *const u32 = ROM_TABLE_LOOKUP_A1;
43
44/// Pointer to the value lookup function supplied by the ROM.
45///
46/// This address is described at `5.5.1. Locating the API Functions`
47#[cfg(not(all(target_arch = "arm", target_os = "none")))]
48const ROM_TABLE_LOOKUP_A2: *const u16 = 0x0000_7DFA as _;
49
50/// Pointer to the value lookup function supplied by the ROM.
51///
52/// This address is described at `5.5.1. Locating the API Functions`
53#[cfg(not(all(target_arch = "arm", target_os = "none")))]
54const ROM_TABLE_LOOKUP_A1: *const u32 = 0x0000_7DF8 as _;
55
56/// Pointer to the data lookup function supplied by the ROM.
57///
58/// On RISC-V, a different function is used to look up data.
59#[cfg(not(all(target_arch = "arm", target_os = "none")))]
60const ROM_DATA_LOOKUP_A2: *const u16 = 0x0000_7DF8 as _;
61
62/// Pointer to the data lookup function supplied by the ROM.
63///
64/// On RISC-V, a different function is used to look up data.
65#[cfg(not(all(target_arch = "arm", target_os = "none")))]
66const ROM_DATA_LOOKUP_A1: *const u32 = 0x0000_7DF4 as _;
67
68/// Address of the version number of the ROM.
69const VERSION_NUMBER: *const u8 = 0x0000_0013 as _;
70
71#[allow(unused)]
72mod rt_flags {
73 pub const FUNC_RISCV: u32 = 0x0001;
74 pub const FUNC_RISCV_FAR: u32 = 0x0003;
75 pub const FUNC_ARM_SEC: u32 = 0x0004;
76 // reserved for 32-bit pointer: 0x0008
77 pub const FUNC_ARM_NONSEC: u32 = 0x0010;
78 // reserved for 32-bit pointer: 0x0020
79 pub const DATA: u32 = 0x0040;
80 // reserved for 32-bit pointer: 0x0080
81 #[cfg(all(target_arch = "arm", target_os = "none"))]
82 pub const FUNC_ARM_SEC_RISCV: u32 = FUNC_ARM_SEC;
83 #[cfg(not(all(target_arch = "arm", target_os = "none")))]
84 pub const FUNC_ARM_SEC_RISCV: u32 = FUNC_RISCV;
85}
86
87/// Retrieve rom content from a table using a code.
88pub fn rom_table_lookup(tag: RomFnTableCode, mask: u32) -> usize {
89 let tag = u16::from_le_bytes(tag) as u32;
90 unsafe {
91 let lookup_func = if rom_version_number() == 1 {
92 ROM_TABLE_LOOKUP_A1.read() as usize
93 } else {
94 ROM_TABLE_LOOKUP_A2.read() as usize
95 };
96 let lookup_func: RomTableLookupFn = core::mem::transmute(lookup_func);
97 lookup_func(tag, mask)
98 }
99}
100
101/// Retrieve rom data content from a table using a code.
102pub fn rom_data_lookup(tag: RomFnTableCode, mask: u32) -> usize {
103 let tag = u16::from_le_bytes(tag) as u32;
104 unsafe {
105 let lookup_func = if rom_version_number() == 1 {
106 ROM_DATA_LOOKUP_A1.read() as usize
107 } else {
108 ROM_DATA_LOOKUP_A2.read() as usize
109 };
110 let lookup_func: RomTableLookupFn = core::mem::transmute(lookup_func);
111 lookup_func(tag, mask)
112 }
113}
114
115macro_rules! declare_rom_function {
116 (
117 $(#[$outer:meta])*
118 fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
119 $lookup:block
120 ) => {
121 #[doc = r"Additional access for the `"]
122 #[doc = stringify!($name)]
123 #[doc = r"` ROM function."]
124 pub mod $name {
125 /// Retrieve a function pointer.
126 #[cfg(not(feature = "rom-func-cache"))]
127 pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
128 let p: usize = $lookup;
129 unsafe {
130 let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
131 func
132 }
133 }
134
135 /// Retrieve a function pointer.
136 #[cfg(feature = "rom-func-cache")]
137 pub fn ptr() -> extern "C" fn( $($argname: $ty),* ) -> $ret {
138 use core::sync::atomic::{AtomicU16, Ordering};
139
140 // All pointers in the ROM fit in 16 bits, so we don't need a
141 // full width word to store the cached value.
142 static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
143 // This is safe because the lookup will always resolve
144 // to the same value. So even if an interrupt or another
145 // core starts at the same time, it just repeats some
146 // work and eventually writes back the correct value.
147 let p: usize = match CACHED_PTR.load(Ordering::Relaxed) {
148 0 => {
149 let raw: usize = $lookup;
150 CACHED_PTR.store(raw as u16, Ordering::Relaxed);
151 raw
152 },
153 val => val as usize,
154 };
155 unsafe {
156 let func : extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
157 func
158 }
159 }
160 }
161
162 $(#[$outer])*
163 pub extern "C" fn $name( $($argname: $ty),* ) -> $ret {
164 $name::ptr()($($argname),*)
165 }
166 };
167
168 (
169 $(#[$outer:meta])*
170 unsafe fn $name:ident( $($argname:ident: $ty:ty),* ) -> $ret:ty
171 $lookup:block
172 ) => {
173 #[doc = r"Additional access for the `"]
174 #[doc = stringify!($name)]
175 #[doc = r"` ROM function."]
176 pub mod $name {
177 /// Retrieve a function pointer.
178 #[cfg(not(feature = "rom-func-cache"))]
179 pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
180 let p: usize = $lookup;
181 unsafe {
182 let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
183 func
184 }
185 }
186
187 /// Retrieve a function pointer.
188 #[cfg(feature = "rom-func-cache")]
189 pub fn ptr() -> unsafe extern "C" fn( $($argname: $ty),* ) -> $ret {
190 use core::sync::atomic::{AtomicU16, Ordering};
191
192 // All pointers in the ROM fit in 16 bits, so we don't need a
193 // full width word to store the cached value.
194 static CACHED_PTR: AtomicU16 = AtomicU16::new(0);
195 // This is safe because the lookup will always resolve
196 // to the same value. So even if an interrupt or another
197 // core starts at the same time, it just repeats some
198 // work and eventually writes back the correct value.
199 let p: usize = match CACHED_PTR.load(Ordering::Relaxed) {
200 0 => {
201 let raw: usize = $lookup;
202 CACHED_PTR.store(raw as u16, Ordering::Relaxed);
203 raw
204 },
205 val => val as usize,
206 };
207 unsafe {
208 let func : unsafe extern "C" fn( $($argname: $ty),* ) -> $ret = core::mem::transmute(p);
209 func
210 }
211 }
212 }
213
214 $(#[$outer])*
215 /// # Safety
216 ///
217 /// This is a low-level C function. It may be difficult to call safely from
218 /// Rust. If in doubt, check the rp235x datasheet for details and do your own
219 /// safety evaluation.
220 pub unsafe extern "C" fn $name( $($argname: $ty),* ) -> $ret {
221 $name::ptr()($($argname),*)
222 }
223 };
224}
225
226// **************** 5.5.7 Low-level Flash Commands ****************
227
228declare_rom_function! {
229 /// Restore all QSPI pad controls to their default state, and connect the
230 /// QMI peripheral to the QSPI pads.
231 ///
232 /// Supported architectures: ARM-S, RISC-V
233 unsafe fn connect_internal_flash() -> () {
234 crate::rom_data::rom_table_lookup(*b"IF", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
235 }
236}
237
238declare_rom_function! {
239 /// Initialise the QMI for serial operations (direct mode)
240 ///
241 /// Also initialise a basic XIP mode, where the QMI will perform 03h serial
242 /// read commands at low speed (CLKDIV=12) in response to XIP reads.
243 ///
244 /// Then, issue a sequence to the QSPI device on chip select 0, designed to
245 /// return it from continuous read mode ("XIP mode") and/or QPI mode to a
246 /// state where it will accept serial commands. This is necessary after
247 /// system reset to restore the QSPI device to a known state, because
248 /// resetting RP2350 does not reset attached QSPI devices. It is also
249 /// necessary when user code, having already performed some
250 /// continuous-read-mode or QPI-mode accesses, wishes to return the QSPI
251 /// device to a state where it will accept the serial erase and programming
252 /// commands issued by the bootrom’s flash access functions.
253 ///
254 /// If a GPIO for the secondary chip select is configured via FLASH_DEVINFO,
255 /// then the XIP exit sequence is also issued to chip select 1.
256 ///
257 /// The QSPI device should be accessible for XIP reads after calling this
258 /// function; the name flash_exit_xip refers to returning the QSPI device
259 /// from its XIP state to a serial command state.
260 ///
261 /// Supported architectures: ARM-S, RISC-V
262 unsafe fn flash_exit_xip() -> () {
263 crate::rom_data::rom_table_lookup(*b"EX", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
264 }
265}
266
267declare_rom_function! {
268 /// Erase count bytes, starting at addr (offset from start of flash).
269 ///
270 /// Optionally, pass a block erase command e.g. D8h block erase, and the
271 /// size of the block erased by this command — this function will use the
272 /// larger block erase where possible, for much higher erase speed. addr
273 /// must be aligned to a 4096-byte sector, and count must be a multiple of
274 /// 4096 bytes.
275 ///
276 /// This is a low-level flash API, and no validation of the arguments is
277 /// performed. See flash_op() for a higher-level API which checks alignment,
278 /// flash bounds and partition permissions, and can transparently apply a
279 /// runtime-to-storage address translation.
280 ///
281 /// The QSPI device must be in a serial command state before calling this
282 /// API, which can be achieved by calling connect_internal_flash() followed
283 /// by flash_exit_xip(). After the erase, the flash cache should be flushed
284 /// via flash_flush_cache() to ensure the modified flash data is visible to
285 /// cached XIP accesses.
286 ///
287 /// Finally, the original XIP mode should be restored by copying the saved
288 /// XIP setup function from bootram into SRAM, and executing it: the bootrom
289 /// provides a default function which restores the flash mode/clkdiv
290 /// discovered during flash scanning, and user programs can override this
291 /// with their own XIP setup function.
292 ///
293 /// For the duration of the erase operation, QMI is in direct mode (Section
294 /// 12.14.5) and attempting to access XIP from DMA, the debugger or the
295 /// other core will return a bus fault. XIP becomes accessible again once
296 /// the function returns.
297 ///
298 /// Supported architectures: ARM-S, RISC-V
299 unsafe fn flash_range_erase(addr: u32, count: usize, block_size: u32, block_cmd: u8) -> () {
300 crate::rom_data::rom_table_lookup(*b"RE", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
301 }
302}
303
304declare_rom_function! {
305 /// Program data to a range of flash storage addresses starting at addr
306 /// (offset from the start of flash) and count bytes in size.
307 ///
308 /// `addr` must be aligned to a 256-byte boundary, and count must be a
309 /// multiple of 256.
310 ///
311 /// This is a low-level flash API, and no validation of the arguments is
312 /// performed. See flash_op() for a higher-level API which checks alignment,
313 /// flash bounds and partition permissions, and can transparently apply a
314 /// runtime-to-storage address translation.
315 ///
316 /// The QSPI device must be in a serial command state before calling this
317 /// API — see notes on flash_range_erase().
318 ///
319 /// Supported architectures: ARM-S, RISC-V
320 unsafe fn flash_range_program(addr: u32, data: *const u8, count: usize) -> () {
321 crate::rom_data::rom_table_lookup(*b"RP", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
322 }
323}
324
325declare_rom_function! {
326 /// Flush the entire XIP cache, by issuing an invalidate by set/way
327 /// maintenance operation to every cache line (Section 4.4.1).
328 ///
329 /// This ensures that flash program/erase operations are visible to
330 /// subsequent cached XIP reads.
331 ///
332 /// Note that this unpins pinned cache lines, which may interfere with
333 /// cache-as-SRAM use of the XIP cache.
334 ///
335 /// No other operations are performed.
336 ///
337 /// Supported architectures: ARM-S, RISC-V
338 unsafe fn flash_flush_cache() -> () {
339 crate::rom_data::rom_table_lookup(*b"FC", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
340 }
341}
342
343declare_rom_function! {
344 /// Configure the QMI to generate a standard 03h serial read command, with
345 /// 24 address bits, upon each XIP access.
346 ///
347 /// This is a slow XIP configuration, but is widely supported. CLKDIV is set
348 /// to 12. The debugger may call this function to ensure that flash is
349 /// readable following a program/erase operation.
350 ///
351 /// Note that the same setup is performed by flash_exit_xip(), and the
352 /// RP2350 flash program/erase functions do not leave XIP in an inaccessible
353 /// state, so calls to this function are largely redundant. It is provided
354 /// for compatibility with RP2040.
355 ///
356 /// Supported architectures: ARM-S, RISC-V
357 unsafe fn flash_enter_cmd_xip() -> () {
358 crate::rom_data::rom_table_lookup(*b"CX", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
359 }
360}
361
362declare_rom_function! {
363 /// Configure QMI for one of a small menu of XIP read modes supported by the
364 /// bootrom. This mode is configured for both memory windows (both chip
365 /// selects), and the clock divisor is also applied to direct mode.
366 ///
367 /// The available modes are:
368 ///
369 /// * 0: `03h` serial read: serial address, serial data, no wait cycles
370 /// * 1: `0Bh` serial read: serial address, serial data, 8 wait cycles
371 /// * 2: `BBh` dual-IO read: dual address, dual data, 4 wait cycles
372 /// (including MODE bits, which are driven to 0)
373 /// * 3: `EBh` quad-IO read: quad address, quad data, 6 wait cycles
374 /// (including MODE bits, which are driven to 0)
375 ///
376 /// The XIP write command/format are not configured by this function. When
377 /// booting from flash, the bootrom tries each of these modes in turn, from
378 /// 3 down to 0. The first mode that is found to work is remembered, and a
379 /// default XIP setup function is written into bootram that calls this
380 /// function (flash_select_xip_read_mode) with the parameters discovered
381 /// during flash scanning. This can be called at any time to restore the
382 /// flash parameters discovered during flash boot.
383 ///
384 /// All XIP modes configured by the bootrom have an 8-bit serial command
385 /// prefix, so that the flash can remain in a serial command state, meaning
386 /// XIP accesses can be mixed more freely with program/erase serial
387 /// operations. This has a performance penalty, so users can perform their
388 /// own flash setup after flash boot using continuous read mode or QPI mode
389 /// to avoid or alleviate the command prefix cost.
390 ///
391 /// Supported architectures: ARM-S, RISC-V
392 unsafe fn flash_select_xip_read_mode(bootrom_xip_mode: u8, clkdiv: u8) -> () {
393 crate::rom_data::rom_table_lookup(*b"XM", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
394 }
395}
396
397declare_rom_function! {
398 /// Restore the QMI address translation registers, ATRANS0 through ATRANS7,
399 /// to their reset state. This makes the runtime- to-storage address map an
400 /// identity map, i.e. the mapped and unmapped address are equal, and the
401 /// entire space is fully mapped.
402 ///
403 /// See [Section 12.14.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the RP2350
404 /// datasheet.
405 ///
406 /// Supported architectures: ARM-S, RISC-V
407 unsafe fn flash_reset_address_trans() -> () {
408 crate::rom_data::rom_table_lookup(*b"RA", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
409 }
410}
411
412// **************** High-level Flash Commands ****************
413
414declare_rom_function! {
415 /// Applies the address translation currently configured by QMI address
416 /// translation registers, ATRANS0 through ATRANS7.
417 ///
418 /// See [Section 12.14.4](https://rptl.io/rp2350-datasheet#section_bootrom) of the RP2350
419 /// datasheet.
420 ///
421 /// Translating an address outside of the XIP runtime address window, or
422 /// beyond the bounds of an ATRANSx_SIZE field, returns
423 /// BOOTROM_ERROR_INVALID_ADDRESS, which is not a valid flash storage
424 /// address. Otherwise, return the storage address which QMI would access
425 /// when presented with the runtime address addr. This is effectively a
426 /// virtual-to-physical address translation for QMI.
427 ///
428 /// Supported architectures: ARM-S, RISC-V
429 unsafe fn flash_runtime_to_storage_addr(addr: u32) -> i32 {
430 crate::rom_data::rom_table_lookup(*b"FA", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
431 }
432}
433
434declare_rom_function! {
435 /// Non-secure version of [flash_runtime_to_storage_addr()]
436 ///
437 /// Supported architectures: ARM-NS
438 #[cfg(all(target_arch = "arm", target_os = "none"))]
439 unsafe fn flash_runtime_to_storage_addr_ns(addr: u32) -> i32 {
440 crate::rom_data::rom_table_lookup(*b"FA", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)
441 }
442}
443
444declare_rom_function! {
445 /// Perform a flash read, erase, or program operation.
446 ///
447 /// Erase operations must be sector-aligned (4096 bytes) and sector-
448 /// multiple-sized, and program operations must be page-aligned (256 bytes)
449 /// and page-multiple-sized; misaligned erase and program operations will
450 /// return BOOTROM_ERROR_BAD_ALIGNMENT. The operation — erase, read, program
451 /// — is selected by the CFLASH_OP_BITS bitfield of the flags argument.
452 ///
453 /// See datasheet section 5.5.8.2 for more details.
454 ///
455 /// Supported architectures: ARM-S, RISC-V
456 unsafe fn flash_op(flags: u32, addr: u32, size_bytes: u32, buffer: *mut u8) -> i32 {
457 crate::rom_data::rom_table_lookup(*b"FO", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
458 }
459}
460
461declare_rom_function! {
462 /// Non-secure version of [flash_op()]
463 ///
464 /// Supported architectures: ARM-NS
465 #[cfg(all(target_arch = "arm", target_os = "none"))]
466 unsafe fn flash_op_ns(flags: u32, addr: u32, size_bytes: u32, buffer: *mut u8) -> i32 {
467 crate::rom_data::rom_table_lookup(*b"FO", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)
468 }
469}
470
471// **************** Security Related Functions ****************
472
473declare_rom_function! {
474 /// Allow or disallow the specific NS API (note all NS APIs default to
475 /// disabled).
476 ///
477 /// See datasheet section 5.5.9.1 for more details.
478 ///
479 /// Supported architectures: ARM-S
480 #[cfg(all(target_arch = "arm", target_os = "none"))]
481 unsafe fn set_ns_api_permission(ns_api_num: u32, allowed: u8) -> i32 {
482 crate::rom_data::rom_table_lookup(*b"SP", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC)
483 }
484}
485
486declare_rom_function! {
487 /// Utility method that can be used by secure ARM code to validate a buffer
488 /// passed to it from Non-secure code.
489 ///
490 /// See datasheet section 5.5.9.2 for more details.
491 ///
492 /// Supported architectures: ARM-S, RISC-V
493 unsafe fn validate_ns_buffer() -> () {
494 crate::rom_data::rom_table_lookup(*b"VB", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
495 }
496}
497
498// **************** Miscellaneous Functions ****************
499
500declare_rom_function! {
501 /// Resets the RP2350 and uses the watchdog facility to restart.
502 ///
503 /// See datasheet section 5.5.10.1 for more details.
504 ///
505 /// Supported architectures: ARM-S, RISC-V
506 fn reboot(flags: u32, delay_ms: u32, p0: u32, p1: u32) -> i32 {
507 crate::rom_data::rom_table_lookup(*b"RB", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
508 }
509}
510
511declare_rom_function! {
512 /// Non-secure version of [reboot()]
513 ///
514 /// Supported architectures: ARM-NS
515 #[cfg(all(target_arch = "arm", target_os = "none"))]
516 fn reboot_ns(flags: u32, delay_ms: u32, p0: u32, p1: u32) -> i32 {
517 crate::rom_data::rom_table_lookup(*b"RB", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)
518 }
519}
520
521declare_rom_function! {
522 /// Resets internal bootrom state.
523 ///
524 /// See datasheet section 5.5.10.2 for more details.
525 ///
526 /// Supported architectures: ARM-S, RISC-V
527 unsafe fn bootrom_state_reset(flags: u32) -> () {
528 crate::rom_data::rom_table_lookup(*b"SR", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
529 }
530}
531
532declare_rom_function! {
533 /// Set a boot ROM callback.
534 ///
535 /// The only supported callback_number is 0 which sets the callback used for
536 /// the secure_call API.
537 ///
538 /// See datasheet section 5.5.10.3 for more details.
539 ///
540 /// Supported architectures: ARM-S, RISC-V
541 unsafe fn set_rom_callback(callback_number: i32, callback_fn: *const ()) -> i32 {
542 crate::rom_data::rom_table_lookup(*b"RC", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
543 }
544}
545
546// **************** System Information Functions ****************
547
548declare_rom_function! {
549 /// Fills a buffer with various system information.
550 ///
551 /// Note that this API is also used to return information over the PICOBOOT
552 /// interface.
553 ///
554 /// See datasheet section 5.5.11.1 for more details.
555 ///
556 /// Supported architectures: ARM-S, RISC-V
557 unsafe fn get_sys_info(out_buffer: *mut u32, out_buffer_word_size: usize, flags: u32) -> i32 {
558 crate::rom_data::rom_table_lookup(*b"GS", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
559 }
560}
561
562declare_rom_function! {
563 /// Non-secure version of [get_sys_info()]
564 ///
565 /// Supported architectures: ARM-NS
566 #[cfg(all(target_arch = "arm", target_os = "none"))]
567 unsafe fn get_sys_info_ns(out_buffer: *mut u32, out_buffer_word_size: usize, flags: u32) -> i32 {
568 crate::rom_data::rom_table_lookup(*b"GS", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)
569 }
570}
571
572declare_rom_function! {
573 /// Fills a buffer with information from the partition table.
574 ///
575 /// Note that this API is also used to return information over the PICOBOOT
576 /// interface.
577 ///
578 /// See datasheet section 5.5.11.2 for more details.
579 ///
580 /// Supported architectures: ARM-S, RISC-V
581 unsafe fn get_partition_table_info(out_buffer: *mut u32, out_buffer_word_size: usize, flags_and_partition: u32) -> i32 {
582 crate::rom_data::rom_table_lookup(*b"GP", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
583 }
584}
585
586declare_rom_function! {
587 /// Non-secure version of [get_partition_table_info()]
588 ///
589 /// Supported architectures: ARM-NS
590 #[cfg(all(target_arch = "arm", target_os = "none"))]
591 unsafe fn get_partition_table_info_ns(out_buffer: *mut u32, out_buffer_word_size: usize, flags_and_partition: u32) -> i32 {
592 crate::rom_data::rom_table_lookup(*b"GP", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)
593 }
594}
595
596declare_rom_function! {
597 /// Loads the current partition table from flash, if present.
598 ///
599 /// See datasheet section 5.5.11.3 for more details.
600 ///
601 /// Supported architectures: ARM-S, RISC-V
602 unsafe fn load_partition_table(workarea_base: *mut u8, workarea_size: usize, force_reload: bool) -> i32 {
603 crate::rom_data::rom_table_lookup(*b"LP", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
604 }
605}
606
607declare_rom_function! {
608 /// Writes data from a buffer into OTP, or reads data from OTP into a buffer.
609 ///
610 /// See datasheet section 5.5.11.4 for more details.
611 ///
612 /// Supported architectures: ARM-S, RISC-V
613 unsafe fn otp_access(buf: *mut u8, buf_len: usize, row_and_flags: u32) -> i32 {
614 crate::rom_data::rom_table_lookup(*b"OA", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
615 }
616}
617
618declare_rom_function! {
619 /// Non-secure version of [otp_access()]
620 ///
621 /// Supported architectures: ARM-NS
622 #[cfg(all(target_arch = "arm", target_os = "none"))]
623 unsafe fn otp_access_ns(buf: *mut u8, buf_len: usize, row_and_flags: u32) -> i32 {
624 crate::rom_data::rom_table_lookup(*b"OA", crate::rom_data::inner::rt_flags::FUNC_ARM_NONSEC)
625 }
626}
627
628// **************** Boot Related Functions ****************
629
630declare_rom_function! {
631 /// Determines which of the partitions has the "better" IMAGE_DEF. In the
632 /// case of executable images, this is the one that would be booted.
633 ///
634 /// See datasheet section 5.5.12.1 for more details.
635 ///
636 /// Supported architectures: ARM-S, RISC-V
637 unsafe fn pick_ab_parition(workarea_base: *mut u8, workarea_size: usize, partition_a_num: u32) -> i32 {
638 crate::rom_data::rom_table_lookup(*b"AB", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
639 }
640}
641
642declare_rom_function! {
643 /// Searches a memory region for a launchable image, and executes it if
644 /// possible.
645 ///
646 /// See datasheet section 5.5.12.2 for more details.
647 ///
648 /// Supported architectures: ARM-S, RISC-V
649 unsafe fn chain_image(workarea_base: *mut u8, workarea_size: usize, region_base: i32, region_size: u32) -> i32 {
650 crate::rom_data::rom_table_lookup(*b"CI", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
651 }
652}
653
654declare_rom_function! {
655 /// Perform an "explicit" buy of an executable launched via an IMAGE_DEF
656 /// which was "explicit buy" flagged.
657 ///
658 /// See datasheet section 5.5.12.3 for more details.
659 ///
660 /// Supported architectures: ARM-S, RISC-V
661 unsafe fn explicit_buy(buffer: *mut u8, buffer_size: u32) -> i32 {
662 crate::rom_data::rom_table_lookup(*b"EB", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
663 }
664}
665
666declare_rom_function! {
667 /// Not yet documented.
668 ///
669 /// See datasheet section 5.5.12.4 for more details.
670 ///
671 /// Supported architectures: ARM-S, RISC-V
672 unsafe fn get_uf2_target_partition(workarea_base: *mut u8, workarea_size: usize, family_id: u32, partition_out: *mut u32) -> i32 {
673 crate::rom_data::rom_table_lookup(*b"GU", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
674 }
675}
676
677declare_rom_function! {
678 /// Returns: The index of the B partition of partition A if a partition
679 /// table is present and loaded, and there is a partition A with a B
680 /// partition; otherwise returns BOOTROM_ERROR_NOT_FOUND.
681 ///
682 /// See datasheet section 5.5.12.5 for more details.
683 ///
684 /// Supported architectures: ARM-S, RISC-V
685 unsafe fn get_b_partition(partition_a: u32) -> i32 {
686 crate::rom_data::rom_table_lookup(*b"GB", crate::rom_data::inner::rt_flags::FUNC_ARM_SEC_RISCV)
687 }
688}
689
690// **************** Non-secure-specific Functions ****************
691
692// NB: The "secure_call" function should be here, but it doesn't have a fixed
693// function signature as it is designed to let you bounce into any secure
694// function from non-secure mode.
695
696// **************** RISC-V Functions ****************
697
698declare_rom_function! {
699 /// Set stack for RISC-V bootrom functions to use.
700 ///
701 /// See datasheet section 5.5.14.1 for more details.
702 ///
703 /// Supported architectures: RISC-V
704 #[cfg(not(all(target_arch = "arm", target_os = "none")))]
705 unsafe fn set_bootrom_stack(base_size: *mut u32) -> i32 {
706 crate::rom_data::rom_table_lookup(*b"SS", crate::rom_data::inner::rt_flags::FUNC_RISCV)
707 }
708}
709
710/// The version number of the rom.
711pub fn rom_version_number() -> u8 {
712 unsafe { *VERSION_NUMBER }
713}
714
715/// The 8 most significant hex digits of the Bootrom git revision.
716pub fn git_revision() -> u32 {
717 let ptr = rom_data_lookup(*b"GR", rt_flags::DATA) as *const u32;
718 unsafe { ptr.read() }
719}
720
721/// A pointer to the resident partition table info.
722///
723/// The resident partition table is the subset of the full partition table that
724/// is kept in memory, and used for flash permissions.
725pub fn partition_table_pointer() -> *const u32 {
726 let ptr = rom_data_lookup(*b"PT", rt_flags::DATA) as *const *const u32;
727 unsafe { ptr.read() }
728}
729
730/// Determine if we are in secure mode
731///
732/// Returns `true` if we are in secure mode and `false` if we are in non-secure
733/// mode.
734#[cfg(all(target_arch = "arm", target_os = "none"))]
735pub fn is_secure_mode() -> bool {
736 // Look at the start of ROM, which is always readable
737 #[allow(clippy::zero_ptr)]
738 let rom_base: *mut u32 = 0x0000_0000 as *mut u32;
739 // Use the 'tt' instruction to check the permissions for that address
740 let tt = cortex_m::asm::tt(rom_base);
741 // Is the secure bit set? => secure mode
742 (tt & (1 << 22)) != 0
743}
744
745/// Determine if we are in secure mode
746///
747/// Always returns `false` on RISC-V as it is impossible to determine if
748/// you are in Machine Mode or User Mode by design.
749#[cfg(not(all(target_arch = "arm", target_os = "none")))]
750pub fn is_secure_mode() -> bool {
751 false
752}
diff --git a/examples/rp23/.cargo/config.toml b/examples/rp23/.cargo/config.toml
index f77e004dc..9a92b1ce2 100644
--- a/examples/rp23/.cargo/config.toml
+++ b/examples/rp23/.cargo/config.toml
@@ -1,6 +1,7 @@
1[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 1[target.'cfg(all(target_arch = "arm", target_os = "none"))']
2#runner = "probe-rs run --chip RP2040" 2#runner = "probe-rs run --chip RP2040"
3runner = "elf2uf2-rs -d" 3#runner = "elf2uf2-rs -d"
4runner = "picotool load -u -v -x -t elf"
4 5
5[build] 6[build]
6target = "thumbv8m.main-none-eabihf" 7target = "thumbv8m.main-none-eabihf"
diff --git a/examples/rp23/src/bin/flash.rs b/examples/rp23/src/bin/flash.rs
index 811561f26..84011e394 100644
--- a/examples/rp23/src/bin/flash.rs
+++ b/examples/rp23/src/bin/flash.rs
@@ -21,7 +21,7 @@ pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ 21pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
22 embassy_rp::binary_info::rp_program_name!(c"example"), 22 embassy_rp::binary_info::rp_program_name!(c"example"),
23 embassy_rp::binary_info::rp_cargo_version!(), 23 embassy_rp::binary_info::rp_cargo_version!(),
24 embassy_rp::binary_info::rp_program_description!(c"Blinky"), 24 embassy_rp::binary_info::rp_program_description!(c"Flash"),
25 embassy_rp::binary_info::rp_program_build_attribute!(), 25 embassy_rp::binary_info::rp_program_build_attribute!(),
26]; 26];
27 27
@@ -41,22 +41,13 @@ async fn main(_spawner: Spawner) {
41 41
42 let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0); 42 let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0);
43 43
44 // Get JEDEC id
45 let jedec = flash.blocking_jedec_id().unwrap();
46 info!("jedec id: 0x{:x}", jedec);
47
48 // Get unique id
49 let mut uid = [0; 8];
50 flash.blocking_unique_id(&mut uid).unwrap();
51 info!("unique id: {:?}", uid);
52
53 erase_write_sector(&mut flash, 0x00); 44 erase_write_sector(&mut flash, 0x00);
54 45
55 multiwrite_bytes(&mut flash, ERASE_SIZE as u32); 46 multiwrite_bytes(&mut flash, ERASE_SIZE as u32);
56 47
57 background_read(&mut flash, (ERASE_SIZE * 2) as u32).await; 48 background_read(&mut flash, (ERASE_SIZE * 2) as u32).await;
58 49
59 loop {} 50 info!("Flash Works!");
60} 51}
61 52
62fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { 53fn 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
82 73
83 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf)); 74 defmt::unwrap!(flash.blocking_read(ADDR_OFFSET + offset, &mut read_buf));
84 info!("Contents after write starts with {=[u8]}", read_buf[0..4]); 75 info!("Contents after write starts with {=[u8]}", read_buf[0..4]);
85 if &read_buf[0..4] != &[0x01, 0x02, 0x03, 0x04] { 76 if read_buf[0..4] != [0x01, 0x02, 0x03, 0x04] {
86 defmt::panic!("unexpected"); 77 defmt::panic!("unexpected");
87 } 78 }
88} 79}
diff --git a/examples/rp23/src/bin/otp.rs b/examples/rp23/src/bin/otp.rs
new file mode 100644
index 000000000..106e514ca
--- /dev/null
+++ b/examples/rp23/src/bin/otp.rs
@@ -0,0 +1,46 @@
1//! This example shows reading the OTP constants on the RP235x.
2
3#![no_std]
4#![no_main]
5
6use defmt::*;
7use embassy_executor::Spawner;
8use embassy_rp::block::ImageDef;
9use embassy_rp::otp;
10use embassy_time::Timer;
11use {defmt_rtt as _, panic_probe as _};
12
13#[link_section = ".start_block"]
14#[used]
15pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
16
17// Program metadata for `picotool info`
18#[link_section = ".bi_entries"]
19#[used]
20pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [
21 embassy_rp::binary_info::rp_program_name!(c"OTP Read Example"),
22 embassy_rp::binary_info::rp_cargo_version!(),
23 embassy_rp::binary_info::rp_program_description!(c"OTP Read Example"),
24 embassy_rp::binary_info::rp_program_build_attribute!(),
25];
26
27#[embassy_executor::main]
28async fn main(_spawner: Spawner) {
29 let _ = embassy_rp::init(Default::default());
30 //
31 // add some delay to give an attached debug probe time to parse the
32 // defmt RTT header. Reading that header might touch flash memory, which
33 // interferes with flash write operations.
34 // https://github.com/knurling-rs/defmt/pull/683
35 Timer::after_millis(10).await;
36
37 let chip_id = unwrap!(otp::get_chipid());
38 info!("Unique id:{:X}", chip_id);
39
40 let private_rand = unwrap!(otp::get_private_random_number());
41 info!("Private Rand:{:X}", private_rand);
42
43 loop {
44 Timer::after_secs(1).await;
45 }
46}