Skip to content

Commit c561bd9

Browse files
authored
Merge pull request #1257 from hermit-os/pci-cap
refactor(virtio/pci): migrate PCI capabilities to virtio-spec
2 parents 5841c7a + 1a64402 commit c561bd9

File tree

5 files changed

+319
-136
lines changed

5 files changed

+319
-136
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.

src/drivers/pci.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ impl<T: ConfigRegionAccess> PciDevice<T> {
8686
Self { address, access }
8787
}
8888

89+
pub fn access(&self) -> &T {
90+
&self.access
91+
}
92+
8993
pub fn header(&self) -> PciHeader {
9094
PciHeader::new(self.address)
9195
}

src/drivers/virtio/transport/pci.rs

Lines changed: 30 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use core::{mem, ptr};
1010

1111
use pci_types::capability::PciCapability;
1212
use virtio_spec::pci::{
13-
CommonCfg, CommonCfgVolatileFieldAccess, CommonCfgVolatileWideFieldAccess,
13+
Cap, CapCfgType, CommonCfg, CommonCfgVolatileFieldAccess, CommonCfgVolatileWideFieldAccess,
1414
IsrStatus as IsrStatusRaw,
1515
};
1616
use virtio_spec::{le32, DeviceStatus};
@@ -39,13 +39,13 @@ use crate::drivers::virtio::error::VirtioError;
3939
pub struct Origin {
4040
cfg_ptr: u16, // Register to be read to reach configuration structure of type cfg_type
4141
dev_id: u16,
42-
cap_struct: PciCapRaw,
42+
cap_struct: Cap,
4343
}
4444

4545
/// Maps a given device specific pci configuration structure and
4646
/// returns a static reference to it.
4747
pub fn map_dev_cfg<T>(cap: &PciCap) -> Option<&'static mut T> {
48-
if cap.cfg_type != virtio_spec::pci::Cap::DeviceCfg {
48+
if cap.cfg_type != CapCfgType::Device {
4949
error!("Capability of device config has wrong id. Mapping not possible...");
5050
return None;
5151
};
@@ -84,10 +84,10 @@ pub fn map_dev_cfg<T>(cap: &PciCap) -> Option<&'static mut T> {
8484
/// as it is not directly mapped into address space from PCI device
8585
/// configuration space.
8686
/// Therefore the struct only contains necessary information to map
87-
/// corresponding config type (`pci::Cap`) into address space.
87+
/// corresponding config type into address space.
8888
#[derive(Clone)]
8989
pub struct PciCap {
90-
cfg_type: virtio_spec::pci::Cap,
90+
cfg_type: CapCfgType,
9191
bar: PciBar,
9292
id: u8,
9393
offset: MemOff,
@@ -172,50 +172,11 @@ impl PciCap {
172172
}
173173
}
174174

175-
/// Virtio's PCI capabilities structure.
176-
/// See Virtio specification v.1.1 - 4.1.4
177-
///
178-
/// WARN: endianness of this structure should be seen as little endian.
179-
/// As this structure is not meant to be used outside of this module and for
180-
/// ease of conversion from reading data into struct from PCI configuration
181-
/// space, no conversion is made for struct fields.
182-
#[derive(Clone, Debug)]
183-
#[repr(C)]
184-
struct PciCapRaw {
185-
cap_vndr: u8,
186-
cap_next: u8,
187-
cap_len: u8,
188-
cfg_type: u8,
189-
bar_index: u8,
190-
id: u8,
191-
padding: [u8; 2],
192-
offset: u32,
193-
length: u32,
194-
}
195-
196-
// This only shows compiler, that structs are identical
197-
// with themselves.
198-
impl Eq for PciCapRaw {}
199-
200-
// In order to compare two PciCapRaw structs PartialEq is needed
201-
impl PartialEq for PciCapRaw {
202-
fn eq(&self, other: &Self) -> bool {
203-
self.cap_vndr == other.cap_vndr
204-
&& self.cap_next == other.cap_next
205-
&& self.cap_len == other.cap_len
206-
&& self.cfg_type == other.cfg_type
207-
&& self.bar_index == other.bar_index
208-
&& self.id == other.id
209-
&& self.offset == other.offset
210-
&& self.length == other.length
211-
}
212-
}
213-
214175
/// Universal Caplist Collections holds all universal capability structures for
215176
/// a given Virtio PCI device.
216177
///
217178
/// As Virtio's PCI devices are allowed to present multiple capability
218-
/// structures of the same config type (`pci::Cap`), the structure
179+
/// structures of the same config type, the structure
219180
/// provides a driver with all capabilities, sorted in descending priority,
220181
/// allowing the driver to choose.
221182
/// The structure contains a special dev_cfg_list field, a vector holding
@@ -597,7 +558,7 @@ impl NotifCfg {
597558
// Assumes the cap_len is a multiple of 8
598559
// This read MIGHT be slow, as it does NOT ensure 32 bit alignment.
599560
let notify_off_multiplier_ptr =
600-
cap.origin.cfg_ptr + u16::try_from(mem::size_of::<PciCapRaw>()).unwrap();
561+
cap.origin.cfg_ptr + u16::try_from(mem::size_of::<Cap>()).unwrap();
601562
let notify_off_multiplier = cap.device.read_register(notify_off_multiplier_ptr);
602563

603564
// define base memory address from which the actual Queue Notify address can be derived via
@@ -785,23 +746,24 @@ impl ShMemCfg {
785746

786747
// Assumes the cap_len is a multiple of 8
787748
// This read MIGHT be slow, as it does NOT ensure 32 bit alignment.
788-
let offset_hi_ptr =
789-
cap.origin.cfg_ptr + u16::try_from(mem::size_of::<PciCapRaw>()).unwrap();
749+
let offset_hi_ptr = cap.origin.cfg_ptr + u16::try_from(mem::size_of::<Cap>()).unwrap();
790750
let offset_hi = cap.device.read_register(offset_hi_ptr);
791751

792752
// Create 64 bit offset from high and low 32 bit values
793-
let offset =
794-
MemOff::from((u64::from(offset_hi) << 32) ^ u64::from(cap.origin.cap_struct.offset));
753+
let offset = MemOff::from(
754+
(u64::from(offset_hi) << 32) ^ u64::from(cap.origin.cap_struct.offset.to_ne()),
755+
);
795756

796757
// Assumes the cap_len is a multiple of 8
797758
// This read MIGHT be slow, as it does NOT ensure 32 bit alignment.
798759
let length_hi_ptr = cap.origin.cfg_ptr
799-
+ u16::try_from(mem::size_of::<PciCapRaw>() + mem::size_of::<u32>()).unwrap();
760+
+ u16::try_from(mem::size_of::<Cap>() + mem::size_of::<u32>()).unwrap();
800761
let length_hi = cap.device.read_register(length_hi_ptr);
801762

802763
// Create 64 bit length from high and low 32 bit values
803-
let length =
804-
MemLen::from((u64::from(length_hi) << 32) ^ u64::from(cap.origin.cap_struct.length));
764+
let length = MemLen::from(
765+
(u64::from(length_hi) << 32) ^ u64::from(cap.origin.cap_struct.length.to_ne()),
766+
);
805767

806768
let virt_addr_raw = cap.bar.mem_addr + offset;
807769
let raw_ptr = ptr::with_exposed_provenance_mut::<u8>(virt_addr_raw.into());
@@ -885,36 +847,6 @@ impl PciBar {
885847
}
886848
}
887849

888-
/// Reads a raw capability struct [PciCapRaw] out of a PCI device's configuration space.
889-
fn read_cap_raw(device: &PciDevice<PciConfigRegion>, register: u16) -> PciCapRaw {
890-
let mut quadruple_word: [u8; 16] = [0; 16];
891-
892-
debug!("Converting read word from PCI device config space into native endian bytes.");
893-
894-
// Write words sequentially into array
895-
for i in 0..4 {
896-
// Read word need to be converted to little endian bytes as PCI is little endian.
897-
// Interpretation of multi byte values needs to be swapped for big endian machines
898-
let word: [u8; 4] = device.read_register(register + 4 * i).to_le_bytes();
899-
let i = 4 * i as usize;
900-
quadruple_word[i..i + 4].copy_from_slice(&word);
901-
}
902-
903-
PciCapRaw {
904-
cap_vndr: quadruple_word[0],
905-
cap_next: quadruple_word[1],
906-
cap_len: quadruple_word[2],
907-
cfg_type: quadruple_word[3],
908-
bar_index: quadruple_word[4],
909-
id: quadruple_word[5],
910-
// Unwrapping is okay here, as transformed array slice is always 2 * u8 long and initialized
911-
padding: quadruple_word[6..8].try_into().unwrap(),
912-
// Unwrapping is okay here, as transformed array slice is always 4 * u8 long and initialized
913-
offset: u32::from_le_bytes(quadruple_word[8..12].try_into().unwrap()),
914-
length: u32::from_le_bytes(quadruple_word[12..16].try_into().unwrap()),
915-
}
916-
}
917-
918850
/// Reads all PCI capabilities, starting at the capabilities list pointer from the
919851
/// PCI device.
920852
///
@@ -933,17 +865,17 @@ fn read_caps(
933865
PciCapability::Vendor(capability) => Some(capability),
934866
_ => None,
935867
})
936-
.map(|capability| (capability.offset, read_cap_raw(device, capability.offset)))
937-
.filter(|(_ptr, capability)| capability.cfg_type != virtio_spec::pci::Cap::PciCfg.into())
868+
.map(|addr| {
869+
let cap = Cap::read(addr.clone(), device.access()).unwrap();
870+
(addr.offset, cap)
871+
})
872+
.filter(|(_ptr, capability)| capability.cfg_type != CapCfgType::Pci.into())
938873
.map(|(ptr, capability)| PciCap {
939-
cfg_type: virtio_spec::pci::Cap::from(capability.cfg_type),
940-
bar: *bars
941-
.iter()
942-
.find(|bar| bar.index == capability.bar_index)
943-
.unwrap(),
874+
cfg_type: CapCfgType::from(capability.cfg_type),
875+
bar: *bars.iter().find(|bar| bar.index == capability.bar).unwrap(),
944876
id: capability.id,
945-
offset: MemOff::from(capability.offset),
946-
length: MemLen::from(capability.length),
877+
offset: MemOff::from(capability.offset.to_ne()),
878+
length: MemLen::from(capability.length.to_ne()),
947879
device: *device,
948880
origin: Origin {
949881
cfg_ptr: ptr,
@@ -1003,36 +935,36 @@ pub(crate) fn map_caps(device: &PciDevice<PciConfigRegion>) -> Result<UniCapsCol
1003935
// Map Caps in virtual memory
1004936
for pci_cap in cap_list {
1005937
match pci_cap.cfg_type {
1006-
virtio_spec::pci::Cap::CommonCfg => match pci_cap.map_common_cfg() {
938+
CapCfgType::Common => match pci_cap.map_common_cfg() {
1007939
Some(cap) => caps.add_cfg_common(ComCfg::new(cap, pci_cap.id)),
1008940
None => error!(
1009941
"Common config capability with id {}, of device {:x}, could not be mapped!",
1010942
pci_cap.id, device_id
1011943
),
1012944
},
1013-
virtio_spec::pci::Cap::NotifyCfg => match NotifCfg::new(&pci_cap) {
945+
CapCfgType::Notify => match NotifCfg::new(&pci_cap) {
1014946
Some(notif) => caps.add_cfg_notif(notif),
1015947
None => error!(
1016948
"Notification config capability with id {}, of device {:x} could not be used!",
1017949
pci_cap.id, device_id
1018950
),
1019951
},
1020-
virtio_spec::pci::Cap::IsrCfg => match pci_cap.map_isr_status() {
952+
CapCfgType::Isr => match pci_cap.map_isr_status() {
1021953
Some(isr_stat) => caps.add_cfg_isr(IsrStatus::new(isr_stat, pci_cap.id)),
1022954
None => error!(
1023955
"ISR status config capability with id {}, of device {:x} could not be used!",
1024956
pci_cap.id, device_id
1025957
),
1026958
},
1027-
virtio_spec::pci::Cap::PciCfg => caps.add_cfg_alt(PciCfgAlt::new(&pci_cap)),
1028-
virtio_spec::pci::Cap::SharedMemoryCfg => match ShMemCfg::new(&pci_cap) {
959+
CapCfgType::Pci => caps.add_cfg_alt(PciCfgAlt::new(&pci_cap)),
960+
CapCfgType::SharedMemory => match ShMemCfg::new(&pci_cap) {
1029961
Some(sh_mem) => caps.add_cfg_sh_mem(sh_mem),
1030962
None => error!(
1031963
"Shared Memory config capability with id {}, of device {:x} could not be used!",
1032964
pci_cap.id, device_id
1033965
),
1034966
},
1035-
virtio_spec::pci::Cap::DeviceCfg => caps.add_cfg_dev(pci_cap),
967+
CapCfgType::Device => caps.add_cfg_dev(pci_cap),
1036968

1037969
// PCI's configuration space is allowed to hold other structures, which are not virtio specific and are therefore ignored
1038970
// in the following

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 }

0 commit comments

Comments
 (0)