Implement secure secure Rust bindings for I2CBus
and I2CSlave, with reference to sysbus.
Suggested-by: Paolo Bonzini <
pbon...@redhat.com>
Signed-off-by: Chao Liu <chao...@openatom.club>
Signed-off-by: Chen Miao <chen...@openatom.club>
---
rust/hw/core/meson.build | 1 +
rust/hw/core/src/
i2cbus.rs | 349 +++++++++++++++++++++++++++++++++++++
rust/hw/core/src/
lib.rs | 3 +
rust/hw/core/src/
qdev.rs | 18 +-
rust/hw/core/wrapper.h | 1 +
5 files changed, 371 insertions(+), 1 deletion(-)
create mode 100644 rust/hw/core/src/
i2cbus.rs
diff --git a/rust/hw/core/meson.build b/rust/hw/core/meson.build
index 1560dd20c6..ab752df8fc 100644
--- a/rust/hw/core/meson.build
+++ b/rust/hw/core/meson.build
@@ -53,6 +53,7 @@ _hwcore_rs = static_library(
'src/
irq.rs',
'src/
qdev.rs',
'src/
sysbus.rs',
+ 'src/
i2cbus.rs',
],
{'.': _hwcore_bindings_inc_rs}
),
diff --git a/rust/hw/core/src/
i2cbus.rs b/rust/hw/core/src/
i2cbus.rs
new file mode 100644
index 0000000000..31228530ca
--- /dev/null
+++ b/rust/hw/core/src/
i2cbus.rs
@@ -0,0 +1,349 @@
+// Copyright 2024 Red Hat, Inc.
+// Author(s): Paolo Bonzini <
pbon...@redhat.com>
+/// A safe wrapper around [`bindings::I2CSlave`].
+#[repr(transparent)]
+#[derive(Debug, common::Wrapper)]
+pub struct I2CSlave(Opaque<bindings::I2CSlave>);
+
+unsafe impl Send for I2CSlave {}
+unsafe impl Sync for I2CSlave {}
+
+unsafe impl ObjectType for I2CSlave {
+ type Class = I2CSlaveClass;
+ const TYPE_NAME: &'static CStr =
+ unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_I2C_SLAVE) };
+}
+
+qom_isa!(I2CSlave: DeviceState, Object);
+
+/// A safe wrapper around [`bindings::I2CSlaveClass`].
+#[repr(transparent)]
+#[derive(Debug, common::Wrapper)]
+pub struct I2CSlaveClass(Opaque<bindings::I2CSlaveClass>);
+
+unsafe impl Send for I2CSlaveClass {}
+unsafe impl Sync for I2CSlaveClass {}
+
+// TODO: add virtual methods
+pub trait I2CSlaveImpl: DeviceImpl + IsA<I2CSlave> {
+ /// Master to slave. Returns non-zero for a NAK, 0 for success.
+ fn send(&self, data: u8) -> i32;
+
+ /// Master to slave (asynchronous). Receiving slave must call i2c_ack().
+ fn send_async(&self, data: u8);
+
+ /// Slave to master. This cannot fail, the device should always return something here.
+ fn recv(&self) -> u8;
+
+ /// Notify the slave of a bus state change. For start event,
+ /// returns non-zero to NAK an operation. For other events the
+ /// return code is not used and should be zero.
+ fn event(&self, event: bindings::i2c_event) -> i32;
+
+ /// Check if this device matches the address provided. Returns bool of
+ /// true if it matches (or broadcast), and updates the device list, false
+ /// otherwise.
+ ///
+ /// If broadcast is true, match should add the device and return true.
+ fn match_and_add(&self, address: u8, broadcast: bool, current_devs: *mut bindings::I2CNodeList) -> bool;
+}
+
+impl I2CSlaveClass {
+ /// Fill in the virtual methods of `I2CSlaveClass` based on the
+ /// definitions in the `I2CSlaveImpl` trait.
+ pub fn class_init<T: I2CSlaveImpl>(self: &mut I2CSlaveClass) {
+ unsafe {
+ // SAFETY: We need to access the underlying C structure to call parent_class.class_init
+ let class_ptr = self.as_mut_ptr() as *mut bindings::I2CSlaveClass;
+ (*class_ptr).parent_class.class_init::<T>();
+ }
+ }
+}
+
+/// Trait for methods of [`I2CSlave`] and its subclasses.
+pub trait I2CSlaveMethods: ObjectDeref
+where
+ Self::Target: IsA<I2CSlave>,
+{
+ /// Create an I2C slave device on the heap.
+ ///
+ /// # Arguments
+ /// * `name` - a device type name
+ /// * `addr` - I2C address of the slave when put on a bus
+ ///
+ /// This only initializes the device state structure and allows
+ /// properties to be set. Type `name` must exist. The device still
+ /// needs to be realized.
+ fn new(name: &str, addr: u8) -> *mut bindings::I2CSlave {
+ assert!(bql::is_locked());
+ unsafe {
+ bindings::i2c_slave_new(name.as_ptr() as *const _, addr)
+ }
+ }
+
+ /// Create and realize an I2C slave device on the heap.
+ ///
+ /// # Arguments
+ /// * `bus` - I2C bus to put it on
+ /// * `name` - I2C slave device type name
+ /// * `addr` - I2C address of the slave when put on a bus
+ ///
+ /// Create the device state structure, initialize it, put it on the
+ /// specified `bus`, and drop the reference to it (the device is realized).
+ fn create_simple(&self, bus: &I2CBus, name: &str, addr: u8) -> *mut bindings::I2CSlave {
+ assert!(bql::is_locked());
+ unsafe {
+ bindings::i2c_slave_create_simple(bus.as_mut_ptr(), name.as_ptr() as *const _, addr)
+ }
+ }
+
+ /// Set the I2C bus address of a slave device
+ ///
+ /// # Arguments
+ /// * `address` - I2C address of the slave when put on a bus
+ fn set_address(&self, address: u8) {
+ assert!(bql::is_locked());
+ unsafe {
+ bindings::i2c_slave_set_address(self.upcast().as_mut_ptr(), address)
+ }
+ }
+
+ /// Get the I2C bus address of a slave device
+ fn get_address(&self) -> u8 {
+ assert!(bql::is_locked());
+ // SAFETY: the BQL ensures that no one else writes to the I2CSlave structure,
+ // and the I2CSlave must be initialized to get an IsA<I2CSlave>.
+ let slave = unsafe { *self.upcast().as_ptr() };
+ slave.address
+ }
+
+ /// Realize and drop a reference an I2C slave device
+ ///
+ /// # Arguments
+ /// * `bus` - I2C bus to put it on
+ ///
+ /// Returns: `true` on success, `false` on failure.
+ ///
+ /// Call 'realize' on the device, put it on the specified `bus`, and drop the
+ /// reference to it.
+ fn realize_and_unref(&self, bus: &I2CBus) -> bool {
+ assert!(bql::is_locked());
+ unsafe {
+ // Correct signature: i2c_slave_realize_and_unref(dev, bus, errp)
+ bindings::i2c_slave_realize_and_unref(
+ self.upcast().as_mut_ptr(),
+ bus.as_mut_ptr(),
+ std::ptr::null_mut() // errp: use null pointer to ignore errors
+ )
+ }
+ }
+}
+
+impl<R: ObjectDeref> I2CSlaveMethods for R where R::Target: IsA<I2CSlave> {}
+
+/// Enum representing I2C events
+#[repr(u32)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum I2CEvent {
+ StartRecv = bindings::I2C_START_RECV,
+ StartSend = bindings::I2C_START_SEND,
+ StartSendAsync = bindings::I2C_START_SEND_ASYNC,
+ Finish = bindings::I2C_FINISH,
+ Nack = bindings::I2C_NACK,
+}
+
+impl From<bindings::i2c_event> for I2CEvent {
+ fn from(event: bindings::i2c_event) -> Self {
+ match event {
+ bindings::I2C_START_RECV => I2CEvent::StartRecv,
+ bindings::I2C_START_SEND => I2CEvent::StartSend,
+ bindings::I2C_START_SEND_ASYNC => I2CEvent::StartSendAsync,
+ bindings::I2C_FINISH => I2CEvent::Finish,
+ bindings::I2C_NACK => I2CEvent::Nack,
+ _ => panic!("Unknown I2C event: {}", event),
+ }
+ }
+}
+
+impl From<I2CEvent> for bindings::i2c_event {
+ fn from(event: I2CEvent) -> Self {
+ match event {
+ I2CEvent::StartRecv => bindings::I2C_START_RECV,
+ I2CEvent::StartSend => bindings::I2C_START_SEND,
+ I2CEvent::StartSendAsync => bindings::I2C_START_SEND_ASYNC,
+ I2CEvent::Finish => bindings::I2C_FINISH,
+ I2CEvent::Nack => bindings::I2C_NACK,
+ }
+ }
+}