From 7f80f94b5e99366b1f2bf4bcd492f0cdf6cd7fb7 Mon Sep 17 00:00:00 2001 From: Frederick Mayle Date: Tue, 3 Dec 2024 15:50:35 -0800 Subject: [PATCH] add macro to read config fields Ensures that the offset and type match. --- src/device/gpu.rs | 6 ++++-- src/transport/mod.rs | 25 +++++++++++++++++++++++++ src/volatile.rs | 6 +++--- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/device/gpu.rs b/src/device/gpu.rs index d299a990..50e0007f 100644 --- a/src/device/gpu.rs +++ b/src/device/gpu.rs @@ -2,6 +2,7 @@ use crate::hal::{BufferDirection, Dma, Hal}; use crate::queue::VirtQueue; +use crate::transport::read_config_field; use crate::transport::Transport; use crate::volatile::{ReadOnly, Volatile, WriteOnly}; use crate::{pages, Error, Result, PAGE_SIZE}; @@ -44,11 +45,11 @@ impl VirtIOGpu { let negotiated_features = transport.begin_init(SUPPORTED_FEATURES); // read configuration space - let events_read = transport.read_config_space::(offset_of!(Config, events_read))?; + let events_read = read_config_field!(transport, Config, events_read)?; let num_scanouts = transport.read_config_space::(offset_of!(Config, num_scanouts))?; info!( "events_read: {:#x}, num_scanouts: {:#x}", - events_read, num_scanouts + events_read.0, num_scanouts ); let control_queue = VirtQueue::new( @@ -292,6 +293,7 @@ impl Drop for VirtIOGpu { } } +#[derive(Default, zerocopy::FromBytes)] #[repr(C)] struct Config { /// Signals pending events to the driver。 diff --git a/src/transport/mod.rs b/src/transport/mod.rs index 3e8a0990..37762db8 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -112,6 +112,31 @@ pub trait Transport { ) -> Result<()>; } +/// Reads a value from the device config space. The parameters are (1) an instance of `Transport`, +/// (2) a type describing the config's memory layout, and (3) a field of that type. +macro_rules! read_config_field { + ($transport:expr, $config_type:ty, $field:ident) => {{ + // We need to know the config field's type, but there is nothing like C++'s "decltype" in + // Rust. Workaround it by creating an instance of the config and a variable `f` initialized + // to the field of that instance. That constrains `f`'s type to match the field's type. + // Then, assign the result of `Transport::read_config_space` to `f` to constrain its type + // parameter to be of the same type. When optimizations are enabled, the extra variables + // and initialization will be elided. + let c: $config_type = FromZeros::new_zeroed(); + #[allow(unused_assignments)] + let mut f = c.$field; + match $transport.read_config_space(offset_of!($config_type, $field)) { + Ok(x) => { + f = x; + Ok(f) + } + Err(e) => Err(e), + } + }}; +} + +pub(crate) use read_config_field; + bitflags! { /// The device status field. Writing 0 into this field resets the device. #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] diff --git a/src/volatile.rs b/src/volatile.rs index 74f527b3..1179ee4d 100644 --- a/src/volatile.rs +++ b/src/volatile.rs @@ -1,5 +1,5 @@ /// An MMIO register which can only be read from. -#[derive(Default)] +#[derive(Default, zerocopy::FromBytes)] #[repr(transparent)] pub struct ReadOnly(pub(crate) T); @@ -11,12 +11,12 @@ impl ReadOnly { } /// An MMIO register which can only be written to. -#[derive(Default)] +#[derive(Default, zerocopy::FromBytes)] #[repr(transparent)] pub struct WriteOnly(pub(crate) T); /// An MMIO register which may be both read and written. -#[derive(Default)] +#[derive(Default, zerocopy::FromBytes)] #[repr(transparent)] pub struct Volatile(T);