diff --git a/rp235x-hal-examples/src/bin/rom_funcs.rs b/rp235x-hal-examples/src/bin/rom_funcs.rs index c572cb59d..73a8d7ab9 100644 --- a/rp235x-hal-examples/src/bin/rom_funcs.rs +++ b/rp235x-hal-examples/src/bin/rom_funcs.rs @@ -13,6 +13,7 @@ // be linked) use panic_halt as _; +use rp235x_hal::rom_data::sys_info_api::{BootType, CpuInfo, FlashDevInfoSize, PartitionIndex}; // Alias for our HAL crate use rp235x_hal as hal; @@ -234,13 +235,22 @@ fn get_sys_info_chip_info(uart: &mut T) where T: core::fmt::Write, { - let mut buffer = [0u32; 16]; - let result = unsafe { hal::rom_data::get_sys_info(buffer.as_mut_ptr(), buffer.len(), 0x0001) }; - _ = writeln!(uart, "get_sys_info(CHIP_INFO/0x0001) -> {}", result); - _ = writeln!(uart, "\tSupported Flags: {:#06x}", buffer[0]); - _ = writeln!(uart, "\tRP2350 Package ID: {:#010x}", buffer[1]); - _ = writeln!(uart, "\tRP2350 Device ID : {:#010x}", buffer[2]); - _ = writeln!(uart, "\tRP2350 Wafer ID : {:#010x}", buffer[3]); + let result = match hal::rom_data::sys_info_api::chip_info() { + Ok(Some(result)) => result, + Ok(None) => { + _ = writeln!(uart, "chip_info() not supported"); + return; + } + Err(e) => { + _ = writeln!(uart, "Failed to get chip info : {:?}", e); + return; + } + }; + + _ = writeln!(uart, "get_sys_info(CHIP_INFO/0x0001)"); + _ = writeln!(uart, "\tRP2350 Package ID: {:#010x}", result.package_sel); + _ = writeln!(uart, "\tRP2350 Device ID : {:#010x}", result.device_id); + _ = writeln!(uart, "\tRP2350 Wafer ID : {:#010x}", result.wafer_id); } /// Run get_sys_info with 0x0004 @@ -248,17 +258,25 @@ fn get_sys_info_cpu_info(uart: &mut T) where T: core::fmt::Write, { - let mut buffer = [0u32; 16]; - let result = unsafe { hal::rom_data::get_sys_info(buffer.as_mut_ptr(), buffer.len(), 0x0004) }; - _ = writeln!(uart, "get_sys_info(CPU_INFO/0x0004) -> {}", result); - _ = writeln!(uart, "\tSupported Flags: {:#06x}", buffer[0]); + let result = match hal::rom_data::sys_info_api::cpu_info() { + Ok(Some(result)) => result, + Ok(None) => { + _ = writeln!(uart, "cpu_info() not supported"); + return; + } + Err(e) => { + _ = writeln!(uart, "Failed to get cpu info: {:?}", e); + return; + } + }; + + _ = writeln!(uart, "get_sys_info(CPU_INFO/0x0004)"); _ = writeln!( uart, "\tCPU Architecture: {}", - match buffer[1] { - 0 => "Arm", - 1 => "RISC-V", - _ => "Unknown", + match result { + CpuInfo::Arm => "Arm", + CpuInfo::Risc => "RISC-V", } ); } @@ -268,28 +286,43 @@ fn get_sys_info_flash_dev_info(uart: &mut T) where T: core::fmt::Write, { - let mut buffer = [0u32; 16]; - let result = unsafe { hal::rom_data::get_sys_info(buffer.as_mut_ptr(), buffer.len(), 0x0008) }; - _ = writeln!(uart, "get_sys_info(FLASH_DEV_INFO/0x0008) -> {}", result); + let result = match hal::rom_data::sys_info_api::flash_dev_info() { + Ok(Some(result)) => result, + Ok(None) => { + _ = writeln!(uart, "flash_dev_info() not supported"); + return; + } + Err(e) => { + _ = writeln!(uart, "Failed to get flash device info: {:?}", e); + return; + } + }; + + _ = writeln!(uart, "get_sys_info(FLASH_DEV_INFO/0x0008)"); let size_lookup = |value| match value { - 0 => "None", - 1 => "8K", - 2 => "16K", - 3 => "32K", - 4 => "64K", - 5 => "128K", - 6 => "256K", - 7 => "512K", - 8 => "1M", - 9 => "2M", - 10 => "4M", - 11 => "8M", - 12 => "16M", - _ => "Unknown", + FlashDevInfoSize::None => "None", + FlashDevInfoSize::K8 => "8K", + FlashDevInfoSize::K16 => "16K", + FlashDevInfoSize::K32 => "32K", + FlashDevInfoSize::K64 => "64K", + FlashDevInfoSize::K128 => "128K", + FlashDevInfoSize::K256 => "256K", + FlashDevInfoSize::K512 => "512K", + FlashDevInfoSize::M1 => "1M", + FlashDevInfoSize::M2 => "2M", + FlashDevInfoSize::M4 => "4M", + FlashDevInfoSize::M8 => "8M", + FlashDevInfoSize::M16 => "16M", + FlashDevInfoSize::Unknown => "Unknown", }; - _ = writeln!(uart, "\tSupported Flags: {:#06x}", buffer[0]); - _ = writeln!(uart, "\tCS0 Size: {}", size_lookup((buffer[1] >> 8) & 15)); - _ = writeln!(uart, "\tCS1 Size: {}", size_lookup((buffer[1] >> 12) & 15)); + _ = writeln!(uart, "\tCS1 GPIO: {}", result.cs1_gpio()); + _ = writeln!( + uart, + "\tD8H Erase Supported: {}", + result.d8h_erase_supported() + ); + _ = writeln!(uart, "\tCS0 Size: {}", size_lookup(result.cs0_size())); + _ = writeln!(uart, "\tCS1 Size: {}", size_lookup(result.cs1_size())); } /// Run get_sys_info with 0x0010 @@ -297,15 +330,20 @@ fn get_sys_info_boot_random(uart: &mut T) where T: core::fmt::Write, { - let mut buffer = [0u32; 16]; - let result = unsafe { hal::rom_data::get_sys_info(buffer.as_mut_ptr(), buffer.len(), 0x0010) }; - _ = writeln!(uart, "get_sys_info(BOOT_RANDOM/0x0010) -> {}", result); - _ = writeln!(uart, "\tSupported Flags: {:#06x}", buffer[0]); - let a = buffer[1]; - let b = buffer[2]; - let c = buffer[3]; - let d = buffer[4]; - _ = writeln!(uart, "\tA random number: 0x{a:08x}{b:08x}{c:08x}{d:08x}"); + let result = match hal::rom_data::sys_info_api::boot_random() { + Ok(Some(result)) => result, + Ok(None) => { + _ = writeln!(uart, "boot_random() not supported"); + return; + } + Err(e) => { + _ = writeln!(uart, "Failed to get random boot integer: {:?}", e); + return; + } + }; + + _ = writeln!(uart, "get_sys_info(BOOT_RANDOM/0x0010)"); + _ = writeln!(uart, "\tA random number: 0x{:32x}", result.0); } /// Run get_sys_info with 0x0040; @@ -313,11 +351,52 @@ fn get_sys_info_start_block(uart: &mut T) where T: core::fmt::Write, { - let mut buffer = [0u32; 16]; - let result = unsafe { hal::rom_data::get_sys_info(buffer.as_mut_ptr(), buffer.len(), 0x0040) }; - _ = writeln!(uart, "get_sys_info(start_block/0x0040) -> {}", result); - _ = writeln!(uart, "\tSupported Flags: {:#06x}", buffer[0]); - _ = writeln!(uart, "\tBoot Info: {:08x?}", &buffer[1..result as usize]); + let result = match hal::rom_data::sys_info_api::boot_info() { + Ok(Some(result)) => result, + Ok(None) => { + _ = writeln!(uart, "boot_info() not supported"); + return; + } + Err(e) => { + _ = writeln!(uart, "Failed to get boot info: {:?}", e); + return; + } + }; + + _ = writeln!(uart, "get_sys_info(start_block/0x0040)"); + _ = writeln!( + uart, + "\tDiagnostic Partition: {}", + match result.diagnostic_partition { + PartitionIndex::Partition(_) => "Numbered partition", + PartitionIndex::None => "None", + PartitionIndex::Slot0 => "Slot 0", + PartitionIndex::Slot1 => "Slot 1", + PartitionIndex::Image => "Image", + PartitionIndex::Unknown => "Unknown", + } + ); + _ = writeln!( + uart, + "\tBoot Type: {}", + match result.boot_type { + BootType::Normal => "Normal", + BootType::BootSel => "bootsel", + BootType::RamImage => "RAM image", + BootType::FlashUpdate => "Flash update", + BootType::PcSp => "pc_sp", + BootType::Unknown => "Unknown", + } + ); + _ = writeln!(uart, "\tChained: {}", result.chained); + _ = writeln!(uart, "\tPartition: {}", result.partition); + _ = writeln!(uart, "\tTBYB Info: {:02x}", result.tbyb_update_info); + _ = writeln!(uart, "\tBoot Diagnostic: {:04x}", result.boot_diagnostic); + _ = writeln!( + uart, + "\tBoot Params: {:04x}, {:04x}", + result.boot_params[0], result.boot_params[1] + ); } /// Run get_partition_table_info diff --git a/rp235x-hal/src/rom_data.rs b/rp235x-hal/src/rom_data.rs index ef9440b97..dcb8d988a 100644 --- a/rp235x-hal/src/rom_data.rs +++ b/rp235x-hal/src/rom_data.rs @@ -109,6 +109,603 @@ pub fn rom_data_lookup(tag: RomFnTableCode, mask: u32) -> usize { } } +/// bootrom API function return codes as defined by section 5.4.3 in the rp2350 data sheet +/// See: https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf +#[repr(i32)] +#[derive(Debug)] +pub enum BootRomApiErrorCode { + /// The operation was disallowed by a security constraint + NotPermitted = -4, + /// One or more parameters passed to the function is outside the range of + /// supported values; [`BootRomApiErrorCode::InvalidAddress`] and + /// [`BootRomApiErrorCode::BadAlignment`] are more specific errors. + InvalidArg = -5, + /// An address argument was out-of-bounds or was determined to be an address + /// that the caller may not access + InvalidAddress = -10, + /// An address passed to the function was not correctly aligned + BadAlignment = -11, + /// Something happened or failed to happen in the past, and consequently the + /// request cannot currently be serviced. + InvalidState = -12, + /// A user-allocated buffer was too small to hold the result or working state + /// of the function + BufferTooSmall = -13, + /// The call failed because another bootrom function must be called first. + PreconditionNotMet = -14, + /// Cached data was determined to be inconsistent with the full version of + /// the data it was copied from + ModifiedData = -15, + /// The contents of a data structure are invalid + InvalidData = -16, + /// An attempt was made to access something that does not exist; or, a search failed + NotFound = -17, + /// Modification is impossible based on current state; e.g. attempted to clear + /// an OTP bit. + UnsupportedModification = -18, + /// A required lock is not owned. See Section 5.4.4. + LockRequired = -19, + /// An unknown error + Unknown = -1, +} + +impl From for BootRomApiErrorCode { + fn from(value: i32) -> Self { + match value { + -4 => Self::NotPermitted, + -5 => Self::InvalidArg, + -10 => Self::InvalidAddress, + -11 => Self::BadAlignment, + -12 => Self::InvalidState, + -13 => Self::BufferTooSmall, + -14 => Self::PreconditionNotMet, + -15 => Self::ModifiedData, + -16 => Self::InvalidData, + -17 => Self::NotFound, + -18 => Self::UnsupportedModification, + -19 => Self::LockRequired, + _ => Self::Unknown, + } + } +} + +/// This module defines a safe api to access the `get_sys_info` bootrom function +#[allow(unused)] +pub mod sys_info_api { + use super::BootRomApiErrorCode; + + /// Flags that the `get_sys_info`/ rom function can take + #[repr(u32)] + pub enum GetSysInfoFlag { + /// The flag used to get a chip's unique identifier + ChipInfo = 0x0001, + /// The flag used to get the critical register's value + Critical = 0x0002, + /// The flag used to get the current running CPU Architecture + CpuInfo = 0x0004, + /// The flag used to get flash device info + FlashDevInfo = 0x0008, + /// The flag used to get the random 128 bit integer generated on boot + BootRandom = 0x0010, + // Ignore nonce for now since it can't/shouldn't be called anyway? + // Nonce = 0x0020, + /// The flag used to get boot diagnostic info + BootInfo = 0x0040, + } + + impl GetSysInfoFlag { + /// Returns the length of the buffer needed to hold the data for the related operation returned + /// by [`super::get_sys_info()`]. This includes the initial segment to indicate which flags + /// were supported. The underlying enum represent a bitmask and these masks can be OR'd + /// together, however the safe API only uses one at a time so adding sizes is not a concern. + const fn buffer_length(&self) -> usize { + match self { + GetSysInfoFlag::ChipInfo => 4, + GetSysInfoFlag::Critical + | GetSysInfoFlag::CpuInfo + | GetSysInfoFlag::FlashDevInfo => 2, + GetSysInfoFlag::BootRandom | GetSysInfoFlag::BootInfo => 5, + } + } + } + + /// The unqiue identifier for each chip as reported by [`chip_info`] + pub struct ChipInfo { + /// The value of the `CHIP_INFO_PACKAGE_SEL` register + pub package_sel: u32, + /// The device's id + pub device_id: u32, + /// The wafer's id + pub wafer_id: u32, + } + + impl From<[u32; 3]> for ChipInfo { + fn from(value: [u32; 3]) -> Self { + ChipInfo { + package_sel: value[0], + device_id: value[1], + wafer_id: value[2], + } + } + } + + /// The value held within the critical register as reported by [`otp_critical_register`] + pub struct OtpCriticalReg(u32); + + impl OtpCriticalReg { + /// Check if secure boot is enabled + pub fn secure_boot_enabled(&self) -> bool { + (self.0 & 0x1) == 1 + } + + /// Check if secure debug is disabled + pub fn secure_debug_disabled(&self) -> bool { + (self.0 & 0x2) >> 1 == 1 + } + + /// Check if debug is disabled + pub fn debug_disabled(&self) -> bool { + (self.0 & 0x4) >> 2 == 1 + } + + /// Check the value of `DEFAULT_ARCHSEL` + pub fn default_arch_sel(&self) -> bool { + (self.0 & 0x8) >> 3 == 1 + } + + /// Check if the glitch detector is enabled + pub fn glitch_detector_enabled(&self) -> bool { + (self.0 & 0x10) >> 4 == 1 + } + + /// Value of `GLITCH_DETECTOR_SENS + pub fn glitch_detector_sens(&self) -> u8 { + ((self.0 & 0x60) >> 5) as _ + } + + /// Check if ARM is disabled + pub fn arm_disabled(&self) -> bool { + (self.0 & 0x10000) >> 16 == 1 + } + + /// Check if Risc-V is disabled + pub fn risc_disabled(&self) -> bool { + (self.0 & 0x20000) >> 17 == 1 + } + } + + impl From<[u32; 1]> for OtpCriticalReg { + fn from(value: [u32; 1]) -> OtpCriticalReg { + OtpCriticalReg(value[0]) + } + } + + #[repr(u32)] + /// CPU architectures that might be running as reported by [`cpu_info`] + pub enum CpuInfo { + /// Arm CPU + Arm, + /// Risc-V CPU + Risc, + } + + impl From<[u32; 1]> for CpuInfo { + fn from(value: [u32; 1]) -> CpuInfo { + if value[0] == 0 { + CpuInfo::Arm + } else { + CpuInfo::Risc + } + } + } + + /// Flash device information as reported by [`flash_dev_info`] + pub struct FlashDevInfo(u32); + + /// A struct to represent possible byte sizes that may be reported in [`FlashDevInfo`] + #[repr(u32)] + pub enum FlashDevInfoSize { + /// 0 bytes + None, + /// 8 KiB + K8, + /// 16 KiB + K16, + /// 32 KiB + K32, + /// 64 KiB + K64, + /// 128 KiB + K128, + /// 256 KiB + K256, + /// 512 KiB + K512, + /// 1 MiB + M1, + /// 2 MiB + M2, + /// 4 Mib + M4, + /// 8 MiB + M8, + /// 16 MiB + M16, + /// Unknown size + Unknown, + } + + impl From for FlashDevInfoSize { + fn from(value: u32) -> Self { + if value > 0xc { + return Self::Unknown; + } + + unsafe { core::mem::transmute::(value) } + } + } + + impl FlashDevInfo { + /// GPIO Number to be used for the secondary flash chip. See datasheet section 13.9 + pub fn cs1_gpio(&self) -> u8 { + (self.0 & 0x1f) as _ + } + + /// Check if all attached devices support a block erase command with a command prefix of + /// `D8h`` + pub fn d8h_erase_supported(&self) -> bool { + (self.0 & 0x80) != 0 + } + + /// Flash/PSRAM size on chip select 0 + pub fn cs0_size(&self) -> FlashDevInfoSize { + FlashDevInfoSize::from((self.0 & 0xf00) >> 8) + } + + /// Flash/PSRAM size on chip select 1 + pub fn cs1_size(&self) -> FlashDevInfoSize { + FlashDevInfoSize::from((self.0 & 0xf000) >> 12) + } + } + + impl From<[u32; 1]> for FlashDevInfo { + fn from(value: [u32; 1]) -> FlashDevInfo { + FlashDevInfo(value[0]) + } + } + + /// 128 bit random integer generated per boot as reported by [`boot_random`] + pub struct BootRandom(pub u128); + + impl From<[u32; 4]> for BootRandom { + fn from(value: [u32; 4]) -> BootRandom { + let mut result = 0; + for word in value { + result = (result << 32) | u128::from(word); + } + BootRandom(result) + } + } + + // based on https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_bootrom/include/pico/bootrom.h + /// Boot diagnostic info as described in 5.4 under the `get_sys_info` function + pub struct BootInfo { + /// Information about which partition is being diagnosed + pub diagnostic_partition: PartitionIndex, + /// Type of boot that occurred + pub boot_type: BootType, + /// Whether it was a chained boot + pub chained: bool, + /// What partition the boot came from + pub partition: i8, + // could probably make a nicer api for tbyb, but documentation is eh so im holding off for now + /// Try Before You Buy info + pub tbyb_update_info: u8, + /// boot diagnostic flags for section A and section B + pub boot_diagnostic: u32, + /// Boot parameters 0 and 1 + pub boot_params: [u32; 2], + } + + /// Recen boot diagnostic partition + pub enum PartitionIndex { + /// A partition along with its number + Partition(u8), + /// None + None, + /// Slot0 + Slot0, + /// Slot1 + Slot1, + /// Image + Image, + /// Unknown + Unknown, + } + + impl From for PartitionIndex { + fn from(value: i8) -> Self { + if !(-4..=15).contains(&value) { + return Self::Unknown; + } + + match value { + -1 => Self::None, + -2 => Self::Slot0, + -3 => Self::Slot1, + -4 => Self::Image, + _ => Self::Partition(value as u8), + } + } + } + + /// The type of boot that occurred + pub enum BootType { + /// Normal + Normal, + /// bootsel + BootSel, + /// Ram image + RamImage, + /// Flash update + FlashUpdate, + /// pc_sp + PcSp, + /// Unknown + Unknown, + } + + impl From for BootType { + fn from(value: u8) -> Self { + match value { + 0 => Self::Normal, + 2 => Self::BootSel, + 3 => Self::RamImage, + 4 => Self::FlashUpdate, + 8..=15 => Self::PcSp, + _ => Self::Unknown, + } + } + } + + #[repr(u16)] + /// Diagnostic flags reported by the upper and lower words in [`BootInfo::boot_diagnostic`] + pub enum BootDiagnosticFlags { + /// The region was searched for a block loop + RegionSearched = 0x0001, + /// A block loop was found but it was invalid + InvalidBlockLoop = 0x0002, + /// A valid block loop was found (Blocks from a loop wholly contained within the region, and + /// the blocks have the correct structure. Each block consists of items whose sizes sum to + /// the size of the block) + ValidBlockLoop = 0x0004, + /// A valid IMAGE_DEF was found in the region. A valid IMAGE_DEF must parse correctly and must + /// be executable + ValidImageDef = 0x0008, + /// Whether a partition table is present. This partition table must have a correct structure + /// formed if [`BootDiagnosticFlags::ValidBlockLoop`] is set. If the partition table turns + /// out to be invalid, then [`BootDiagnosticFlags::InvalidBlockLoop`] is set too (thus both + /// [`BootDiagnosticFlags::ValidBlockLoop`] and [`BootDiagnosticFlags::InvalidBlockLoop`] + /// will both be set) + HasPartitionTable = 0x0010, + /// There was a choice of partition/slot and this one was considered. The first slot/partition + /// is chosen based on a number of factors. If the first choice fails verification, then the + /// other choice will be considered. + /// + /// * the version of the PARTITION_TABLE/IMAGE_DEF present in the slot/partition respectively. + /// * whether the slot/partition is the "update region" as per a FLASH_UPDATE reboot. + /// * whether an IMAGE_DEF is marked as "explicit buy" + Considered = 0x0020, + /// This slot/partition was chosen (or was the only choice) + Chosen = 0x0040, + /// if a signature is required for the PARTITION_TABLE (via OTP setting), then whether the + /// PARTITION_TABLE is signed with a key matching one of the four stored in OTP + PartitionTableMatchingKeyForVerify = 0x0080, + /// set if a hash value check could be performed. In the case a signature is required, this + /// value is identical to [`BootDiagnosticFlags::PartitionTableMatchingKeyForVerify`] + PartitionTableHashForVerify = 0x0100, + /// whether the PARTITION_TABLE passed verification (signature/hash if present/required) + PartitionTableVerifiedOk = 0x0200, + /// if a signature is required for the IMAGE_DEF due to secure boot, then whether the + /// IMAGE_DEF is signed with a key matching one of the four stored in OTP + ImageDefMatchingKeyForVerify = 0x0400, + /// set if a hash value check could be performed. In the case a signature is required, this + /// value is identical to [`BootDiagnosticFlags::ImageDefMatchingKeyForVerify`] + ImageDefHashForVerify = 0x0800, + /// whether the PARTITION_TABLE passed verification (signature/hash if present/required) and + /// any LOAD_MAP is valid + ImageDefVerifiedOk = 0x1000, + /// whether any code was copied into RAM due to a LOAD_MAP + LoadMapEntriesLoaded = 0x2000, + /// whether an IMAGE_DEF from this region was launched + ImageLaunched = 0x4000, + /// whether the IMAGE_DEF failed final checks before launching; these checks include + /// + /// * verification failed (if it hasn’t been verified earlier in the CONSIDERED phase). + /// * a problem occurred setting up any rolling window. + /// * the rollback version could not be set in OTP (if required in Secure mode) + /// * the image was marked as Non-secure + /// * the image was marked as "explicit buy", and this was a flash boot, but then region was + /// not the "flash update" region + /// * the image has the wrong architecture, but architecture auto-switch is disabled (or the + /// correct architecture is disabled) + ImageConditionFailure = 0x8000, + } + + impl From<[u32; 4]> for BootInfo { + fn from(value: [u32; 4]) -> Self { + let word0 = value[0]; + + BootInfo { + diagnostic_partition: PartitionIndex::from((word0 & 0xFF) as i8), + boot_type: BootType::from((word0 >> 8) as u8), + chained: (word0 >> 8) & 0x80 > 0, + partition: (word0 >> 16) as _, + tbyb_update_info: (word0 >> 24) as _, + boot_diagnostic: value[1], + boot_params: [value[2], value[3]], + } + } + } + + impl BootInfo { + fn check_flag(diagnostics: u16, flag: BootDiagnosticFlags) -> bool { + (diagnostics & flag as u16) != 0 + } + + /// Check if the diagnostic flag in section A (the lower word) is set + pub fn check_section_a_flag(&self, flag: BootDiagnosticFlags) -> bool { + Self::check_flag(self.boot_diagnostic as u16, flag) + } + + /// Check if the diagnostic flag in section B (the upper word) is set + pub fn check_section_b_flag(&self, flag: BootDiagnosticFlags) -> bool { + Self::check_flag((self.boot_diagnostic >> 8) as u16, flag) + } + } + + #[macro_export] + /// Generates a function with the following signature: + /// + /// ```rs + /// pub fn $function_name() -> Result, BootRomApiErrorCode> + /// ``` + /// + /// Which safely calls [`get_sys_info`](super::get_sys_info()) using the flag provided via + /// the `flag` argument. `flag` is an expression that must resolve to a const variant of + /// [`GetSysInfoFlag`] + macro_rules! declare_get_sys_info_function { + ($(#[$meta:meta])* $function_name:ident, $ok_ret_type:ty, $flag:expr) => { + $(#[$meta])* + pub fn $function_name() -> Result, BootRomApiErrorCode> { + const FLAG: GetSysInfoFlag = $flag; + const BUFFER_LEN: usize = FLAG.buffer_length(); + let mut buffer = [0u32; FLAG.buffer_length()]; + let result = + unsafe { super::get_sys_info(buffer.as_mut_ptr(), buffer.len(), FLAG as u32) }; + + if result < 0 { + return Err(BootRomApiErrorCode::from(result)); + } else if buffer[0] == 0 { + // The operation returned successfully but the flag wasn't supported + // for one reason or another + return Ok(None); + } + + Ok(Some(<$ok_ret_type>::from( + TryInto::<[u32; BUFFER_LEN - 1]>::try_into(&buffer[1..]).unwrap(), + ))) + } + }; + } + + #[macro_export] + #[cfg(all(target_arch = "arm", target_os = "none"))] + /// Generates a function with the following signature: + /// + /// ```rs + /// pub fn $function_name() -> Result, BootRomApiErrorCode> + /// ``` + /// + /// Which safely calls [`get_sys_info_ns`](super::get_sys_info_ns()) using the flag provided via + /// the `flag` argument. `flag` is an expression that must resolve to a const variant of + /// [`GetSysInfoFlag`] + macro_rules! declare_get_sys_info_ns_function { + ($(#[$meta:meta])* $function_name:ident, $ok_ret_type:ty, $flag:expr) => { + $(#[$meta])* + pub fn $function_name() -> Result, BootRomApiErrorCode> { + const FLAG: GetSysInfoFlag = $flag; + const BUFFER_LEN: usize = FLAG.buffer_length(); + let mut buffer = [0u32; FLAG.buffer_length()]; + let result = + unsafe { super::get_sys_info_ns(buffer.as_mut_ptr(), buffer.len(), FLAG as u32) }; + + if result < 0 { + return Err(BootRomApiErrorCode::from(result)); + } else if buffer[0] == 0 { + // The operation returned successfully but the flag wasn't supported + // for one reason or another + return Ok(None); + } + + Ok(Some(<$ok_ret_type>::from( + TryInto::<[u32; BUFFER_LEN - 1]>::try_into(&buffer[1..]).unwrap(), + ))) + } + }; + } + + declare_get_sys_info_function!( + /// Get the unique identifier for the chip + chip_info, ChipInfo, GetSysInfoFlag::ChipInfo + ); + + declare_get_sys_info_function!( + /// Get the value of the OTP critical register + otp_critical_register, + OtpCriticalReg, + GetSysInfoFlag::Critical + ); + + declare_get_sys_info_function!( + /// Get the current running CPU's info + cpu_info, CpuInfo, GetSysInfoFlag::CpuInfo + ); + + declare_get_sys_info_function!( + /// Get flash device info in the format of OTP FLASH_DEVINFO + flash_dev_info, FlashDevInfo, GetSysInfoFlag::FlashDevInfo + ); + + declare_get_sys_info_function!( + /// Get a 128-bit random number generated on each boot + boot_random, BootRandom, GetSysInfoFlag::BootRandom + ); + + declare_get_sys_info_function!( + /// Get diagnostic boot info + boot_info, BootInfo, GetSysInfoFlag::BootInfo + ); + + #[cfg(all(target_arch = "arm", target_os = "none"))] + declare_get_sys_info_ns_function!( + /// Get the unique identifier for the chip + chip_info_ns, ChipInfo, GetSysInfoFlag::ChipInfo + ); + + #[cfg(all(target_arch = "arm", target_os = "none"))] + declare_get_sys_info_ns_function!( + /// Get the value of the OTP critical register + otp_critical_register_ns, + OtpCriticalReg, + GetSysInfoFlag::Critical + ); + + #[cfg(all(target_arch = "arm", target_os = "none"))] + declare_get_sys_info_ns_function!( + /// Get the current running CPU's info + cpu_info_ns, CpuInfo, GetSysInfoFlag::CpuInfo + ); + + #[cfg(all(target_arch = "arm", target_os = "none"))] + declare_get_sys_info_ns_function!( + /// Get flash device info in the format of OTP FLASH_DEVINFO + flash_dev_info_ns, FlashDevInfo, GetSysInfoFlag::FlashDevInfo + ); + + #[cfg(all(target_arch = "arm", target_os = "none"))] + declare_get_sys_info_ns_function!( + /// Get a 128-bit random number generated on each boot + boot_random_ns, BootRandom, GetSysInfoFlag::BootRandom + ); + + #[cfg(all(target_arch = "arm", target_os = "none"))] + declare_get_sys_info_ns_function!( + /// Get diagnostic boot info + boot_info_ns, BootInfo, GetSysInfoFlag::BootInfo + ); +} + macro_rules! declare_rom_function { ( $(#[$outer:meta])*