Skip to content

Commit

Permalink
Merge pull request #164 from rcore-os/pkvm
Browse files Browse the repository at this point in the history
Add hypercall-based PCI CAM and transport for x86-64 pKVM.
  • Loading branch information
qwandor authored Jan 31, 2025
2 parents 3e40330 + 28b1426 commit c620d59
Show file tree
Hide file tree
Showing 6 changed files with 544 additions and 38 deletions.
2 changes: 2 additions & 0 deletions src/transport/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ pub mod fake;
pub mod mmio;
pub mod pci;
mod some;
#[cfg(target_arch = "x86_64")]
pub mod x86_64;

use crate::{PhysAddr, Result, PAGE_SIZE};
use bitflags::{bitflags, Flags};
Expand Down
74 changes: 36 additions & 38 deletions src/transport/pci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use core::{
use zerocopy::{FromBytes, Immutable, IntoBytes};

/// The PCI vendor ID for VirtIO devices.
const VIRTIO_VENDOR_ID: u16 = 0x1af4;
pub const VIRTIO_VENDOR_ID: u16 = 0x1af4;

/// The offset to add to a VirtIO device ID to get the corresponding PCI device ID.
const PCI_DEVICE_ID_OFFSET: u16 = 0x1040;
Expand All @@ -35,24 +35,24 @@ const TRANSITIONAL_ENTROPY_SOURCE: u16 = 0x1005;
const TRANSITIONAL_9P_TRANSPORT: u16 = 0x1009;

/// The offset of the bar field within `virtio_pci_cap`.
const CAP_BAR_OFFSET: u8 = 4;
pub(crate) const CAP_BAR_OFFSET: u8 = 4;
/// The offset of the offset field with `virtio_pci_cap`.
const CAP_BAR_OFFSET_OFFSET: u8 = 8;
pub(crate) const CAP_BAR_OFFSET_OFFSET: u8 = 8;
/// The offset of the `length` field within `virtio_pci_cap`.
const CAP_LENGTH_OFFSET: u8 = 12;
pub(crate) const CAP_LENGTH_OFFSET: u8 = 12;
/// The offset of the`notify_off_multiplier` field within `virtio_pci_notify_cap`.
const CAP_NOTIFY_OFF_MULTIPLIER_OFFSET: u8 = 16;
pub(crate) const CAP_NOTIFY_OFF_MULTIPLIER_OFFSET: u8 = 16;

/// Common configuration.
const VIRTIO_PCI_CAP_COMMON_CFG: u8 = 1;
pub const VIRTIO_PCI_CAP_COMMON_CFG: u8 = 1;
/// Notifications.
const VIRTIO_PCI_CAP_NOTIFY_CFG: u8 = 2;
pub const VIRTIO_PCI_CAP_NOTIFY_CFG: u8 = 2;
/// ISR Status.
const VIRTIO_PCI_CAP_ISR_CFG: u8 = 3;
pub const VIRTIO_PCI_CAP_ISR_CFG: u8 = 3;
/// Device specific configuration.
const VIRTIO_PCI_CAP_DEVICE_CFG: u8 = 4;
pub const VIRTIO_PCI_CAP_DEVICE_CFG: u8 = 4;

fn device_type(pci_device_id: u16) -> DeviceType {
pub(crate) fn device_type(pci_device_id: u16) -> DeviceType {
match pci_device_id {
TRANSITIONAL_NETWORK => DeviceType::Network,
TRANSITIONAL_BLOCK => DeviceType::Block,
Expand Down Expand Up @@ -394,34 +394,34 @@ impl Drop for PciTransport {

/// `virtio_pci_common_cfg`, see 4.1.4.3 "Common configuration structure layout".
#[repr(C)]
struct CommonCfg {
device_feature_select: Volatile<u32>,
device_feature: ReadOnly<u32>,
driver_feature_select: Volatile<u32>,
driver_feature: Volatile<u32>,
msix_config: Volatile<u16>,
num_queues: ReadOnly<u16>,
device_status: Volatile<u8>,
config_generation: ReadOnly<u8>,
queue_select: Volatile<u16>,
queue_size: Volatile<u16>,
queue_msix_vector: Volatile<u16>,
queue_enable: Volatile<u16>,
queue_notify_off: Volatile<u16>,
queue_desc: Volatile<u64>,
queue_driver: Volatile<u64>,
queue_device: Volatile<u64>,
pub(crate) struct CommonCfg {
pub device_feature_select: Volatile<u32>,
pub device_feature: ReadOnly<u32>,
pub driver_feature_select: Volatile<u32>,
pub driver_feature: Volatile<u32>,
pub msix_config: Volatile<u16>,
pub num_queues: ReadOnly<u16>,
pub device_status: Volatile<u8>,
pub config_generation: ReadOnly<u8>,
pub queue_select: Volatile<u16>,
pub queue_size: Volatile<u16>,
pub queue_msix_vector: Volatile<u16>,
pub queue_enable: Volatile<u16>,
pub queue_notify_off: Volatile<u16>,
pub queue_desc: Volatile<u64>,
pub queue_driver: Volatile<u64>,
pub queue_device: Volatile<u64>,
}

/// Information about a VirtIO structure within some BAR, as provided by a `virtio_pci_cap`.
#[derive(Clone, Debug, Eq, PartialEq)]
struct VirtioCapabilityInfo {
pub(crate) struct VirtioCapabilityInfo {
/// The bar in which the structure can be found.
bar: u8,
pub bar: u8,
/// The offset within the bar.
offset: u32,
pub offset: u32,
/// The length in bytes of the structure within the bar.
length: u32,
pub length: u32,
}

fn get_bar_region<H: Hal, T, C: ConfigurationAccess>(
Expand All @@ -447,7 +447,7 @@ fn get_bar_region<H: Hal, T, C: ConfigurationAccess>(
let vaddr = unsafe { H::mmio_phys_to_virt(paddr, struct_info.length as usize) };
if vaddr.as_ptr() as usize % align_of::<T>() != 0 {
return Err(VirtioPciError::Misaligned {
vaddr,
address: vaddr.as_ptr() as usize,
alignment: align_of::<T>(),
});
}
Expand Down Expand Up @@ -494,13 +494,11 @@ pub enum VirtioPciError {
/// The offset for some capability was greater than the length of the BAR.
#[error("Capability offset greater than BAR length.")]
BarOffsetOutOfRange,
/// The virtual address was not aligned as expected.
#[error(
"Virtual address {vaddr:#018?} was not aligned to a {alignment} byte boundary as expected."
)]
/// The address was not aligned as expected.
#[error("Address {address:#018} was not aligned to a {alignment} byte boundary as expected.")]
Misaligned {
/// The virtual address in question.
vaddr: NonNull<u8>,
/// The address in question.
address: usize,
/// The expected alignment in bytes.
alignment: usize,
},
Expand Down
35 changes: 35 additions & 0 deletions src/transport/some.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ pub enum SomeTransport {
Mmio(MmioTransport),
/// A PCI transport.
Pci(PciTransport),
/// An x86-64 pKVM PCI transport.
#[cfg(target_arch = "x86_64")]
HypPci(super::x86_64::HypPciTransport),
}

impl From<MmioTransport> for SomeTransport {
Expand All @@ -29,62 +32,80 @@ impl Transport for SomeTransport {
match self {
Self::Mmio(mmio) => mmio.device_type(),
Self::Pci(pci) => pci.device_type(),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.device_type(),
}
}

fn read_device_features(&mut self) -> u64 {
match self {
Self::Mmio(mmio) => mmio.read_device_features(),
Self::Pci(pci) => pci.read_device_features(),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.read_device_features(),
}
}

fn write_driver_features(&mut self, driver_features: u64) {
match self {
Self::Mmio(mmio) => mmio.write_driver_features(driver_features),
Self::Pci(pci) => pci.write_driver_features(driver_features),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.write_driver_features(driver_features),
}
}

fn max_queue_size(&mut self, queue: u16) -> u32 {
match self {
Self::Mmio(mmio) => mmio.max_queue_size(queue),
Self::Pci(pci) => pci.max_queue_size(queue),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.max_queue_size(queue),
}
}

fn notify(&mut self, queue: u16) {
match self {
Self::Mmio(mmio) => mmio.notify(queue),
Self::Pci(pci) => pci.notify(queue),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.notify(queue),
}
}

fn get_status(&self) -> DeviceStatus {
match self {
Self::Mmio(mmio) => mmio.get_status(),
Self::Pci(pci) => pci.get_status(),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.get_status(),
}
}

fn set_status(&mut self, status: DeviceStatus) {
match self {
Self::Mmio(mmio) => mmio.set_status(status),
Self::Pci(pci) => pci.set_status(status),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.set_status(status),
}
}

fn set_guest_page_size(&mut self, guest_page_size: u32) {
match self {
Self::Mmio(mmio) => mmio.set_guest_page_size(guest_page_size),
Self::Pci(pci) => pci.set_guest_page_size(guest_page_size),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.set_guest_page_size(guest_page_size),
}
}

fn requires_legacy_layout(&self) -> bool {
match self {
Self::Mmio(mmio) => mmio.requires_legacy_layout(),
Self::Pci(pci) => pci.requires_legacy_layout(),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.requires_legacy_layout(),
}
}

Expand All @@ -99,41 +120,53 @@ impl Transport for SomeTransport {
match self {
Self::Mmio(mmio) => mmio.queue_set(queue, size, descriptors, driver_area, device_area),
Self::Pci(pci) => pci.queue_set(queue, size, descriptors, driver_area, device_area),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.queue_set(queue, size, descriptors, driver_area, device_area),
}
}

fn queue_unset(&mut self, queue: u16) {
match self {
Self::Mmio(mmio) => mmio.queue_unset(queue),
Self::Pci(pci) => pci.queue_unset(queue),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.queue_unset(queue),
}
}

fn queue_used(&mut self, queue: u16) -> bool {
match self {
Self::Mmio(mmio) => mmio.queue_used(queue),
Self::Pci(pci) => pci.queue_used(queue),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.queue_used(queue),
}
}

fn ack_interrupt(&mut self) -> bool {
match self {
Self::Mmio(mmio) => mmio.ack_interrupt(),
Self::Pci(pci) => pci.ack_interrupt(),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.ack_interrupt(),
}
}

fn read_config_generation(&self) -> u32 {
match self {
Self::Mmio(mmio) => mmio.read_config_generation(),
Self::Pci(pci) => pci.read_config_generation(),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.read_config_generation(),
}
}

fn read_config_space<T: FromBytes>(&self, offset: usize) -> Result<T> {
match self {
Self::Mmio(mmio) => mmio.read_config_space(offset),
Self::Pci(pci) => pci.read_config_space(offset),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.read_config_space(offset),
}
}

Expand All @@ -145,6 +178,8 @@ impl Transport for SomeTransport {
match self {
Self::Mmio(mmio) => mmio.write_config_space(offset, value),
Self::Pci(pci) => pci.write_config_space(offset, value),
#[cfg(target_arch = "x86_64")]
Self::HypPci(pci) => pci.write_config_space(offset, value),
}
}
}
Loading

0 comments on commit c620d59

Please sign in to comment.