diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-09-10 21:36:49 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-09-10 21:36:49 +0000 |
| commit | 0bf9a2591b062f2f8409fe27c590e1783b139e38 (patch) | |
| tree | c738996dd476da1fde72f23ef5eb05620498a8d4 | |
| parent | aff1e7486210f6fa80627b92da5b24a1c8bf6017 (diff) | |
| parent | e75903138a92e6c749454cee516c812df618bbfa (diff) | |
Merge pull request #3297 from CBJamo/rp2350_flash
rp: 2350 flash write and OTP
| -rw-r--r-- | embassy-rp/src/flash.rs | 67 | ||||
| -rw-r--r-- | embassy-rp/src/lib.rs | 7 | ||||
| -rw-r--r-- | embassy-rp/src/otp.rs | 108 | ||||
| -rw-r--r-- | embassy-rp/src/rom_data/mod.rs | 33 | ||||
| -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.rs | 752 | ||||
| -rw-r--r-- | examples/rp23/.cargo/config.toml | 3 | ||||
| -rw-r--r-- | examples/rp23/src/bin/flash.rs | 15 | ||||
| -rw-r--r-- | examples/rp23/src/bin/otp.rs | 46 |
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. |
| 18 | pub const FLASH_BASE: *const u32 = 0x10000000 as _; | 18 | pub 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")] | ||
| 22 | pub 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. |
| 22 | pub const USE_BOOT2: bool = !cfg!(feature = "run-from-ram"); | 26 | pub 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")] |
| 16 | mod critical_section_impl; | 16 | mod critical_section_impl; |
| 17 | 17 | ||
| 18 | #[cfg(feature = "rp2040")] | ||
| 18 | mod intrinsics; | 19 | mod intrinsics; |
| 19 | 20 | ||
| 20 | pub mod adc; | 21 | pub mod adc; |
| @@ -31,6 +32,8 @@ pub mod gpio; | |||
| 31 | pub mod i2c; | 32 | pub mod i2c; |
| 32 | pub mod i2c_slave; | 33 | pub mod i2c_slave; |
| 33 | pub mod multicore; | 34 | pub mod multicore; |
| 35 | #[cfg(feature = "_rp235x")] | ||
| 36 | pub mod otp; | ||
| 34 | pub mod pwm; | 37 | pub mod pwm; |
| 35 | mod reset; | 38 | mod reset; |
| 36 | pub mod rom_data; | 39 | pub 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"))] |
| 405 | macro_rules! select_bootloader { | 408 | macro_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"))] |
| 422 | select_bootloader! { | 425 | select_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))] | ||
| 9 | pub 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. | ||
| 21 | pub 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. | ||
| 26 | pub const OTP_DATA_RAW_BASE: *const u32 = 0x4013_4000 as *const u32; | ||
| 27 | |||
| 28 | /// How many pages in OTP (post error-correction) | ||
| 29 | pub const NUM_PAGES: usize = 64; | ||
| 30 | |||
| 31 | /// How many rows in one page in OTP (post error-correction) | ||
| 32 | pub const NUM_ROWS_PER_PAGE: usize = 64; | ||
| 33 | |||
| 34 | /// How many rows in OTP (post error-correction) | ||
| 35 | pub const NUM_ROWS: usize = NUM_PAGES * NUM_ROWS_PER_PAGE; | ||
| 36 | |||
| 37 | /// Read one ECC protected word from the OTP | ||
| 38 | pub 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. | ||
| 60 | pub 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. | ||
| 77 | pub 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 | ||
| 95 | pub 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 | #, 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 | # 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")] | ||
| 32 | mod inner; | ||
| 33 | pub 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. | ||
| 15 | pub type RomFnTableCode = [u8; 2]; | ||
| 16 | |||
| 17 | /// This function searches for the tag which matches the mask. | ||
| 18 | type 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"))] | ||
| 24 | const 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"))] | ||
| 30 | const 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"))] | ||
| 36 | const 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"))] | ||
| 42 | const 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")))] | ||
| 48 | const 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")))] | ||
| 54 | const 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")))] | ||
| 60 | const 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")))] | ||
| 66 | const ROM_DATA_LOOKUP_A1: *const u32 = 0x0000_7DF4 as _; | ||
| 67 | |||
| 68 | /// Address of the version number of the ROM. | ||
| 69 | const VERSION_NUMBER: *const u8 = 0x0000_0013 as _; | ||
| 70 | |||
| 71 | #[allow(unused)] | ||
| 72 | mod 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. | ||
| 88 | pub 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. | ||
| 102 | pub 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 | |||
| 115 | macro_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 | |||
| 228 | declare_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 | |||
| 238 | declare_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 | |||
| 267 | declare_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 | |||
| 304 | declare_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 | |||
| 325 | declare_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 | |||
| 343 | declare_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 | |||
| 362 | declare_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 | |||
| 397 | declare_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 | |||
| 414 | declare_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 | |||
| 434 | declare_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 | |||
| 444 | declare_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 | |||
| 461 | declare_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 | |||
| 473 | declare_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 | |||
| 486 | declare_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 | |||
| 500 | declare_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 | |||
| 511 | declare_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 | |||
| 521 | declare_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 | |||
| 532 | declare_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 | |||
| 548 | declare_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 | |||
| 562 | declare_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 | |||
| 572 | declare_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 | |||
| 586 | declare_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 | |||
| 596 | declare_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 | |||
| 607 | declare_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 | |||
| 618 | declare_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 | |||
| 630 | declare_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 | |||
| 642 | declare_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 | |||
| 654 | declare_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 | |||
| 666 | declare_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 | |||
| 677 | declare_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 | |||
| 698 | declare_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. | ||
| 711 | pub fn rom_version_number() -> u8 { | ||
| 712 | unsafe { *VERSION_NUMBER } | ||
| 713 | } | ||
| 714 | |||
| 715 | /// The 8 most significant hex digits of the Bootrom git revision. | ||
| 716 | pub 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. | ||
| 725 | pub 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"))] | ||
| 735 | pub 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")))] | ||
| 750 | pub 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" |
| 3 | runner = "elf2uf2-rs -d" | 3 | #runner = "elf2uf2-rs -d" |
| 4 | runner = "picotool load -u -v -x -t elf" | ||
| 4 | 5 | ||
| 5 | [build] | 6 | [build] |
| 6 | target = "thumbv8m.main-none-eabihf" | 7 | target = "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(); | |||
| 21 | pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ | 21 | pub 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 | ||
| 62 | fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { | 53 | 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 | |||
| 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 | |||
| 6 | use defmt::*; | ||
| 7 | use embassy_executor::Spawner; | ||
| 8 | use embassy_rp::block::ImageDef; | ||
| 9 | use embassy_rp::otp; | ||
| 10 | use embassy_time::Timer; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[link_section = ".start_block"] | ||
| 14 | #[used] | ||
| 15 | pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); | ||
| 16 | |||
| 17 | // Program metadata for `picotool info` | ||
| 18 | #[link_section = ".bi_entries"] | ||
| 19 | #[used] | ||
| 20 | pub 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] | ||
| 28 | async 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 | } | ||
