From e6ac881b323cd4c2bbd6815f6bebe963031527c4 Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 1 Aug 2025 17:19:00 +0800 Subject: [PATCH] swmbx: Introduce mailbox target driver Introduce software mailbox driver, which enables the BMC to communicate with the Root of Trust (RoT) over an I2C target interface. Execute the following command at the BMC prompt to verify SWMBX functionality: - Verify the SWMBX address: aspeed-pfr-tool -w 0x13 0x8 aspeed-pfr-tool -r 0x13 [Expected output: 0x8] - Verify the protected SWMBX address: aspeed-pfr-tool -w 0x12 0x8 aspeed-pfr-tool -r 0x12 [Expected output: 0x0] - Verify the notify functionality: aspeed-pfr-tool -w 0x13 0x8 On Ast1060 console, you should see [NOTIFY] SWMBX: notify triggered on port 0 addr 0x13 - Verify the FIFO functionality: aspeed-pfr-tool -w 0xd 0x1f aspeed-pfr-tool -w 0xd 0x2f aspeed-pfr-tool -w 0xd 0x3f aspeed-pfr-tool -r 0xd aspeed-pfr-tool -r 0xd aspeed-pfr-tool -r 0xd aspeed-pfr-tool -r 0xd [Expected output: 0x1f, 0x2f, 0x3f, 0x0] Signed-off-by: Steven Lee --- Cargo.lock | 20 +- Cargo.toml | 11 +- link.x | 2 +- src/i2c/ast1060_i2c.rs | 29 +- src/i2c/mod.rs | 2 + src/i2c/pfr/mod.rs | 4 + src/i2c/pfr/swmbx.rs | 468 +++++++++++++++++++++++++++++ src/i2c/target/mod.rs | 4 + src/i2c/target/swmbx_target.rs | 191 ++++++++++++ src/main.rs | 61 +++- src/tests/functional/i2c_test.rs | 102 +------ src/tests/functional/mod.rs | 2 + src/tests/functional/swmbx_test.rs | 200 ++++++++++++ 13 files changed, 990 insertions(+), 106 deletions(-) create mode 100644 src/i2c/pfr/mod.rs create mode 100644 src/i2c/pfr/swmbx.rs create mode 100644 src/i2c/target/mod.rs create mode 100644 src/i2c/target/swmbx_target.rs create mode 100644 src/tests/functional/swmbx_test.rs diff --git a/Cargo.lock b/Cargo.lock index 1623fe3..6283775 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,11 +63,14 @@ name = "aspeed-ddk" version = "0.1.0" dependencies = [ "ast1060-pac", + "base64ct", "cortex-m", "cortex-m-rt", + "cortex-m-semihosting", "embedded-hal 1.0.0", "embedded-hal 1.0.0-alpha.1", "embedded-io", + "embedded-storage", "fugit", "heapless", "hex-literal", @@ -106,6 +109,12 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "base64ct" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" + [[package]] name = "bitfield" version = "0.13.2" @@ -197,6 +206,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cortex-m-semihosting" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c23234600452033cc77e4b761e740e02d2c4168e11dbf36ab14a0f58973592b0" +dependencies = [ + "cortex-m", +] + [[package]] name = "embedded-hal" version = "0.2.7" @@ -364,7 +382,7 @@ dependencies = [ [[package]] name = "proposed-traits" version = "0.1.0" -source = "git+https://github.com/rusty1968/proposed_traits.git?rev=85641310df5a5276c67f81621b104322cff0286c#85641310df5a5276c67f81621b104322cff0286c" +source = "git+https://github.com/stevenlee7189/proposed_traits.git?rev=496dba90a91aab8e4ae88bbaaa0e162df84ebe74#496dba90a91aab8e4ae88bbaaa0e162df84ebe74" dependencies = [ "async-trait", "embedded-hal 1.0.0", diff --git a/Cargo.toml b/Cargo.toml index 0e7d403..09b7e14 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ resolver = "2" [workspace.package] edition = "2021" rust-version = "1.83.0" - [package] name = "aspeed-ddk" version = "0.1.0" @@ -32,12 +31,20 @@ embedded-hal = { version = "1.0.0" } embedded-hal-old = { git = "https://github.com/rust-embedded/embedded-hal.git", rev = "599d44fdc7e709cb9ae6580ec11c0b7f7f102", package = "embedded-hal" } embedded-io = "0.6.1" fugit = "0.3.7" -proposed-traits = { git = "https://github.com/rusty1968/proposed_traits.git", package = "proposed-traits", rev = "85641310df5a5276c67f81621b104322cff0286c" } +base64ct = "<1.8.0" +proposed-traits = { git = "https://github.com/stevenlee7189/proposed_traits.git", package = "proposed-traits", rev = "496dba90a91aab8e4ae88bbaaa0e162df84ebe74" } +embedded-storage = "0.3" hex-literal = "0.4" heapless = "0.8.0" paste = "1.0" cortex-m = { version = "0.7.5" } cortex-m-rt = { version = "0.6.5", features = ["device"] } +cortex-m-semihosting = "0.5" panic-halt = "1.0.0" +[profile.release] +codegen-units = 1 # better optimizations +debug = true # symbols are nice and they don't increase the size on Flash +lto = true # better optimizations + diff --git a/link.x b/link.x index 7f82b12..2832570 100644 --- a/link.x +++ b/link.x @@ -54,7 +54,7 @@ EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */ /* # Pre-initialization function */ /* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function, then the function this points to will be called before the RAM is initialized. */ -PROVIDE(__pre_init = DefaultPreInit); +/* PROVIDE(__pre_init = DefaultPreInit); */ /* # Sections */ SECTIONS diff --git a/src/i2c/ast1060_i2c.rs b/src/i2c/ast1060_i2c.rs index e007a8e..d5c2675 100644 --- a/src/i2c/ast1060_i2c.rs +++ b/src/i2c/ast1060_i2c.rs @@ -14,6 +14,8 @@ use core::sync::atomic::{AtomicBool, Ordering}; use embedded_hal::delay::DelayNs; use embedded_hal::i2c::{NoAcknowledgeSource, Operation, SevenBitAddress}; use proposed_traits::i2c_target::I2CTarget; +#[cfg(feature = "i2c_target")] +use proposed_traits::i2c_target::TransactionDirection; static I2CGLOBAL_INIT: AtomicBool = AtomicBool::new(false); @@ -1348,7 +1350,14 @@ impl<'a, I2C: Instance, I2CT: I2CTarget, L: Logger> Ast1060I2c<'a, I2C, I2CT, L> if event == I2cSEvent::SlaveRdReq { i2c_debug!(self.logger, "read_requested"); if let Some(target) = self.i2c_data.slave_target.as_mut() { - target.on_transaction_start(false); + if let Ok(Some(val)) = + target.on_transaction_start(TransactionDirection::Read, false) + { + if let Some(slice) = self.sdma_buf.as_mut_slice(0, 1).get_mut(0) { + *slice = val; + i2c_debug!(self.logger, "read_requested val {:#x}", val); + } + } } } else if event == I2cSEvent::SlaveRdProc { i2c_debug!(self.logger, "read_processed"); @@ -1390,7 +1399,7 @@ impl<'a, I2C: Instance, I2CT: I2CTarget, L: Logger> Ast1060I2c<'a, I2C, I2CT, L> //if slave is ready to receive i2c_debug!(self.logger, "write_requested"); if let Some(target) = self.i2c_data.slave_target.as_mut() { - target.on_transaction_start(false); + let _ = target.on_transaction_start(TransactionDirection::Write, false); } } else if event == I2cSEvent::SlaveWrRecvd { //Another I2C master has sent a byte to us which needs to be set in ‘val’ @@ -1440,7 +1449,7 @@ impl<'a, I2C: Instance, I2CT: I2CTarget, L: Logger> Ast1060I2c<'a, I2C, I2CT, L> if event == I2cSEvent::SlaveWrReq { i2c_debug!(self.logger, "byte write_requested"); if let Some(target) = self.i2c_data.slave_target.as_mut() { - target.on_transaction_start(false); + let _ = target.on_transaction_start(TransactionDirection::Write, false); } } else if event == I2cSEvent::SlaveWrRecvd { i2c_debug!(self.logger, "byte write_received"); @@ -1454,7 +1463,18 @@ impl<'a, I2C: Instance, I2CT: I2CTarget, L: Logger> Ast1060I2c<'a, I2C, I2CT, L> if event == I2cSEvent::SlaveRdReq { i2c_debug!(self.logger, "byte read_requested"); if let Some(target) = self.i2c_data.slave_target.as_mut() { - target.on_transaction_start(false); + match target.on_transaction_start(TransactionDirection::Read, false) { + Ok(Some(v)) => { + *val = v; + } + Ok(None) => { + *val = 0; + } + Err(e) => { + i2c_debug!(self.logger, "Failed on read_requested: {:?}", e); + *val = 0; + } + } } } else if event == I2cSEvent::SlaveRdProc { i2c_debug!(self.logger, "byte read_processed"); @@ -1660,6 +1680,7 @@ impl<'a, I2C: Instance, I2CT: I2CTarget, L: Logger> Ast1060I2c<'a, I2C, I2CT, L> cmd = SLAVE_TRIGGER_CMD; match self.xfer_mode { I2cXferMode::DmaMode => { + self.i2c.i2cs4c().write(|w| unsafe { w.bits(0) }); self.i2c.i2cs2c().modify(|_, w| unsafe { w.dmarx_buf_len_byte() .bits(u16::try_from(I2C_SLAVE_BUF_SIZE - 1).unwrap()) diff --git a/src/i2c/mod.rs b/src/i2c/mod.rs index eadc1f0..a5b9807 100644 --- a/src/i2c/mod.rs +++ b/src/i2c/mod.rs @@ -3,3 +3,5 @@ pub mod ast1060_i2c; pub mod common; pub mod i2c_controller; +pub mod pfr; +pub mod target; diff --git a/src/i2c/pfr/mod.rs b/src/i2c/pfr/mod.rs new file mode 100644 index 0000000..afd4aac --- /dev/null +++ b/src/i2c/pfr/mod.rs @@ -0,0 +1,4 @@ +// Licensed under the Apache-2.0 license + +#[cfg(feature = "i2c_target")] +pub mod swmbx; diff --git a/src/i2c/pfr/swmbx.rs b/src/i2c/pfr/swmbx.rs new file mode 100644 index 0000000..b2b3c3a --- /dev/null +++ b/src/i2c/pfr/swmbx.rs @@ -0,0 +1,468 @@ +// Licensed under the Apache-2.0 license + +use crate::uart::UartController; +use core::cell::Cell; +use core::mem::MaybeUninit; +use core::ptr::{read_volatile, write_volatile}; +use core::sync::atomic::{AtomicBool, Ordering}; +use embedded_io::Write; +use heapless::spsc::Queue; + +// Constants +pub const SWMBX_DEV_COUNT: usize = 2; +pub const SWMBX_NODE_COUNT: usize = 256; +pub const SWMBX_FIFO_COUNT: usize = 4; +pub const SWMBX_BUF_BASE: usize = 0x7e7b_0e00; +pub const SWMBX_BUF_SIZE: usize = 256; +pub const SWMBX_FIFO_DEPTH: usize = 256; +pub const SWMBX_INFO_BASE: usize = 0x7e7b_0f00; + +// Behavior flags +pub const SWMBX_PROTECT: u8 = 1 << 0; +pub const SWMBX_NOTIFY: u8 = 1 << 1; +pub const SWMBX_FIFO: u8 = 1 << 2; +pub const FLAG_MASK: u8 = SWMBX_PROTECT | SWMBX_NOTIFY | SWMBX_FIFO; + +// FIFO notify flags +pub const SWMBX_FIFO_NOTIFY_START: u8 = 1 << 0; +pub const SWMBX_FIFO_NOTIFY_STOP: u8 = 1 << 1; +pub const FIFO_NOTIFY_MASK: u8 = SWMBX_FIFO_NOTIFY_START | SWMBX_FIFO_NOTIFY_STOP; + +pub static mut SWMBX_CTRL: MaybeUninit = MaybeUninit::uninit(); +static INIT_DONE: AtomicBool = AtomicBool::new(false); + +extern "Rust" { + static mut UART_PTR: Option<&'static mut UartController<'static>>; +} + +#[macro_export] +macro_rules! swmbx_log { + ($($arg:tt)*) => {{ + use core::fmt::Write; + if let Some(uart) = unsafe { UART_PTR.as_mut() } { + let mut buf: heapless::String<64> = heapless::String::new(); + let _ = write!(buf, $($arg)*); + let _ = uart.write_all(b"[SWMBX] "); + let _ = uart.write_all(buf.as_bytes()); + let _ = uart.write_all(b"\r\n"); + } + }}; +} + +// --- FIFO Entry --- +#[derive(Copy, Clone, Debug)] +pub struct SwmbxFifoEntry { + pub value: u8, +} + +// --- Node entry per device+addr --- +#[derive(Copy, Clone, Debug, Default)] +pub struct SwmbxNode { + pub notify_flag: bool, + pub enabled_flags: u8, // uses SWMBX_PROTECT / SWMBX_NOTIFY bitmask +} + +// --- FIFO buffer per index --- +pub struct SwmbxFifo { + pub queue: Queue, + pub notify_flag: u8, // FIFO_NOTIFY_START/STOP + pub notify_start: bool, + pub fifo_write: bool, + pub fifo_offset: u8, + pub enabled: bool, + pub msg_index: usize, + pub max_msg_count: u8, +} + +impl SwmbxFifo { + pub const fn new() -> Self { + Self { + queue: Queue::new(), + notify_flag: 0, + notify_start: false, + fifo_write: false, + fifo_offset: 0, + enabled: false, + msg_index: 0, + max_msg_count: SWMBX_FIFO_DEPTH as u8, + } + } + + pub fn append_write(&mut self, value: u8) -> Result<(), ()> { + if self.queue.len() < self.max_msg_count as usize { + self.queue + .enqueue(SwmbxFifoEntry { value }) + .map_err(|_| ())?; + if self.msg_index == (self.max_msg_count - 1) as usize { + self.msg_index = 0; + } else { + self.msg_index = (self.msg_index + 1) % self.max_msg_count as usize; + } + swmbx_log!( + "append_write: value {:#x} added to FIFO, current index: {}", + value, + self.msg_index + ); + Ok(()) + } else { + swmbx_log!("append_write: FIFO full, cannot add value {:#x}", value); + Err(()) + } + } + + pub fn peek_read(&mut self) -> Result { + if let Some(entry) = self.queue.dequeue() { + swmbx_log!("peek_read: value {:#x} read from FIFO", entry.value,); + Ok(entry.value) + } else { + Err(()) + } + } + pub fn flush(&mut self) { + while self.queue.dequeue().is_some() {} + self.msg_index = 0; + } +} + +// --- Main SWMBX controller data --- +pub struct SwmbxCtrl { + pub mbx_en: Cell, + + pub node: [[SwmbxNode; SWMBX_NODE_COUNT]; SWMBX_DEV_COUNT], + pub fifo: [SwmbxFifo; SWMBX_FIFO_COUNT], + + pub mbx_fifo_execute: [bool; SWMBX_DEV_COUNT], + pub mbx_fifo_addr: [u8; SWMBX_DEV_COUNT], + pub mbx_fifo_idx: [u8; SWMBX_DEV_COUNT], + pub buffer_size: usize, +} + +impl SwmbxCtrl { + pub fn init(buffer_size: usize) -> &'static mut SwmbxCtrl { + if INIT_DONE.load(Ordering::SeqCst) { + return unsafe { SWMBX_CTRL.assume_init_mut() }; + } + + let ctrl = SwmbxCtrl { + mbx_en: Cell::new(0), + node: [[SwmbxNode::default(); SWMBX_NODE_COUNT]; SWMBX_DEV_COUNT], + fifo: [ + SwmbxFifo::new(), + SwmbxFifo::new(), + SwmbxFifo::new(), + SwmbxFifo::new(), + ], + mbx_fifo_execute: [false; SWMBX_DEV_COUNT], + mbx_fifo_addr: [0; SWMBX_DEV_COUNT], + mbx_fifo_idx: [0; SWMBX_DEV_COUNT], + buffer_size, + }; + + let ctrl_ref = unsafe { + SWMBX_CTRL.write(ctrl); + SWMBX_CTRL.assume_init_mut() + }; + + INIT_DONE.store(true, Ordering::SeqCst); + ctrl_ref + } + + pub fn store_ctrl_ptr(ctrl: &'static Self) { + unsafe { + let ptr = SWMBX_INFO_BASE as *mut *const SwmbxCtrl; + ptr.write_volatile(ctrl as *const _); + } + } + + pub fn load_ctrl_ptr() -> &'static Self { + unsafe { + let ptr = SWMBX_INFO_BASE as *const *const SwmbxCtrl; + &**ptr + } + } + + pub fn load_ctrl_ptr_mut() -> &'static mut Self { + unsafe { + let ptr = SWMBX_INFO_BASE as *const *mut SwmbxCtrl; + &mut **ptr + } + } + + pub fn update_notify(&mut self, port: usize, addr: u8, enable: bool) -> Result<(), ()> { + if port >= SWMBX_DEV_COUNT { + return Err(()); + } + + let node = &mut self.node[port][addr as usize]; + + if enable { + node.enabled_flags |= SWMBX_NOTIFY; + } else { + node.enabled_flags &= !SWMBX_NOTIFY; + } + + Ok(()) + } + pub fn update_fifo( + &mut self, + index: usize, + addr: u8, + depth: u8, + notify: u8, + enable: bool, + ) -> Result<(), ()> { + if index >= SWMBX_FIFO_COUNT { + return Err(()); + } + + let fifo = &mut self.fifo[index]; + fifo.enabled = enable; + + if enable { + fifo.fifo_offset = addr; + fifo.max_msg_count = depth; + fifo.notify_flag = notify; + fifo.msg_index = 0; + fifo.queue = Queue::new(); + } else { + fifo.queue = Queue::new(); + } + + Ok(()) + } + + pub fn flush_fifo(&mut self, index: usize) -> Result<(), ()> { + if index >= SWMBX_FIFO_COUNT { + return Err(()); + } + + self.fifo[index].flush(); + Ok(()) + } + pub fn enable_behavior(&self, flag: u8, enable: bool) -> Result<(), ()> { + if (flag & FLAG_MASK) == 0 { + return Err(()); + } + + let old = self.mbx_en.get(); + if enable { + self.mbx_en.set(old | flag); + } else { + self.mbx_en.set(old & !flag); + } + swmbx_log!("enable_behavior: {:#x} -> {:#x}", old, self.mbx_en.get()); + + Ok(()) + } + pub fn update_protect(&mut self, port: usize, addr: u8, enable: bool) -> Result<(), ()> { + if port >= SWMBX_DEV_COUNT { + return Err(()); + } + + let node = &mut self.node[port][addr as usize]; + if enable { + node.enabled_flags |= SWMBX_PROTECT; + } else { + node.enabled_flags &= !SWMBX_PROTECT; + } + + Ok(()) + } + + pub fn apply_protect( + &mut self, + port: usize, + bitmap: &[u32], + start_idx: usize, + ) -> Result<(), ()> { + if port >= SWMBX_DEV_COUNT || start_idx + bitmap.len() > 8 { + return Err(()); + } + + for (i, &val) in bitmap.iter().enumerate() { + let base = (start_idx + i) << 5; + for bit in 0..32 { + let addr = base + bit; + if addr >= SWMBX_NODE_COUNT { + break; + } + let node = &mut self.node[port][addr]; + if (val >> bit) & 1 != 0 { + node.enabled_flags |= SWMBX_PROTECT; + } else { + node.enabled_flags &= !SWMBX_PROTECT; + } + } + } + Ok(()) + } + + pub fn get_msg(&mut self, port: usize, addr: u8) -> u8 { + if (port as usize) >= SWMBX_DEV_COUNT { + swmbx_log!("get_msg invalid port {}", port); + return 0; + } + + if self.mbx_fifo_execute[port] && (self.mbx_en.get() & SWMBX_FIFO) != 0 { + let fifo_index = self.mbx_fifo_idx[port as usize] as usize; + + match self.fifo[fifo_index].peek_read() { + Ok(val) => { + return val; + } + Err(_) => { + // In C: give semaphore here — not implemented in Rust + swmbx_log!("FIFO empty at index {} (port {})", fifo_index, port); + return 0; + } + } + } + + let val = SwmbxBuffer::read(addr); + swmbx_log!("get_msg port: {}, addr: {:#x}, val: {:#x}", port, addr, val); + val + } + + pub fn send_msg(&mut self, port: usize, addr: u8, val: u8) { + if port >= SWMBX_DEV_COUNT { + return; + } + + swmbx_log!( + "send_msg port: {}, addr: {:#x}, val: {:#x}", + port, + addr, + val + ); + + let mut write_to_buffer = false; + + if self.mbx_fifo_execute[port] && (self.mbx_en.get() & SWMBX_FIFO) != 0 { + let fifo_addr = self.mbx_fifo_addr[port]; + let fifo_index = self.mbx_fifo_idx[port] as usize; + + if self.fifo[fifo_index].append_write(val).is_err() { + self.node[port][addr as usize].notify_flag = true; + return; + } + + if (self.mbx_en.get() & SWMBX_NOTIFY) != 0 + && (self.fifo[fifo_index].notify_flag & SWMBX_FIFO_NOTIFY_START) != 0 + && !self.fifo[fifo_index].notify_start + && (self.node[port][fifo_addr as usize].enabled_flags & SWMBX_NOTIFY) != 0 + { + self.node[port][fifo_addr as usize].notify_flag = true; + self.fifo[fifo_index].notify_start = true; + } + + if !self.fifo[fifo_index].fifo_write { + self.fifo[fifo_index].fifo_write = true; + } + } else { + let node = &mut self.node[port][addr as usize]; + + if (node.enabled_flags & SWMBX_PROTECT) == 0 || (self.mbx_en.get() & SWMBX_PROTECT) == 0 + { + write_to_buffer = true; + } + + if (self.mbx_en.get() & SWMBX_NOTIFY) != 0 && (node.enabled_flags & SWMBX_NOTIFY) != 0 { + node.notify_flag = true; + } + } + + if write_to_buffer { + SwmbxBuffer::write(addr, val); + } + } + pub fn send_start(&mut self, port: usize, addr: u8) { + if port >= SWMBX_DEV_COUNT { + return; + } + + swmbx_log!("send_start port: {}, addr: {:#x}", port, addr); + swmbx_log!("mbx_en: {:#x}", self.mbx_en.get()); + if let Some(fifo_index) = self.check_fifo(addr) { + self.mbx_fifo_execute[port] = true; + self.mbx_fifo_addr[port] = addr; + self.mbx_fifo_idx[port] = fifo_index as u8; + } else { + self.mbx_fifo_execute[port] = false; + } + } + + pub fn check_fifo(&self, addr: u8) -> Option { + self.fifo + .iter() + .position(|f| f.fifo_offset == addr && f.enabled) + } + + pub fn send_stop(&mut self, port: usize) { + if port >= SWMBX_DEV_COUNT { + return; + } + + swmbx_log!("send_stop port: {}", port); + if self.mbx_fifo_execute[port] { + let fifo_addr = self.mbx_fifo_addr[port]; + let fifo_index = self.mbx_fifo_idx[port] as usize; + + if (self.mbx_en.get() & SWMBX_NOTIFY) != 0 + && (self.fifo[fifo_index].notify_flag & SWMBX_FIFO_NOTIFY_STOP) != 0 + && self.fifo[fifo_index].fifo_write + { + if (self.node[port][fifo_addr as usize].enabled_flags & SWMBX_NOTIFY) != 0 { + self.node[port][fifo_addr as usize].notify_flag = true; + } + } + + self.fifo[fifo_index].notify_start = false; + self.fifo[fifo_index].fifo_write = false; + self.mbx_fifo_execute[port] = false; + self.mbx_fifo_addr[port] = 0; + self.mbx_fifo_idx[port] = 0; + } + } + pub fn swmbx_write(&mut self, fifo: bool, addr: u8, val: u8) -> Result<(), ()> { + if fifo { + if let Some(index) = self.check_fifo(addr) { + self.fifo[index].append_write(val)?; + Ok(()) + } else { + Err(()) + } + } else { + SwmbxBuffer::write(addr, val); + Ok(()) + } + } + + pub fn swmbx_read(&mut self, fifo: bool, addr: u8) -> Result { + if fifo { + if let Some(index) = self.check_fifo(addr) { + self.fifo[index].peek_read() + } else { + Err(()) + } + } else { + Ok(SwmbxBuffer::read(addr)) + } + } +} + +// --- SWMBX buffer abstraction --- +pub struct SwmbxBuffer; + +impl SwmbxBuffer { + #[inline] + pub fn read(addr: u8) -> u8 { + assert!((addr as usize) < SWMBX_BUF_SIZE); + unsafe { read_volatile((SWMBX_BUF_BASE + addr as usize) as *const u8) } + } + + #[inline] + pub fn write(addr: u8, val: u8) { + assert!((addr as usize) < SWMBX_BUF_SIZE); + unsafe { write_volatile((SWMBX_BUF_BASE + addr as usize) as *mut u8, val) }; + } +} diff --git a/src/i2c/target/mod.rs b/src/i2c/target/mod.rs new file mode 100644 index 0000000..4c71e08 --- /dev/null +++ b/src/i2c/target/mod.rs @@ -0,0 +1,4 @@ +// Licensed under the Apache-2.0 license + +#[cfg(feature = "i2c_target")] +pub mod swmbx_target; diff --git a/src/i2c/target/swmbx_target.rs b/src/i2c/target/swmbx_target.rs new file mode 100644 index 0000000..630c288 --- /dev/null +++ b/src/i2c/target/swmbx_target.rs @@ -0,0 +1,191 @@ +// Licensed under the Apache-2.0 license + +use crate::common::{NoOpLogger, UartLogger}; +use crate::i2c::ast1060_i2c::Ast1060I2c; +use crate::i2c::common::{I2cConfigBuilder, I2cSpeed, I2cXferMode}; +use crate::i2c::i2c_controller::{HardwareInterface, I2cController}; +use crate::i2c::pfr::swmbx::SwmbxCtrl; +use crate::pinctrl; +use crate::uart::UartController; +use embedded_hal::i2c::ErrorKind; +use embedded_io::Write; +use proposed_traits::i2c_target::{ + I2CCoreTarget, ReadTarget, RegisterAccess, TransactionDirection, WriteReadTarget, WriteTarget, +}; + +extern "Rust" { + static mut UART_PTR: Option<&'static mut UartController<'static>>; +} + +#[macro_export] +macro_rules! swmbx_target_log { + ($($arg:tt)*) => {{ + use core::fmt::Write; + if let Some(uart) = unsafe { UART_PTR.as_mut() } { + let mut buf: heapless::String<64> = heapless::String::new(); + let _ = write!(buf, $($arg)*); + let _ = uart.write_all(b"[SWMBX_TARGET] "); + let _ = uart.write_all(buf.as_bytes()); + let _ = uart.write_all(b"\r\n"); + } + }}; +} + +#[derive(Debug)] +pub enum SwmbxI2CError { + OtherError, +} + +impl embedded_hal::i2c::Error for SwmbxI2CError { + fn kind(&self) -> ErrorKind { + ErrorKind::Other + } +} + +impl embedded_hal::i2c::ErrorType for SwmbxI2CTarget { + type Error = SwmbxI2CError; +} + +pub struct SwmbxI2CTarget { + address: u8, + read_idx: usize, + buffer_idx: u8, + first_write: bool, + port: usize, +} + +impl I2CCoreTarget for SwmbxI2CTarget { + fn init(&mut self, address: u8) -> Result<(), Self::Error> { + if address == 0 { + return Err(SwmbxI2CError::OtherError); + } + self.address = address; + Ok(()) + } + // read_requested or write_requested + fn on_transaction_start( + &mut self, + direction: TransactionDirection, + _repeated: bool, + ) -> Result, Self::Error> { + self.read_idx = 0; + + match direction { + TransactionDirection::Write => { + swmbx_target_log!("Write requested on port {}", self.port); + self.first_write = true; + Ok(None) + } + TransactionDirection::Read => { + let ctrl = SwmbxCtrl::load_ctrl_ptr_mut(); + let val = ctrl.get_msg(self.port, self.buffer_idx); + swmbx_target_log!("Read requested on port {}: val: {}", self.port, val); + return Ok(Some(val)); + } + } + } + fn on_stop(&mut self) { + swmbx_target_log!("Stop on port {}", self.port); + let ctrl = SwmbxCtrl::load_ctrl_ptr_mut(); + ctrl.send_stop(self.port); + self.first_write = true; + } + fn on_address_match(&mut self, address: u8) -> bool { + self.address == address + } +} + +impl ReadTarget for SwmbxI2CTarget { + // read_processed + fn on_read(&mut self, buffer: &mut [u8]) -> Result { + buffer[0] = 0; + let ctrl = SwmbxCtrl::load_ctrl_ptr_mut(); + let mut idx = self.buffer_idx as usize; + idx %= ctrl.buffer_size; // Ensure idx is within bounds + self.buffer_idx = idx as u8; + let _val = ctrl.get_msg(self.port, self.buffer_idx); + swmbx_target_log!("Read processed on port {}: val: {}", self.port, _val); + Ok(1) + } +} + +impl WriteTarget for SwmbxI2CTarget { + // write_received + fn on_write(&mut self, data: &[u8]) -> Result<(), Self::Error> { + if data.len() == 0 { + // ignore empty writes + swmbx_target_log!("Empty write received on port {}", self.port); + return Ok(()); + } + + swmbx_target_log!("Write received on port {}: data: {:?}", self.port, data); + if self.first_write { + self.buffer_idx = data[0]; + self.first_write = false; + let ctrl = SwmbxCtrl::load_ctrl_ptr_mut(); + ctrl.send_start(self.port, self.buffer_idx) + } else { + let ctrl = SwmbxCtrl::load_ctrl_ptr_mut(); + ctrl.send_msg(self.port, self.buffer_idx, data[0]); + let mut idx = self.buffer_idx as usize; + idx += 1; + idx %= ctrl.buffer_size; // Ensure idx is within bounds + self.buffer_idx = idx as u8; + } + + Ok(()) + } +} + +impl WriteReadTarget for SwmbxI2CTarget {} + +impl RegisterAccess for SwmbxI2CTarget { + fn write_register(&mut self, _address: u8, _data: u8) -> Result<(), Self::Error> { + swmbx_target_log!("Write register called on port {}", self.port); + Ok(()) + } + fn read_register(&mut self, _address: u8, _buffer: &mut [u8]) -> Result { + swmbx_target_log!("Read register called on port {}", self.port); + Ok(1) + } +} + +#[cfg(feature = "i2c_target")] +impl SwmbxI2CTarget { + pub fn new(port: usize, address: u8) -> Result { + if address == 0 { + return Err(SwmbxI2CError::OtherError); + } + Ok(Self { + address, + read_idx: 0, + buffer_idx: 0, + first_write: false, + port, + }) + } + pub fn attach( + &mut self, + i2c: &mut I2cController< + Ast1060I2c<'static, ast1060_pac::I2c, SwmbxI2CTarget, UartLogger<'static>>, + NoOpLogger, + >, + ) -> Result<(), SwmbxI2CError> { + let mut config = I2cConfigBuilder::new() + .xfer_mode(I2cXferMode::DmaMode) + .multi_master(true) + .smbus_timeout(true) + .smbus_alert(false) + .speed(I2cSpeed::Standard) + .build(); + + pinctrl::Pinctrl::apply_pinctrl_group(pinctrl::PINCTRL_I2C0); + i2c.hardware.init(&mut config); + + let static_self = unsafe { core::mem::transmute::<&mut Self, &'static mut Self>(self) }; + + i2c.hardware + .i2c_aspeed_slave_register(self.address, Some(static_self)) + .map_err(|_| SwmbxI2CError::OtherError) + } +} diff --git a/src/main.rs b/src/main.rs index fbb2dd1..7c68f97 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,12 +8,13 @@ use core::sync::atomic::AtomicBool; use aspeed_ddk::uart::{Config, UartController}; use aspeed_ddk::watchdog::WdtController; use ast1060_pac::Peripherals; -use ast1060_pac::{Wdt, Wdt1}; +use ast1060_pac::{Spipf, Wdt, Wdt1}; use aspeed_ddk::ecdsa::AspeedEcdsa; use aspeed_ddk::hace_controller::HaceController; use aspeed_ddk::rsa::AspeedRsa; use aspeed_ddk::spi; +use aspeed_ddk::spimonitor::{RegionInfo, SpiMonitor, SpimExtMuxSel}; use aspeed_ddk::syscon::{ClockId, ResetId, SysCon}; use fugit::MillisDurationU32 as MilliSeconds; @@ -23,6 +24,8 @@ 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::rsa_test::run_rsa_tests; +#[cfg(feature = "i2c_target")] +use aspeed_ddk::tests::functional::swmbx_test; use panic_halt as _; use proposed_traits::system_control::ResetControl; @@ -97,6 +100,51 @@ fn test_wdt(uart: &mut UartController<'_>) { } } +fn release_bmc_spi(uart: &mut UartController<'_>) { + uart.write_all(b"\r\n####### SPIM0 setup #######\r\n") + .unwrap(); + let allow_cmds: [u8; 27] = [ + 0x03, 0x13, 0x0b, 0x0c, 0x6b, 0x6c, 0x01, 0x05, 0x35, 0x06, 0x04, 0x20, 0x21, 0x9f, 0x5a, + 0xb7, 0xe9, 0x32, 0x34, 0xd8, 0xdc, 0x02, 0x12, 0x15, 0x31, 0x3b, 0x3c, + ]; + + let read_blocked_regions = [RegionInfo { + /*pfm*/ + start: 0x0400_0000, + length: 0x0002_0000, + }]; + + let write_blocked_regions = [RegionInfo { + start: 0x0000_0000, + length: 0x0800_0000, + }]; + let mut spi_monitor0 = SpiMonitor::::new( + true, + SpimExtMuxSel::SpimExtMuxSel1, + &allow_cmds, + u8::try_from(allow_cmds.len()).unwrap(), + &read_blocked_regions, + u8::try_from(read_blocked_regions.len()).unwrap(), + &write_blocked_regions, + u8::try_from(write_blocked_regions.len()).unwrap(), + ); + spi_monitor0.spim_sw_rst(); + spi_monitor0.aspeed_spi_monitor_init(); + spi_monitor0.spim_ext_mux_config(SpimExtMuxSel::SpimExtMuxSel0); + // print spim pointer value +} + +fn setup_bmc_sequence(uart_controller: &mut UartController) { + // Enable BMC flash power + gpio_test::test_gpio_flash_power(uart_controller); + + // Release BMC SPI + release_bmc_spi(uart_controller); + + // Release BMC Reset + gpio_test::test_gpio_bmc_reset(uart_controller); +} + #[no_mangle] pub static HALT: AtomicBool = AtomicBool::new(true); @@ -164,10 +212,9 @@ fn main() -> ! { let mut rsa = AspeedRsa::new(&secure, delay); run_rsa_tests(&mut uart_controller, &mut rsa); - gpio_test::test_gpioa(&mut uart_controller); + // gpio_test::test_gpioa(&mut uart_controller); i2c_test::test_i2c_master(&mut uart_controller); - #[cfg(feature = "i2c_target")] - i2c_test::test_i2c_slave(&mut uart_controller); + test_wdt(&mut uart_controller); let test_spicontroller = false; if test_spicontroller { @@ -178,6 +225,12 @@ fn main() -> ! { spi::spitest::test_spi2(&mut uart_controller); } // Initialize the peripherals here if needed + + setup_bmc_sequence(&mut uart_controller); + // Start SWMBX test + #[cfg(feature = "i2c_target")] + swmbx_test::test_swmbx(&mut uart_controller); + loop { cortex_m::asm::wfi(); } diff --git a/src/tests/functional/i2c_test.rs b/src/tests/functional/i2c_test.rs index 2f33a50..ad5af24 100644 --- a/src/tests/functional/i2c_test.rs +++ b/src/tests/functional/i2c_test.rs @@ -7,12 +7,10 @@ use crate::i2c::i2c_controller::{HardwareInterface, I2cController}; use crate::pinctrl; use crate::uart::{self, Config, UartController}; use ast1060_pac::Peripherals; -#[cfg(feature = "i2c_target")] -use cortex_m::peripheral::NVIC; use embedded_hal::i2c::ErrorKind; use embedded_io::Write; use proposed_traits::i2c_target::{ - I2CCoreTarget, ReadTarget, RegisterAccess, WriteReadTarget, WriteTarget, + I2CCoreTarget, ReadTarget, RegisterAccess, TransactionDirection, WriteReadTarget, WriteTarget, }; #[derive(Debug)] @@ -44,7 +42,13 @@ impl I2CCoreTarget for DummyI2CTarget { self.address = address; Ok(()) } - fn on_transaction_start(&mut self, _repeated: bool) {} + fn on_transaction_start( + &mut self, + _direction: TransactionDirection, + _repeated: bool, + ) -> Result, Self::Error> { + Ok(None) + } fn on_stop(&mut self) {} fn on_address_match(&mut self, address: u8) -> bool { self.address == address @@ -211,93 +215,3 @@ pub fn test_i2c_master(uart: &mut UartController<'_>) { } } } - -#[cfg(feature = "i2c_target")] -static mut UART_PTR: Option<&'static mut UartController<'static>> = None; -#[cfg(feature = "i2c_target")] -static mut I2C0_INSTANCE: Option< - I2cController, NoOpLogger>, -> = None; - -#[cfg(feature = "i2c_target")] -#[no_mangle] -pub extern "C" fn i2c() { - unsafe { - if let Some(uart) = UART_PTR.as_mut() { - let _ = uart.write_all(b"[ISR] i2c0\r\n"); - } - if let Some(i2c0) = I2C0_INSTANCE.as_mut() { - let () = i2c0.hardware.handle_interrupt(); - } - } -} - -#[cfg(feature = "i2c_target")] -static mut TEST_TARGET: DummyI2CTarget = DummyI2CTarget { - address: 0x42, - buffer: [0; 16], - read_idx: 0, -}; -#[cfg(feature = "i2c_target")] -pub fn test_i2c_slave(uart: &mut UartController<'_>) { - writeln!(uart, "\r\n####### I2C slave test #######\r\n").unwrap(); - - let peripherals = unsafe { Peripherals::steal() }; - let mut delay = DummyDelay {}; - unsafe { - let mut dbg_uart = UartController::new( - peripherals.uart, - core::mem::transmute::<&mut DummyDelay, &'static mut DummyDelay>(&mut delay), - ); - - 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 i2c_config = I2cConfigBuilder::new() - .xfer_mode(I2cXferMode::DmaMode) - .multi_master(true) - .smbus_timeout(true) - .smbus_alert(false) - .speed(I2cSpeed::Standard) - .build(); - //on DC-SCM board, i2c0 of ast1060 is connected to i2c4 of ast2600 - let mut i2c0: I2cController< - Ast1060I2c, - NoOpLogger, - > = I2cController { - hardware: Ast1060I2c::new(UartLogger::new(core::mem::transmute::< - &mut UartController<'_>, - &'static mut UartController<'static>, - >(&mut dbg_uart))), - config: i2c_config, - logger: NoOpLogger {}, - }; - - pinctrl::Pinctrl::apply_pinctrl_group(pinctrl::PINCTRL_I2C0); - i2c0.hardware.init(&mut i2c0.config); - - match i2c0 - .hardware - .i2c_aspeed_slave_register(TEST_TARGET.address, None) - { - Ok(val) => { - writeln!(uart, "i2c slave register ok: {val:?}\r").unwrap(); - } - Err(e) => { - writeln!(uart, "i2c slave register err: {e:?}\r").unwrap(); - } - } - - UART_PTR = Some(core::mem::transmute::< - &mut UartController<'_>, - &'static mut UartController<'static>, - >(uart)); - I2C0_INSTANCE = Some(i2c0); - NVIC::unmask(ast1060_pac::Interrupt::i2c); - } -} diff --git a/src/tests/functional/mod.rs b/src/tests/functional/mod.rs index 0dfa9ae..d990c7a 100644 --- a/src/tests/functional/mod.rs +++ b/src/tests/functional/mod.rs @@ -7,3 +7,5 @@ pub mod hmac_test; pub mod i2c_test; pub mod rsa_test; pub mod rsa_test_vec; +#[cfg(feature = "i2c_target")] +pub mod swmbx_test; diff --git a/src/tests/functional/swmbx_test.rs b/src/tests/functional/swmbx_test.rs new file mode 100644 index 0000000..330cc69 --- /dev/null +++ b/src/tests/functional/swmbx_test.rs @@ -0,0 +1,200 @@ +// Licensed under the Apache-2.0 license + +use crate::common::{DummyDelay, NoOpLogger, UartLogger}; +use crate::i2c::ast1060_i2c::Ast1060I2c; +use crate::i2c::common::I2cConfigBuilder; +use crate::i2c::i2c_controller::{HardwareInterface, I2cController}; +use crate::pinctrl; +use crate::uart::{self, Config, UartController}; +use ast1060_pac::Peripherals; +use embedded_io::Write; + +use crate::i2c::pfr::swmbx; +use crate::i2c::pfr::swmbx::SwmbxCtrl; +use crate::i2c::target::swmbx_target::SwmbxI2CTarget; +use core::mem::MaybeUninit; +use cortex_m::peripheral::NVIC; + +static mut SWMBX_TARGET: MaybeUninit = MaybeUninit::uninit(); +static mut DBG_UART: MaybeUninit> = MaybeUninit::uninit(); +static mut DELAY: MaybeUninit = MaybeUninit::uninit(); + +pub const UFM_WRITE_FIFO: u8 = 0xd; +pub const UFM_READ_FIFO: u8 = 0xe; +pub const SWMBX_WRITE_FIFO_SIZE: u8 = 64; +pub const SWMBX_READ_FIFO_SIZE: u8 = 128; + +pub const BMC_UPDATE_INTENT: u8 = 0x13; + +#[no_mangle] +static mut UART_PTR: Option<&'static mut UartController<'static>> = None; + +#[cfg(feature = "i2c_target")] +static mut I2C_PTR: Option< + &'static mut I2cController< + Ast1060I2c, + NoOpLogger, + >, +> = None; +#[cfg(feature = "i2c_target")] +pub fn test_swmbx(uart: &mut UartController<'_>) { + unsafe { + UART_PTR = Some(core::mem::transmute::< + &mut UartController<'_>, + &'static mut UartController<'static>, + >(uart)); + } + writeln!(uart, "\r\n####### SWMBX test #######\r\n").unwrap(); + + let p = unsafe { Peripherals::steal() }; + + let delay: &'static mut DummyDelay = unsafe { + DELAY.write(DummyDelay {}); + &mut *DELAY.as_mut_ptr() + }; + + let dbg_uart: &'static mut UartController<'static> = unsafe { + DBG_UART.write(UartController::new(p.uart, delay)); + &mut *DBG_UART.as_mut_ptr() + }; + 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, + }); + } + + writeln!(uart, "\r\n## SWMBX: Starting up...\r\n").ok(); + + let swmbx = SwmbxCtrl::init(swmbx::SWMBX_BUF_SIZE); + let _ = swmbx.enable_behavior( + swmbx::SWMBX_PROTECT | swmbx::SWMBX_NOTIFY | swmbx::SWMBX_FIFO, + true, + ); + let _ = swmbx.update_fifo( + 0, + UFM_WRITE_FIFO, + SWMBX_WRITE_FIFO_SIZE, + swmbx::SWMBX_FIFO_NOTIFY_STOP, + true, + ); + let _ = swmbx.update_fifo( + 1, + UFM_READ_FIFO, + SWMBX_READ_FIFO_SIZE, + swmbx::SWMBX_FIFO_NOTIFY_STOP, + true, + ); + let _ = swmbx.update_notify(0, BMC_UPDATE_INTENT, true); + let access_control: [[u32; 8]; 2] = [ + // BMC + [ + 0xfff704ff, 0xffffffff, 0xffffffff, 0xfffffff2, 0xffffffff, 0xffffffff, 0x00000000, + 0x00000000, + ], + // PCH + [ + 0xfff884ff, 0xffffffff, 0xffffffff, 0xfffffff5, 0x00000000, 0x00000000, 0xffffffff, + 0xffffffff, + ], + ]; + let _ = swmbx.apply_protect(0, &access_control[0], 0); + let _ = swmbx.apply_protect(1, &access_control[1], 0); + SwmbxCtrl::store_ctrl_ptr(swmbx); + + let target = unsafe { + SWMBX_TARGET.write(SwmbxI2CTarget::new(0, 0x38).expect("invalid address")); + &mut *SWMBX_TARGET.as_mut_ptr() + }; + + let mut i2c0 = I2cController { + hardware: Ast1060I2c::new(UartLogger::new(dbg_uart)), + config: I2cConfigBuilder::new().build(), + logger: NoOpLogger {}, + }; + + target + .attach(&mut i2c0) + .expect("i2c target register failed"); + unsafe { + pinctrl::Pinctrl::apply_pinctrl_group(pinctrl::PINCTRL_I2C0); + I2C_PTR = Some(core::mem::transmute::< + &mut I2cController< + Ast1060I2c, + NoOpLogger, + >, + &'static mut I2cController< + Ast1060I2c, + NoOpLogger, + >, + >(&mut i2c0)); + NVIC::unmask(ast1060_pac::Interrupt::i2c); + } + loop { + // Execute the following command at the BMC prompt to verify SWMBX functionality: + // - Verify the SWMBX address: + // aspeed-pfr-tool -w 0x13 0x8 + // aspeed-pfr-tool -r 0x13 + // [Expected output: 0x8] + // + // - Verify the protected SWMBX address: + // aspeed-pfr-tool -w 0x12 0x8 + // aspeed-pfr-tool -r 0x12 + // [Expected output: 0x0] + // + // - Verify the notify functionality: + // aspeed-pfr-tool -w 0x13 0x8 + // On Ast1060 console, you should see [NOTIFY] SWMBX: notify triggered on port 0 addr 0x13 + // + // - Verify the FIFO functionality: + // aspeed-pfr-tool -w 0xd 0x1f + // aspeed-pfr-tool -w 0xd 0x2f + // aspeed-pfr-tool -w 0xd 0x3f + // aspeed-pfr-tool -r 0xd + // aspeed-pfr-tool -r 0xd + // aspeed-pfr-tool -r 0xd + // aspeed-pfr-tool -r 0xd + // [Expected output: 0x1f, 0x2f, 0x3f, 0x0] + poll_notify(); + } +} + +fn poll_notify() { + unsafe { + if let Some(uart) = UART_PTR.as_mut() { + let ctrl = SwmbxCtrl::load_ctrl_ptr_mut(); + + for port in 0..swmbx::SWMBX_DEV_COUNT { + for addr in 0..swmbx::SWMBX_NODE_COUNT { + let node = &mut ctrl.node[port][addr]; + if node.notify_flag { + if let Err(e) = writeln!( + uart, + "[NOTIFY] SWMBX: notify triggered on port {} addr {:#x}\r\n", + port, addr + ) { + let _ = writeln!(uart, "write error: {:?}\r\n", e); + } + + node.notify_flag = false; + } + } + } + } + } +} + +#[no_mangle] +pub extern "C" fn i2c() { + unsafe { + if let Some(uart) = UART_PTR.as_mut() { + let _ = uart.write_all(b"[ISR] I2C\r\n"); + } + if let Some(i2c) = I2C_PTR.as_mut() { + i2c.hardware.handle_interrupt(); + } + } +}