Skip to content

Commit f10132a

Browse files
committed
feat(virtio-spec): add PCI Capabilities
Signed-off-by: Martin Kröning <[email protected]>
1 parent 2d4bd6b commit f10132a

File tree

3 files changed

+247
-0
lines changed

3 files changed

+247
-0
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

virtio-spec/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ categories = ["no-std", "no-std::no-alloc"]
1212
bitflags = "2"
1313
endian-num = { version = "0.1", features = ["bitflags", "linux-types"] }
1414
num_enum = { version = "0.7", default-features = false }
15+
pci_types = "0.7"
1516
volatile = "0.6"
1617
volatile-macro = "0.6"
1718
zerocopy = { version = "0.7", optional = true, default-features = false }

virtio-spec/src/pci.rs

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,258 @@
11
//! Definitions for Virtio over PCI bus.
22
3+
use core::mem;
4+
5+
use endian_num::{le64, Le};
36
use num_enum::{FromPrimitive, IntoPrimitive};
7+
use pci_types::capability::PciCapabilityAddress;
8+
use pci_types::ConfigRegionAccess;
49
use volatile::access::{ReadOnly, ReadWrite, RestrictAccess};
510
use volatile::VolatilePtr;
611
use volatile_macro::VolatileFieldAccess;
712

813
use crate::volatile::WideVolatilePtr;
914
use crate::{le16, le32, DeviceStatus};
1015

16+
/// PCI Capability
17+
///
18+
/// See [`CapData`] for reading additional fields.
19+
#[doc(alias = "virtio_pci_cap")]
20+
#[cfg_attr(
21+
feature = "zerocopy",
22+
derive(zerocopy_derive::FromZeroes, zerocopy_derive::FromBytes)
23+
)]
24+
#[derive(Clone, Copy, Debug)]
25+
#[repr(C)]
26+
pub struct Cap {
27+
/// Generic PCI field: `PCI_CAP_ID_VNDR`
28+
///
29+
/// 0x09; Identifies a vendor-specific capability.
30+
pub cap_vndr: u8,
31+
32+
/// Generic PCI field: next ptr.
33+
///
34+
/// Link to next capability in the capability list in the PCI configuration space.
35+
pub cap_next: u8,
36+
37+
/// Generic PCI field: capability length
38+
///
39+
/// Length of this capability structure, including the whole of
40+
/// struct virtio_pci_cap, and extra data if any.
41+
/// This length MAY include padding, or fields unused by the driver.
42+
pub cap_len: u8,
43+
44+
/// Identifies the structure.
45+
///
46+
/// Each structure is detailed individually below.
47+
///
48+
/// The device MAY offer more than one structure of any type - this makes it
49+
/// possible for the device to expose multiple interfaces to drivers. The order of
50+
/// the capabilities in the capability list specifies the order of preference
51+
/// suggested by the device. A device may specify that this ordering mechanism be
52+
/// overridden by the use of the `id` field.
53+
///
54+
/// <div class="warning">
55+
///
56+
/// For example, on some hypervisors, notifications using IO accesses are
57+
/// faster than memory accesses. In this case, the device would expose two
58+
/// capabilities with `cfg_type` set to VIRTIO_PCI_CAP_NOTIFY_CFG:
59+
/// the first one addressing an I/O BAR, the second one addressing a memory BAR.
60+
/// In this example, the driver would use the I/O BAR if I/O resources are available, and fall back on
61+
/// memory BAR when I/O resources are unavailable.
62+
///
63+
/// </div>
64+
pub cfg_type: u8,
65+
66+
/// Where to find it.
67+
///
68+
/// values 0x0 to 0x5 specify a Base Address register (BAR) belonging to
69+
/// the function located beginning at 10h in PCI Configuration Space
70+
/// and used to map the structure into Memory or I/O Space.
71+
/// The BAR is permitted to be either 32-bit or 64-bit, it can map Memory Space
72+
/// or I/O Space.
73+
///
74+
/// Any other value is reserved for future use.
75+
pub bar: u8,
76+
77+
/// Multiple capabilities of the same type
78+
///
79+
/// Used by some device types to uniquely identify multiple capabilities
80+
/// of a certain type. If the device type does not specify the meaning of
81+
/// this field, its contents are undefined.
82+
pub id: u8,
83+
84+
/// Pad to full dword.
85+
pub padding: [u8; 2],
86+
87+
/// Offset within bar.
88+
///
89+
/// indicates where the structure begins relative to the base address associated
90+
/// with the BAR. The alignment requirements of `offset` are indicated
91+
/// in each structure-specific section below.
92+
pub offset: le32,
93+
94+
/// Length of the structure, in bytes.
95+
///
96+
/// indicates the length of the structure.
97+
///
98+
/// `length` MAY include padding, or fields unused by the driver, or
99+
/// future extensions.
100+
///
101+
/// <div class="warning">
102+
///
103+
/// For example, a future device might present a large structure size of several
104+
/// MBytes.
105+
/// As current devices never utilize structures larger than 4KBytes in size,
106+
/// driver MAY limit the mapped structure size to e.g.
107+
/// 4KBytes (thus ignoring parts of structure after the first
108+
/// 4KBytes) to allow forward compatibility with such devices without loss of
109+
/// functionality and without wasting resources.
110+
///
111+
/// </div>
112+
pub length: le32,
113+
}
114+
115+
impl Cap {
116+
pub fn read(addr: PciCapabilityAddress, access: &impl ConfigRegionAccess) -> Option<Self> {
117+
let data = unsafe { access.read(addr.address, addr.offset) };
118+
let [cap_vndr, _cap_next, cap_len, _cfg_type] = data.to_ne_bytes();
119+
120+
if cap_vndr != 0x09 {
121+
return None;
122+
}
123+
124+
if cap_len < 16 {
125+
return None;
126+
}
127+
128+
let data = [
129+
data,
130+
unsafe { access.read(addr.address, addr.offset + 4) },
131+
unsafe { access.read(addr.address, addr.offset + 8) },
132+
unsafe { access.read(addr.address, addr.offset + 12) },
133+
];
134+
135+
let this = unsafe { mem::transmute::<[u32; 4], Self>(data) };
136+
137+
Some(this)
138+
}
139+
}
140+
141+
/// PCI Capability 64
142+
#[doc(alias = "virtio_pci_cap64")]
143+
#[cfg_attr(
144+
feature = "zerocopy",
145+
derive(zerocopy_derive::FromZeroes, zerocopy_derive::FromBytes)
146+
)]
147+
#[derive(Clone, Copy, Debug)]
148+
#[repr(C)]
149+
pub struct Cap64 {
150+
pub cap: Cap,
151+
pub offset_hi: le32,
152+
pub length_hi: le32,
153+
}
154+
155+
/// PCI Notify Capability
156+
#[doc(alias = "virtio_pci_notify_cap")]
157+
#[cfg_attr(
158+
feature = "zerocopy",
159+
derive(zerocopy_derive::FromZeroes, zerocopy_derive::FromBytes)
160+
)]
161+
#[derive(Clone, Copy, Debug)]
162+
#[repr(C)]
163+
pub struct NotifyCap {
164+
pub cap: Cap,
165+
166+
/// Multiplier for queue_notify_off.
167+
pub notify_off_multiplier: le32,
168+
}
169+
170+
/// PCI Configuration Capability
171+
#[doc(alias = "virtio_pci_cfg_cap")]
172+
#[cfg_attr(
173+
feature = "zerocopy",
174+
derive(zerocopy_derive::FromZeroes, zerocopy_derive::FromBytes)
175+
)]
176+
#[derive(Clone, Copy, Debug)]
177+
#[repr(C)]
178+
pub struct CfgCap {
179+
pub cap: Cap,
180+
181+
/// Data for BAR access.
182+
pub pci_cfg_data: [u8; 4],
183+
}
184+
185+
/// PCI Capability Data
186+
#[derive(Clone, Copy, Debug)]
187+
pub struct CapData {
188+
/// Identifies the structure.
189+
pub cfg_type: CapCfgType,
190+
191+
/// Where to find it.
192+
pub bar: u8,
193+
194+
/// Multiple capabilities of the same type
195+
pub id: u8,
196+
197+
/// Offset within bar.
198+
pub offset: le64,
199+
200+
/// Length of the structure, in bytes.
201+
pub length: le64,
202+
203+
/// Multiplier for queue_notify_off.
204+
pub notify_off_multiplier: Option<le32>,
205+
}
206+
207+
impl CapData {
208+
pub fn read(addr: PciCapabilityAddress, access: &impl ConfigRegionAccess) -> Option<Self> {
209+
let cap = Cap::read(addr.clone(), access)?;
210+
let cfg_type = CapCfgType::from(cap.cfg_type);
211+
212+
let (offset, length) = match cfg_type {
213+
CapCfgType::SharedMemory => {
214+
if cap.cap_len < 24 {
215+
return None;
216+
}
217+
218+
let offset_hi = unsafe { access.read(addr.address, addr.offset + 16) };
219+
let offset_hi = Le(offset_hi);
220+
let offset = le64::from([cap.offset, offset_hi]);
221+
222+
let length_hi = unsafe { access.read(addr.address, addr.offset + 20) };
223+
let length_hi = Le(length_hi);
224+
let length = le64::from([cap.length, length_hi]);
225+
226+
(offset, length)
227+
}
228+
_ => (le64::from(cap.offset), le64::from(cap.length)),
229+
};
230+
231+
let notify_off_multiplier = match cfg_type {
232+
CapCfgType::Notify => {
233+
if cap.cap_len < 20 {
234+
return None;
235+
}
236+
237+
let notify_off_multiplier = unsafe { access.read(addr.address, addr.offset + 16) };
238+
let notify_off_multiplier = Le(notify_off_multiplier);
239+
240+
Some(notify_off_multiplier)
241+
}
242+
_ => None,
243+
};
244+
245+
Some(Self {
246+
cfg_type,
247+
bar: cap.bar,
248+
id: cap.id,
249+
offset,
250+
length,
251+
notify_off_multiplier,
252+
})
253+
}
254+
}
255+
11256
/// PCI Capability Configuration Type
12257
#[derive(IntoPrimitive, FromPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
13258
#[non_exhaustive]

0 commit comments

Comments
 (0)