diff --git a/src/lib.rs b/src/lib.rs index 5a5bd51..de7267d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ pub mod hace_controller; pub mod hash; pub mod hmac; pub mod i2c; +pub mod otp; pub mod pinctrl; pub mod rsa; pub mod spi; diff --git a/src/main.rs b/src/main.rs index 0587686..2779e8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ use aspeed_ddk::tests::functional::gpio_test; use aspeed_ddk::tests::functional::hash_test::run_hash_tests; use aspeed_ddk::tests::functional::hmac_test::run_hmac_tests; use aspeed_ddk::tests::functional::i2c_test; +use aspeed_ddk::tests::functional::otp_test; use aspeed_ddk::tests::functional::rsa_test::run_rsa_tests; use aspeed_ddk::tests::functional::timer_test::run_timer_tests; use panic_halt as _; @@ -171,6 +172,10 @@ fn main() -> ! { test_wdt(&mut uart_controller); run_timer_tests(&mut uart_controller); + if false { + otp_test::test_otp(&mut uart_controller); + } + let test_spicontroller = false; if test_spicontroller { spi::spitest::test_fmc(&mut uart_controller); diff --git a/src/otp.rs b/src/otp.rs new file mode 100644 index 0000000..ad11b81 --- /dev/null +++ b/src/otp.rs @@ -0,0 +1,1288 @@ +// Licensed under the Apache-2.0 license + +use crate::{ + common::{DummyDelay, Logger}, + otp::common::{AspeedChipVersion, AspeedOtpRegion, OtpError, SessionInfo, StrapStatus}, +}; +use ast1060_pac::{Scu, Secure}; +use core::fmt::Debug; +use core::fmt::Write; +use embedded_hal::delay::DelayNs; + +type SbRegBlock = ast1060_pac::secure::RegisterBlock; + +pub mod common; +pub mod hal; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[repr(u8)] +pub enum OtpSoak { + Default = 0, + NormalProg = 1, + SoakProg = 2, +} + +pub struct OtpController { + pub sb: &'static SbRegBlock, + pub scu: Scu, + pub locked: bool, + pub session_active: bool, + pub logger: L, +} + +impl proposed_traits::otp::ErrorType for OtpController { + type Error = OtpError; +} + +macro_rules! otp_debug { + ($logger:expr, $($arg:tt)*) => { + let mut buf: heapless::String<64> = heapless::String::new(); + write!(buf, $($arg)*).unwrap(); + $logger.debug(buf.as_str()); + }; +} + +macro_rules! otp_error { + ($logger:expr, $($arg:tt)*) => { + let mut buf: heapless::String<64> = heapless::String::new(); + write!(buf, $($arg)*).unwrap(); + $logger.error(buf.as_str()); + }; +} + +//major minor build +const OTP_VER: &str = "2.1.1"; + +const ID0_AST1060A1: u32 = 0xA001_0000; +const ID1_AST1060A1: u32 = 0xA001_0000; +const ID0_AST1060A2: u32 = 0xA003_0000; +const ID1_AST1060A2: u32 = 0xA003_0000; +const ID0_AST1060A2_ENG: u32 = 0x8003_0000; +const ID1_AST1060A2_ENG: u32 = 0x8003_0000; +//const OTP_AST1060A1: u32 = 3; +//const OTP_AST1060A2: u32 = 4; +const OTP_PASSWD: u32 = 0x349f_e38a; +const OTP_READ_CMD: u32 = 0x23b1_e361; +const OTP_WRITE_CMD: u32 = 0x23b1_e362; +//const OTP_COMP_CMD: u32 = 0x23b1_e363; +const OTP_PROG_CMD: u32 = 0x23b1_e364; + +const OTP_MEM_LIMIT: u32 = 2144; //67kbits +const OTP_MEM_LIMIT_DATA: usize = 2048; +//const OTP_MEM_ECC_OFFSET: u32 = 1792; //DWORD + +/// timing +const OTP_TIMING_200US: u32 = 0x0419_1388; +const OTP_TIMING_600US: u32 = 0x0419_3a98; +const OTP_OP_RETRIES: u8 = 20; +///OTP memory layout +/// +/// OTP region protection +pub const OTP_CONF_OFFSET: u32 = 0x800; +pub const OTP_MEM_LOCK_ENBLE: u32 = 1 << 31; +pub const OTP_KEY_PROT_ENBLE: u32 = 1 << 29; +pub const OTP_STRAP_PROT_ENBLE: u32 = 1 << 25; +pub const OTP_CONF_PROT_ENBLE: u32 = 1 << 24; +//data secure,user,ecc +pub const OTP_USER_ECC_PROT_ENBLE: u32 = 1 << 23; +const OTP_SECURE_PROT_ENBLE: u32 = 1 << 22; +pub const OTP_SECURE_SIZE_BIT_POS: u32 = 16; +const OTP_SECURE_SIZE_MASK: u32 = 0x3f; + +#[derive(Debug, Clone, Copy)] +pub struct SoakProInfo { + pub address: u32, + pub data: u32, +} +/// Write MRA +/// Write MRB +/// Write MR +pub static SOAK_PROG_DEFAULT: &[SoakProInfo] = &[ + SoakProInfo { + address: 0x3000, + data: 0, + }, + SoakProInfo { + address: 0x5000, + data: 0, + }, + SoakProInfo { + address: 0x1000, + data: 0, + }, +]; +pub static SOAK_PROG_NORMAL: &[SoakProInfo] = &[ + SoakProInfo { + address: 0x3000, + data: 0x1320, + }, + SoakProInfo { + address: 0x5000, + data: 0x1008, + }, + SoakProInfo { + address: 0x1000, + data: 0x0024, + }, +]; +pub static SOAK_PROG_SOAK: &[SoakProInfo] = &[ + SoakProInfo { + address: 0x3000, + data: 0x1320, + }, + SoakProInfo { + address: 0x5000, + data: 0x0007, + }, + SoakProInfo { + address: 0x1000, + data: 0x0100, + }, +]; + +pub struct RegionInfo { + region_type: AspeedOtpRegion, + start: usize, + cdw_size: usize, + alignment: usize, //data aligment +} +pub static REGION_IDS: &[AspeedOtpRegion] = &[ + AspeedOtpRegion::Data, + AspeedOtpRegion::Configuration, + AspeedOtpRegion::Strap, + AspeedOtpRegion::ScuProtection, +]; + +pub static REGION_INFO: &[RegionInfo] = &[ + RegionInfo { + region_type: AspeedOtpRegion::Data, + start: 0, + cdw_size: OTP_MEM_LIMIT_DATA, + alignment: 4, + }, + RegionInfo { + //[otpcfg0, otpcfg31] + region_type: AspeedOtpRegion::Configuration, + start: 0x800, + cdw_size: 32, + alignment: 4, + }, + RegionInfo { + //[otpcfg16, otpcfg27] + region_type: AspeedOtpRegion::Strap, + start: 0xC00, + cdw_size: 2, + alignment: 4, + }, + RegionInfo { + //[otpcfg28, otpcfg29] + region_type: AspeedOtpRegion::ScuProtection, + start: 0xE08, + cdw_size: 2, + alignment: 4, + }, +]; +static mut DATA_REGION: [u32; OTP_MEM_LIMIT_DATA] = [0; OTP_MEM_LIMIT_DATA]; + +impl OtpController { + pub fn new(scu: Scu, logger: L) -> Self { + let locked: bool = false; + let session_active = false; + let sb = unsafe { &*Secure::PTR }; + Self { + sb, + scu, + locked, + session_active, + logger, + } + } + pub fn chip_version(&self) -> AspeedChipVersion { + let revid0 = self.scu.scu004().read().bits(); + let revid1 = self.scu.scu014().read().bits(); + + if revid0 == ID0_AST1060A1 && revid1 == ID1_AST1060A1 { + return AspeedChipVersion::Ast1060A1; + } else if revid0 == ID0_AST1060A2 && revid1 == ID1_AST1060A2 + || revid0 == ID0_AST1060A2_ENG && revid1 == ID1_AST1060A2_ENG + { + return AspeedChipVersion::Ast1060A2; + } + AspeedChipVersion::Unknown + } + pub fn wait_complete(&self) -> bool { + let mut tries: u32 = 1000; + let mut delay = DummyDelay {}; + + delay.delay_ns(100_000); // 100us + + //check if OTP controller is idle (1) + //OTP memory is idle + while !self.sb.secure014().read().otpctrl_sts().bit() + || !self.sb.secure014().read().otpmemory_sts().bit() + { + tries -= 1; + if tries == 0 { + break; + } + } + if self.sb.secure014().read().otpctrl_sts().bit() + && self.sb.secure014().read().otpmemory_sts().bit() + { + return true; + } + false + } + + pub fn otp_write(&self, otp_addr: u32, data: u32) -> bool { + self.sb.secure010().write(|w| unsafe { w.bits(otp_addr) }); + self.sb.secure020().write(|w| unsafe { w.bits(data) }); + self.sb + .secure004() + .write(|w| unsafe { w.bits(OTP_WRITE_CMD) }); + self.wait_complete() + } + + pub fn otp_soak(&self, otp_soak: OtpSoak) -> bool { + match otp_soak { + OtpSoak::Default => { + self.otp_write(SOAK_PROG_DEFAULT[0].address, SOAK_PROG_DEFAULT[0].data); + self.otp_write(SOAK_PROG_DEFAULT[1].address, SOAK_PROG_DEFAULT[1].data); + self.otp_write(SOAK_PROG_DEFAULT[2].address, SOAK_PROG_DEFAULT[2].data); + } + OtpSoak::NormalProg => { + self.otp_write(SOAK_PROG_NORMAL[0].address, SOAK_PROG_NORMAL[0].data); + self.otp_write(SOAK_PROG_NORMAL[1].address, SOAK_PROG_NORMAL[1].data); + self.otp_write(SOAK_PROG_NORMAL[2].address, SOAK_PROG_NORMAL[2].data); + self.sb + .secure008() + .write(|w| unsafe { w.bits(OTP_TIMING_200US) }); + } + OtpSoak::SoakProg => { + self.otp_write(SOAK_PROG_SOAK[0].address, SOAK_PROG_SOAK[0].data); + self.otp_write(SOAK_PROG_SOAK[1].address, SOAK_PROG_SOAK[1].data); + self.otp_write(SOAK_PROG_SOAK[2].address, SOAK_PROG_SOAK[2].data); + self.sb + .secure008() + .write(|w| unsafe { w.bits(OTP_TIMING_600US) }); + } + } + self.wait_complete() + } + /// + /// Read 2 DWORD data + /// + pub fn otp_read_data(&self, otp_addr: u32, buffer: &mut [u32]) -> Result<(), OtpError> { + if buffer.len() < 2 { + return Err(OtpError::ReadFailed); + } + self.sb.secure010().write(|w| unsafe { w.bits(otp_addr) }); + self.sb + .secure004() + .write(|w| unsafe { w.bits(OTP_READ_CMD) }); + if !self.wait_complete() { + return Err(OtpError::ReadFailed); + } + buffer[0] = self.sb.secure020().read().bits(); + buffer[1] = self.sb.secure024().read().bits(); + Ok(()) + } + /// + /// Read whole data region to the static buffer + /// + pub fn otp_read_data_region(&self) -> Result<(), OtpError> { + for i in (0..OTP_MEM_LIMIT_DATA).step_by(2) { + unsafe { + self.otp_read_data(u32::try_from(i).unwrap(), &mut DATA_REGION[i..=i])?; + } + } + Ok(()) + } + /// + /// Read configruation + /// + fn otp_read_conf(&self, addr: u32) -> Result { + self.sb.secure010().write(|w| unsafe { w.bits(addr) }); + self.sb + .secure004() + .write(|w| unsafe { w.bits(OTP_READ_CMD) }); + if !self.wait_complete() { + return Err(OtpError::ReadFailed); + } + let data = self.sb.secure020().read().bits(); + Ok(data) + } + + /// This function does OTPCONFGX read. + /// # Arguments + /// + /// * `reg_idx` - OTPCFG register number eg. 1-OTPCFG1 + /// The address offset: 0x800 (OTPFCG0),0x802 (OTPFCG1) + /// A00(OTPCFG8-15), C00(OTPCFG16-31) + fn otp_read_conf_idx(&self, reg_idx: u32) -> Result { + let mut addr = OTP_CONF_OFFSET; + + addr |= (reg_idx / 8) * 0x200; + addr |= (reg_idx % 8) * 0x2; + + self.otp_read_conf(addr) + } + + fn otp_prog(&mut self, otp_addr: u32, prog_bit: u32) -> Result<(), OtpError> { + if self.otp_write(0, prog_bit) { + self.sb.secure010().write(|w| unsafe { w.bits(otp_addr) }); + self.sb.secure020().write(|w| unsafe { w.bits(prog_bit) }); + self.sb + .secure004() + .write(|w| unsafe { w.bits(OTP_PROG_CMD) }); + if self.wait_complete() { + Ok(()) + } else { + Err(OtpError::Timeout) + } + } else { + otp_error!(self.logger, "otp_prog failed"); + Err(OtpError::WriteFailed) + } + } + /// + /// Inverse the data + /// + fn otp_prog_bit_helper( + &mut self, + value: u32, + address: u32, + bit_offset: u32, + ) -> Result<(), OtpError> { + let mut prog_bit: u32 = 0; + + if address & 0x1 == 0 { + //even address, default data is 0x0 + if value != 0 { + prog_bit = !(0x1 << bit_offset); + } + } else { + //odd address, default data is 0xffff_ffff + if value == 0 { + prog_bit = 0x1 << bit_offset; + } + } + if prog_bit > 0 { + self.otp_prog(address, prog_bit) + } else { + Err(OtpError::Timeout) + } + } + //lock registers + pub fn otp_lock_reg(&self) { + self.sb + .secure000() + .write(|w| unsafe { w.prot_key().bits(1) }); + } + pub fn otp_unlock_reg(&self) { + self.sb + .secure000() + .write(|w| unsafe { w.prot_key().bits(OTP_PASSWD) }); + } + fn verify_bit(&mut self, value: u32, otp_addr: u32, bit_offset: u32) -> Result<(), OtpError> { + let mut ret: [u32; 2] = [0, 0]; + let mut success: bool = false; + let addr: u32 = if otp_addr & 0x1 == 0 { + otp_addr + } else { + //make it even + otp_addr - 1 + }; + self.otp_read_data(addr, &mut ret)?; + + if otp_addr & 0x1 == 0 { + if (ret[0] >> bit_offset) & 1 == value { + success = true; + } + } else { + //Odd address takes takes the 2nd Dword + if (ret[1] >> bit_offset) & 1 == value { + success = true; + } + } + if !success { + return Err(OtpError::VerificationFailed); + } + Ok(()) + } + + /// + /// bit program + /// + pub fn otp_prog_dc_b( + &mut self, + value: u32, + address: u32, + bit_offset: u32, + ) -> Result<(), OtpError> { + let mut pass: bool = false; + + self.otp_soak(OtpSoak::NormalProg); + self.otp_prog_bit_helper(value, address, bit_offset)?; + for _i in 0..OTP_OP_RETRIES { + if self.verify_bit(value, address, bit_offset).is_err() { + self.otp_soak(OtpSoak::SoakProg); + self.otp_prog_bit_helper(value, address, bit_offset)?; + if self.verify_bit(value, address, bit_offset).is_ok() { + self.otp_soak(OtpSoak::NormalProg); + } else { + pass = true; + break; + } + } else { + pass = true; + break; + } + } + if !pass { + return Err(OtpError::Timeout); + } + Ok(()) + } + /// + /// program a DWORD. will do verification after program a DWORD for efficiency + /// * `ignore` - bit position mask. don't program the bits shown in the mask + /// + fn otp_prog_dw(&mut self, value: u32, ignore: u32, address: u32) -> Result<(), OtpError> { + let mut result: Result<(), OtpError>; + let mut bit_value: u32; + let mut prog_bit: u32; + //1-bit at a time + for bit_pos in 0..32 { + if (ignore >> bit_pos) & 0x1 == 0x1 { + //don't do anything + continue; + } + bit_value = (value >> bit_pos) & 0x1; + //inverse + if address & 0x1 == 0 { + if bit_value == 0x1 { + prog_bit = !(0x1 << bit_pos); + } else { + continue; + } + } else if bit_value == 0x1 { + continue; + } else { + prog_bit = 0x1 << bit_pos; + } + result = self.otp_prog(address, prog_bit); + if result != Ok(()) { + return result; + } + } + Ok(()) + } + + /// + /// verify 1 DWORD from odd/even address + /// + pub fn verify_dw(&self, address: u32, data: u32, ignore: u32, compare: &mut u32) -> bool { + let mut ret: [u32; 2] = [0, 0]; + + let otp_addr = address & !(1 << 15); + + let addr = if otp_addr & 0x1 == 0 { + otp_addr + } else { + otp_addr - 1 + }; + if self.otp_read_data(addr, &mut ret) != Ok(()) { + return false; + } + if otp_addr & 0x1 == 0 { + //retrieve 1st DWORD + if (data & !ignore) == (ret[0] & !ignore) { + *compare = 0; + return true; + } + *compare = data ^ ret[0]; + false + } else { + //odd address: retrieve 2nd DWORD + if (data & !ignore) == (ret[1] & !ignore) { + *compare = !0; + return true; + } + *compare = !(data ^ ret[1]); + false + } + } + + /// + /// verify 2 DWORD + /// + pub fn verify_2dw( + &mut self, + address: u32, + value: &[u32], + ignore: &[u32], + num_dw: u32, + compare: &mut [u32], + ) -> bool { + let mut ret: [u32; 2] = [0, 0]; + + let otp_addr = address & !(1 << 15); + + if num_dw == 1 { + return self.verify_dw(address, value[0], ignore[0], &mut compare[0]); + } else if num_dw == 2 { + //otp_addr should already be even + if self.otp_read_data(otp_addr, &mut ret) != Ok(()) { + return false; + } + if (value[0] & !ignore[0]) == (ret[0] & !ignore[0]) + && (value[1] & !ignore[1]) == (ret[1] & !ignore[1]) + { + compare[0] = 0; + compare[1] = !0; + return true; + } + compare[0] = value[0] ^ ret[0]; + compare[1] = !(value[1] ^ ret[1]); + } + false + } + + /// + /// Check if prorammed data is valid + /// + pub fn is_program_data_valid(&mut self, addr: u32, otp_data: u32, buffer_data: u32) -> bool { + for i in 0..32 { + if addr & 0x1 == 0 { + //even location, default is 0x0000_0000 + //only able to write b'1 + //it's already b'1, can't program it b'0 + if ((otp_data >> i) & 0x1) == 1 && ((buffer_data >> i) & 0x1) == 0 { + return false; + } + } else if ((otp_data >> i) & 0x1) == 0 && ((buffer_data >> i) & 0x1) == 1 { + return false; + } + } + true + } + /// + /// Program 2 DWORD and verify in data region + /// + pub fn otp_prog_verify_2dw( + &mut self, + address: u32, + otp_data: &[u32], + buffer: &[u32], + ignore: &[u32], + ) -> Result<(), OtpError> { + let mut ignore_mask: [u32; 2] = [0, 0]; + let mut compare: [u32; 2] = [0, 0]; + let mut pass: bool; + let mut verify_size = 0; + ignore_mask[0] = ignore[0]; + ignore_mask[1] = ignore[1]; + let data0_masked = otp_data[0] & !ignore_mask[0]; + let buf0_masked = buffer[0] & !ignore_mask[0]; + let data1_masked = otp_data[1] & !ignore_mask[1]; + let buf1_masked = buffer[1] & !ignore_mask[1]; + //if bits to be programmed is the same as + //already programmed bits, no need to program + if data0_masked == buf0_masked { + ignore_mask[0] = 0xffff_ffff; + } + if data1_masked == buf1_masked { + ignore_mask[1] = 0xffff_ffff; + } + + //check if data to be written is the same on otp + if data0_masked == buf0_masked && data1_masked == buf1_masked { + otp_debug!( + self.logger, + "otp_prog_verify_2dw: data is the same, no need to program" + ); + return Ok(()); + } + if ignore_mask[0] != 0xffff_ffff + && !self.is_program_data_valid(address, data0_masked, buf0_masked) + { + return Err(OtpError::WriteFailed); + } + if ignore_mask[1] != 0xffff_ffff + && !self.is_program_data_valid(address + 1, data1_masked, buf1_masked) + { + return Err(OtpError::WriteFailed); + } + if !self.otp_soak(OtpSoak::NormalProg) { + return Err(OtpError::Timeout); + } + + //ignore + if ignore_mask[0] != 0xffff_ffff { + self.otp_prog_dw(buffer[0], ignore_mask[0], address)?; + verify_size += 1; + } + if ignore_mask[1] != 0xffff_ffff { + self.otp_prog_dw(buffer[1], ignore_mask[1], address + 1)?; + verify_size += 1; + } + pass = false; + for _j in 0..OTP_OP_RETRIES { + if self.verify_2dw(address, buffer, &ignore_mask, verify_size, &mut compare) { + pass = true; + break; + } + self.otp_soak(OtpSoak::SoakProg); + if compare[0] != 0 { + self.otp_prog_dw(compare[0], ignore_mask[0], address)?; + } + if verify_size == 2 && compare[1] != !0 { + self.otp_prog_dw(compare[1], ignore_mask[1], address + 1)?; + } + if self.verify_2dw(address, buffer, &ignore_mask, verify_size, &mut compare) { + pass = true; + break; + } + self.otp_soak(OtpSoak::NormalProg); + } + if !pass { + self.otp_soak(OtpSoak::Default); + return Err(OtpError::WriteFailed); + } + Ok(()) + } + + /// + /// bit programing retry + /// + pub fn otp_prog_verify_retry(&mut self, addr: u32, data: u32, ignore: u32) -> bool { + let mut compare: u32 = 0; + let mut pass: bool = false; + + for _j in 0..OTP_OP_RETRIES { + if self.verify_dw(addr, data, ignore, &mut compare) { + pass = true; + break; + } + self.otp_soak(OtpSoak::SoakProg); + if let Err(_e) = self.otp_prog_dw(compare, ignore, addr) { + pass = false; + break; + } + if self.verify_dw(addr, data, ignore, &mut compare) { + pass = true; + break; + } + self.otp_soak(OtpSoak::NormalProg); + } + pass + } + /// + /// Program OTP data region + /// starts from "address" with "buffer" contents + /// + #[allow(clippy::needless_range_loop)] + pub fn aspeed_otp_prog_data( + &mut self, + address: usize, + buffer: &mut [u32], + ) -> Result<(), OtpError> { + let mut result = Ok(()); + let ignore: u32 = 0; //ignore bits mask + let mut addr: u32; + let len: usize = buffer.len(); + let mut pass: bool; + + if address + len > OTP_MEM_LIMIT_DATA || address & 0x3 != 0 { + return Err(OtpError::InvalidAddress); + } + self.otp_unlock_reg(); + + for i in 0..len { + addr = u32::try_from(address + i).unwrap(); + self.otp_soak(OtpSoak::NormalProg); + result = self.otp_prog_dw(buffer[i], ignore, addr); + if result != Ok(()) { + return result; + } + pass = self.otp_prog_verify_retry(buffer[i], ignore, addr); + if !pass { + self.otp_soak(OtpSoak::Default); + result = Err(OtpError::WriteFailed); + break; + } + } + self.otp_lock_reg(); + result + } + //lock otp memory + pub fn otp_lock_mem(&mut self) -> Result<(), OtpError> { + if !self.is_otp_locked() && self.otp_prog(0, 31).is_ok() { + self.locked = true; + } + Ok(()) + } + pub fn is_otp_locked(&self) -> bool { + let otp_conf: u32 = self.otp_read_conf_idx(0).unwrap_or_default(); + otp_conf & OTP_MEM_LOCK_ENBLE == OTP_MEM_LOCK_ENBLE + } + pub fn is_key_protected(&self) -> bool { + let otp_conf: u32 = self.otp_read_conf_idx(0).unwrap_or_default(); + otp_conf & OTP_KEY_PROT_ENBLE == OTP_KEY_PROT_ENBLE + } + + pub fn update_prot_info(&self, session: &mut SessionInfo) { + session.chip_version = self.chip_version(); + match session.chip_version { + AspeedChipVersion::Ast1060A1 => { + session.version_name = *b"AST1060A1\0"; + } + AspeedChipVersion::Ast1060A2 => { + session.version_name = *b"AST1060A2\0"; + } + _ => { + session.version_name = *b"ASUnknown\0"; + } + } + let otp_conf: u32 = self.otp_read_conf_idx(0).unwrap_or_default(); + session.protection_status.memory_locked = + otp_conf & OTP_MEM_LOCK_ENBLE == OTP_MEM_LOCK_ENBLE; + session.protection_status.strap_protected = + otp_conf & OTP_STRAP_PROT_ENBLE == OTP_STRAP_PROT_ENBLE; + session.protection_status.user_ecc_protected = + otp_conf & OTP_USER_ECC_PROT_ENBLE == OTP_USER_ECC_PROT_ENBLE; + + session.protection_status.security_protected = + otp_conf & OTP_SECURE_PROT_ENBLE == OTP_SECURE_PROT_ENBLE; + let mut secure_size = otp_conf >> OTP_SECURE_SIZE_BIT_POS; + if secure_size != 0 { + secure_size = (secure_size & (OTP_SECURE_SIZE_MASK + 1)) << 5; + } + session.protection_status.security_size = secure_size; + } + pub fn get_sw_revision(&self, sw_rid: &mut [u32; 2]) { + sw_rid[0] = self.sb.secure068().read().bits(); + sw_rid[1] = self.sb.secure06c().read().bits(); + } + pub fn get_tool_verion(&self) -> &[u8] { + return OTP_VER.as_bytes(); + } + pub fn get_key_count(&self) -> u8 { + let key_num = self.sb.secure078().read().sec_boot_key_number_regs().bits(); + + key_num + } + #[allow(clippy::needless_range_loop)] + pub fn otp_strap_status(&self, os: &mut [StrapStatus]) -> Result<(), OtpError> { + let mut otpstrap_raw: [u32; 2] = [0; 2]; + + for j in 0..64 { + os[j].value = false; + os[j].remaining_writes = 6; + os[j].writable_option = 0xff; + os[j].protected = false; + } + let strap_end: usize = 28; // Final strap address to process + + self.otp_soak(OtpSoak::Default); + + for i in (16..strap_end).step_by(2) { + let option = u8::try_from((i - 16) / 2).unwrap(); + + otpstrap_raw[0] = self.otp_read_conf_idx(i.try_into().unwrap())?; + otpstrap_raw[1] = self.otp_read_conf_idx((i + 1).try_into().unwrap())?; + for j in 0..32 { + let bit_value = ((otpstrap_raw[0] >> j) & 0x1) as u8; + + if bit_value == 0 && os[j].writable_option == 0xff { + os[j].writable_option = option; + } + if bit_value == 1 { + os[j].remaining_writes -= 1; + } + os[j].value ^= bit_value != 0; + os[j].options[option as usize] = bit_value; + } + + for j in 32..64 { + let bit_value = ((otpstrap_raw[1] >> (j - 32)) & 0x1) as u8; + + if bit_value == 0 && os[j].writable_option == 0xff { + os[j].writable_option = option; + } + if bit_value == 1 { + os[j].remaining_writes -= 1; + } + os[j].value ^= bit_value != 0; + os[j].options[option as usize] = bit_value; + } + } + otpstrap_raw[0] = self.otp_read_conf_idx(30)?; + otpstrap_raw[1] = self.otp_read_conf_idx(31)?; + + for j in 0..32 { + if (otpstrap_raw[0] >> j) & 0x1 == 1 { + os[j].protected = true; + } + } + + for j in 32..64 { + if (otpstrap_raw[1] >> (j - 32)) & 0x1 == 1 { + os[j].protected = true; + } + } + Ok(()) + } + + /// + /// Read from data region + /// + fn aspeed_otp_read_data(&self, offset: usize, buffer: &mut [u32]) -> Result<(), OtpError> { + let mut temp: [u32; 2] = [0, 0]; + let cdw_len: usize = buffer.len(); + if cdw_len + offset > OTP_MEM_LIMIT_DATA { + return Err(OtpError::BoundaryError); + } + if offset & 0x4 != 0 { + return Err(OtpError::AlignmentError); + } + for i in (offset..offset + cdw_len).step_by(2) { + let idx = i - offset; + match self.otp_read_data(u32::try_from(i).unwrap(), &mut temp) { + Ok(()) => { + buffer[idx] = temp[0]; + buffer[idx + 1] = temp[1]; + } + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// + /// Read from configuration region + /// + fn aspeed_otp_read_conf(&self, offset: u32, buffer: &mut [u32]) -> Result<(), OtpError> { + let mut result: Result<(), OtpError> = Ok(()); + let cdw_len = u32::try_from(buffer.len()).unwrap(); + if cdw_len + offset > 32 { + return Err(OtpError::BoundaryError); + } + self.otp_unlock_reg(); + self.otp_soak(OtpSoak::Default); + for i in offset..offset + cdw_len { + let idx = (i - offset) as usize; + buffer[idx] = match self.otp_read_conf_idx(i) { + Ok(value) => value, + Err(e) => { + result = Err(e); + break; + } + }; + } + self.otp_lock_reg(); + result + } + /// + ///Read OTP strap into buffer. + ///buf: output OTP strap into buffer. + /// + #[allow(dead_code)] + fn aspeed_otp_read_strap(&self, offset: usize, buf: &mut [u32]) -> Result<(), OtpError> { + let mut strap_status: [StrapStatus; 64] = [StrapStatus { + value: false, + protected: false, + options: [0; 7], + remaining_writes: 6, + writable_option: 0xff, + }; 64]; + let cdw_len = buf.len(); + + if cdw_len + offset > 2 { + return Err(OtpError::BoundaryError); + } + self.otp_unlock_reg(); + let result = self.otp_strap_status(&mut strap_status); + if result == Ok(()) { + for i in offset..offset + cdw_len { + let idx = i - offset; + buf[idx] = 0; + for j in 0..32 { + buf[idx] |= u32::from(strap_status[i * 32 + j].value) << j; + } + } + } + self.otp_lock_reg(); + result + } + + /// + ///Read OTP protect into buffer. + ///offset-offset to scu protect region + ///OTPCFG28,OTPCFG29 + /// + pub fn aspeed_otp_read_scuprot( + &self, + offset: usize, + buffer: &mut [u32], + ) -> Result<(), OtpError> { + let mut result: Result<(), OtpError> = Ok(()); + let cdw_len = buffer.len(); + + if cdw_len + offset > 2 { + return Err(OtpError::BoundaryError); + } + self.otp_unlock_reg(); + + if result == Ok(()) { + for i in offset..offset + cdw_len { + let idx = i - offset; + buffer[idx] = match self.otp_read_conf_idx(28 + u32::try_from(offset).unwrap()) { + Ok(value) => value, + Err(e) => { + result = Err(e); + break; + } + }; + } + } + self.otp_lock_reg(); + result + } + + /// + /// Write data to data region + /// + pub fn otp_prog_data(&mut self, offset: usize, data: &[u32]) -> Result<(), OtpError> { + let mut result: Result<(), OtpError> = Ok(()); + let ignore: [u32; 2] = [0, 0]; + let cdw_len = data.len(); + + if cdw_len + offset > OTP_MEM_LIMIT_DATA { + return Err(OtpError::BoundaryError); + } + if offset & 0x3 != 0 { + return Err(OtpError::AlignmentError); + } + self.otp_unlock_reg(); + //Read whole data region + //self.otp_read_data_region()?; + + for i in (offset..offset + cdw_len).step_by(2) { + let idx0 = i - offset; + let idx1 = i; + unsafe { + result = self.otp_read_data( + u32::try_from(idx1).unwrap(), + &mut DATA_REGION[idx1..idx1 + 2], + ); + if result != Ok(()) { + otp_debug!( + self.logger, + "otp_prog_data: read fail {:?}", + result.unwrap() + ); + break; + } + otp_debug!(self.logger, "otp_prog_data: idx0={:}, idx1={:}", idx0, idx1); + result = self.otp_prog_verify_2dw( + u32::try_from(i).unwrap(), + &DATA_REGION[idx1..idx1 + 2], + &data[idx0..idx0 + 2], + &ignore, + ); + if result != Ok(()) { + break; + } + } + } + + self.otp_soak(OtpSoak::Default); + self.otp_lock_reg(); + result + } + + /// + /// Program strap bits + /// All non proteced bits will be programmed + /// + #[allow(clippy::needless_range_loop)] + pub fn otp_prog_strap(&mut self, start_bit: usize, strap: &[u32]) -> Result<(), OtpError> { + let mut prog_address: u32; + let mut bit: u32; + let mut offset: u32; + let mut prog_flag: u32; + let mut count_prot: u32 = 0; + let mut count_cant_write: u32 = 0; + + if start_bit > 63 { + return Err(OtpError::InvalidAddress); + } + + let mut os: [StrapStatus; 64] = [StrapStatus { + value: false, + protected: false, + options: [0; 7], + remaining_writes: 6, + writable_option: 0xff, + }; 64]; + + self.otp_strap_status(&mut os)?; + //all strap bits + for i in start_bit..64 { + prog_address = OTP_CONF_OFFSET; + if i < 32 { + offset = u32::try_from(i).unwrap(); + bit = (strap[0] >> (offset - u32::try_from(start_bit).unwrap())) & 0x1; + prog_address |= ((u32::from(os[i].writable_option) * 2 + 16) / 8) * 0x200; + prog_address |= ((u32::from(os[i].writable_option) * 2 + 16) % 8) * 0x2; + } else { + offset = u32::try_from(i - 32).unwrap(); + if i - start_bit < 32 { + bit = (strap[0] >> offset) & 0x1; + } else { + bit = (strap[1] >> (offset - u32::try_from(start_bit).unwrap())) & 0x1; + } + prog_address |= ((u32::from(os[i].writable_option) * 2 + 17) / 8) * 0x200; + prog_address |= ((u32::from(os[i].writable_option) * 2 + 17) % 8) * 0x2; + } + //check if program bit value is the same as the programmed bit value + if bit == u32::from(os[i].value) { + prog_flag = 0; //no need to proram + otp_debug!(self.logger, "otp_prog_strap: bit {:} no need to program", i); + } else { + prog_flag = 1; + otp_debug!( + self.logger, + "otp_prog_strap: program bit {:} from {:} to {:}", + i, + u32::from(os[i].value), + bit + ); + } + //bit to be prgrammed is protected + if os[i].protected && prog_flag == 1 { + count_prot += 1; + continue; + } + if os[i].remaining_writes == 0 && prog_flag == 1 { + count_cant_write += 1; + continue; + } + if prog_flag == 1 { + match self.otp_prog_dc_b(1, prog_address, offset) { + Ok(()) => {} + Err(e) => { + return Err(e); + } + } + } + } + self.otp_soak(OtpSoak::Default); + if count_prot > 0 || count_cant_write > 0 { + //return Err() + } + Ok(()) + } + + /// + /// OTP conf + /// + pub fn otp_prog_conf(&mut self, start_conf: usize, conf: &[u32]) -> Result<(), OtpError> { + let mut result: Result<(), OtpError> = Ok(()); + let conf_ignore: u32 = 0; + let mut otp_conf: u32; + let mut pass: bool = false; + let mut addr: usize; + let mut data_masked: u32; + let mut buf_masked: u32; + let cdw_len = conf.len(); + + if cdw_len + start_conf > 32 { + return Err(OtpError::BoundaryError); + } + self.otp_unlock_reg(); + self.otp_soak(OtpSoak::Default); + for i in start_conf..start_conf + cdw_len { + //from 0 + let idx = i - start_conf; + //read conf from OTP + otp_conf = match self.otp_read_conf_idx(u32::try_from(i).unwrap()) { + Ok(value) => value, + Err(e) => { + result = Err(e); + break; + } + }; + data_masked = otp_conf & !conf_ignore; + buf_masked = conf[idx] & !conf_ignore; + addr = REGION_INFO[AspeedOtpRegion::Configuration as usize].start; + addr |= (i / 8) * 0x200; + addr |= (i % 8) * 0x2; + otp_debug!(self.logger, "otp_prog_conf: addr = {:#x}", addr); + if data_masked == buf_masked { + pass = true; + continue; + } + self.otp_soak(OtpSoak::NormalProg); + result = self.otp_prog_dw(conf[idx], conf_ignore, u32::try_from(addr).unwrap()); + if result != Ok(()) { + break; + } + pass = self.otp_prog_verify_retry(u32::try_from(addr).unwrap(), conf[idx], conf_ignore); + + if !pass { + break; + } + } + self.otp_soak(OtpSoak::Default); + self.otp_lock_reg(); + if !pass { + return Err(OtpError::WriteFailed); + } + result + } + + /// + /// SCU protect + /// + #[allow(clippy::needless_range_loop)] + pub fn otp_prog_scu_protect(&mut self, start: usize, otp_scu: &[u32]) -> Result<(), OtpError> { + let mut scu_pro: [u32; 2] = [0; 2]; + let ignore: u32 = 0; + let mut data_masked: u32; + let mut buf_masked: u32; + let mut addr: usize; + let mut pass: bool = false; + let scupro_start = REGION_INFO[AspeedOtpRegion::ScuProtection as usize].start; + let cdw_size = otp_scu.len(); + let total_size = REGION_INFO[AspeedOtpRegion::ScuProtection as usize].cdw_size; + + if start + cdw_size > total_size { + return Err(OtpError::BoundaryError); + } + scu_pro[0] = self.otp_read_conf_idx(28)?; + scu_pro[1] = self.otp_read_conf_idx(29)?; + self.otp_unlock_reg(); + self.otp_soak(OtpSoak::Default); + + for i in start..start + cdw_size { + let idx = i - start; + data_masked = scu_pro[i] & !ignore; + buf_masked = otp_scu[idx] & !ignore; + addr = scupro_start + i * 2; + if data_masked == buf_masked { + pass = true; + continue; + } + self.otp_soak(OtpSoak::Default); + self.otp_prog_dw(otp_scu[idx], ignore, u32::try_from(addr).unwrap())?; + + pass = self.otp_prog_verify_retry(u32::try_from(addr).unwrap(), otp_scu[idx], ignore); + + if !pass { + break; + } + } + self.otp_soak(OtpSoak::Default); + self.otp_lock_reg(); + if !pass { + return Err(OtpError::WriteFailed); + } + Ok(()) + } + + pub fn total_capacity(&self) -> usize { + let mut cdw_size: usize = 0; + + cdw_size += REGION_INFO[AspeedOtpRegion::Data as usize].cdw_size; + cdw_size += REGION_INFO[AspeedOtpRegion::Configuration as usize].cdw_size; + cdw_size << 2 + } + + pub fn region_capacity(&self, region: AspeedOtpRegion) -> usize { + REGION_INFO[region as usize].cdw_size << 2 + } + #[allow(clippy::unused_self)] + fn region_alignment(&self, region: AspeedOtpRegion) -> usize { + REGION_INFO[region as usize].alignment + } + #[allow(clippy::match_same_arms)] + fn is_region_protected(&self, region: AspeedOtpRegion) -> Result { + let mut protected: bool = false; + + self.otp_unlock_reg(); + let otp_conf: u32 = self.otp_read_conf_idx(0)?; + self.otp_lock_reg(); + match region { + AspeedOtpRegion::Data => { + if otp_conf & OTP_USER_ECC_PROT_ENBLE == OTP_USER_ECC_PROT_ENBLE + && otp_conf & OTP_SECURE_PROT_ENBLE == OTP_SECURE_PROT_ENBLE + { + protected = true; + } + } + AspeedOtpRegion::Configuration => { + if otp_conf & OTP_CONF_PROT_ENBLE == OTP_CONF_PROT_ENBLE { + protected = true; + } + } + AspeedOtpRegion::Strap => { + if otp_conf & OTP_STRAP_PROT_ENBLE == OTP_STRAP_PROT_ENBLE { + protected = true; + } + } + AspeedOtpRegion::ScuProtection => {} + } + Ok(protected) + } + + /// Enable protection for a specific region + /// + /// # Parameters + /// - `region`: The region to protect + /// + /// # Returns + /// - `Ok(())`: Protection enabled successfully + /// - `Err(Self::Error)`: Failed to enable protection + fn enable_region_protection(&mut self, region: AspeedOtpRegion) -> Result<(), OtpError> { + let mut value: [u32; 1] = [0; 1]; + + if self.is_region_protected(region) == Ok(true) { + return Ok(()); + } + match region { + AspeedOtpRegion::Data => { + value[0] = OTP_USER_ECC_PROT_ENBLE | OTP_SECURE_PROT_ENBLE; + } + AspeedOtpRegion::Configuration => { + value[0] = OTP_CONF_PROT_ENBLE; + } + AspeedOtpRegion::Strap => { + value[0] = OTP_STRAP_PROT_ENBLE; + } + AspeedOtpRegion::ScuProtection => {} + } + self.otp_prog_conf(0, &value) + } + #[allow(clippy::unused_self)] + #[allow(clippy::unnecessary_wraps)] + fn is_feature_supported(&self, _feature: &str) -> Result { + Ok(false) + } + #[allow(clippy::unused_self)] + #[allow(clippy::unnecessary_wraps)] + fn list_regions(&self) -> Result<&[AspeedOtpRegion], OtpError> { + Ok(REGION_IDS) + } + #[allow(clippy::unused_self)] + fn get_region_info(&self, region: AspeedOtpRegion) -> Result<(usize, usize, usize), OtpError> { + for each in REGION_INFO { + if each.region_type == region { + return Ok((each.start, each.cdw_size, each.alignment)); + } + } + Err(OtpError::Unknown) + } +} diff --git a/src/otp/common.rs b/src/otp/common.rs new file mode 100644 index 0000000..d5996c1 --- /dev/null +++ b/src/otp/common.rs @@ -0,0 +1,128 @@ +// Licensed under the Apache-2.0 license + +use proposed_traits::otp::ErrorKind; +/// ASPEED chip version information +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AspeedChipVersion { + /// AST1030 A0 revision + Ast1030A0, + /// AST1030 A1 revision + Ast1030A1, + /// AST1035 A1 revision + Ast1035A1, + /// AST1060 A1 revision + Ast1060A1, + /// AST1060 A2 revision + Ast1060A2, + /// Unknown or unsupported version + Unknown, +} + +/// Memory region types in ASPEED OTP +/// Data region: +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AspeedOtpRegion { + /// Data region (2048 double-words, 0x0000-0x0FFF) + Data, + /// Configuration region (32 double-words, 0x800-0x81F) + Configuration, + /// Strap region (64 bits, multiple programming options) + Strap, + /// SCU protection region (2 double-words, 0x1C-0x1D) + ScuProtection, +} + +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[non_exhaustive] +pub enum OtpError { + InvalidAddress, + InvalidBufSize, + MemoryLocked, + WriteFailed, + ReadFailed, + LockFailed, + VerificationFailed, + WriteExhausted, + NoSession, + RegionProtected, + AlignmentError, + BoundaryError, + Timeout, + UnknowRevID, + Unknown, +} +use crate::otp::common::OtpError::{ + AlignmentError, BoundaryError, InvalidBufSize, NoSession, RegionProtected, Timeout, + UnknowRevID, VerificationFailed, WriteExhausted, +}; +impl proposed_traits::otp::Error for OtpError { + fn kind(&self) -> ErrorKind { + match *self { + Self::InvalidAddress => ErrorKind::InvalidAddress, + Self::MemoryLocked => ErrorKind::MemoryLocked, + Self::WriteFailed => ErrorKind::WriteFailed, + Self::ReadFailed => ErrorKind::ReadFailed, + Self::LockFailed => ErrorKind::LockFailed, + self::VerificationFailed => ErrorKind::VerificationFailed, + self::WriteExhausted => ErrorKind::WriteExhausted, + self::NoSession => ErrorKind::NoSession, + self::RegionProtected => ErrorKind::RegionProtected, + self::AlignmentError => ErrorKind::AlignmentError, + self::BoundaryError | self::InvalidBufSize => ErrorKind::BoundaryError, + self::Timeout => ErrorKind::Timeout, + Self::Unknown | self::UnknowRevID => ErrorKind::Unknown, + } + } +} + +/// Protection status for different OTP regions +#[allow(clippy::struct_excessive_bools)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +pub struct ProtectionStatus { + /// Memory lock status (prevents all modifications) + pub memory_locked: bool, + /// Key return protection status + pub key_protected: bool, + /// Strap region protection status + pub strap_protected: bool, + /// Configuration region protection status + pub config_protected: bool, + /// User region protection status + pub user_ecc_protected: bool, + /// Security region protection status + pub security_protected: bool, + /// Security region size in bytes + pub security_size: u32, +} + +/// Strap bit programming status +#[derive(Debug, Clone, Copy)] +pub struct StrapStatus { + /// Current strap bit value + pub value: bool, + /// Programming options available + pub options: [u8; 7], + /// Remaining write attempts + pub remaining_writes: u8, + /// Next writable option + pub writable_option: u8, + /// Protection status for this strap bit + pub protected: bool, +} + +/// Session information provided during OTP session establishment +#[derive(Debug, Clone)] +pub struct SessionInfo { + /// Chip version detected + pub chip_version: AspeedChipVersion, + /// Version name string + pub version_name: [u8; 10], + /// Current protection status + pub protection_status: ProtectionStatus, + /// Tool version information + pub tool_version: [u8; 32], + /// Software revision ID + pub software_revision: u32, + /// Number of cryptographic keys stored + pub key_count: u32, +} diff --git a/src/otp/hal.rs b/src/otp/hal.rs new file mode 100644 index 0000000..fe7edc6 --- /dev/null +++ b/src/otp/hal.rs @@ -0,0 +1,340 @@ +// Licensed under the Apache-2.0 license + +use crate::{ + common::Logger, + otp::{ + common::{ + AspeedChipVersion, AspeedOtpRegion, OtpError, ProtectionStatus, SessionInfo, + StrapStatus, + }, + OtpController, OTP_MEM_LIMIT, + }, +}; +use proposed_traits::otp::{ + OtpIdentification, OtpMemory, OtpMemoryLayout, OtpProtection, OtpRegions, OtpSession, +}; + +impl OtpMemory for OtpController { + /// + /// Read 1 DWORD + /// + fn read(&self, address: usize) -> Result { + let mut buffer: [u32; 1] = [0]; + self.read_region(AspeedOtpRegion::Data, address, &mut buffer)?; + Ok(buffer[0]) + } + fn write(&mut self, address: usize, data: u32) -> Result<(), Self::Error> { + let ignore_mask: [u32; 2] = [0, 0xffff_ffff]; + let mut buffer: [u32; 2] = [0, 0]; + let mut otp_data: [u32; 2] = [0, 0]; + if self.locked { + return Err(OtpError::MemoryLocked); + } + if address > OTP_MEM_LIMIT as usize { + return Err(OtpError::InvalidAddress); + } + otp_data[0] = self.read(address)?; + buffer[0] = data; + self.otp_prog_verify_2dw( + u32::try_from(address).unwrap(), + &otp_data, + &buffer, + &ignore_mask, + ) + } + + fn lock(&mut self) -> Result<(), Self::Error> { + self.otp_lock_mem() + } + + fn is_locked(&self) -> bool { + self.is_otp_locked() + } +} + +impl OtpSession for OtpController { + type SessionInfo = SessionInfo; + /// Session information type returned when establishing a session + /// Establish an OTP session with hardware access + /// + /// # Returns + /// - `Ok(SessionInfo)`: Session established successfully + /// - `Err(Self::Error)`: Failed to establish session + fn begin_session(&mut self) -> Result { + if self.session_active { + return Err(OtpError::NoSession); + } + let ver_bytes = self.get_tool_verion(); + let mut session_info = SessionInfo { + chip_version: AspeedChipVersion::Ast1060A2, + version_name: *b"AST1060A2\0", + protection_status: ProtectionStatus { + memory_locked: false, + key_protected: false, + strap_protected: false, + config_protected: false, + user_ecc_protected: false, + security_protected: false, + security_size: 0, + }, + tool_version: [0; 32], + software_revision: 0x1234_5678, + key_count: 5, + }; + if ver_bytes.len() > session_info.tool_version.len() { + return Err(OtpError::BoundaryError); + } + self.update_prot_info(&mut session_info); + session_info.chip_version = self.chip_version(); + session_info.tool_version[..ver_bytes.len()].copy_from_slice(ver_bytes); + session_info.key_count = u32::from(self.get_key_count()); + + self.session_active = true; + + Ok(session_info) + } + + /// Terminate the OTP session and release resources + /// + /// # Returns + /// - `Ok(())`: Session terminated successfully + /// - `Err(Self::Error)`: Failed to terminate session properly + fn end_session(&mut self) -> Result<(), Self::Error> { + if !self.session_active { + return Err(OtpError::NoSession); + } + self.session_active = false; + Ok(()) + } + + /// Check if a session is currently active + fn is_session_active(&self) -> bool { + self.session_active + } +} + +impl OtpRegions for OtpController { + /// Region identifier type + type Region = AspeedOtpRegion; + + /// Read data from a specific OTP region + /// + /// # Parameters + /// - `region`: The region to read from + /// - `offset`: Offset within the region. For strap, it's `strap_bit_offset` + /// - `buffer`: Buffer to store read data + /// + /// # Returns + /// - `Ok(())`: Data read successfully + /// - `Err(Self::Error)`: Read operation failed + #[allow(clippy::needless_range_loop)] + fn read_region( + &self, + region: Self::Region, + offset: usize, + buffer: &mut [u32], + ) -> Result<(), Self::Error> { + match region { + AspeedOtpRegion::Data => self.aspeed_otp_read_data(offset, buffer), + AspeedOtpRegion::Configuration => { + self.aspeed_otp_read_conf(u32::try_from(offset).unwrap(), buffer) + } + AspeedOtpRegion::Strap => { + if buffer.len() < 2 { + return Err(OtpError::InvalidBufSize); + } + let mut strap_status: [StrapStatus; 64] = [StrapStatus { + value: false, + protected: false, + options: [0; 7], + remaining_writes: 6, + writable_option: 0xff, + }; 64]; + match self.otp_strap_status(&mut strap_status) { + Ok(()) => { + buffer[0] = 0; + for i in 0usize..32 { + buffer[0] |= u32::from(strap_status[i].value) << i; + } + buffer[1] = 0; + for i in 32usize..64 { + buffer[1] |= u32::from(strap_status[i].value) << (i - 32); + } + Ok(()) + } + Err(e) => Err(e), + } + } + AspeedOtpRegion::ScuProtection => self.aspeed_otp_read_scuprot(offset, buffer), + } + } + + /// Write data to a specific OTP region + /// + /// # Parameters + /// - `region`: The region to write to + /// - `offset`: Offset within the region. For strap, it's `strap_bit_offset` + /// - `data`: Data to write + /// + /// # Returns + /// - `Ok(())`: Data written successfully + /// - `Err(Self::Error)`: Write operation failed + fn write_region( + &mut self, + region: Self::Region, + offset: usize, + data: &[u32], + ) -> Result<(), Self::Error> { + match region { + AspeedOtpRegion::Data => self.otp_prog_data(offset, data), + AspeedOtpRegion::Configuration => self.otp_prog_conf(offset, data), + AspeedOtpRegion::Strap => self.otp_prog_strap(offset, data), + AspeedOtpRegion::ScuProtection => self.otp_prog_scu_protect(offset, data), + } + } + /// Get the capacity of a specific region + /// + /// # Parameters + /// - `region`: The region to query + /// + /// # Returns + /// The capacity of the region in elements of type T + fn region_capacity(&self, region: Self::Region) -> usize { + self.region_capacity(region) + } + /// Get the alignment requirement for a specific region + /// + /// # Parameters + /// - `region`: The region to query + /// + /// # Returns + /// The alignment requirement in bytes + fn region_alignment(&self, region: Self::Region) -> usize { + self.region_alignment(region) + } +} + +impl OtpProtection for OtpController { + type Region = AspeedOtpRegion; + + /// Check if a specific region is protected + /// + /// # Parameters + /// - `region`: The region to check + /// + /// # Returns + /// - `Ok(bool)`: Protection status (true = protected) + /// - `Err(Self::Error)`: Failed to check protection status + fn is_region_protected(&self, region: Self::Region) -> Result { + self.is_region_protected(region) + } + + /// Enable protection for a specific region + /// + /// # Parameters + /// - `region`: The region to protect + /// + /// # Returns + /// - `Ok(())`: Protection enabled successfully + /// - `Err(Self::Error)`: Failed to enable protection + fn enable_region_protection(&mut self, region: Self::Region) -> Result<(), Self::Error> { + self.enable_region_protection(region) + } + + /// Check if the entire memory is globally locked + /// + /// # Returns + /// - `Ok(bool)`: Lock status (true = locked) + /// - `Err(Self::Error)`: Failed to check lock status + fn is_globally_locked(&self) -> Result { + Ok(self.is_otp_locked()) + } + + /// Enable global memory lock (typically irreversible) + /// + /// This operation permanently locks all OTP regions and usually cannot be undone. + /// Use with extreme caution. + /// + /// # Returns + /// - `Ok(())`: Global lock enabled successfully + /// - `Err(Self::Error)`: Failed to enable global lock + fn enable_global_lock(&mut self) -> Result<(), Self::Error> { + self.otp_lock_mem() + } +} + +impl OtpIdentification for OtpController { + /// Chip version or identifier type + type ChipVersion = AspeedChipVersion; + + /// Get the chip version or hardware identifier + /// + /// # Returns + /// - `Ok(ChipVersion)`: Hardware version information + /// - `Err(Self::Error)`: Failed to read chip identification + fn get_chip_version(&self) -> Result { + Ok(self.chip_version()) + } + + /// Check if a specific feature is supported by this chip version + /// + /// # Parameters + /// - `feature`: Feature identifier to check + /// + /// # Returns + /// - `Ok(bool)`: Feature support status (true = supported) + /// - `Err(Self::Error)`: Failed to check feature support + fn is_feature_supported(&self, feature: &str) -> Result { + self.is_feature_supported(feature) + } +} + +impl OtpMemoryLayout for OtpController { + /// Region identifier type + type Region = AspeedOtpRegion; + + /// Get the total memory capacity in bytes + fn total_capacity(&self) -> usize { + self.total_capacity() + } + + /// Get the minimum alignment requirement for write operations + /// + /// # Returns + /// Alignment requirement in bytes (e.g., 1, 4, 8) + fn write_alignment(&self) -> usize { + 4 + } + + /// Get the size of the minimum programmable unit + /// + /// # Returns + /// Size in bytes of the smallest unit that can be programmed independently + fn programming_granularity(&self) -> usize { + 4 + } + + /// List all available memory regions + /// + /// Returns an iterator over available regions. The exact collection type + /// depends on the implementation (could be array, slice, or heap-allocated). + /// + /// # Returns + /// - `Ok(regions)`: Iterator over available regions + /// - `Err(Self::Error)`: Failed to enumerate regions + fn list_regions(&self) -> Result<&[Self::Region], Self::Error> { + self.list_regions() + } + + /// Get detailed information about a specific region + /// + /// # Parameters + /// - `region`: The region to query + /// + /// # Returns + /// - `Ok((start_addr, size, alignment))`: Region details + /// - `Err(Self::Error)`: Failed to get region information + fn get_region_info(&self, region: Self::Region) -> Result<(usize, usize, usize), Self::Error> { + self.get_region_info(region) + } +} diff --git a/src/tests/functional/mod.rs b/src/tests/functional/mod.rs index f37c0d5..370b23f 100644 --- a/src/tests/functional/mod.rs +++ b/src/tests/functional/mod.rs @@ -5,6 +5,7 @@ pub mod gpio_test; pub mod hash_test; pub mod hmac_test; pub mod i2c_test; +pub mod otp_test; pub mod rsa_test; pub mod rsa_test_vec; pub mod timer_test; diff --git a/src/tests/functional/otp_test.rs b/src/tests/functional/otp_test.rs new file mode 100644 index 0000000..a36ce27 --- /dev/null +++ b/src/tests/functional/otp_test.rs @@ -0,0 +1,550 @@ +// Licensed under the Apache-2.0 license + +use crate::common::{DummyDelay, Logger, UartLogger}; +use crate::otp::common::{AspeedOtpRegion, StrapStatus}; +use crate::otp::{ + OtpController, OTP_CONF_OFFSET, OTP_CONF_PROT_ENBLE, OTP_KEY_PROT_ENBLE, OTP_MEM_LOCK_ENBLE, + OTP_SECURE_SIZE_BIT_POS, OTP_STRAP_PROT_ENBLE, OTP_USER_ECC_PROT_ENBLE, +}; +use crate::uart::{self, Config, UartController}; +use ast1060_pac::Peripherals; +use embedded_io::Write; +use proposed_traits::otp::OtpRegions; + +/// OTPCFG in OTP memory +/// OTPCFG0-31 +fn test_otp_read_conf( + uart: &mut UartController<'_>, + otp: &mut OtpController, + conf_reg: u32, +) { + writeln!(uart, "########## test read OTPCFG ######\r").unwrap(); + + let mut data: [u32; 32] = [0; 32]; + match otp.read_region(AspeedOtpRegion::Configuration, 0, &mut data) { + Ok(()) => { + for (i, each) in data.iter().enumerate() { + writeln!( + uart, + "read OTPCFG{:#x} ok: {:#x}\r", + conf_reg + u32::try_from(i).unwrap(), + each + ) + .unwrap(); + } + } + Err(e) => { + writeln!(uart, "read OTPCFG{conf_reg:#x} err: {e:?}\r").unwrap(); + } + } +} +#[allow(clippy::too_many_lines)] +fn test_otp_write_conf_b( + uart: &mut UartController<'_>, + otp: &mut OtpController, + otp_addr: u32, + otp_bit_offset: u32, + otp_value: u32, +) { + let mut conf0: u32 = 0; + read_otp_conf(uart, otp, 0, &mut conf0); + if conf0 & OTP_MEM_LOCK_ENBLE != 0 { + writeln!(uart, "OTP memory is locked!\r").unwrap(); + return; + } + if otp_addr != 4 && otp_addr != 10 && otp_addr != 11 && otp_addr < 16 { + if conf0 & OTP_CONF_PROT_ENBLE != 0 { + writeln!(uart, "OTP config region is protected!\r").unwrap(); + return; + } + } else if otp_addr == 10 || otp_addr == 11 { + let mut otp_rid: [u32; 2] = [0, 0]; + let mut sw_rid: [u32; 2] = [0, 0]; + read_otp_conf(uart, otp, 10, &mut otp_rid[0]); + read_otp_conf(uart, otp, 11, &mut otp_rid[1]); + otp.get_sw_revision(&mut sw_rid); + + if otp_addr == 10 { + otp_rid[0] |= 1 << otp_bit_offset; + } else { + otp_rid[1] |= 1 << otp_bit_offset; + } + + if otp_rid[1] > sw_rid[1] { + writeln!( + uart, + "update revision id should not be bigger than current SW revision id!\r" + ) + .unwrap(); + return; + } + if otp_rid[1] == sw_rid[1] || otp_rid[0] > sw_rid[0] { + writeln!( + uart, + "update revision id should not be bigger than current SW revision id!\r" + ) + .unwrap(); + return; + } + } else if otp_addr == 4 { + if conf0 & OTP_KEY_PROT_ENBLE != 0 { + writeln!(uart, "OTPCFG4 is protected!\r").unwrap(); + return; + } + if (otp_bit_offset <= 7) || (16..=23).contains(&otp_bit_offset) { + let key_num = otp.get_key_count(); + let retire: u32 = if otp_bit_offset >= 16 { + otp_bit_offset - 16 + } else { + otp_bit_offset + }; + if retire >= u32::from(key_num) { + writeln!( + uart, + "retire key is equal or greater than current boot key!\r" + ) + .unwrap(); + return; + } + } + } else if (16..=31).contains(&otp_addr) { + if conf0 & OTP_STRAP_PROT_ENBLE != 0 { + writeln!(uart, "OTP strap region is protected!\r").unwrap(); + return; + } + if otp_addr < 28 { + let mut otp_strap_pro: u32 = 0; + if otp_addr % 2 == 0 { + read_otp_conf(uart, otp, 30, &mut otp_strap_pro); + } else { + read_otp_conf(uart, otp, 31, &mut otp_strap_pro); + } + if otp_strap_pro >> otp_bit_offset & 1 != 0 { + writeln!( + uart, + "OTPCFG{otp_addr:#x}[{otp_bit_offset:#x}] is protected!\r", + ) + .unwrap(); + } + } + } + let mut conf: u32 = 0; + read_otp_conf(uart, otp, otp_addr, &mut conf); + if (conf >> otp_bit_offset & 0x1) == otp_value { + writeln!( + uart, + "OTPCFG{otp_addr:#x}[{otp_bit_offset:#x}] = 1; no need to program\r", + ) + .unwrap(); + return; + } + if (conf >> otp_bit_offset & 0x1) == 1 && otp_value == 0 { + writeln!( + uart, + "OTPCFG{otp_addr:#x}[{otp_bit_offset:#x}] = 1; cannot be cleared\r", + ) + .unwrap(); + return; + } + let mut prog_addr: u32 = OTP_CONF_OFFSET; + prog_addr |= (otp_addr / 8) * 0x200; + prog_addr |= (otp_addr % 8) * 0x2; + match otp.otp_prog_dc_b(otp_value, prog_addr, otp_bit_offset) { + Ok(()) => { + writeln!( + uart, + "program OTPCFG{otp_addr:#x}[{otp_bit_offset:#x}] successfully!\r", + ) + .unwrap(); + } + Err(e) => { + writeln!(uart, "program OTPCFG{otp_addr:#x} err: {e:?}\r").unwrap(); + } + } +} + +fn test_otp_write_conf_d( + uart: &mut UartController<'_>, + otp: &mut OtpController, + offset: u8, +) { + let data: [u32; 2] = [0xabcd_beef, 0x1234_5678]; + + match otp.write_region(AspeedOtpRegion::Configuration, offset as usize, &data) { + Ok(()) => { + writeln!(uart, "write OTPCONF{offset:#x} success\r").unwrap(); + } + Err(e) => { + writeln!(uart, "write OTPCONF{offset:#x} err: {e:?}\r").unwrap(); + } + } +} + +fn read_otp_conf( + uart: &mut UartController<'_>, + otp: &mut OtpController, + idx: u32, + conf: &mut u32, +) { + let mut data = [0; 1]; + match otp.read_region(AspeedOtpRegion::Configuration, idx as usize, &mut data) { + Ok(()) => { + *conf = data[0]; + } + Err(e) => { + writeln!(uart, "read OTPCFG0x0 err: {e:?}\r").unwrap(); + } + } +} + +fn read_otp_data( + uart: &mut UartController<'_>, + otp: &mut OtpController, + offset: u32, + data: &mut [u32], +) { + if let Err(e) = otp.read_region(AspeedOtpRegion::Data, 0, data) { + writeln!(uart, "read OTPDATA{offset:#x} err: {e:?}\r").unwrap(); + } +} + +fn test_otp_write_strap_b( + uart: &mut UartController<'_>, + otp: &mut OtpController, + bit_offset: u8, + value: u8, +) { + writeln!(uart, "########## test write OTPSTRAP ######\r").unwrap(); + if bit_offset >= 64 || (value != 0 && value != 1) { + writeln!(uart, "invalid offset or program value\r").unwrap(); + return; + } + + let mut conf0: u32 = 0; + read_otp_conf(uart, otp, 0, &mut conf0); + if conf0 & OTP_MEM_LOCK_ENBLE != 0 { + writeln!(uart, "OTP memory is locked!\r").unwrap(); + return; + } + + if conf0 & OTP_STRAP_PROT_ENBLE != 0 { + writeln!(uart, "OTP strap region is protected!\r").unwrap(); + return; + } + + let mut strap_status: [StrapStatus; 64] = [StrapStatus { + value: false, + protected: false, + options: [0; 7], + remaining_writes: 6, + writable_option: 0xff, + }; 64]; + if let Err(e) = otp.otp_strap_status(&mut strap_status) { + writeln!(uart, "otp_strap_status error: {e:?}\r").unwrap(); + return; + } + //test_otp_read_strap(uart, otp, u32::from(bit_offset), 1); + if value == u8::from(strap_status[bit_offset as usize].value) { + writeln!(uart, "The value is same as before.\r").unwrap(); + return; + } + if strap_status[bit_offset as usize].protected { + writeln!(uart, "This bit is protected and is not writable.\r").unwrap(); + return; + } + if strap_status[bit_offset as usize].remaining_writes == 0 { + writeln!(uart, "This bit has no remaining chance to write.\r").unwrap(); + return; + } + writeln!( + uart, + "Write 1 to OTPSTRAP[{:#x}] OPTION[{:#x}], that value changes from {:#x} to {:#x} \r", + bit_offset, + strap_status[bit_offset as usize].writable_option + 1, + u8::from(strap_status[bit_offset as usize].value), + u8::from(strap_status[bit_offset as usize].value) ^ 1 + ) + .unwrap(); + + let mut prog_addr: u32 = OTP_CONF_OFFSET; + let offset: u32; + if bit_offset < 32 { + offset = u32::from(bit_offset); + prog_addr |= + ((u32::from(strap_status[bit_offset as usize].writable_option) * 2 + 16) / 8) * 0x200; + prog_addr |= + ((u32::from(strap_status[bit_offset as usize].writable_option) * 2 + 16) % 8) * 0x2; + } else { + offset = u32::from(bit_offset) - 32; + prog_addr |= + ((u32::from(strap_status[bit_offset as usize].writable_option) * 2 + 17) / 8) * 0x200; + prog_addr |= + ((u32::from(strap_status[bit_offset as usize].writable_option) * 2 + 17) % 8) * 0x2; + } + match otp.otp_prog_dc_b(1, prog_addr, offset) { + Ok(()) => { + writeln!(uart, "program OTPSTRAP[{offset:#x}] successfully!\r").unwrap(); + } + Err(e) => { + writeln!(uart, "program OTPSTRAP err: {e:?}\r").unwrap(); + } + } +} + +fn test_otp_write_strap_d( + uart: &mut UartController<'_>, + otp: &mut OtpController, + start_bit: usize, + strap: &[u32], +) { + writeln!(uart, "OTPSTRAP start_bit {start_bit:}, strap = {strap:?}\r",).unwrap(); + match otp.write_region(AspeedOtpRegion::Strap, start_bit, strap) { + Ok(()) => { + writeln!(uart, "program OTPSTRAP dword success\r").unwrap(); + } + Err(e) => { + writeln!(uart, "program OTPSTRAP dword err: {e:?}\r").unwrap(); + } + } +} + +fn test_otp_read_strap( + uart: &mut UartController<'_>, + otp: &mut OtpController, + start: u32, + count: u32, +) { + writeln!(uart, "########## test read OTPSTRAP ######\r").unwrap(); + let remains: u32 = 6; + let mut strap_status: [StrapStatus; 64] = [StrapStatus { + value: false, + protected: false, + options: [0; 7], + remaining_writes: 6, + writable_option: 0xff, + }; 64]; + writeln!(uart, "BIT(hex) Value Option Status\r").unwrap(); + writeln!(uart, "------------------------------------------\r").unwrap(); + if otp.otp_strap_status(&mut strap_status).is_ok() { + for i in start..start + count { + write!( + uart, + "0x{:<8x} {:<7} ", + i, + u8::from(strap_status[i as usize].value) + ) + .unwrap(); + for j in 0..remains { + write!(uart, "{} ", strap_status[i as usize].options[j as usize]).unwrap(); + } + write!(uart, " ").unwrap(); + if strap_status[i as usize].protected { + writeln!(uart, "protected and not writable").unwrap(); + } else { + write!(uart, "not protected ").unwrap(); + if strap_status[i as usize].remaining_writes == 0 { + writeln!(uart, "and no remaining times to write.\r").unwrap(); + } else { + writeln!( + uart, + "and still can write {} times.\r", + strap_status[i as usize].remaining_writes + ) + .unwrap(); + } + } + } + } else { + writeln!(uart, "read otp strap fail!\r").unwrap(); + } + + // trait API + let mut buffer: [u32; 2] = [0, 0]; + match otp.read_region(AspeedOtpRegion::Strap, 0, &mut buffer) { + Ok(()) => { + writeln!(uart, "read OTPSTRAP {buffer:?}\r").unwrap(); + } + Err(e) => { + writeln!(uart, "read OTPSTRAP err: {e:?}\r").unwrap(); + } + } +} + +fn test_otp_write_data_b( + uart: &mut UartController<'_>, + otp: &mut OtpController, + otp_addr: u32, + bit_offset: u32, + value: u32, +) { + let mut conf0: u32 = 0; + read_otp_conf(uart, otp, 0, &mut conf0); + if conf0 & OTP_MEM_LOCK_ENBLE != 0 { + writeln!(uart, "OTP memory is locked!\r").unwrap(); + return; + } + let sec_area_size = (conf0 >> OTP_SECURE_SIZE_BIT_POS) & 0x3f; + if sec_area_size == 0 { + if conf0 & OTP_USER_ECC_PROT_ENBLE != 0 { + writeln!(uart, "OTP data region is protected!\r").unwrap(); + return; + } + } else if otp_addr < sec_area_size && otp_addr > 16 { + writeln!( + uart, + "OTP secure region is not readable, skip it to prevent unpredictable result!\r" + ) + .unwrap(); + return; + } else if otp_addr < sec_area_size { + writeln!(uart, "OTP secure region is protected!\r").unwrap(); + return; + } else if conf0 & OTP_USER_ECC_PROT_ENBLE != 0 { + writeln!(uart, "OTP data region is protected!\r").unwrap(); + return; + } + + let mut data: [u32; 2] = [0; 2]; + let otp_bit: u32; + if otp_addr % 2 == 0 { + read_otp_data(uart, otp, otp_addr, &mut data); + otp_bit = (data[0] >> bit_offset) & 0x1; + if otp_bit == 1 && value == 0 { + writeln!(uart, "OTPDATA{otp_addr:#x}[{bit_offset:#x}] = 1\r").unwrap(); + writeln!(uart, "OTP is programmed, which can't be cleared!\r").unwrap(); + return; + } + } else { + read_otp_data(uart, otp, otp_addr - 1, &mut data); + otp_bit = (data[1] >> bit_offset) & 0x1; + if otp_bit == 0 && value == 1 { + writeln!(uart, "OTPDATA{otp_addr:#x}[{bit_offset:#x}] = 1\r").unwrap(); + writeln!(uart, "OTP is programmed, which can't be written!\r").unwrap(); + return; + } + } + + if otp_bit == value { + writeln!(uart, "OTPDATA{otp_addr:#x}[{bit_offset:#x}] = {value}\r",).unwrap(); + writeln!(uart, "No need to program!\r").unwrap(); + return; + } + + writeln!( + uart, + "Program OTPDATA{otp_addr:#x}[{bit_offset:#x}] to {value}\r", + ) + .unwrap(); + + match otp.otp_prog_dc_b(value, otp_addr, bit_offset) { + Ok(()) => { + writeln!( + uart, + "program OTPDATA{otp_addr:#x}[{bit_offset:#x}] successfully!\r", + ) + .unwrap(); + } + Err(e) => { + writeln!( + uart, + "program OTPDATA{otp_addr:#x}[{bit_offset:#x}] err: {e:?}\r", + ) + .unwrap(); + } + } +} + +fn test_otp_write_data_d( + uart: &mut UartController<'_>, + otp: &mut OtpController, + otp_addr: u32, + data: &[u32], +) { + match otp.write_region(AspeedOtpRegion::Data, otp_addr as usize, data) { + Ok(()) => { + writeln!(uart, "write OTPDATA{otp_addr:#x} success\r").unwrap(); + } + Err(e) => { + writeln!(uart, "write OTPDATA{otp_addr:#x} err: {e:?}\r").unwrap(); + } + } +} + +fn test_otp_read_data( + uart: &mut UartController<'_>, + otp: &mut OtpController, + conf_reg: u32, +) { + writeln!(uart, "########## test read OTPDATA ######\r").unwrap(); + + let mut data: [u32; 32] = [0; 32]; + match otp.read_region(AspeedOtpRegion::Data, 0, &mut data) { + Ok(()) => { + for (i, each) in data.iter().enumerate() { + writeln!( + uart, + "read OTPDATA{:#x} ok: {:#x}\r", + conf_reg + u32::try_from(i).unwrap(), + each + ) + .unwrap(); + } + } + Err(e) => { + writeln!(uart, "read OTPDATA{conf_reg:#x} err: {e:?}\r").unwrap(); + } + } +} + +pub fn test_otp(uart: &mut UartController<'_>) { + let peripherals = unsafe { Peripherals::steal() }; + let scu = peripherals.scu; + let mut delay = DummyDelay {}; + let mut dbg_uart = UartController::new(peripherals.uart, &mut delay); + unsafe { + dbg_uart.init(&Config { + baud_rate: 115_200, + word_length: uart::WordLength::Eight as u8, + parity: uart::Parity::None, + stop_bits: uart::StopBits::One, + clock: 24_000_000, + }); + } + let mut otp_controller = OtpController::new(scu, UartLogger::new(&mut dbg_uart)); + //otp_controller.otp_unlock_reg(); + + //test otpcfg + if false { + test_otp_read_conf(uart, &mut otp_controller, 0); + test_otp_write_conf_b(uart, &mut otp_controller, 0, 5, 1); + //test_otp_write_conf_b(uart, &mut otp_controller, 0, 5, 1); + //test_otp_write_conf_b(uart, &mut otp_controller, 0, 5, 0); + test_otp_write_conf_d(uart, &mut otp_controller, 5); + test_otp_read_conf(uart, &mut otp_controller, 0); + } + + //test otpstrap + if false { + test_otp_read_strap(uart, &mut otp_controller, 0, 32); + for i in 1u8..8 { + test_otp_write_strap_b(uart, &mut otp_controller, 0, i & 0x1); + } + + let strap: [u32; 2] = [0xffff_ffff, 0xffff_ffff]; + test_otp_write_strap_d(uart, &mut otp_controller, 30, &strap); + + test_otp_read_strap(uart, &mut otp_controller, 0, 64); + } + + //test otpdata + if false { + test_otp_read_data(uart, &mut otp_controller, 0); + test_otp_write_data_b(uart, &mut otp_controller, 0, 0, 0); + + let data = [0xabcd_dead, 0x1234_5678]; + test_otp_write_data_d(uart, &mut otp_controller, 4, &data); + test_otp_read_data(uart, &mut otp_controller, 0); + } + + //otp_controller.otp_lock_reg(); +}