diff --git a/Cargo.lock b/Cargo.lock index 9d0f763f11..0687394029 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1558,7 +1558,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "virtio-spec" -version = "0.0.0" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93a09b1c5e4df5cb0251ce5b19ab95064584a01665117b378d692eb39b84b0b" dependencies = [ "allocator-api2", "bitfield-struct", diff --git a/Cargo.toml b/Cargo.toml index 147cf8fa24..d0c1b16aae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,7 +74,7 @@ mmap = [] [dependencies] hermit-macro = { path = "hermit-macro" } -virtio = { package = "virtio-spec", path = "virtio-spec", features = ["alloc", "mmio", "nightly", "zerocopy"] } +virtio = { package = "virtio-spec", version = "0.1", features = ["alloc", "mmio", "nightly", "zerocopy"] } ahash = { version = "0.8", default-features = false } align-address = "0.3" anstyle = { version = "1", default-features = false } @@ -165,7 +165,6 @@ llvm-tools = "0.1" [workspace] members = [ "hermit-macro", - "virtio-spec", "xtask", ] exclude = [ diff --git a/src/arch/riscv64/kernel/devicetree.rs b/src/arch/riscv64/kernel/devicetree.rs index d0a42c9d9c..ee36f6c658 100644 --- a/src/arch/riscv64/kernel/devicetree.rs +++ b/src/arch/riscv64/kernel/devicetree.rs @@ -3,7 +3,7 @@ use core::ptr::NonNull; use fdt::Fdt; #[cfg(all(feature = "tcp", not(feature = "pci")))] -use virtio::mmio::{DeviceRegisterVolatileFieldAccess, DeviceRegisters}; +use virtio::mmio::{DeviceRegisters, DeviceRegistersVolatileFieldAccess}; #[cfg(all(feature = "tcp", not(feature = "pci")))] use volatile::VolatileRef; diff --git a/src/arch/x86_64/kernel/mmio.rs b/src/arch/x86_64/kernel/mmio.rs index 510e9d44f0..61e1e1b4ad 100644 --- a/src/arch/x86_64/kernel/mmio.rs +++ b/src/arch/x86_64/kernel/mmio.rs @@ -5,7 +5,7 @@ use core::{ptr, str}; use align_address::Align; use hermit_sync::{without_interrupts, InterruptTicketMutex}; -use virtio::mmio::{DeviceRegisterVolatileFieldAccess, DeviceRegisters}; +use virtio::mmio::{DeviceRegisters, DeviceRegistersVolatileFieldAccess}; use volatile::VolatileRef; use crate::arch::x86_64::mm::paging::{ diff --git a/src/drivers/net/virtio/mmio.rs b/src/drivers/net/virtio/mmio.rs index 495210b583..84fbd609ed 100644 --- a/src/drivers/net/virtio/mmio.rs +++ b/src/drivers/net/virtio/mmio.rs @@ -7,7 +7,7 @@ use alloc::vec::Vec; use core::str::FromStr; use smoltcp::phy::ChecksumCapabilities; -use virtio::mmio::{DeviceRegisterVolatileFieldAccess, DeviceRegisters}; +use virtio::mmio::{DeviceRegisters, DeviceRegistersVolatileFieldAccess}; use volatile::VolatileRef; use crate::drivers::net::virtio::{CtrlQueue, NetDevCfg, RxQueues, TxQueues, VirtioNetDriver}; diff --git a/src/drivers/virtio/transport/mmio.rs b/src/drivers/virtio/transport/mmio.rs index ffdcb52b6f..da826ec3b0 100644 --- a/src/drivers/virtio/transport/mmio.rs +++ b/src/drivers/virtio/transport/mmio.rs @@ -6,7 +6,7 @@ use core::mem; use virtio::mmio::{ - DeviceRegisterVolatileFieldAccess, DeviceRegisterVolatileWideFieldAccess, DeviceRegisters, + DeviceRegisters, DeviceRegistersVolatileFieldAccess, DeviceRegistersVolatileWideFieldAccess, InterruptStatus, NotificationData, }; use virtio::{le32, DeviceStatus}; diff --git a/virtio-spec/Cargo.toml b/virtio-spec/Cargo.toml deleted file mode 100644 index fabfd5d0fb..0000000000 --- a/virtio-spec/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "virtio-spec" -authors = ["Martin Kröning "] -edition = "2021" -description = "Definitions from the Virtual I/O Device (VIRTIO) specification." -repository = "https://github.com/hermit-os/kernel" -license = "MIT OR Apache-2.0" -keywords = ["virtio", "driver", "volatile"] -categories = ["no-std", "no-std::no-alloc"] - -[dependencies] -allocator-api2 = { version = "0.2", default-features = false, features = ["alloc"], optional = true } -bitfield-struct = "0.8" -bitflags = "2" -endian-num = { version = "0.1", features = ["bitflags", "linux-types"] } -num_enum = { version = "0.7", default-features = false } -pci_types = { version = "0.10", optional = true } -volatile = "0.6" -volatile-macro = "0.6" -zerocopy = { version = "0.7", optional = true, default-features = false } -zerocopy-derive = { version = "0.7", optional = true } - -[features] -alloc = ["dep:allocator-api2"] -mmio = [] -nightly = ["allocator-api2/nightly"] -pci = ["dep:pci_types"] -zerocopy = ["dep:zerocopy", "dep:zerocopy-derive", "endian-num/zerocopy"] diff --git a/virtio-spec/rustfmt.toml b/virtio-spec/rustfmt.toml deleted file mode 100644 index f34acacfe1..0000000000 --- a/virtio-spec/rustfmt.toml +++ /dev/null @@ -1,3 +0,0 @@ -format_code_in_doc_comments = true -group_imports = "StdExternalCrate" -imports_granularity = "Module" diff --git a/virtio-spec/src/bitflags.rs b/virtio-spec/src/bitflags.rs deleted file mode 100644 index 02f535b261..0000000000 --- a/virtio-spec/src/bitflags.rs +++ /dev/null @@ -1,432 +0,0 @@ -macro_rules! _bitflags_base { - ( - $(#[$outer:meta])* - $vis:vis struct $BitFlags:ident: $T:ty; - - $($t:tt)* - ) => { - #[cfg_attr( - feature = "zerocopy", - derive( - zerocopy_derive::FromZeroes, - zerocopy_derive::FromBytes, - zerocopy_derive::AsBytes - ) - )] - #[derive(Default, Clone, Copy, PartialEq, Eq, Hash)] - #[repr(transparent)] - $(#[$outer])* - $vis struct $BitFlags($T); - - impl ::core::fmt::Debug for $BitFlags { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - struct Inner<'a>(&'a $BitFlags); - - impl<'a> ::core::fmt::Debug for Inner<'a> { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - if self.0.is_empty() { - f.write_str("0x0") - } else { - ::bitflags::parser::to_writer(self.0, f) - } - } - } - - f.debug_tuple(::core::stringify!($BitFlags)) - .field(&Inner(self)) - .finish() - } - } - - _bitflags_base! { - $($t)* - } - }; - () => {}; -} - -macro_rules! virtio_bitflags { - ( - $(#[$outer:meta])* - $vis:vis struct $BitFlags:ident: $T:ty { - $( - $(#[$inner:ident $($args:tt)*])* - const $Flag:tt = $value:expr; - )* - } - - $($t:tt)* - ) => { - _bitflags_base! { - $(#[$outer])* - $vis struct $BitFlags: $T; - } - - ::bitflags::bitflags! { - impl $BitFlags: $T { - $( - $(#[$inner $($args)*])* - const $Flag = $value; - )* - - const _ = !0; - } - } - - virtio_bitflags! { - $($t)* - } - }; - () => {}; -} - -macro_rules! impl_fmt { - ($Trait:ident for $SelfT:ty) => { - impl ::core::fmt::$Trait for $SelfT { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - self.0.fmt(f) - } - } - }; -} - -macro_rules! endian_bitflags { - ( - $(#[$outer:meta])* - $vis:vis struct $BitFlags:ident: $T:ty { - $( - $(#[$inner:ident $($args:tt)*])* - const $Flag:tt = $value:expr; - )* - } - - $($t:tt)* - ) => { - _bitflags_base! { - $(#[$outer])* - $vis struct $BitFlags: $T; - } - - impl $BitFlags { - $( - $(#[$inner $($args)*])* - pub const $Flag: Self = Self(<$T>::from_ne($value)); - )* - } - - impl ::bitflags::Flags for $BitFlags { - const FLAGS: &'static [::bitflags::Flag] = &[ - $( - ::bitflags::Flag::new(::core::stringify!($Flag), Self::$Flag), - )* - ::bitflags::Flag::new("", Self::all()), - ]; - - type Bits = $T; - - fn from_bits_retain(bits: Self::Bits) -> Self { - Self(bits) - } - - fn bits(&self) -> Self::Bits { - self.0 - } - } - - impl $BitFlags{ - /// Get a flags value with all bits unset. - #[inline] - pub const fn empty() -> Self { - Self(<$T as ::bitflags::Bits>::EMPTY) - } - - /// Get a flags value with all known bits set. - #[inline] - pub const fn all() -> Self { - Self(<$T as ::bitflags::Bits>::ALL) - } - - /// Get the underlying bits value. - /// - /// The returned value is exactly the bits set in this flags value. - #[inline] - pub const fn bits(&self) -> $T { - self.0 - } - - /// Convert from a bits value. - /// - /// This method will return `None` if any unknown bits are set. - #[inline] - pub const fn from_bits(bits: $T) -> Option { - Some(Self(bits)) - } - - /// Convert from a bits value, unsetting any unknown bits. - #[inline] - pub const fn from_bits_truncate(bits: $T) -> Self { - Self(bits) - } - - /// Convert from a bits value exactly. - #[inline] - pub const fn from_bits_retain(bits: $T) -> Self { - Self(bits) - } - - /// Get a flags value with the bits of a flag with the given name set. - /// - /// This method will return `None` if `name` is empty or doesn't - /// correspond to any named flag. - #[inline] - pub fn from_name(name: &str) -> Option { - ::from_name(name) - } - - /// Whether all bits in this flags value are unset. - #[inline] - pub const fn is_empty(&self) -> bool { - self.bits().to_ne() == <$T as ::bitflags::Bits>::EMPTY.to_ne() - } - - /// Whether all known bits in this flags value are set. - #[inline] - pub const fn is_all(&self) -> bool { - Self::all().bits().to_ne() | self.bits().to_ne() == self.bits().to_ne() - } - - /// Whether any set bits in a source flags value are also set in a target flags value. - #[inline] - pub const fn intersects(&self, other: Self) -> bool { - self.bits().to_ne() & other.bits().to_ne() != <$T as ::bitflags::Bits>::EMPTY.to_ne() - } - - /// Whether all set bits in a source flags value are also set in a target flags value. - #[inline] - pub const fn contains(&self, other: Self) -> bool { - self.bits().to_ne() & other.bits().to_ne() == other.bits().to_ne() - } - - /// The bitwise or (`|`) of the bits in two flags values. - #[inline] - pub fn insert(&mut self, other: Self) { - *self = Self::from_bits_retain(self.bits()).union(other); - } - - /// The intersection of a source flags value with the complement of a target flags value (`&!`). - /// - /// This method is not equivalent to `self & !other` when `other` has unknown bits set. - /// `remove` won't truncate `other`, but the `!` operator will. - #[inline] - pub fn remove(&mut self, other: Self) { - *self = Self::from_bits_retain(self.bits()).difference(other); - } - - /// The bitwise exclusive-or (`^`) of the bits in two flags values. - #[inline] - pub fn toggle(&mut self, other: Self) { - *self = Self::from_bits_retain(self.bits()).symmetric_difference(other); - } - - /// Call `insert` when `value` is `true` or `remove` when `value` is `false`. - #[inline] - pub fn set(&mut self, other: Self, value: bool) { - if value { - self.insert(other); - } else { - self.remove(other); - } - } - - /// The bitwise and (`&`) of the bits in two flags values. - #[inline] - #[must_use] - pub const fn intersection(self, other: Self) -> Self { - Self::from_bits_retain(<$T>::from_ne(self.bits().to_ne() & other.bits().to_ne())) - } - - /// The bitwise or (`|`) of the bits in two flags values. - #[inline] - #[must_use] - pub const fn union(self, other: Self) -> Self { - Self::from_bits_retain(<$T>::from_ne(self.bits().to_ne() | other.bits().to_ne())) - } - - /// The intersection of a source flags value with the complement of a target flags value (`&!`). - /// - /// This method is not equivalent to `self & !other` when `other` has unknown bits set. - /// `difference` won't truncate `other`, but the `!` operator will. - #[inline] - #[must_use] - pub const fn difference(self, other: Self) -> Self { - Self::from_bits_retain(<$T>::from_ne(self.bits().to_ne() & !other.bits().to_ne())) - } - - /// The bitwise exclusive-or (`^`) of the bits in two flags values. - #[inline] - #[must_use] - pub const fn symmetric_difference(self, other: Self) -> Self { - Self::from_bits_retain(<$T>::from_ne(self.bits().to_ne() ^ other.bits().to_ne())) - } - - /// The bitwise negation (`!`) of the bits in a flags value, truncating the result. - #[inline] - #[must_use] - pub const fn complement(self) -> Self { - Self::from_bits_truncate(<$T>::from_ne(!self.bits().to_ne())) - } - } - - impl_fmt!(Binary for $BitFlags); - impl_fmt!(Octal for $BitFlags); - impl_fmt!(LowerHex for $BitFlags); - impl_fmt!(UpperHex for $BitFlags); - - impl ::core::ops::BitOr for $BitFlags { - type Output = Self; - - /// The bitwise or (`|`) of the bits in two flags values. - #[inline] - fn bitor(self, other: Self) -> Self { - self.union(other) - } - } - - impl ::core::ops::BitOrAssign for $BitFlags { - /// The bitwise or (`|`) of the bits in two flags values. - #[inline] - fn bitor_assign(&mut self, other: Self) { - self.insert(other); - } - } - - impl ::core::ops::BitXor for $BitFlags { - type Output = Self; - - /// The bitwise exclusive-or (`^`) of the bits in two flags values. - #[inline] - fn bitxor(self, other: Self) -> Self { - self.symmetric_difference(other) - } - } - - impl ::core::ops::BitXorAssign for $BitFlags { - /// The bitwise exclusive-or (`^`) of the bits in two flags values. - #[inline] - fn bitxor_assign(&mut self, other: Self) { - self.toggle(other); - } - } - - impl ::core::ops::BitAnd for $BitFlags { - type Output = Self; - - /// The bitwise and (`&`) of the bits in two flags values. - #[inline] - fn bitand(self, other: Self) -> Self { - self.intersection(other) - } - } - - impl ::core::ops::BitAndAssign for $BitFlags { - /// The bitwise and (`&`) of the bits in two flags values. - #[inline] - fn bitand_assign(&mut self, other: Self) { - *self = Self::from_bits_retain(self.bits()).intersection(other); - } - } - - impl ::core::ops::Sub for $BitFlags { - type Output = Self; - - /// The intersection of a source flags value with the complement of a target flags value (`&!`). - /// - /// This method is not equivalent to `self & !other` when `other` has unknown bits set. - /// `difference` won't truncate `other`, but the `!` operator will. - #[inline] - fn sub(self, other: Self) -> Self { - self.difference(other) - } - } - - impl ::core::ops::SubAssign for $BitFlags { - /// The intersection of a source flags value with the complement of a target flags value (`&!`). - /// - /// This method is not equivalent to `self & !other` when `other` has unknown bits set. - /// `difference` won't truncate `other`, but the `!` operator will. - #[inline] - fn sub_assign(&mut self, other: Self) { - self.remove(other); - } - } - - impl ::core::ops::Not for $BitFlags { - type Output = Self; - - /// The bitwise negation (`!`) of the bits in a flags value, truncating the result. - #[inline] - fn not(self) -> Self { - self.complement() - } - } - - impl ::core::iter::Extend<$BitFlags> for $BitFlags { - /// The bitwise or (`|`) of the bits in each flags value. - fn extend(&mut self, iterator: T) - where - T: ::core::iter::IntoIterator, - { - for item in iterator { - self.insert(item) - } - } - } - - impl ::core::iter::FromIterator<$BitFlags> for $BitFlags { - /// The bitwise or (`|`) of the bits in each flags value. - fn from_iter(iterator: T) -> Self - where - T: ::core::iter::IntoIterator, - { - use ::core::iter::Extend; - - let mut result = Self::empty(); - result.extend(iterator); - result - } - } - - impl $BitFlags { - /// Yield a set of contained flags values. - /// - /// Each yielded flags value will correspond to a defined named flag. Any unknown bits - /// will be yielded together as a final flags value. - #[inline] - pub fn iter(&self) -> ::bitflags::iter::Iter { - ::bitflags::Flags::iter(self) - } - - /// Yield a set of contained named flags values. - /// - /// This method is like [`iter`](#method.iter), except only yields bits in contained named flags. - /// Any unknown bits, or bits not corresponding to a contained flag will not be yielded. - #[inline] - pub fn iter_names(&self) -> ::bitflags::iter::IterNames { - ::bitflags::Flags::iter_names(self) - } - } - - impl ::core::iter::IntoIterator for $BitFlags { - type Item = Self; - type IntoIter = ::bitflags::iter::Iter; - fn into_iter(self) -> Self::IntoIter { - self.iter() - } - } - - endian_bitflags! { - $($t)* - } - }; - () => {}; -} diff --git a/virtio-spec/src/driver_notifications.rs b/virtio-spec/src/driver_notifications.rs deleted file mode 100644 index 4ad12f793a..0000000000 --- a/virtio-spec/src/driver_notifications.rs +++ /dev/null @@ -1,83 +0,0 @@ -use bitfield_struct::bitfield; - -use crate::le32; - -/// Notification Data. -#[bitfield(u32, repr = le32, from = le32::from_ne, into = le32::to_ne)] -pub struct NotificationData { - /// VQ number to be notified. - pub vqn: u16, - - /// Offset - /// within the ring where the next available ring entry - /// will be written. - /// When [`VIRTIO_F_RING_PACKED`] has not been negotiated this refers to the - /// 15 least significant bits of the available index. - /// When `VIRTIO_F_RING_PACKED` has been negotiated this refers to the offset - /// (in units of descriptor entries) - /// within the descriptor ring where the next available - /// descriptor will be written. - /// - /// [`VIRTIO_F_RING_PACKED`]: F::RING_PACKED - #[bits(15)] - pub next_off: u16, - - /// Wrap Counter. - /// With [`VIRTIO_F_RING_PACKED`] this is the wrap counter - /// referring to the next available descriptor. - /// Without `VIRTIO_F_RING_PACKED` this is the most significant bit - /// (bit 15) of the available index. - /// - /// [`VIRTIO_F_RING_PACKED`]: F::RING_PACKED - #[bits(1)] - pub next_wrap: u8, -} - -impl NotificationData { - const NEXT_IDX_BITS: usize = 16; - const NEXT_IDX_OFFSET: usize = 16; - - /// Available index - /// - ///
- /// - /// This collides with [`Self::next_off`] and [`Self::next_wrap`]. - /// - ///
- /// - /// Bits: 16..32 - pub const fn next_idx(&self) -> u16 { - let mask = u32::MAX >> (u32::BITS - Self::NEXT_IDX_BITS as u32); - let this = (le32::to_ne(self.0) >> Self::NEXT_IDX_OFFSET) & mask; - this as u16 - } - - /// Available index - /// - ///
- /// - /// This collides with [`Self::with_next_off`] and [`Self::with_next_wrap`]. - /// - ///
- /// - /// Bits: 16..32 - pub const fn with_next_idx(self, value: u16) -> Self { - let mask = u32::MAX >> (u32::BITS - Self::NEXT_IDX_BITS as u32); - let bits = le32::to_ne(self.0) & !(mask << Self::NEXT_IDX_OFFSET) - | (value as u32 & mask) << Self::NEXT_IDX_OFFSET; - Self(le32::from_ne(bits)) - } - - /// Available index - /// - ///
- /// - /// This collides with [`Self::set_next_off`] and [`Self::set_next_wrap`]. - /// - ///
- /// - /// Bits: 16..32 - pub fn set_next_idx(&mut self, value: u16) { - *self = self.with_next_idx(value); - } -} diff --git a/virtio-spec/src/features.rs b/virtio-spec/src/features.rs deleted file mode 100644 index c43aa17803..0000000000 --- a/virtio-spec/src/features.rs +++ /dev/null @@ -1,523 +0,0 @@ -//! Feature Bits - -use crate::le128; - -/// Feature Bits -#[doc(alias = "VIRTIO_F")] -pub trait FeatureBits: bitflags::Flags -where - Self: From + AsRef + AsMut, - F: From + AsRef + AsMut, -{ - /// Returns the feature that this feature requires. - /// - /// If `self` is a single feature and multiple features are returned, `self` requires only one of them. - /// - /// # Examples - /// - /// ``` - /// # use virtio_spec as virtio; - /// use virtio::FeatureBits; - /// - /// assert_eq!( - /// virtio::net::F::GUEST_TSO4.requirements(), - /// virtio::net::F::GUEST_CSUM - /// ); - /// assert_eq!( - /// virtio::net::F::GUEST_ECN.requirements(), - /// virtio::net::F::GUEST_TSO4 | virtio::net::F::GUEST_TSO6 - /// ); - /// ``` - fn requirements(&self) -> Self { - Self::empty() - } - - /// Returns `true` if all internal feature requirements are satisfied. - /// - /// # Examples - /// - /// ``` - /// # use virtio_spec as virtio; - /// use virtio::FeatureBits; - /// - /// assert!((virtio::net::F::GUEST_TSO4 | virtio::net::F::GUEST_CSUM).requirements_satisfied()); - /// assert!( - /// (virtio::net::F::GUEST_ECN | virtio::net::F::GUEST_TSO4 | virtio::net::F::GUEST_CSUM) - /// .requirements_satisfied() - /// ); - /// ``` - fn requirements_satisfied(&self) -> bool { - self.iter() - .map(|feature| feature.requirements()) - .filter(|requirements| !requirements.is_empty()) - .all(|requirements| self.intersects(requirements)) - } -} - -endian_bitflags! { - /// Device-independent Feature Bits - #[doc(alias = "VIRTIO_F")] - pub struct F: le128 { - /// Negotiating this feature indicates - /// that the driver can use descriptors with the VIRTQ_DESC_F_INDIRECT - /// flag set, as described in _Basic Facilities of a Virtio - /// Device / Virtqueues / The Virtqueue Descriptor Table / Indirect - /// Descriptors_ _Basic Facilities of a Virtio Device / - /// Virtqueues / The Virtqueue Descriptor Table / Indirect - /// Descriptors_ and _Packed Virtqueues / Indirect Flag: Scatter-Gather Support_ _Packed Virtqueues / Indirect Flag: Scatter-Gather Support_. - #[doc(alias = "VIRTIO_F_INDIRECT_DESC")] - const INDIRECT_DESC = 1 << 28; - - /// This feature enables the _used_event_ - /// and the _avail_event_ fields as described in - /// _Basic Facilities of a Virtio Device / Virtqueues / Used Buffer Notification Suppression_, _Basic Facilities of a Virtio Device / Virtqueues / The Virtqueue Used Ring_ and _Packed Virtqueues / Driver and Device Event Suppression_. - #[doc(alias = "VIRTIO_F_EVENT_IDX")] - const EVENT_IDX = 1 << 29; - - /// This indicates compliance with this - /// specification, giving a simple way to detect legacy devices or drivers. - #[doc(alias = "VIRTIO_F_VERSION_1")] - const VERSION_1 = 1 << 32; - - /// This feature indicates that - /// the device can be used on a platform where device access to data - /// in memory is limited and/or translated. E.g. this is the case if the device can be located - /// behind an IOMMU that translates bus addresses from the device into physical - /// addresses in memory, if the device can be limited to only access - /// certain memory addresses or if special commands such as - /// a cache flush can be needed to synchronise data in memory with - /// the device. Whether accesses are actually limited or translated - /// is described by platform-specific means. - /// If this feature bit is set to 0, then the device - /// has same access to memory addresses supplied to it as the - /// driver has. - /// In particular, the device will always use physical addresses - /// matching addresses used by the driver (typically meaning - /// physical addresses used by the CPU) - /// and not translated further, and can access any address supplied to it by - /// the driver. When clear, this overrides any platform-specific description of - /// whether device access is limited or translated in any way, e.g. - /// whether an IOMMU may be present. - #[doc(alias = "VIRTIO_F_ACCESS_PLATFORM")] - const ACCESS_PLATFORM = 1 << 33; - - /// This feature indicates - /// support for the packed virtqueue layout as described in - /// _Basic Facilities of a Virtio Device / Packed Virtqueues_ _Basic Facilities of a Virtio Device / Packed Virtqueues_. - #[doc(alias = "VIRTIO_F_RING_PACKED")] - const RING_PACKED = 1 << 34; - - /// This feature indicates - /// that all buffers are used by the device in the same - /// order in which they have been made available. - #[doc(alias = "VIRTIO_F_IN_ORDER")] - const IN_ORDER = 1 << 35; - - /// This feature indicates - /// that memory accesses by the driver and the device are ordered - /// in a way described by the platform. - /// - /// If this feature bit is negotiated, the ordering in effect for any - /// memory accesses by the driver that need to be ordered in a specific way - /// with respect to accesses by the device is the one suitable for devices - /// described by the platform. This implies that the driver needs to use - /// memory barriers suitable for devices described by the platform; e.g. - /// for the PCI transport in the case of hardware PCI devices. - /// - /// If this feature bit is not negotiated, then the device - /// and driver are assumed to be implemented in software, that is - /// they can be assumed to run on identical CPUs - /// in an SMP configuration. - /// Thus a weaker form of memory barriers is sufficient - /// to yield better performance. - #[doc(alias = "VIRTIO_F_ORDER_PLATFORM")] - const ORDER_PLATFORM = 1 << 36; - - /// This feature indicates that - /// the device supports Single Root I/O Virtualization. - /// Currently only PCI devices support this feature. - #[doc(alias = "VIRTIO_F_SR_IOV")] - const SR_IOV = 1 << 37; - - /// This feature indicates - /// that the driver passes extra data (besides identifying the virtqueue) - /// in its device notifications. - /// See _Virtqueues / Driver notifications_ _Virtqueues / Driver notifications_. - #[doc(alias = "VIRTIO_F_NOTIFICATION_DATA")] - const NOTIFICATION_DATA = 1 << 38; - - /// This feature indicates that the driver - /// uses the data provided by the device as a virtqueue identifier in available - /// buffer notifications. - /// As mentioned in section _Virtqueues / Driver notifications_, when the - /// driver is required to send an available buffer notification to the device, it - /// sends the virtqueue number to be notified. The method of delivering - /// notifications is transport specific. - /// With the PCI transport, the device can optionally provide a per-virtqueue value - /// for the driver to use in driver notifications, instead of the virtqueue number. - /// Some devices may benefit from this flexibility by providing, for example, - /// an internal virtqueue identifier, or an internal offset related to the - /// virtqueue number. - /// - /// This feature indicates the availability of such value. The definition of the - /// data to be provided in driver notification and the delivery method is - /// transport specific. - /// For more details about driver notifications over PCI see _Virtio Transport Options / Virtio Over PCI Bus / PCI-specific Initialization And Device Operation / Available Buffer Notifications_. - #[doc(alias = "VIRTIO_F_NOTIF_CONFIG_DATA")] - const NOTIF_CONFIG_DATA = 1 << 39; - - /// This feature indicates - /// that the driver can reset a queue individually. - /// See _Basic Facilities of a Virtio Device / Virtqueues / Virtqueue Reset_. - #[doc(alias = "VIRTIO_F_RING_RESET")] - const RING_RESET = 1 << 40; - } -} - -impl AsRef for F { - fn as_ref(&self) -> &F { - self - } -} - -impl AsMut for F { - fn as_mut(&mut self) -> &mut F { - self - } -} - -impl FeatureBits for F {} - -macro_rules! feature_bits { - ( - $(#[$outer:meta])* - $vis:vis struct $BitFlags:ident: $T:ty { - $( - $(#[$inner:ident $($args:tt)*])* - const $Flag:tt = $value:expr; - )* - } - - $($t:tt)* - ) => { - endian_bitflags! { - $(#[$outer])* - $vis struct $BitFlags: $T { - $( - $(#[$inner $($args)*])* - const $Flag = $value; - )* - - /// Device-independent Bit. See [`virtio::F::INDIRECT_DESC`](crate::F::INDIRECT_DESC). - const INDIRECT_DESC = $crate::F::INDIRECT_DESC.bits().to_ne(); - - /// Device-independent Bit. See [`virtio::F::EVENT_IDX`](crate::F::EVENT_IDX). - const EVENT_IDX = $crate::F::EVENT_IDX.bits().to_ne(); - - /// Device-independent Bit. See [`virtio::F::VERSION_1`](crate::F::VERSION_1). - const VERSION_1 = $crate::F::VERSION_1.bits().to_ne(); - - /// Device-independent Bit. See [`virtio::F::ACCESS_PLATFORM`](crate::F::ACCESS_PLATFORM). - const ACCESS_PLATFORM = $crate::F::ACCESS_PLATFORM.bits().to_ne(); - - /// Device-independent Bit. See [`virtio::F::RING_PACKED`](crate::F::RING_PACKED). - const RING_PACKED = $crate::F::RING_PACKED.bits().to_ne(); - - /// Device-independent Bit. See [`virtio::F::IN_ORDER`](crate::F::IN_ORDER). - const IN_ORDER = $crate::F::IN_ORDER.bits().to_ne(); - - /// Device-independent Bit. See [`virtio::F::ORDER_PLATFORM`](crate::F::ORDER_PLATFORM). - const ORDER_PLATFORM = $crate::F::ORDER_PLATFORM.bits().to_ne(); - - /// Device-independent Bit. See [`virtio::F::SR_IOV`](crate::F::SR_IOV). - const SR_IOV = $crate::F::SR_IOV.bits().to_ne(); - - /// Device-independent Bit. See [`virtio::F::NOTIFICATION_DATA`](crate::F::NOTIFICATION_DATA). - const NOTIFICATION_DATA = $crate::F::NOTIFICATION_DATA.bits().to_ne(); - - /// Device-independent Bit. See [`virtio::F::NOTIF_CONFIG_DATA`](crate::F::NOTIF_CONFIG_DATA). - const NOTIF_CONFIG_DATA = $crate::F::NOTIF_CONFIG_DATA.bits().to_ne(); - - /// Device-independent Bit. See [`virtio::F::RING_RESET`](crate::F::RING_RESET). - const RING_RESET = $crate::F::RING_RESET.bits().to_ne(); - } - } - - impl From<$crate::F> for $BitFlags { - fn from(value: $crate::F) -> Self { - Self::from_bits_retain(value.bits()) - } - } - - impl AsRef<$BitFlags> for $crate::F { - fn as_ref(&self) -> &$BitFlags { - unsafe { &*(self as *const Self as *const $BitFlags) } - } - } - - impl AsMut<$BitFlags> for $crate::F { - fn as_mut(&mut self) -> &mut $BitFlags { - unsafe { &mut *(self as *mut Self as *mut $BitFlags) } - } - } - - impl From<$BitFlags> for $crate::F { - /// Returns the device-independent feature bits while retaining device-specific feature bits. - fn from(value: $BitFlags) -> Self { - $crate::F::from_bits_retain(value.bits()) - } - } - - impl AsRef<$crate::F> for $BitFlags { - /// Returns a shared reference to the device-independent features while retaining device-specific feature bits. - fn as_ref(&self) -> &$crate::F { - unsafe { &*(self as *const Self as *const $crate::F) } - } - } - - impl AsMut<$crate::F> for $BitFlags { - /// Returns a mutable reference to the device-independent features while retaining device-specific feature bits. - fn as_mut(&mut self) -> &mut $crate::F { - unsafe { &mut *(self as *mut Self as *mut $crate::F) } - } - } - - feature_bits! { - $($t)* - } - }; - () => {}; -} - -pub mod net { - use crate::le128; - - feature_bits! { - /// Network Device Feature Bits - #[doc(alias = "VIRTIO_NET_F")] - pub struct F: le128 { - /// Device handles packets with partial checksum. This - /// “checksum offload” is a common feature on modern network cards. - #[doc(alias = "VIRTIO_NET_F_CSUM")] - const CSUM = 1 << 0; - - /// Driver handles packets with partial checksum. - #[doc(alias = "VIRTIO_NET_F_GUEST_CSUM")] - const GUEST_CSUM = 1 << 1; - - /// Control channel offloads - /// reconfiguration support. - #[doc(alias = "VIRTIO_NET_F_CTRL_GUEST_OFFLOADS")] - const CTRL_GUEST_OFFLOADS = 1 << 2; - - /// Device maximum MTU reporting is supported. If - /// offered by the device, device advises driver about the value of - /// its maximum MTU. If negotiated, the driver uses _mtu_ as - /// the maximum MTU value. - #[doc(alias = "VIRTIO_NET_F_MTU")] - const MTU = 1 << 3; - - /// Device has given MAC address. - #[doc(alias = "VIRTIO_NET_F_MAC")] - const MAC = 1 << 5; - - /// Driver can receive TSOv4. - #[doc(alias = "VIRTIO_NET_F_GUEST_TSO4")] - const GUEST_TSO4 = 1 << 7; - - /// Driver can receive TSOv6. - #[doc(alias = "VIRTIO_NET_F_GUEST_TSO6")] - const GUEST_TSO6 = 1 << 8; - - /// Driver can receive TSO with ECN. - #[doc(alias = "VIRTIO_NET_F_GUEST_ECN")] - const GUEST_ECN = 1 << 9; - - /// Driver can receive UFO. - #[doc(alias = "VIRTIO_NET_F_GUEST_UFO")] - const GUEST_UFO = 1 << 10; - - /// Device can receive TSOv4. - #[doc(alias = "VIRTIO_NET_F_HOST_TSO4")] - const HOST_TSO4 = 1 << 11; - - /// Device can receive TSOv6. - #[doc(alias = "VIRTIO_NET_F_HOST_TSO6")] - const HOST_TSO6 = 1 << 12; - - /// Device can receive TSO with ECN. - #[doc(alias = "VIRTIO_NET_F_HOST_ECN")] - const HOST_ECN = 1 << 13; - - /// Device can receive UFO. - #[doc(alias = "VIRTIO_NET_F_HOST_UFO")] - const HOST_UFO = 1 << 14; - - /// Driver can merge receive buffers. - #[doc(alias = "VIRTIO_NET_F_MRG_RXBUF")] - const MRG_RXBUF = 1 << 15; - - /// Configuration status field is - /// available. - #[doc(alias = "VIRTIO_NET_F_STATUS")] - const STATUS = 1 << 16; - - /// Control channel is available. - #[doc(alias = "VIRTIO_NET_F_CTRL_VQ")] - const CTRL_VQ = 1 << 17; - - /// Control channel RX mode support. - #[doc(alias = "VIRTIO_NET_F_CTRL_RX")] - const CTRL_RX = 1 << 18; - - /// Control channel VLAN filtering. - #[doc(alias = "VIRTIO_NET_F_CTRL_VLAN")] - const CTRL_VLAN = 1 << 19; - - /// Driver can send gratuitous - /// packets. - #[doc(alias = "VIRTIO_NET_F_GUEST_ANNOUNCE")] - const GUEST_ANNOUNCE = 1 << 21; - - /// Device supports multiqueue with automatic - /// receive steering. - #[doc(alias = "VIRTIO_NET_F_MQ")] - const MQ = 1 << 22; - - /// Set MAC address through control - /// channel. - #[doc(alias = "VIRTIO_NET_F_CTRL_MAC_ADDR")] - const CTRL_MAC_ADDR = 1 << 23; - - /// Device can receive USO packets. Unlike UFO - /// (fragmenting the packet) the USO splits large UDP packet - /// to several segments when each of these smaller packets has UDP header. - #[doc(alias = "VIRTIO_NET_F_HOST_USO")] - const HOST_USO = 1 << 56; - - /// Device can report per-packet hash - /// value and a type of calculated hash. - #[doc(alias = "VIRTIO_NET_F_HASH_REPORT")] - const HASH_REPORT = 1 << 57; - - /// Driver can provide the exact _hdr_len_ - /// value. Device benefits from knowing the exact header length. - #[doc(alias = "VIRTIO_NET_F_GUEST_HDRLEN")] - const GUEST_HDRLEN = 1 << 59; - - /// Device supports RSS (receive-side scaling) - /// with Toeplitz hash calculation and configurable hash - /// parameters for receive steering. - #[doc(alias = "VIRTIO_NET_F_RSS")] - const RSS = 1 << 60; - - /// Device can process duplicated ACKs - /// and report number of coalesced segments and duplicated ACKs. - #[doc(alias = "VIRTIO_NET_F_RSC_EXT")] - const RSC_EXT = 1 << 61; - - /// Device may act as a standby for a primary - /// device with the same MAC address. - #[doc(alias = "VIRTIO_NET_F_STANDBY")] - const STANDBY = 1 << 62; - - /// Device reports speed and duplex. - #[doc(alias = "VIRTIO_NET_F_SPEED_DUPLEX")] - const SPEED_DUPLEX = 1 << 63; - } - } - - impl crate::FeatureBits for F { - fn requirements(&self) -> Self { - let mut requirements = Self::empty(); - - for feature in self.iter() { - let requirement = match feature { - Self::GUEST_TSO4 => Self::GUEST_CSUM, - Self::GUEST_TSO6 => Self::GUEST_CSUM, - Self::GUEST_ECN => Self::GUEST_TSO4 | Self::GUEST_TSO6, - Self::GUEST_UFO => Self::GUEST_CSUM, - Self::HOST_TSO4 => Self::CSUM, - Self::HOST_TSO6 => Self::CSUM, - Self::HOST_ECN => Self::HOST_TSO4 | Self::HOST_TSO6, - Self::HOST_UFO => Self::CSUM, - Self::HOST_USO => Self::CSUM, - Self::CTRL_RX => Self::CTRL_VQ, - Self::CTRL_VLAN => Self::CTRL_VQ, - Self::GUEST_ANNOUNCE => Self::CTRL_VQ, - Self::MQ => Self::CTRL_VQ, - Self::CTRL_MAC_ADDR => Self::CTRL_VQ, - Self::RSC_EXT => Self::HOST_TSO4 | Self::HOST_TSO6, - Self::RSS => Self::CTRL_VQ, - _ => Self::empty(), - }; - requirements.insert(requirement); - } - - requirements - } - } -} - -pub mod fs { - use crate::le128; - - feature_bits! { - /// File System Device Feature Bits - #[doc(alias = "VIRTIO_FS_F")] - pub struct F: le128 { - /// Device has support for FUSE notify - /// messages. The notification queue is virtqueue 1. - #[doc(alias = "VIRTIO_FS_F_NOTIFICATION")] - const NOTIFICATION = 1 << 0; - } - } - - impl crate::FeatureBits for F {} -} - -pub mod vsock { - use crate::le128; - - feature_bits! { - /// Socket Device Feature Bits - #[doc(alias = "VIRTIO_VSOCK_F")] - pub struct F: le128 { - /// stream socket type is supported. - #[doc(alias = "VIRTIO_VSOCK_F_STREAM")] - const STREAM = 1 << 0; - - /// seqpacket socket type is supported. - #[doc(alias = "VIRTIO_VSOCK_F_SEQPACKET")] - const SEQPACKET = 1 << 1; - } - } - - impl crate::FeatureBits for F {} -} - -#[cfg(test)] -mod tests { - use super::*; - - #[rustfmt::skip] - #[test] - fn requirements_satisfied() { - assert!(F::INDIRECT_DESC.requirements_satisfied()); - - assert!(net::F::CSUM.requirements_satisfied()); - - assert!(!net::F::MQ.requirements_satisfied()); - assert!((net::F::MQ | net::F::CTRL_VQ).requirements_satisfied()); - - assert!(!net::F::HOST_TSO4.requirements_satisfied()); - assert!((net::F::HOST_TSO4 | net::F::CSUM).requirements_satisfied()); - assert!((net::F::HOST_TSO4 | net::F::HOST_TSO6 | net::F::CSUM).requirements_satisfied()); - - assert!(!net::F::HOST_ECN.requirements_satisfied()); - assert!(!(net::F::HOST_ECN | net::F::CSUM).requirements_satisfied()); - assert!(!(net::F::HOST_ECN | net::F::HOST_TSO4).requirements_satisfied()); - assert!((net::F::HOST_ECN | net::F::HOST_TSO4 | net::F::CSUM).requirements_satisfied()); - assert!((net::F::HOST_ECN | net::F::HOST_TSO4 | net::F::HOST_TSO6 | net::F::CSUM).requirements_satisfied()); - } -} diff --git a/virtio-spec/src/fs.rs b/virtio-spec/src/fs.rs deleted file mode 100644 index 0a35b712b5..0000000000 --- a/virtio-spec/src/fs.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! File System Device - -use volatile::access::ReadOnly; -use volatile_macro::VolatileFieldAccess; - -pub use super::features::fs::F; -use super::le32; - -/// Device configuration -#[doc(alias = "virtio_fs_config")] -#[cfg_attr( - feature = "zerocopy", - derive(zerocopy_derive::FromZeroes, zerocopy_derive::FromBytes) -)] -#[derive(VolatileFieldAccess)] -#[repr(C)] -pub struct Config { - /// This is the name associated with this file system. The tag is - /// encoded in UTF-8 and padded with NUL bytes if shorter than the - /// available space. This field is not NUL-terminated if the encoded bytes - /// take up the entire field. - #[access(ReadOnly)] - tag: [u8; 36], - - /// This is the total number of request virtqueues - /// exposed by the device. Each virtqueue offers identical functionality and - /// there are no ordering guarantees between requests made available on - /// different queues. Use of multiple queues is intended to increase - /// performance. - #[access(ReadOnly)] - num_request_queues: le32, - - /// This is the minimum number of bytes required for each - /// buffer in the notification queue. - #[access(ReadOnly)] - notify_buf_size: le32, -} diff --git a/virtio-spec/src/lib.rs b/virtio-spec/src/lib.rs deleted file mode 100644 index 5860f79d8f..0000000000 --- a/virtio-spec/src/lib.rs +++ /dev/null @@ -1,284 +0,0 @@ -//! This crate provides common definitions from the Virtio 1.2 specification. -//! This crate does not provide any additional driver-related functionality. -//! -//! For the actual specification see [Virtual I/O Device (VIRTIO) Version 1.2—Committee Specification 01](https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.html). - -#![cfg_attr(not(test), no_std)] -#![cfg_attr(feature = "nightly", feature(allocator_api))] - -#[cfg(feature = "alloc")] -extern crate alloc; - -#[macro_use] -mod bitflags; -#[macro_use] -pub mod volatile; -#[cfg(any(feature = "mmio", feature = "pci"))] -mod driver_notifications; -mod features; -pub mod fs; -#[cfg(feature = "mmio")] -pub mod mmio; -pub mod net; -#[cfg(feature = "pci")] -pub mod pci; -pub mod pvirtq; -pub mod virtq; -pub mod vsock; - -pub use endian_num::{be128, be16, be32, be64, le128, le16, le32, le64, Be, Le}; -use num_enum::{FromPrimitive, IntoPrimitive, TryFromPrimitive}; - -pub use self::features::{FeatureBits, F}; - -virtio_bitflags! { - /// Device Status Field - /// - /// During device initialization by a driver, - /// the driver follows the sequence of steps specified in - /// _General Initialization And Device Operation / Device - /// Initialization_. - /// - /// The `device status` field provides a simple low-level - /// indication of the completed steps of this sequence. - /// It's most useful to imagine it hooked up to traffic - /// lights on the console indicating the status of each device. The - /// following bits are defined (listed below in the order in which - /// they would be typically set): - pub struct DeviceStatus: u8 { - /// Indicates that the guest OS has found the - /// device and recognized it as a valid virtio device. - const ACKNOWLEDGE = 1; - - /// Indicates that the guest OS knows how to drive the - /// device. - /// - ///
- /// - /// There could be a significant (or infinite) delay before setting - /// this bit. For example, under Linux, drivers can be loadable modules. - /// - ///
- const DRIVER = 2; - - /// Indicates that something went wrong in the guest, - /// and it has given up on the device. This could be an internal - /// error, or the driver didn't like the device for some reason, or - /// even a fatal error during device operation. - const FAILED = 128; - - /// Indicates that the driver has acknowledged all the - /// features it understands, and feature negotiation is complete. - const FEATURES_OK = 8; - - /// Indicates that the driver is set up and ready to - /// drive the device. - const DRIVER_OK = 4; - - /// Indicates that the device has experienced - /// an error from which it can't recover. - const DEVICE_NEEDS_RESET = 64; - } -} - -/// Virtio Device IDs -#[derive(IntoPrimitive, FromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] -#[non_exhaustive] -#[repr(u8)] -pub enum Id { - /// reserved (invalid) - Reserved = 0, - - /// network card - Net = 1, - - /// block device - Block = 2, - - /// console - Console = 3, - - /// entropy source - Rng = 4, - - /// memory ballooning (traditional) - Balloon = 5, - - /// ioMemory - IoMem = 6, - - /// rpmsg - Rpmsg = 7, - - /// SCSI host - Scsi = 8, - - /// 9P transport - NineP = 9, - - /// mac80211 wlan - Mac80211Wlan = 10, - - /// rproc serial - RprocSerial = 11, - - /// virtio CAIF - Caif = 12, - - /// memory balloon - MemoryBalloon = 13, - - /// GPU device - Gpu = 16, - - /// Timer/Clock device - Clock = 17, - - /// Input device - Input = 18, - - /// Socket device - Vsock = 19, - - /// Crypto device - Crypto = 20, - - /// Signal Distribution Module - SignalDist = 21, - - /// pstore device - Pstore = 22, - - /// IOMMU device - Iommu = 23, - - /// Memory device - Mem = 24, - - /// Audio device - Sound = 25, - - /// file system device - Fs = 26, - - /// PMEM device - Pmem = 27, - - /// RPMB device - Rpmb = 28, - - /// mac80211 hwsim wireless simulation device - Mac80211Hwsim = 29, - - /// Video encoder device - VideoEncoder = 30, - - /// Video decoder device - VideoDecoder = 31, - - /// SCMI device - Scmi = 32, - - /// NitroSecureModule - NitroSecMod = 33, - - /// I2C adapter - I2cAdapter = 34, - - /// Watchdog - Watchdog = 35, - - /// CAN device - Can = 36, - - /// Parameter Server - ParamServ = 38, - - /// Audio policy device - AudioPolicy = 39, - - /// Bluetooth device - Bt = 40, - - /// GPIO device - Gpio = 41, - - /// RDMA device - Rdma = 42, - - /// Unknown device - #[num_enum(catch_all)] - Unknown(u8), -} - -/// Descriptor Ring Change Event Flags -#[doc(alias = "RING_EVENT_FLAGS")] -#[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] -#[non_exhaustive] -#[repr(u8)] -pub enum RingEventFlags { - /// Enable events - #[doc(alias = "RING_EVENT_FLAGS_ENABLE")] - Enable = 0x0, - - /// Disable events - #[doc(alias = "RING_EVENT_FLAGS_DISABLE")] - Disable = 0x1, - - /// Enable events for a specific descriptor - /// (as specified by Descriptor Ring Change Event Offset/Wrap Counter). - /// Only valid if VIRTIO_F_EVENT_IDX has been negotiated. - #[doc(alias = "RING_EVENT_FLAGS_DESC")] - Desc = 0x2, - - Reserved = 0x3, -} - -impl RingEventFlags { - const fn from_bits(bits: u8) -> Self { - match bits { - 0x0 => Self::Enable, - 0x1 => Self::Disable, - 0x2 => Self::Desc, - 0x3 => Self::Reserved, - _ => unreachable!(), - } - } - - const fn into_bits(self) -> u8 { - self as u8 - } -} - -/// Common device configuration space functionality. -pub trait DeviceConfigSpace: Sized { - /// Read from device configuration space. - /// - /// This function should be used when reading from fields greater than - /// 32 bits wide or when reading from multiple fields. - /// - /// As described in _Driver Requirements: Device Configuration Space_, - /// this method checks the configuration atomicity value of the device - /// and only returns once the value was the same before and after the - /// provided function. - /// - /// # Examples - /// - /// ```rust - /// # use virtio_spec as virtio; - /// use virtio::net::ConfigVolatileFieldAccess; - /// use virtio::DeviceConfigSpace; - /// use volatile::access::ReadOnly; - /// use volatile::VolatilePtr; - /// - /// fn read_mac( - /// common_cfg: VolatilePtr<'_, virtio::pci::CommonCfg, ReadOnly>, - /// net_cfg: VolatilePtr<'_, virtio::net::Config, ReadOnly>, - /// ) -> [u8; 6] { - /// common_cfg.read_config_with(|| net_cfg.mac().read()) - /// } - /// ``` - fn read_config_with(self, f: F) -> T - where - F: FnMut() -> T; -} diff --git a/virtio-spec/src/mmio.rs b/virtio-spec/src/mmio.rs deleted file mode 100644 index 899b1e7706..0000000000 --- a/virtio-spec/src/mmio.rs +++ /dev/null @@ -1,595 +0,0 @@ -//! Definitions for Virtio over MMIO. - -use core::mem; - -use volatile::access::{ReadOnly, ReadWrite, Readable, RestrictAccess, WriteOnly}; -use volatile::VolatilePtr; - -#[doc(inline)] -pub use crate::driver_notifications::NotificationData; -use crate::volatile::{OveralignedVolatilePtr, WideVolatilePtr}; -use crate::{le16, le32, DeviceConfigSpace, DeviceStatus, Id}; - -/// MMIO Device Registers -#[repr(transparent)] -pub struct DeviceRegisters([le32; 0x100 / mem::size_of::()]); - -macro_rules! field_fn { - ( - $(#[doc = $doc:literal])* - #[doc(alias = $alias:literal)] - #[access($Access:ty)] - $field:ident: le32, - ) => { - $(#[doc = $doc])* - #[doc(alias = $alias)] - fn $field(self) -> VolatilePtr<'a, le32, A::Restricted> - where - A: RestrictAccess<$Access>; - }; - ( - $(#[doc = $doc:literal])* - #[doc(alias = $alias:literal)] - #[access($Access:ty)] - $field:ident: (), - ) => { - $(#[doc = $doc])* - #[doc(alias = $alias)] - fn $field(self) -> VolatilePtr<'a, (), A::Restricted> - where - A: RestrictAccess<$Access>; - }; - ( - $(#[doc = $doc:literal])* - #[doc(alias = $alias:literal)] - #[access($Access:ty)] - $field:ident: $T:ty, - ) => { - $(#[doc = $doc])* - #[doc(alias = $alias)] - fn $field(self) -> OveralignedVolatilePtr<'a, $T, le32, A::Restricted> - where - A: RestrictAccess<$Access>; - }; -} - -macro_rules! field_impl { - ( - #[offset($offset:literal)] - #[access($Access:ty)] - $field:ident: le32, - ) => { - fn $field(self) -> VolatilePtr<'a, le32, A::Restricted> - where - A: RestrictAccess<$Access>, - { - unsafe { - self.map(|ptr| ptr.cast::().byte_add($offset)) - .restrict() - } - } - }; - ( - #[offset($offset:literal)] - #[access($Access:ty)] - $field:ident: (), - ) => { - fn $field(self) -> VolatilePtr<'a, (), A::Restricted> - where - A: RestrictAccess<$Access>, - { - unsafe { - self.map(|ptr| ptr.cast::<()>().byte_add($offset)) - .restrict() - } - } - }; - ( - #[offset($offset:literal)] - #[access($Access:ty)] - $field:ident: $T:ty, - ) => { - fn $field(self) -> OveralignedVolatilePtr<'a, $T, le32, A::Restricted> - where - A: RestrictAccess<$Access>, - { - let ptr = unsafe { self.map(|ptr| ptr.cast::().byte_add($offset)) }; - OveralignedVolatilePtr::new(ptr.restrict()) - } - }; -} - -macro_rules! device_register_impl { - ( - $(#[doc = $outer_doc:literal])* - pub struct DeviceRegisters { - $( - $(#[doc = $doc:literal])* - #[doc(alias = $alias:literal)] - #[offset($offset:literal)] - #[access($Access:ty)] - $field:ident: $T:tt, - )* - } - ) => { - $(#[doc = $outer_doc])* - pub trait DeviceRegisterVolatileFieldAccess<'a, A> { - $( - field_fn! { - $(#[doc = $doc])* - #[doc(alias = $alias)] - #[access($Access)] - $field: $T, - } - )* - } - - impl<'a, A> DeviceRegisterVolatileFieldAccess<'a, A> for VolatilePtr<'a, DeviceRegisters, A> { - $( - field_impl! { - #[offset($offset)] - #[access($Access)] - $field: $T, - } - )* - } - }; -} - -device_register_impl! { - /// MMIO Device Registers - pub struct DeviceRegisters { - /// Magic Value - /// - /// 0x74726976 - /// (a Little Endian equivalent of the “virt” string). - #[doc(alias = "MagicValue")] - #[offset(0x000)] - #[access(ReadOnly)] - magic_value: le32, - - /// Device version number - /// - /// 0x2. - /// - ///
- /// - /// Legacy devices (see _Virtio Transport Options / Virtio Over MMIO / Legacy interface_) used 0x1. - /// - ///
- #[doc(alias = "Version")] - #[offset(0x004)] - #[access(ReadOnly)] - version: le32, - - /// Virtio Subsystem Device ID - /// - /// See _Device Types_ for possible values. - /// Value zero (0x0) is used to - /// define a system memory map with placeholder devices at static, - /// well known addresses, assigning functions to them depending - /// on user's needs. - #[doc(alias = "DeviceID")] - #[offset(0x008)] - #[access(ReadOnly)] - device_id: Id, - - /// Virtio Subsystem Vendor ID - #[doc(alias = "VendorID")] - #[offset(0x00c)] - #[access(ReadOnly)] - vendor_id: le32, - - /// Flags representing features the device supports - /// - /// Reading from this register returns 32 consecutive flag bits, - /// the least significant bit depending on the last value written to - /// `DeviceFeaturesSel`. Access to this register returns - /// bits `DeviceFeaturesSel`*32 to (`DeviceFeaturesSel`*32)+31, eg. - /// feature bits 0 to 31 if `DeviceFeaturesSel` is set to 0 and - /// features bits 32 to 63 if `DeviceFeaturesSel` is set to 1. - /// Also see _Basic Facilities of a Virtio Device / Feature Bits_. - #[doc(alias = "DeviceFeatures")] - #[offset(0x010)] - #[access(ReadOnly)] - device_features: le32, - - /// Device (host) features word selection. - /// - /// Writing to this register selects a set of 32 device feature bits - /// accessible by reading from `DeviceFeatures`. - #[doc(alias = "DeviceFeaturesSel")] - #[offset(0x014)] - #[access(WriteOnly)] - device_features_sel: le32, - - /// Flags representing device features understood and activated by the driver - /// - /// Writing to this register sets 32 consecutive flag bits, the least significant - /// bit depending on the last value written to `DriverFeaturesSel`. - /// Access to this register sets bits `DriverFeaturesSel`*32 - /// to (`DriverFeaturesSel`*32)+31, eg. feature bits 0 to 31 if - /// `DriverFeaturesSel` is set to 0 and features bits 32 to 63 if - /// `DriverFeaturesSel` is set to 1. Also see _Basic Facilities of a Virtio Device / Feature Bits_. - #[doc(alias = "DriverFeatures")] - #[offset(0x020)] - #[access(WriteOnly)] - driver_features: le32, - - /// Activated (guest) features word selection - /// - /// Writing to this register selects a set of 32 activated feature - /// bits accessible by writing to `DriverFeatures`. - #[doc(alias = "DriverFeaturesSel")] - #[offset(0x024)] - #[access(WriteOnly)] - driver_features_sel: le32, - - /// Virtual queue index - /// - /// Writing to this register selects the virtual queue that the - /// following operations on `QueueNumMax`, `QueueNum`, `QueueReady`, - /// `QueueDescLow`, `QueueDescHigh`, `QueueDriverlLow`, `QueueDriverHigh`, - /// `QueueDeviceLow`, `QueueDeviceHigh` and `QueueReset` apply to. The index - /// number of the first queue is zero (0x0). - #[doc(alias = "QueueSel")] - #[offset(0x030)] - #[access(WriteOnly)] - queue_sel: le16, - - /// Maximum virtual queue size - /// - /// Reading from the register returns the maximum size (number of - /// elements) of the queue the device is ready to process or - /// zero (0x0) if the queue is not available. This applies to the - /// queue selected by writing to `QueueSel`. - #[doc(alias = "QueueNumMax")] - #[offset(0x034)] - #[access(ReadOnly)] - queue_num_max: le16, - - /// Virtual queue size - /// - /// Queue size is the number of elements in the queue. - /// Writing to this register notifies the device what size of the - /// queue the driver will use. This applies to the queue selected by - /// writing to `QueueSel`. - #[doc(alias = "QueueNum")] - #[offset(0x038)] - #[access(WriteOnly)] - queue_num: le16, - - /// Virtual queue ready bit - /// - /// Writing one (0x1) to this register notifies the device that it can - /// execute requests from this virtual queue. Reading from this register - /// returns the last value written to it. Both read and write - /// accesses apply to the queue selected by writing to `QueueSel`. - #[doc(alias = "QueueReady")] - #[offset(0x044)] - #[access(ReadWrite)] - queue_ready: bool, - - /// Queue notifier - /// - /// Writing a value to this register notifies the device that - /// there are new buffers to process in a queue. - /// - /// When VIRTIO_F_NOTIFICATION_DATA has not been negotiated, - /// the value written is the queue index. - /// - /// When VIRTIO_F_NOTIFICATION_DATA has been negotiated, - /// the `Notification data` value has the following format: - /// - /// ```c - /// le32 { - /// vqn : 16; - /// next_off : 15; - /// next_wrap : 1; - /// }; - /// ``` - /// - /// See _Virtqueues / Driver notifications_ - /// for the definition of the components. - #[doc(alias = "QueueNotify")] - #[offset(0x050)] - #[access(WriteOnly)] - queue_notify: le32, - - /// Interrupt status - /// - /// Reading from this register returns a bit mask of events that - /// caused the device interrupt to be asserted. - #[doc(alias = "InterruptStatus")] - #[offset(0x060)] - #[access(ReadOnly)] - interrupt_status: InterruptStatus, - - /// Interrupt acknowledge - /// - /// Writing a value with bits set as defined in `InterruptStatus` - /// to this register notifies the device that events causing - /// the interrupt have been handled. - #[doc(alias = "InterruptACK")] - #[offset(0x064)] - #[access(WriteOnly)] - interrupt_ack: InterruptStatus, - - /// Device status - /// - /// Reading from this register returns the current device status - /// flags. - /// Writing non-zero values to this register sets the status flags, - /// indicating the driver progress. Writing zero (0x0) to this - /// register triggers a device reset. - /// See also p. _Virtio Transport Options / Virtio Over MMIO / MMIO-specific Initialization And Device Operation / Device Initialization_. - #[doc(alias = "Status")] - #[offset(0x070)] - #[access(ReadWrite)] - status: DeviceStatus, - - /// Virtual queue's Descriptor Area 64 bit long physical address - /// - /// Writing to these two registers (lower 32 bits of the address - /// to `QueueDescLow`, higher 32 bits to `QueueDescHigh`) notifies - /// the device about location of the Descriptor Area of the queue - /// selected by writing to `QueueSel` register. - #[doc(alias = "QueueDescLow")] - #[offset(0x080)] - #[access(WriteOnly)] - queue_desc_low: le32, - - /// Virtual queue's Descriptor Area 64 bit long physical address - /// - /// Writing to these two registers (lower 32 bits of the address - /// to `QueueDescLow`, higher 32 bits to `QueueDescHigh`) notifies - /// the device about location of the Descriptor Area of the queue - /// selected by writing to `QueueSel` register. - #[doc(alias = "QueueDescHigh")] - #[offset(0x084)] - #[access(WriteOnly)] - queue_desc_high: le32, - - /// Virtual queue's Driver Area 64 bit long physical address - /// - /// Writing to these two registers (lower 32 bits of the address - /// to `QueueDriverLow`, higher 32 bits to `QueueDriverHigh`) notifies - /// the device about location of the Driver Area of the queue - /// selected by writing to `QueueSel`. - #[doc(alias = "QueueDriverLow")] - #[offset(0x090)] - #[access(WriteOnly)] - queue_driver_low: le32, - - /// Virtual queue's Driver Area 64 bit long physical address - /// - /// Writing to these two registers (lower 32 bits of the address - /// to `QueueDriverLow`, higher 32 bits to `QueueDriverHigh`) notifies - /// the device about location of the Driver Area of the queue - /// selected by writing to `QueueSel`. - #[doc(alias = "QueueDriverHigh")] - #[offset(0x094)] - #[access(WriteOnly)] - queue_driver_high: le32, - - /// Virtual queue's Device Area 64 bit long physical address - /// - /// Writing to these two registers (lower 32 bits of the address - /// to `QueueDeviceLow`, higher 32 bits to `QueueDeviceHigh`) notifies - /// the device about location of the Device Area of the queue - /// selected by writing to `QueueSel`. - #[doc(alias = "QueueDeviceLow")] - #[offset(0x0a0)] - #[access(WriteOnly)] - queue_device_low: le32, - - /// Virtual queue's Device Area 64 bit long physical address - /// - /// Writing to these two registers (lower 32 bits of the address - /// to `QueueDeviceLow`, higher 32 bits to `QueueDeviceHigh`) notifies - /// the device about location of the Device Area of the queue - /// selected by writing to `QueueSel`. - #[doc(alias = "QueueDeviceHigh")] - #[offset(0x0a4)] - #[access(WriteOnly)] - queue_device_high: le32, - - /// Shared memory id - /// - /// Writing to this register selects the shared memory region _Basic Facilities of a Virtio Device / Shared Memory Regions_ - /// following operations on `SHMLenLow`, `SHMLenHigh`, - /// `SHMBaseLow` and `SHMBaseHigh` apply to. - #[doc(alias = "SHMSel")] - #[offset(0x0ac)] - #[access(WriteOnly)] - shm_sel: le32, - - /// Shared memory region 64 bit long length - /// - /// These registers return the length of the shared memory - /// region in bytes, as defined by the device for the region selected by - /// the `SHMSel` register. The lower 32 bits of the length - /// are read from `SHMLenLow` and the higher 32 bits from - /// `SHMLenHigh`. Reading from a non-existent - /// region (i.e. where the ID written to `SHMSel` is unused) - /// results in a length of -1. - #[doc(alias = "SHMLenLow")] - #[offset(0x0b0)] - #[access(ReadOnly)] - shm_len_low: le32, - - /// Shared memory region 64 bit long length - /// - /// These registers return the length of the shared memory - /// region in bytes, as defined by the device for the region selected by - /// the `SHMSel` register. The lower 32 bits of the length - /// are read from `SHMLenLow` and the higher 32 bits from - /// `SHMLenHigh`. Reading from a non-existent - /// region (i.e. where the ID written to `SHMSel` is unused) - /// results in a length of -1. - #[doc(alias = "SHMLenHigh")] - #[offset(0x0b4)] - #[access(ReadOnly)] - shm_len_high: le32, - - /// Shared memory region 64 bit long physical address - /// - /// The driver reads these registers to discover the base address - /// of the region in physical address space. This address is - /// chosen by the device (or other part of the VMM). - /// The lower 32 bits of the address are read from `SHMBaseLow` - /// with the higher 32 bits from `SHMBaseHigh`. Reading - /// from a non-existent region (i.e. where the ID written to - /// `SHMSel` is unused) results in a base address of - /// 0xffffffffffffffff. - #[doc(alias = "SHMBaseLow")] - #[offset(0x0b8)] - #[access(ReadOnly)] - shm_base_low: le32, - - /// Shared memory region 64 bit long physical address - /// - /// The driver reads these registers to discover the base address - /// of the region in physical address space. This address is - /// chosen by the device (or other part of the VMM). - /// The lower 32 bits of the address are read from `SHMBaseLow` - /// with the higher 32 bits from `SHMBaseHigh`. Reading - /// from a non-existent region (i.e. where the ID written to - /// `SHMSel` is unused) results in a base address of - /// 0xffffffffffffffff. - #[doc(alias = "SHMBaseHigh")] - #[offset(0x0bc)] - #[access(ReadOnly)] - shm_base_high: le32, - - /// Virtual queue reset bit - /// - /// If VIRTIO_F_RING_RESET has been negotiated, writing one (0x1) to this - /// register selectively resets the queue. Both read and write accesses - /// apply to the queue selected by writing to `QueueSel`. - #[doc(alias = "QueueReset")] - #[offset(0x0c0)] - #[access(ReadWrite)] - queue_reset: le32, - - /// Configuration atomicity value - /// - /// Reading from this register returns a value describing a version of the device-specific configuration space (see `Config`). - /// The driver can then access the configuration space and, when finished, read `ConfigGeneration` again. - /// If no part of the configuration space has changed between these two `ConfigGeneration` reads, the returned values are identical. - /// If the values are different, the configuration space accesses were not atomic and the driver has to perform the operations again. - /// See also _Basic Facilities of a Virtio Device / Device Configuration Space_. - #[doc(alias = "ConfigGeneration")] - #[offset(0x0fc)] - #[access(ReadOnly)] - config_generation: le32, - - /// Configuration space - /// - /// Device-specific configuration space starts at the offset 0x100 - /// and is accessed with byte alignment. Its meaning and size - /// depend on the device and the driver. - #[doc(alias = "Config")] - #[offset(0x100)] - #[access(ReadWrite)] - config: (), - } -} - -impl_wide_field_access! { - /// MMIO Device Registers - pub trait DeviceRegisterVolatileWideFieldAccess<'a, A>: DeviceRegisters { - /// Virtual queue's Descriptor Area 64 bit long physical address - /// - /// Writing to these two registers (lower 32 bits of the address - /// to `QueueDescLow`, higher 32 bits to `QueueDescHigh`) notifies - /// the device about location of the Descriptor Area of the queue - /// selected by writing to `QueueSel` register. - #[doc(alias = "QueueDesc")] - #[access(WriteOnly)] - queue_desc: queue_desc_low, queue_desc_high; - - /// Virtual queue's Driver Area 64 bit long physical address - /// - /// Writing to these two registers (lower 32 bits of the address - /// to `QueueDriverLow`, higher 32 bits to `QueueDriverHigh`) notifies - /// the device about location of the Driver Area of the queue - /// selected by writing to `QueueSel`. - #[doc(alias = "QueueDriver")] - #[access(WriteOnly)] - queue_driver: queue_driver_low, queue_driver_high; - - /// Virtual queue's Device Area 64 bit long physical address - /// - /// Writing to these two registers (lower 32 bits of the address - /// to `QueueDeviceLow`, higher 32 bits to `QueueDeviceHigh`) notifies - /// the device about location of the Device Area of the queue - /// selected by writing to `QueueSel`. - #[doc(alias = "QueueDevice")] - #[access(WriteOnly)] - queue_device: queue_device_low, queue_device_high; - - /// Shared memory region 64 bit long length - /// - /// These registers return the length of the shared memory - /// region in bytes, as defined by the device for the region selected by - /// the `SHMSel` register. The lower 32 bits of the length - /// are read from `SHMLenLow` and the higher 32 bits from - /// `SHMLenHigh`. Reading from a non-existent - /// region (i.e. where the ID written to `SHMSel` is unused) - /// results in a length of -1. - #[doc(alias = "SHMLen")] - #[access(ReadOnly)] - shm_len: shm_len_low, shm_len_high; - - /// Shared memory region 64 bit long physical address - /// - /// The driver reads these registers to discover the base address - /// of the region in physical address space. This address is - /// chosen by the device (or other part of the VMM). - /// The lower 32 bits of the address are read from `SHMBaseLow` - /// with the higher 32 bits from `SHMBaseHigh`. Reading - /// from a non-existent region (i.e. where the ID written to - /// `SHMSel` is unused) results in a base address of - /// 0xffffffffffffffff. - #[doc(alias = "SHMBase")] - #[access(ReadOnly)] - shm_base: shm_base_low, shm_base_high; - } -} - -impl<'a, A> DeviceConfigSpace for VolatilePtr<'a, DeviceRegisters, A> -where - A: RestrictAccess, - A::Restricted: Readable, -{ - fn read_config_with(self, f: F) -> T - where - F: FnMut() -> T, - { - let mut f = f; - loop { - let before = self.config_generation().read(); - let read = f(); - let after = self.config_generation().read(); - if after == before { - break read; - } - } - } -} - -virtio_bitflags! { - /// Interrupt Status - pub struct InterruptStatus: u8 { - /// Used Buffer Notification - /// - /// The interrupt was asserted because the device has used a buffer in at least one of the active virtual queues. - const USED_BUFFER_NOTIFICATION = 1 << 0; - - /// Configuration Change Notification - /// - /// The interrupt was asserted because the configuration of the device has changed. - const CONFIGURATION_CHANGE_NOTIFICATION = 1 << 1; - } -} diff --git a/virtio-spec/src/net.rs b/virtio-spec/src/net.rs deleted file mode 100644 index a9e011ddfe..0000000000 --- a/virtio-spec/src/net.rs +++ /dev/null @@ -1,334 +0,0 @@ -//! Network Device - -use num_enum::{FromPrimitive, IntoPrimitive, TryFromPrimitive}; -use volatile::access::ReadOnly; -use volatile_macro::VolatileFieldAccess; - -pub use super::features::net::F; -use crate::{le16, le32}; - -endian_bitflags! { - /// Network Device Status Flags - #[doc(alias = "VIRTIO_NET_S")] - pub struct S: le16 { - #[doc(alias = "VIRTIO_NET_S_LINK_UP")] - const LINK_UP = 1; - - #[doc(alias = "VIRTIO_NET_S_ANNOUNCE")] - const ANNOUNCE = 2; - } -} - -/// Network Device Configuration Layout -#[doc(alias = "virtio_net_config")] -#[cfg_attr( - feature = "zerocopy", - derive(zerocopy_derive::FromZeroes, zerocopy_derive::FromBytes) -)] -#[derive(VolatileFieldAccess)] -#[repr(C)] -pub struct Config { - #[access(ReadOnly)] - mac: [u8; 6], - - #[access(ReadOnly)] - status: S, - - #[access(ReadOnly)] - max_virtqueue_pairs: le16, - - #[access(ReadOnly)] - mtu: le16, - - #[access(ReadOnly)] - speed: le32, - - #[access(ReadOnly)] - duplex: u8, - - #[access(ReadOnly)] - rss_max_key_size: u8, - - #[access(ReadOnly)] - rss_max_indirection_table_length: le16, - - #[access(ReadOnly)] - supported_hash_types: le32, -} - -virtio_bitflags! { - /// Network Device Header Flags - #[doc(alias = "VIRTIO_NET_HDR_F")] - pub struct HdrF: u8 { - #[doc(alias = "VIRTIO_NET_HDR_F_NEEDS_CSUM")] - const NEEDS_CSUM = 1; - - #[doc(alias = "VIRTIO_NET_HDR_F_DATA_VALID")] - const DATA_VALID = 2; - - #[doc(alias = "VIRTIO_NET_HDR_F_RSC_INFO")] - const RSC_INFO = 4; - } -} - -virtio_bitflags! { - /// Network Device Header GSO Type - #[doc(alias = "VIRTIO_NET_HDR_GSO")] - pub struct HdrGso: u8 { - #[doc(alias = "VIRTIO_NET_HDR_GSO_NONE")] - const NONE = 0; - - #[doc(alias = "VIRTIO_NET_HDR_GSO_TCPV4")] - const TCPV4 = 1; - - #[doc(alias = "VIRTIO_NET_HDR_GSO_UDP")] - const UDP = 3; - - #[doc(alias = "VIRTIO_NET_HDR_GSO_TCPV6")] - const TCPV6 = 4; - - #[doc(alias = "VIRTIO_NET_HDR_GSO_UDP_L4")] - const UDP_L4 = 5; - - #[doc(alias = "VIRTIO_NET_HDR_GSO_ECN")] - const ECN = 0x80; - } -} - -/// Network Device Header -#[doc(alias = "virtio_net_hdr")] -#[cfg_attr( - feature = "zerocopy", - derive( - zerocopy_derive::FromZeroes, - zerocopy_derive::FromBytes, - zerocopy_derive::AsBytes - ) -)] -#[derive(Default, Clone, Copy, Debug)] -#[repr(C)] -pub struct Hdr { - pub flags: HdrF, - pub gso_type: HdrGso, - pub hdr_len: le16, - pub gso_size: le16, - pub csum_start: le16, - pub csum_offset: le16, - pub num_buffers: le16, -} - -/// Network Device Header Hash Report -/// -/// Only if VIRTIO_NET_F_HASH_REPORT negotiated -#[doc(alias = "virtio_net_hdr")] -#[cfg_attr( - feature = "zerocopy", - derive( - zerocopy_derive::FromZeroes, - zerocopy_derive::FromBytes, - zerocopy_derive::AsBytes - ) -)] -#[derive(Default, Clone, Copy, Debug)] -#[repr(C)] -pub struct HdrHashReport { - /// Only if VIRTIO_NET_F_HASH_REPORT negotiated - pub hash_value: le32, - /// Only if VIRTIO_NET_F_HASH_REPORT negotiated - pub hash_report: le16, - /// Only if VIRTIO_NET_F_HASH_REPORT negotiated - pub padding_reserved: le16, -} - -endian_bitflags! { - /// Hash Type - #[doc(alias = "VIRTIO_NET_HASH_TYPE")] - pub struct HashType: le32 { - #[doc(alias = "VIRTIO_NET_HASH_TYPE_IPv4")] - const IPV4 = 1 << 0; - - #[doc(alias = "VIRTIO_NET_HASH_TYPE_TCPv4")] - const TCPV4 = 1 << 1; - - #[doc(alias = "VIRTIO_NET_HASH_TYPE_UDPv4")] - const UDPV4 = 1 << 2; - - #[doc(alias = "VIRTIO_NET_HASH_TYPE_IPv6")] - const IPV6 = 1 << 3; - - #[doc(alias = "VIRTIO_NET_HASH_TYPE_TCPv6")] - const TCPV6 = 1 << 4; - - #[doc(alias = "VIRTIO_NET_HASH_TYPE_UDPv6")] - const UDPV6 = 1 << 5; - - #[doc(alias = "VIRTIO_NET_HASH_TYPE_IP_EX")] - const IP_EX = 1 << 6; - - #[doc(alias = "VIRTIO_NET_HASH_TYPE_TCP_EX")] - const TCP_EX = 1 << 7; - - #[doc(alias = "VIRTIO_NET_HASH_TYPE_UDP_EX")] - const UDP_EX = 1 << 8; - } -} - -/// Hash Report -#[doc(alias = "VIRTIO_NET_HASH_REPORT")] -#[derive(IntoPrimitive, FromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] -#[non_exhaustive] -#[repr(u16)] -pub enum HashReport { - #[doc(alias = "VIRTIO_NET_HASH_REPORT_NONE")] - None = 0, - - #[doc(alias = "VIRTIO_NET_HASH_REPORT_IPv4")] - Ipv4 = 1, - - #[doc(alias = "VIRTIO_NET_HASH_REPORT_TCPv4")] - Tcpv4 = 2, - - #[doc(alias = "VIRTIO_NET_HASH_REPORT_UDPv4")] - Udpv4 = 3, - - #[doc(alias = "VIRTIO_NET_HASH_REPORT_IPv6")] - IPv6 = 4, - - #[doc(alias = "VIRTIO_NET_HASH_REPORT_TCPv6")] - Tcpv6 = 5, - - #[doc(alias = "VIRTIO_NET_HASH_REPORT_UDPv6")] - Udpv6 = 6, - - #[doc(alias = "VIRTIO_NET_HASH_REPORT_IPv6_EX")] - Ipv6Ex = 7, - - #[doc(alias = "VIRTIO_NET_HASH_REPORT_TCPv6_EX")] - Tcpv6Ex = 8, - - #[doc(alias = "VIRTIO_NET_HASH_REPORT_UDPv6_EX")] - Udpv6Ex = 9, - - #[num_enum(catch_all)] - Unknown(u16), -} - -/// Command class -#[doc(alias = "VIRTIO_NET_CTRL")] -#[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] -#[non_exhaustive] -#[repr(u8)] -pub enum Ctrl { - #[doc(alias = "VIRTIO_NET_CTRL_RX")] - Rx = 0, - - #[doc(alias = "VIRTIO_NET_CTRL_MAC")] - Mac = 1, - - #[doc(alias = "VIRTIO_NET_CTRL_VLAN")] - Vlan = 2, - - #[doc(alias = "VIRTIO_NET_CTRL_ANNOUNCE")] - Announce = 3, - - #[doc(alias = "VIRTIO_NET_CTRL_MQ")] - Mq = 4, - - #[doc(alias = "VIRTIO_NET_CTRL_GUEST_OFFLOADS")] - GuestOffloads = 5, -} - -/// Commands -pub mod ctrl { - use num_enum::{IntoPrimitive, TryFromPrimitive}; - - /// Packed Receive Filtering commands - #[doc(alias = "VIRTIO_NET_CTRL_RX")] - #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] - #[non_exhaustive] - #[repr(u8)] - pub enum Rx { - #[doc(alias = "VIRTIO_NET_CTRL_RX_PROMISC")] - Promisc = 0, - - #[doc(alias = "VIRTIO_NET_CTRL_RX_ALLMULTI")] - Allmulti = 1, - - #[doc(alias = "VIRTIO_NET_CTRL_RX_ALLUNI")] - Alluni = 2, - - #[doc(alias = "VIRTIO_NET_CTRL_RX_NOMULTI")] - Nomulti = 3, - - #[doc(alias = "VIRTIO_NET_CTRL_RX_NOUNI")] - Nouni = 4, - - #[doc(alias = "VIRTIO_NET_CTRL_RX_NOBCAST")] - Nobcast = 5, - } - - /// MAC Address Filtering commands - #[doc(alias = "VIRTIO_NET_CTRL_MAC")] - #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] - #[non_exhaustive] - #[repr(u8)] - pub enum Mac { - #[doc(alias = "VIRTIO_NET_CTRL_MAC_TABLE_SET")] - TableSet = 0, - - #[doc(alias = "VIRTIO_NET_CTRL_MAC_ADDR_SET")] - AddrSet = 1, - } - - /// VLAN filtering commands - #[doc(alias = "VIRTIO_NET_CTRL_VLAN")] - #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] - #[non_exhaustive] - #[repr(u8)] - pub enum Vlan { - #[doc(alias = "VIRTIO_NET_CTRL_VLAN_ADD")] - Add = 0, - - #[doc(alias = "VIRTIO_NET_CTRL_VLAN_DEL")] - Del = 1, - } - - /// Gratuitous Packet Sending commands - #[doc(alias = "VIRTIO_NET_CTRL_ANNOUNCE")] - #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] - #[non_exhaustive] - #[repr(u8)] - pub enum Announce { - #[doc(alias = "VIRTIO_NET_CTRL_ANNOUNCE_ACK")] - Ack = 0, - } - - /// Multiqueue mode commands - #[doc(alias = "VIRTIO_NET_CTRL_MQ")] - #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] - #[non_exhaustive] - #[repr(u8)] - pub enum Mq { - /// For automatic receive steering - #[doc(alias = "VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET")] - VqPairsSet = 0, - - /// For configurable receive steering - #[doc(alias = "VIRTIO_NET_CTRL_MQ_RSS_CONFIG")] - RssConfig = 1, - - /// For configurable hash calculation - #[doc(alias = "VIRTIO_NET_CTRL_MQ_HASH_CONFIG")] - HashConfig = 2, - } - - /// Setting Offloads State commands - #[doc(alias = "VIRTIO_NET_CTRL_GUEST_OFFLOADS")] - #[derive(IntoPrimitive, TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] - #[non_exhaustive] - #[repr(u8)] - pub enum GuestOffloads { - #[doc(alias = "VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET")] - Set = 0, - } -} diff --git a/virtio-spec/src/pci.rs b/virtio-spec/src/pci.rs deleted file mode 100644 index 38d3eb0ad6..0000000000 --- a/virtio-spec/src/pci.rs +++ /dev/null @@ -1,463 +0,0 @@ -//! Definitions for Virtio over PCI bus. - -use core::mem; - -use num_enum::{FromPrimitive, IntoPrimitive}; -use pci_types::capability::PciCapabilityAddress; -use pci_types::ConfigRegionAccess; -use volatile::access::{ReadOnly, ReadWrite, Readable, RestrictAccess}; -use volatile::VolatilePtr; -use volatile_macro::VolatileFieldAccess; - -#[doc(inline)] -pub use crate::driver_notifications::NotificationData; -use crate::volatile::WideVolatilePtr; -use crate::{le16, le32, le64, DeviceConfigSpace, DeviceStatus, Le}; - -/// PCI Capability -/// -/// See [`CapData`] for reading additional fields. -#[doc(alias = "virtio_pci_cap")] -#[cfg_attr( - feature = "zerocopy", - derive(zerocopy_derive::FromZeroes, zerocopy_derive::FromBytes) -)] -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct Cap { - /// Generic PCI field: `PCI_CAP_ID_VNDR` - /// - /// 0x09; Identifies a vendor-specific capability. - pub cap_vndr: u8, - - /// Generic PCI field: next ptr. - /// - /// Link to next capability in the capability list in the PCI configuration space. - pub cap_next: u8, - - /// Generic PCI field: capability length - /// - /// Length of this capability structure, including the whole of - /// struct virtio_pci_cap, and extra data if any. - /// This length MAY include padding, or fields unused by the driver. - pub cap_len: u8, - - /// Identifies the structure. - /// - /// Each structure is detailed individually below. - /// - /// The device MAY offer more than one structure of any type - this makes it - /// possible for the device to expose multiple interfaces to drivers. The order of - /// the capabilities in the capability list specifies the order of preference - /// suggested by the device. A device may specify that this ordering mechanism be - /// overridden by the use of the `id` field. - /// - ///
- /// - /// For example, on some hypervisors, notifications using IO accesses are - /// faster than memory accesses. In this case, the device would expose two - /// capabilities with `cfg_type` set to VIRTIO_PCI_CAP_NOTIFY_CFG: - /// the first one addressing an I/O BAR, the second one addressing a memory BAR. - /// In this example, the driver would use the I/O BAR if I/O resources are available, and fall back on - /// memory BAR when I/O resources are unavailable. - /// - ///
- pub cfg_type: u8, - - /// Where to find it. - /// - /// values 0x0 to 0x5 specify a Base Address register (BAR) belonging to - /// the function located beginning at 10h in PCI Configuration Space - /// and used to map the structure into Memory or I/O Space. - /// The BAR is permitted to be either 32-bit or 64-bit, it can map Memory Space - /// or I/O Space. - /// - /// Any other value is reserved for future use. - pub bar: u8, - - /// Multiple capabilities of the same type - /// - /// Used by some device types to uniquely identify multiple capabilities - /// of a certain type. If the device type does not specify the meaning of - /// this field, its contents are undefined. - pub id: u8, - - /// Pad to full dword. - pub padding: [u8; 2], - - /// Offset within bar. - /// - /// indicates where the structure begins relative to the base address associated - /// with the BAR. The alignment requirements of `offset` are indicated - /// in each structure-specific section below. - pub offset: le32, - - /// Length of the structure, in bytes. - /// - /// indicates the length of the structure. - /// - /// `length` MAY include padding, or fields unused by the driver, or - /// future extensions. - /// - ///
- /// - /// For example, a future device might present a large structure size of several - /// MBytes. - /// As current devices never utilize structures larger than 4KBytes in size, - /// driver MAY limit the mapped structure size to e.g. - /// 4KBytes (thus ignoring parts of structure after the first - /// 4KBytes) to allow forward compatibility with such devices without loss of - /// functionality and without wasting resources. - /// - ///
- pub length: le32, -} - -impl Cap { - pub fn read(addr: PciCapabilityAddress, access: impl ConfigRegionAccess) -> Option { - let data = unsafe { access.read(addr.address, addr.offset) }; - let [cap_vndr, _cap_next, cap_len, _cfg_type] = data.to_ne_bytes(); - - if cap_vndr != 0x09 { - return None; - } - - if cap_len < 16 { - return None; - } - - let data = [ - data, - unsafe { access.read(addr.address, addr.offset + 4) }, - unsafe { access.read(addr.address, addr.offset + 8) }, - unsafe { access.read(addr.address, addr.offset + 12) }, - ]; - - let this = unsafe { mem::transmute::<[u32; 4], Self>(data) }; - - Some(this) - } -} - -/// PCI Capability 64 -#[doc(alias = "virtio_pci_cap64")] -#[cfg_attr( - feature = "zerocopy", - derive(zerocopy_derive::FromZeroes, zerocopy_derive::FromBytes) -)] -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct Cap64 { - pub cap: Cap, - pub offset_hi: le32, - pub length_hi: le32, -} - -/// PCI Notify Capability -#[doc(alias = "virtio_pci_notify_cap")] -#[cfg_attr( - feature = "zerocopy", - derive(zerocopy_derive::FromZeroes, zerocopy_derive::FromBytes) -)] -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct NotifyCap { - pub cap: Cap, - - /// Multiplier for queue_notify_off. - pub notify_off_multiplier: le32, -} - -/// PCI Configuration Capability -#[doc(alias = "virtio_pci_cfg_cap")] -#[cfg_attr( - feature = "zerocopy", - derive(zerocopy_derive::FromZeroes, zerocopy_derive::FromBytes) -)] -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct CfgCap { - pub cap: Cap, - - /// Data for BAR access. - pub pci_cfg_data: [u8; 4], -} - -/// PCI Capability Data -#[derive(Clone, Copy, Debug)] -pub struct CapData { - /// Identifies the structure. - pub cfg_type: CapCfgType, - - /// Where to find it. - pub bar: u8, - - /// Multiple capabilities of the same type - pub id: u8, - - /// Offset within bar. - pub offset: le64, - - /// Length of the structure, in bytes. - pub length: le64, - - /// Multiplier for queue_notify_off. - pub notify_off_multiplier: Option, -} - -impl CapData { - pub fn read(addr: PciCapabilityAddress, access: impl ConfigRegionAccess) -> Option { - let cap = Cap::read(addr, &access)?; - let cfg_type = CapCfgType::from(cap.cfg_type); - - let (offset, length) = match cfg_type { - CapCfgType::SharedMemory => { - if cap.cap_len < 24 { - return None; - } - - let offset_hi = unsafe { access.read(addr.address, addr.offset + 16) }; - let offset_hi = Le(offset_hi); - let offset = le64::from([cap.offset, offset_hi]); - - let length_hi = unsafe { access.read(addr.address, addr.offset + 20) }; - let length_hi = Le(length_hi); - let length = le64::from([cap.length, length_hi]); - - (offset, length) - } - _ => (le64::from(cap.offset), le64::from(cap.length)), - }; - - let notify_off_multiplier = match cfg_type { - CapCfgType::Notify => { - if cap.cap_len < 20 { - return None; - } - - let notify_off_multiplier = unsafe { access.read(addr.address, addr.offset + 16) }; - let notify_off_multiplier = Le(notify_off_multiplier); - - Some(notify_off_multiplier) - } - _ => None, - }; - - Some(Self { - cfg_type, - bar: cap.bar, - id: cap.id, - offset, - length, - notify_off_multiplier, - }) - } -} - -/// PCI Capability Configuration Type -#[doc(alias = "VIRTIO_PCI_CAP")] -#[derive(IntoPrimitive, FromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] -#[non_exhaustive] -#[repr(u8)] -pub enum CapCfgType { - /// Common configuration - #[doc(alias = "VIRTIO_PCI_CAP_COMMON_CFG")] - Common = 1, - - /// Notifications - #[doc(alias = "VIRTIO_PCI_CAP_NOTIFY_CFG")] - Notify = 2, - - /// ISR Status - #[doc(alias = "VIRTIO_PCI_CAP_ISR_CFG")] - Isr = 3, - - /// Device specific configuration - #[doc(alias = "VIRTIO_PCI_CAP_DEVICE_CFG")] - Device = 4, - - /// PCI configuration access - #[doc(alias = "VIRTIO_PCI_CAP_PCI_CFG")] - Pci = 5, - - /// Shared memory region - #[doc(alias = "VIRTIO_PCI_CAP_SHARED_MEMORY_CFG")] - SharedMemory = 8, - - /// Vendor-specific data - #[doc(alias = "VIRTIO_PCI_CAP_VENDOR_CFG")] - Vendor = 9, - - /// Unknown device - #[num_enum(catch_all)] - Unknown(u8), -} - -/// Common configuration structure -/// -/// The common configuration structure is found at the bar and offset within the [`VIRTIO_PCI_CAP_COMMON_CFG`] capability. -/// -/// [`VIRTIO_PCI_CAP_COMMON_CFG`]: CapCfgType::Common -#[doc(alias = "virtio_pci_common_cfg")] -#[cfg_attr( - feature = "zerocopy", - derive(zerocopy_derive::FromZeroes, zerocopy_derive::FromBytes) -)] -#[derive(VolatileFieldAccess)] -#[repr(C)] -pub struct CommonCfg { - /// The driver uses this to select which feature bits `device_feature` shows. - /// Value 0x0 selects Feature Bits 0 to 31, 0x1 selects Feature Bits 32 to 63, etc. - device_feature_select: le32, - - /// The device uses this to report which feature bits it is - /// offering to the driver: the driver writes to - /// `device_feature_select` to select which feature bits are presented. - #[access(ReadOnly)] - device_feature: le32, - - /// The driver uses this to select which feature bits `driver_feature` shows. - /// Value 0x0 selects Feature Bits 0 to 31, 0x1 selects Feature Bits 32 to 63, etc. - driver_feature_select: le32, - - /// The driver writes this to accept feature bits offered by the device. - /// Driver Feature Bits selected by `driver_feature_select`. - driver_feature: le32, - - /// The driver sets the Configuration Vector for MSI-X. - config_msix_vector: le16, - - /// The device specifies the maximum number of virtqueues supported here. - #[access(ReadOnly)] - num_queues: le16, - - /// The driver writes the device status here (see [`DeviceStatus`]). Writing 0 into this - /// field resets the device. - device_status: DeviceStatus, - - /// Configuration atomicity value. The device changes this every time the - /// configuration noticeably changes. - #[access(ReadOnly)] - config_generation: u8, - - /// Queue Select. The driver selects which virtqueue the following - /// fields refer to. - queue_select: le16, - - /// Queue Size. On reset, specifies the maximum queue size supported by - /// the device. This can be modified by the driver to reduce memory requirements. - /// A 0 means the queue is unavailable. - queue_size: le16, - - /// The driver uses this to specify the queue vector for MSI-X. - queue_msix_vector: le16, - - /// The driver uses this to selectively prevent the device from executing requests from this virtqueue. - /// 1 - enabled; 0 - disabled. - queue_enable: le16, - - /// The driver reads this to calculate the offset from start of Notification structure at - /// which this virtqueue is located. - /// - ///
- /// - /// This is _not_ an offset in bytes. - /// See _Virtio Transport Options / Virtio Over PCI Bus / PCI Device Layout / Notification capability_ below. - /// - ///
- #[access(ReadOnly)] - queue_notify_off: le16, - - /// The driver writes the physical address of Descriptor Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. - queue_desc_low: le32, - - /// The driver writes the physical address of Descriptor Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. - queue_desc_high: le32, - - /// The driver writes the physical address of Driver Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. - queue_driver_low: le32, - - /// The driver writes the physical address of Driver Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. - queue_driver_high: le32, - - /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. - queue_device_low: le32, - - /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. - queue_device_high: le32, - - /// This field exists only if [`VIRTIO_F_NOTIF_CONFIG_DATA`] has been negotiated. - /// The driver will use this value to put it in the 'virtqueue number' field - /// in the available buffer notification structure. - /// See section _Virtio Transport Options / Virtio Over PCI Bus / PCI-specific Initialization And Device Operation / Available Buffer Notifications_. - /// - ///
- /// - /// This field provides the device with flexibility to determine how virtqueues - /// will be referred to in available buffer notifications. - /// In a trivial case the device can set `queue_notify_data`=vqn. Some devices - /// may benefit from providing another value, for example an internal virtqueue - /// identifier, or an internal offset related to the virtqueue number. - /// - ///
- /// - /// [`VIRTIO_F_NOTIF_CONFIG_DATA`]: crate::F::NOTIF_CONFIG_DATA - #[access(ReadOnly)] - queue_notify_data: le16, - - /// The driver uses this to selectively reset the queue. - /// This field exists only if [`VIRTIO_F_RING_RESET`] has been - /// negotiated. (see _Basic Facilities of a Virtio Device / Virtqueues / Virtqueue Reset_). - /// - /// [`VIRTIO_F_RING_RESET`]: crate::F::RING_RESET - queue_reset: le16, -} - -impl_wide_field_access! { - /// Common configuration structure - pub trait CommonCfgVolatileWideFieldAccess<'a, A>: CommonCfg { - /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. - #[access(ReadWrite)] - queue_desc: queue_desc_low, queue_desc_high; - - /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. - #[access(ReadWrite)] - queue_driver: queue_driver_low, queue_driver_high; - - /// The driver writes the physical address of Device Area here. See section _Basic Facilities of a Virtio Device / Virtqueues_. - #[access(ReadWrite)] - queue_device: queue_device_low, queue_device_high; - } -} - -impl<'a, A> DeviceConfigSpace for VolatilePtr<'a, CommonCfg, A> -where - A: RestrictAccess, - A::Restricted: Readable, -{ - fn read_config_with(self, f: F) -> T - where - F: FnMut() -> T, - { - let mut f = f; - loop { - let before = self.config_generation().read(); - let read = f(); - let after = self.config_generation().read(); - if after == before { - break read; - } - } - } -} - -virtio_bitflags! { - /// ISR Status - pub struct IsrStatus: u8 { - /// Queue Interrupt - const QUEUE_INTERRUPT = 1 << 0; - - /// Device Configuration Interrupt - const DEVICE_CONFIGURATION_INTERRUPT = 1 << 1; - } -} diff --git a/virtio-spec/src/pvirtq.rs b/virtio-spec/src/pvirtq.rs deleted file mode 100644 index a9a677441e..0000000000 --- a/virtio-spec/src/pvirtq.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! Packed virtqueue definitions - -use bitfield_struct::bitfield; - -use crate::{le16, le32, le64, virtq, RingEventFlags}; - -/// Packed Virtqueue Descriptor -#[doc(alias = "pvirtq_desc")] -#[repr(C)] -pub struct Desc { - /// Buffer Address. - pub addr: le64, - - /// Buffer Length. - pub len: le32, - - /// Buffer ID. - pub id: le16, - - /// The flags depending on descriptor type. - pub flags: virtq::DescF, -} - -/// Event Suppression Descriptor -#[doc(alias = "pvirtq_event_suppress")] -#[repr(C)] -pub struct EventSuppress { - /// If desc_event_flags set to RING_EVENT_FLAGS_DESC - pub desc: EventSuppressDesc, - pub flags: EventSuppressFlags, -} - -/// Event Suppression Flags -#[bitfield(u16, repr = le16, from = le16::from_ne, into = le16::to_ne)] -pub struct EventSuppressDesc { - /// Descriptor Ring Change Event Offset - #[bits(15)] - pub desc_event_off: u16, - - /// Descriptor Ring Change Event Wrap Counter - #[bits(1)] - pub desc_event_wrap: u8, -} - -#[bitfield(u16, repr = le16, from = le16::from_ne, into = le16::to_ne)] -pub struct EventSuppressFlags { - /// Descriptor Ring Change Event Flags - #[bits(2)] - pub desc_event_flags: RingEventFlags, - - /// Reserved, set to 0 - #[bits(14)] - pub reserved: u16, -} diff --git a/virtio-spec/src/virtq/alloc.rs b/virtio-spec/src/virtq/alloc.rs deleted file mode 100644 index 69441d6bc1..0000000000 --- a/virtio-spec/src/virtq/alloc.rs +++ /dev/null @@ -1,96 +0,0 @@ -use ::alloc::alloc::handle_alloc_error; -use allocator_api2::alloc::{AllocError, Allocator, Global}; -use allocator_api2::boxed::Box; - -use super::*; - -impl Avail { - pub fn new(queue_size: u16, has_event_idx: bool) -> Box { - Self::new_in(queue_size, has_event_idx, Global) - } - - pub fn try_new(queue_size: u16, has_event_idx: bool) -> Result, AllocError> { - Self::try_new_in(queue_size, has_event_idx, Global) - } - - pub fn new_in(queue_size: u16, has_event_idx: bool, alloc: A) -> Box { - Self::try_new_in(queue_size, has_event_idx, alloc) - .unwrap_or_else(|_| handle_alloc_error(Self::layout(queue_size, has_event_idx))) - } - - pub fn try_new_in( - queue_size: u16, - has_event_idx: bool, - alloc: A, - ) -> Result, AllocError> { - let layout = Self::layout(queue_size, has_event_idx); - - let mem = alloc.allocate_zeroed(layout)?; - let mem = NonNull::slice_from_raw_parts(mem.cast(), layout.size()); - let raw = Self::from_ptr(mem).unwrap(); - let boxed = unsafe { Box::from_raw_in(raw.as_ptr(), alloc) }; - - debug_assert_eq!(Layout::for_value(&*boxed), layout); - debug_assert_eq!(boxed.ring(has_event_idx).len(), queue_size.into()); - debug_assert_eq!(boxed.used_event(has_event_idx).is_some(), has_event_idx); - - Ok(boxed) - } -} - -impl Used { - pub fn new(queue_size: u16, has_event_idx: bool) -> Box { - Self::new_in(queue_size, has_event_idx, Global) - } - - pub fn try_new(queue_size: u16, has_event_idx: bool) -> Result, AllocError> { - Self::try_new_in(queue_size, has_event_idx, Global) - } - - pub fn new_in(queue_size: u16, has_event_idx: bool, alloc: A) -> Box { - Self::try_new_in(queue_size, has_event_idx, alloc) - .unwrap_or_else(|_| handle_alloc_error(Self::layout(queue_size, has_event_idx))) - } - - pub fn try_new_in( - queue_size: u16, - has_event_idx: bool, - alloc: A, - ) -> Result, AllocError> { - let layout = Self::layout(queue_size, has_event_idx); - - let mem = alloc.allocate_zeroed(layout)?; - let mem = NonNull::slice_from_raw_parts(mem.cast(), layout.size()); - let raw = Self::from_ptr(mem, has_event_idx).unwrap(); - let boxed = unsafe { Box::from_raw_in(raw.as_ptr(), alloc) }; - - debug_assert_eq!(Layout::for_value(&*boxed), layout); - debug_assert_eq!(boxed.ring().len(), queue_size.into()); - debug_assert_eq!(boxed.avail_event().is_some(), has_event_idx); - - Ok(boxed) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn avail_layout() { - for queue_size in [255, 256, 257] { - for has_event_idx in [false, true] { - Avail::new(queue_size, has_event_idx); - } - } - } - - #[test] - fn used_layout() { - for queue_size in [255, 256, 257] { - for has_event_idx in [false, true] { - Used::new(queue_size, has_event_idx); - } - } - } -} diff --git a/virtio-spec/src/virtq/mod.rs b/virtio-spec/src/virtq/mod.rs deleted file mode 100644 index 60ca82d12b..0000000000 --- a/virtio-spec/src/virtq/mod.rs +++ /dev/null @@ -1,262 +0,0 @@ -//! Virtqueue definitions - -#[cfg(feature = "alloc")] -mod alloc; - -use core::alloc::Layout; -use core::ptr::{addr_of_mut, NonNull}; -use core::{mem, ptr}; - -use crate::{le16, le32, le64}; - -/// Split Virtqueue Descriptor -#[doc(alias = "virtq_desc")] -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct Desc { - /// Address (guest-physical). - pub addr: le64, - - /// Length. - pub len: le32, - - /// The flags as indicated in [`DescF`]. - pub flags: DescF, - - /// Next field if flags & NEXT - pub next: le16, -} - -endian_bitflags! { - /// Virtqueue descriptor flags - #[doc(alias = "VIRTQ_DESC_F")] - pub struct DescF: le16 { - /// This marks a buffer as continuing via the next field. - #[doc(alias = "VIRTQ_DESC_F_NEXT")] - const NEXT = 1; - - /// This marks a buffer as device write-only (otherwise device read-only). - #[doc(alias = "VIRTQ_DESC_F_WRITE")] - const WRITE = 2; - - /// This means the buffer contains a list of buffer descriptors. - #[doc(alias = "VIRTQ_DESC_F_INDIRECT")] - const INDIRECT = 4; - - #[doc(alias = "VIRTQ_DESC_F_AVAIL")] - const AVAIL = 1 << 7; - - #[doc(alias = "VIRTQ_DESC_F_USED")] - const USED = 1 << 15; - } -} - -/// The Virtqueue Available Ring -#[doc(alias = "virtq_avail")] -#[derive(Debug)] -#[repr(C)] -pub struct Avail { - pub flags: AvailF, - pub idx: le16, - ring_and_used_event: [le16], -} - -impl Avail { - pub fn layout(queue_size: u16, has_event_idx: bool) -> Layout { - Layout::array::(2 + usize::from(queue_size) + usize::from(has_event_idx)).unwrap() - } - - pub fn from_ptr(ptr: NonNull<[u8]>) -> Option> { - let len = ptr.as_ptr().len(); - // FIXME: use ptr::as_mut_ptr once stable - // https://github.com/rust-lang/rust/issues/74265 - let ptr = ptr.as_ptr() as *mut u8; - - if !ptr.cast::().is_aligned() { - return None; - } - - if len % mem::size_of::() != 0 { - return None; - } - - let len = len / mem::size_of::() - 2; - let ptr = ptr::slice_from_raw_parts_mut(ptr, len) as *mut Self; - Some(NonNull::new(ptr).unwrap()) - } - - pub fn ring_ptr(this: NonNull, has_event_idx: bool) -> NonNull<[le16]> { - let ptr = unsafe { addr_of_mut!((*this.as_ptr()).ring_and_used_event) }; - let len = if cfg!(debug_assertions) { - ptr.len() - .checked_sub(usize::from(has_event_idx)) - .expect("`has_event_idx` cannot be true if it was not true at creation") - } else { - ptr.len().saturating_sub(usize::from(has_event_idx)) - }; - let ptr = NonNull::new(ptr).unwrap().cast::(); - NonNull::slice_from_raw_parts(ptr, len) - } - - pub fn ring(&self, has_event_idx: bool) -> &[le16] { - let ptr = Self::ring_ptr(NonNull::from(self), has_event_idx); - unsafe { ptr.as_ref() } - } - - pub fn ring_mut(&mut self, has_event_idx: bool) -> &mut [le16] { - let mut ptr = Self::ring_ptr(NonNull::from(self), has_event_idx); - unsafe { ptr.as_mut() } - } - - pub fn used_event_ptr(this: NonNull, has_event_idx: bool) -> Option> { - if !has_event_idx { - return None; - } - - let ptr = unsafe { addr_of_mut!((*this.as_ptr()).ring_and_used_event) }; - let len = ptr.len(); - - if len == 0 { - return None; - } - - let ptr = NonNull::new(ptr).unwrap().cast::(); - let ptr = unsafe { ptr.add(len - 1) }; - Some(ptr) - } - - pub fn used_event(&self, has_event_idx: bool) -> Option<&le16> { - Self::used_event_ptr(NonNull::from(self), has_event_idx).map(|ptr| unsafe { ptr.as_ref() }) - } - - pub fn used_event_mut(&mut self, has_event_idx: bool) -> Option<&mut le16> { - Self::used_event_ptr(NonNull::from(self), has_event_idx) - .map(|mut ptr| unsafe { ptr.as_mut() }) - } -} - -endian_bitflags! { - /// Virtqueue available ring flags - #[doc(alias = "VIRTQ_AVAIL_F")] - pub struct AvailF: le16 { - /// The driver uses this in avail->flags to advise the device: don’t - /// interrupt me when you consume a buffer. It’s unreliable, so it’s - /// simply an optimization. - #[doc(alias = "VIRTQ_AVAIL_F_NO_INTERRUPT")] - const NO_INTERRUPT = 1; - } -} - -/// The Virtqueue Used Ring -#[doc(alias = "virtq_used")] -#[derive(Debug)] -#[repr(C)] -#[repr(align(4))] // mem::align_of:: -pub struct Used { - pub flags: UsedF, - pub idx: le16, - ring_and_avail_event: [le16], -} - -impl Used { - pub fn layout(queue_size: u16, has_event_idx: bool) -> Layout { - let event_idx_layout = if has_event_idx { - Layout::new::() - } else { - Layout::new::<()>() - }; - - Layout::array::(2) - .unwrap() - .extend(Layout::array::(queue_size.into()).unwrap()) - .unwrap() - .0 - .extend(event_idx_layout) - .unwrap() - .0 - .pad_to_align() - } - - pub fn from_ptr(ptr: NonNull<[u8]>, has_event_idx: bool) -> Option> { - let len = ptr.len(); - let ptr = ptr.cast::().as_ptr(); - - if !ptr.cast::().is_aligned() { - return None; - } - - if len % mem::size_of::() != usize::from(!has_event_idx) * mem::size_of::() - { - return None; - } - - let len = len / mem::size_of::() - 2 - usize::from(has_event_idx); - let ptr = ptr::slice_from_raw_parts(ptr, len) as *mut Used; - Some(NonNull::new(ptr).unwrap()) - } - - pub fn ring_ptr(this: NonNull) -> NonNull<[UsedElem]> { - let ptr = unsafe { addr_of_mut!((*this.as_ptr()).ring_and_avail_event) }; - let len = ptr.len() * mem::size_of::() / mem::size_of::(); - let ptr = NonNull::new(ptr).unwrap().cast::(); - NonNull::slice_from_raw_parts(ptr, len) - } - - pub fn ring(&self) -> &[UsedElem] { - let ptr = Self::ring_ptr(NonNull::from(self)); - unsafe { ptr.as_ref() } - } - - pub fn ring_mut(&mut self) -> &mut [UsedElem] { - let mut ptr = Self::ring_ptr(NonNull::from(self)); - unsafe { ptr.as_mut() } - } - - pub fn avail_event_ptr(this: NonNull) -> Option> { - let ptr = unsafe { addr_of_mut!((*this.as_ptr()).ring_and_avail_event) }; - - if ptr.len() * mem::size_of::() % mem::size_of::() != mem::size_of::() - { - return None; - } - - let start = ptr as *mut le16; - let ptr = unsafe { start.add(ptr.len() - 1) }; - Some(NonNull::new(ptr).unwrap()) - } - - pub fn avail_event(&self) -> Option<&le16> { - Self::avail_event_ptr(NonNull::from(self)).map(|ptr| unsafe { ptr.as_ref() }) - } - - pub fn avail_event_mut(&mut self) -> Option<&mut le16> { - Self::avail_event_ptr(NonNull::from(self)).map(|mut ptr| unsafe { ptr.as_mut() }) - } -} - -endian_bitflags! { - /// Virtqueue used ring flags - #[doc(alias = "VIRTQ_USED_F")] - pub struct UsedF: le16 { - /// The device uses this in used->flags to advise the driver: don’t kick me - /// when you add a buffer. It’s unreliable, so it’s simply an - /// optimization. - #[doc(alias = "VIRTQ_USED_F_NO_NOTIFY")] - const NO_NOTIFY = 1; - } -} - -/// Used Ring Entry -#[doc(alias = "virtq_used_elem")] -#[derive(Clone, Copy, Debug)] -#[repr(C)] -pub struct UsedElem { - /// Index of start of used descriptor chain. - /// - /// [`le32`] is used here for ids for padding reasons. - pub id: le32, - - /// The number of bytes written into the device writable portion of - /// the buffer described by the descriptor chain. - pub len: le32, -} diff --git a/virtio-spec/src/volatile.rs b/virtio-spec/src/volatile.rs deleted file mode 100644 index 468e68e355..0000000000 --- a/virtio-spec/src/volatile.rs +++ /dev/null @@ -1,309 +0,0 @@ -//! Volatile Pointer Types. - -use core::marker::PhantomData; - -use volatile::access::{Readable, Writable}; -use volatile::VolatilePtr; - -use crate::{be32, be64, le16, le32, le64, DeviceStatus, Id}; - -/// A wide volatile pointer for 64-bit fields. -/// -/// In virtio, 64-bit fields are to be treated as two 32-bit fields, with low 32 bit part followed by the high 32 bit part. -/// This type mimics [`VolatilePtr`], and allows easy access to 64-bit fields. -pub struct WideVolatilePtr<'a, T, A> -where - T: ?Sized, -{ - low: VolatilePtr<'a, T, A>, - high: VolatilePtr<'a, T, A>, -} - -impl<'a, T, A> Copy for WideVolatilePtr<'a, T, A> where T: ?Sized {} - -impl<'a, T, A> Clone for WideVolatilePtr<'a, T, A> -where - T: ?Sized, -{ - fn clone(&self) -> Self { - *self - } -} - -impl<'a, T, A> WideVolatilePtr<'a, T, A> { - /// Creates a new wide volatile pointer from pointers to the low and to the high part. - pub fn from_low_high(low: VolatilePtr<'a, T, A>, high: VolatilePtr<'a, T, A>) -> Self { - Self { low, high } - } -} - -impl<'a, A> WideVolatilePtr<'a, le32, A> { - /// Performs a volatile read of the contained value. - /// - /// See [`VolatilePtr::read`]. - pub fn read(self) -> le64 - where - A: Readable, - { - let low = self.low.read(); - let high = self.high.read(); - le64::from([low, high]) - } - - /// Performs a volatile write, setting the contained value to the given `value`. - /// - /// See [`VolatilePtr::write`]. - pub fn write(self, value: le64) - where - A: Writable, - { - let [low, high] = value.into(); - self.low.write(low); - self.high.write(high); - } - - /// Updates the contained value using the given closure and volatile instructions. - /// - /// See [`VolatilePtr::update`]. - pub fn update(self, f: impl FnOnce(le64) -> le64) - where - A: Readable + Writable, - { - let new = f(self.read()); - self.write(new); - } -} - -impl<'a, A> WideVolatilePtr<'a, be32, A> { - /// Performs a volatile read of the contained value. - /// - /// See [`VolatilePtr::read`]. - pub fn read(self) -> be64 - where - A: Readable, - { - let low = self.low.read(); - let high = self.high.read(); - be64::from([low, high]) - } - - /// Performs a volatile write, setting the contained value to the given `value`. - /// - /// See [`VolatilePtr::write`]. - pub fn write(self, value: be64) - where - A: Writable, - { - let [low, high] = value.into(); - self.low.write(low); - self.high.write(high); - } - - /// Updates the contained value using the given closure and volatile instructions. - /// - /// See [`VolatilePtr::update`]. - pub fn update(self, f: impl FnOnce(be64) -> be64) - where - A: Readable + Writable, - { - let new = f(self.read()); - self.write(new); - } -} - -#[cfg(any(feature = "mmio", feature = "pci"))] -macro_rules! impl_wide_field_access { - ( - $(#[$outer:meta])* - $vis:vis trait $Trait:ident<'a, A>: $T:ty { - $( - $(#[doc = $doc:literal])* - $(#[doc(alias = $alias:literal)])? - #[access($Access:ty)] - $field:ident: $field_low:ident, $field_high:ident; - )* - } - ) => { - $(#[$outer])* - $vis trait $Trait<'a, A> { - $( - $(#[doc = $doc])* - $(#[doc(alias = $alias)])? - fn $field(self) -> WideVolatilePtr<'a, le32, A::Restricted> - where - A: RestrictAccess<$Access>; - )* - } - - impl<'a, A> $Trait<'a, A> for VolatilePtr<'a, $T, A> { - $( - fn $field(self) -> WideVolatilePtr<'a, le32, A::Restricted> - where - A: RestrictAccess<$Access>, - { - WideVolatilePtr::from_low_high(self.$field_low(), self.$field_high()) - } - )* - } - }; -} - -/// An overaligned volatile pointer for fields that require wider access operations. -/// -/// In virtio, some fields require wider access operations than their type indicate, such as for [`mmio::DeviceRegisters`]. -/// -/// [`mmio::DeviceRegisters`]: crate::mmio::DeviceRegisters -pub struct OveralignedVolatilePtr<'a, T, F, A> -where - T: ?Sized, - F: ?Sized, -{ - ptr: VolatilePtr<'a, F, A>, - ty: PhantomData>, -} - -impl<'a, T, F, A> Copy for OveralignedVolatilePtr<'a, T, F, A> -where - T: ?Sized, - F: ?Sized, -{ -} - -impl<'a, T, F, A> Clone for OveralignedVolatilePtr<'a, T, F, A> -where - T: ?Sized, - F: ?Sized, -{ - fn clone(&self) -> Self { - *self - } -} - -impl<'a, T, F, A> OveralignedVolatilePtr<'a, T, F, A> -where - T: OveralignedField, - F: Copy, -{ - /// Creates a new overaligned volatile pointer. - pub fn new(ptr: VolatilePtr<'a, F, A>) -> Self { - Self { - ptr, - ty: PhantomData, - } - } - - /// Performs a volatile read of the contained value. - /// - /// See [`VolatilePtr::read`]. - pub fn read(self) -> T - where - A: Readable, - { - T::from_field(self.ptr.read()) - } - - /// Performs a volatile write, setting the contained value to the given `value`. - /// - /// See [`VolatilePtr::write`]. - pub fn write(self, value: T) - where - A: Writable, - { - self.ptr.write(value.into_field()) - } - - /// Updates the contained value using the given closure and volatile instructions. - /// - /// See [`VolatilePtr::update`]. - pub fn update(self, f: impl FnOnce(T) -> T) - where - A: Readable + Writable, - { - let new = f(self.read()); - self.write(new); - } -} - -/// A trait for fields that can be accessed via [`OveralignedVolatilePtr`]. -pub trait OveralignedField: private::Sealed { - /// Converts to this type from the overaligned field. - fn from_field(field: F) -> Self; - - /// Converts this type into the overaligned field. - fn into_field(self) -> F; -} - -impl OveralignedField for le16 { - fn from_field(field: le32) -> Self { - field.try_into().unwrap() - } - - fn into_field(self) -> le32 { - self.into() - } -} - -impl OveralignedField for bool { - fn from_field(field: le32) -> Self { - field.to_ne() == 1 - } - - fn into_field(self) -> le32 { - le32::from_ne(self as u32) - } -} - -impl OveralignedField for u8 { - fn from_field(field: le32) -> Self { - field.to_ne().try_into().unwrap() - } - - fn into_field(self) -> le32 { - le32::from_ne(self.into()) - } -} - -impl OveralignedField for Id { - fn from_field(field: le32) -> Self { - Self::from(u8::from_field(field)) - } - - fn into_field(self) -> le32 { - u8::from(self).into_field() - } -} - -impl OveralignedField for DeviceStatus { - fn from_field(field: le32) -> Self { - Self::from_bits_retain(u8::from_field(field)) - } - - fn into_field(self) -> le32 { - self.bits().into_field() - } -} - -#[cfg(feature = "mmio")] -impl OveralignedField for crate::mmio::InterruptStatus { - fn from_field(field: le32) -> Self { - Self::from_bits_retain(u8::from_field(field)) - } - - fn into_field(self) -> le32 { - self.bits().into_field() - } -} - -mod private { - use crate::{le16, le32, DeviceStatus, Id}; - - pub trait Sealed {} - - impl Sealed for bool {} - impl Sealed for u8 {} - impl Sealed for le16 {} - impl Sealed for Id {} - impl Sealed for DeviceStatus {} - #[cfg(feature = "mmio")] - impl Sealed for crate::mmio::InterruptStatus {} -} diff --git a/virtio-spec/src/vsock.rs b/virtio-spec/src/vsock.rs deleted file mode 100644 index 366bc0535d..0000000000 --- a/virtio-spec/src/vsock.rs +++ /dev/null @@ -1,142 +0,0 @@ -//! Socket Device - -use endian_num::{le16, le32}; -use num_enum::{IntoPrimitive, TryFromPrimitive, UnsafeFromPrimitive}; -use volatile_macro::VolatileFieldAccess; - -pub use super::features::vsock::F; -use crate::le64; - -/// Socket Device Configuration Layout -#[doc(alias = "virtio_vsock_config")] -#[cfg_attr( - feature = "zerocopy", - derive(zerocopy_derive::FromZeroes, zerocopy_derive::FromBytes) -)] -#[derive(VolatileFieldAccess)] -#[repr(C)] -pub struct Config { - guest_cid: le64, -} - -/// Socket Device Header -#[doc(alias = "virtio_vsock_hdr")] -#[cfg_attr( - feature = "zerocopy", - derive( - zerocopy_derive::FromZeroes, - zerocopy_derive::FromBytes, - zerocopy_derive::AsBytes - ) -)] -#[derive(Default, Clone, Copy, Debug)] -#[repr(C, packed)] -pub struct Hdr { - pub src_cid: le64, - pub dst_cid: le64, - pub src_port: le32, - pub dst_port: le32, - pub len: le32, - pub type_: le16, - pub op: le16, - pub flags: le32, - pub buf_alloc: le32, - pub fwd_cnt: le32, -} - -#[doc(alias = "VIRTIO_VSOCK_OP")] -#[derive( - IntoPrimitive, TryFromPrimitive, UnsafeFromPrimitive, PartialEq, Eq, Clone, Copy, Debug, -)] -#[non_exhaustive] -#[repr(u16)] -pub enum Op { - #[doc(alias = "VIRTIO_VSOCK_OP_INVALID")] - Invalid = 0, - - #[doc(alias = "VIRTIO_VSOCK_OP_REQUEST")] - Request = 1, - - #[doc(alias = "VIRTIO_VSOCK_OP_RESPONSE")] - Response = 2, - - #[doc(alias = "VIRTIO_VSOCK_OP_RST")] - Rst = 3, - - #[doc(alias = "VIRTIO_VSOCK_OP_SHUTDOWN")] - Shutdown = 4, - - #[doc(alias = "VIRTIO_VSOCK_OP_RW")] - Rw = 5, - - #[doc(alias = "VIRTIO_VSOCK_OP_CREDIT_UPDATE")] - CreditUpdate = 6, - - #[doc(alias = "VIRTIO_VSOCK_OP_CREDIT_REQUEST")] - CreditRequest = 7, -} - -#[doc(alias = "VIRTIO_VSOCK_TYPE")] -#[derive( - IntoPrimitive, TryFromPrimitive, UnsafeFromPrimitive, PartialEq, Eq, Clone, Copy, Debug, -)] -#[non_exhaustive] -#[repr(u16)] -pub enum Type { - #[doc(alias = "VIRTIO_VSOCK_TYPE_STREAM")] - Stream = 1, - - #[doc(alias = "VIRTIO_VSOCK_TYPE_SEQPACKET")] - Seqpacket = 2, -} - -endian_bitflags! { - /// Socket Device Shutdown Flags - #[doc(alias = "VIRTIO_VSOCK_SHUTDOWN_F")] - pub struct ShutdownF: le32 { - #[doc(alias = "VIRTIO_VSOCK_SHUTDOWN_F_RECEIVE")] - const RECEIVE = 1 << 0; - - #[doc(alias = "VIRTIO_VSOCK_SHUTDOWN_F_SEND")] - const SEND = 1 << 1; - } -} - -endian_bitflags! { - /// Socket Device Sequence Flags - #[doc(alias = "VIRTIO_VSOCK_SEQ")] - pub struct Seq: le32 { - #[doc(alias = "VIRTIO_VSOCK_SEQ_EOM")] - const EOM = 1 << 0; - - #[doc(alias = "VIRTIO_VSOCK_SEQ_EOR")] - const EOR = 1 << 1; - } -} - -#[doc(alias = "VIRTIO_VSOCK_EVENT")] -#[derive( - IntoPrimitive, TryFromPrimitive, UnsafeFromPrimitive, PartialEq, Eq, Clone, Copy, Debug, -)] -#[non_exhaustive] -#[repr(u32)] -pub enum EventId { - #[doc(alias = "VIRTIO_VSOCK_EVENT_TRANSPORT_RESET")] - TransportReset = 0, -} - -/// Socket Device Event -#[doc(alias = "virtio_vsock_event")] -#[cfg_attr( - feature = "zerocopy", - derive( - zerocopy_derive::FromZeroes, - zerocopy_derive::FromBytes, - zerocopy_derive::AsBytes - ) -)] -#[derive(Default, Clone, Copy, Debug)] -#[repr(C)] -pub struct Event { - id: le32, -}