Merge #255 #257 #258

255: libmctp: Initial support of MCTP support r=jrvanwhy a=alistair23

MCTP (Management Component Transport Protocol) is a transport layer that is commonly used for server management. It supports multiple underlying protocols, but in this case we are focued on SMBus.

This adds initial support for a MCTP example in libtock-rs.

257: Add ninedof example r=jrvanwhy a=l162

For unit testing.

258: stm32f3discovery support r=jrvanwhy a=l162

Adds support for stm32f3discovery.

Co-authored-by: Alistair Francis <alistair.francis@wdc.com>
Co-authored-by: l162 <l162@hey.com>
diff --git a/Cargo.toml b/Cargo.toml
index caeae02..99543d9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,6 +29,7 @@
 p256 = { version = "0.5.0" , default-features = false, features = ["arithmetic", "ecdsa", "ecdsa-core"] }
 subtle = { version = "2.3.0", default-features = false, features = ["i128"] }
 generic-array = { version = "0.14.3" }
+libmctp = { version = "0.1.0" }
 
 # We need to override this to allow builds for targets that don't support atomics.
 # Once a version newer then 0.4.11 is released we can update to use that.
diff --git a/Makefile b/Makefile
index e32decb..81829e2 100644
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,7 @@
 	@echo " - nrf52"
 	@echo " - imxrt1050"
 	@echo " - apollo3"
+	@echo " - stm32f3discovery"
 	@echo
 	@echo "Run 'make setup' to setup Rust to build libtock-rs."
 	@echo "Run 'make <board>' to build libtock-rs for that board"
@@ -132,6 +133,14 @@
 flash-nrf52840:
 	PLATFORM=nrf52840 cargo run $(release) --target=thumbv7em-none-eabi --example $(EXAMPLE) $(features)
 
+.PHONY: stm32f3discovery
+stm32f3discovery:
+	PLATFORM=stm32f3discovery cargo build $(release) --target=thumbv7em-none-eabi --examples $(features)
+	
+.PHONY: flash-stm32f3discovery
+flash-stm32f3discovery:
+	PLATFORM=stm32f3discovery cargo run $(release) --target=thumbv7em-none-eabi --example $(EXAMPLE) $(features)
+
 .PHONY: opentitan
 opentitan:
 	PLATFORM=opentitan cargo build $(release) --target=riscv32imc-unknown-none-elf --examples $(features)
diff --git a/boards/layout_stm32f3discovery.ld b/boards/layout_stm32f3discovery.ld
new file mode 100644
index 0000000..9368003
--- /dev/null
+++ b/boards/layout_stm32f3discovery.ld
@@ -0,0 +1,11 @@
+/* Layout for the stm32f3discovery board, usable by the examples in this repository. */
+
+MEMORY {
+  /* The application region is 64 bytes (0x40) */
+  FLASH (rx) : ORIGIN = 0x08020040, LENGTH = 0x00020000
+  SRAM (rwx) : ORIGIN = 0x20004000, LENGTH = 48K
+}
+
+MPU_MIN_ALIGN = 8K;
+
+INCLUDE layout_generic.ld
diff --git a/examples/libmctp.rs b/examples/libmctp.rs
new file mode 100644
index 0000000..8623665
--- /dev/null
+++ b/examples/libmctp.rs
@@ -0,0 +1,105 @@
+#![no_std]
+/// This is a example of a MCTP master device
+use libmctp::smbus::{MCTPSMBusContext, VendorIDFormat};
+use libtock::i2c_master::I2cBuffer;
+use libtock::println;
+use libtock::result::TockResult;
+use libtock::syscalls;
+
+libtock_core::stack_size! {0x800}
+
+// The address of this device
+const MY_ID: u8 = 0x23;
+const DEST_ID: u8 = 0x10;
+// Support vendor defined protocol 0x7E
+const MSG_TYPES: [u8; 1] = [0x7E];
+// Specify a PCI vendor ID that we support
+const VENDOR_IDS: [libmctp::smbus::VendorIDFormat; 1] = [VendorIDFormat {
+    // PCI Vendor ID
+    format: 0x00,
+    // PCI VID
+    data: 0x1414,
+    // Extra data
+    numeric_value: 4,
+}];
+
+#[libtock::main]
+async fn main() -> TockResult<()> {
+    let mut drivers = libtock::retrieve_drivers()?;
+    drivers.console.create_console();
+    println!("Starting libmctp example");
+    let i2c_driver = drivers.i2c.init_driver()?;
+
+    // Check that I2C exists
+    if i2c_driver.check_present().is_err() {
+        println!("No I2C device, yielding");
+        loop {
+            unsafe { syscalls::raw::yieldk() };
+        }
+    }
+    println!("Found the I2C device");
+
+    println!("Setting callback");
+    let mut callback = |_, _| {
+        println!("I2C Callback");
+    };
+
+    let _subscription = i2c_driver.subscribe(&mut callback)?;
+    println!("Creating MCTP SMBus Context");
+    let ctx = MCTPSMBusContext::new(MY_ID, &MSG_TYPES, &VENDOR_IDS);
+
+    let mut buf: [u8; 32] = [0; 32];
+
+    println!("Creating the request");
+    let len = ctx
+        .get_request()
+        .get_vendor_defined_message_support(0xB, 0, &mut buf);
+
+    println!("Creating master write buffer");
+    let mut master_write_buffer = I2cBuffer::default();
+    // Skip the first byte, as that is the destination address
+    for (i, d) in buf[1..].iter().enumerate() {
+        master_write_buffer[i] = *d;
+    }
+    let dest_buffer = i2c_driver.init_buffer(&mut master_write_buffer)?;
+    println!("  done");
+
+    let _ = i2c_driver.write(DEST_ID as usize, len.unwrap());
+
+    unsafe { syscalls::raw::yieldk() };
+
+    // Read 4 bytes for the SMBus header
+    let _ = i2c_driver.read(DEST_ID as usize, 4);
+
+    unsafe { syscalls::raw::yieldk() };
+
+    // Copy into a temp buffer
+    let mut temp_buffer = [0; libtock::hmac::DEST_BUFFER_SIZE];
+    dest_buffer.read_bytes(&mut temp_buffer[1..4]);
+
+    // Determine the full length
+    let bytes = ctx.get_length(&temp_buffer).unwrap();
+
+    // Read the full packet. The slave will re-send the data, so do
+    // a full read
+    let _ = i2c_driver.read(DEST_ID as usize, bytes - 1);
+
+    unsafe { syscalls::raw::yieldk() };
+
+    // Copy in the full packet, with space for the destination address
+    dest_buffer.read_bytes(&mut temp_buffer[1..bytes - 1]);
+
+    // Set the destination address as this isn't filled in the buffer from
+    // the kernel
+    temp_buffer[0] = DEST_ID << 1;
+
+    // Decode the response
+    let ret = ctx.decode_packet(&temp_buffer[0..bytes]);
+
+    // Print the outcome of the decode
+    println!("ret: {:?}", ret);
+
+    loop {
+        unsafe { syscalls::raw::yieldk() };
+    }
+}
diff --git a/examples/ninedof.rs b/examples/ninedof.rs
new file mode 100644
index 0000000..ec0724c
--- /dev/null
+++ b/examples/ninedof.rs
@@ -0,0 +1,23 @@
+#![no_std]
+
+use libtock::println;
+use libtock::result::TockResult;
+use libtock::timer::Duration;
+
+libtock_core::stack_size! {0x800}
+
+#[libtock::main]
+async fn main() -> TockResult<()> {
+    let mut drivers = libtock::retrieve_drivers()?;
+
+    let mut timer_driver = drivers.timer.create_timer_driver();
+    let timer_driver = timer_driver.activate()?;
+    drivers.console.create_console();
+
+    loop {
+        println!("Mag:   {}\n", drivers.ninedof.read_magnetometer()?);
+        println!("Accel: {}\n", drivers.ninedof.read_acceleration()?);
+        println!("Gyro:  {}\n", drivers.ninedof.read_gyroscope()?);
+        timer_driver.sleep(Duration::from_ms(500)).await?;
+    }
+}