Skip to content
Open
Show file tree
Hide file tree
Changes from 29 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion openvmm/hvlite_core/src/worker/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1786,6 +1786,7 @@ impl InitializedVm {
.into_iter()
.map(|rp_cfg| GenericPcieRootPortDefinition {
name: rp_cfg.name.into(),
hotplug: rp_cfg.hotplug,
})
.collect();

Expand Down Expand Up @@ -1828,7 +1829,8 @@ impl InitializedVm {
.add(|_services| {
let definition = pcie::switch::GenericPcieSwitchDefinition {
name: switch.name.clone().into(),
downstream_port_count: switch.num_downstream_ports as usize,
downstream_port_count: switch.num_downstream_ports,
hotplug: switch.hotplug,
};
GenericPcieSwitch::new(definition)
})?;
Expand Down
2 changes: 2 additions & 0 deletions openvmm/hvlite_defs/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,13 +186,15 @@ pub struct PcieRootComplexConfig {
#[derive(Debug, MeshPayload)]
pub struct PcieRootPortConfig {
pub name: String,
pub hotplug: bool,
}

#[derive(Debug, MeshPayload)]
pub struct PcieSwitchConfig {
pub name: String,
pub num_downstream_ports: u8,
pub parent_port: String,
pub hotplug: bool,
}

#[derive(Debug, MeshPayload)]
Expand Down
121 changes: 97 additions & 24 deletions openvmm/openvmm_entry/src/cli_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -571,11 +571,15 @@ options:

/// Attach a PCI Express root complex to the VM
#[clap(long_help = r#"
e.g: --pcie-root-complex rc0,segment=0,start_bus=0,end_bus=255,low_mmio=4M,high_mmio=1G
Attach root complexes to the VM.

syntax: <name>[,opt=arg,...]
Examples:
# Attach root complex rc0 on segment 0 with bus and MMIO ranges
--pcie-root-complex rc0,segment=0,start_bus=0,end_bus=255,low_mmio=4M,high_mmio=1G

options:
Syntax: <name>[,opt=arg,...]

Options:
`segment=<value>` configures the PCI Express segment, default 0
`start_bus=<value>` lowest valid bus number, default 0
`end_bus=<value>` highest valid bus number, default 255
Expand All @@ -587,37 +591,51 @@ options:

/// Attach a PCI Express root port to the VM
#[clap(long_help = r#"
e.g: --pcie-root-port rc0:rc0rp0
Attach root ports to root complexes.

Examples:
# Attach root port rc0rp0 to root complex rc0
--pcie-root-port rc0:rc0rp0

# Attach root port rc0rp1 to root complex rc0 with hotplug support
--pcie-root-port rc0:rc0rp1,hotplug

syntax: <root_complex_name>:<name>
Syntax: <root_complex_name>:<name>[,hotplug]

Options:
`hotplug` enable hotplug support for this root port
"#)]
#[clap(long, conflicts_with("pcat"))]
pub pcie_root_port: Vec<PcieRootPortCli>,

/// Attach a PCI Express switch to the VM
#[clap(long_help = r#"
Attach switches to root ports or other switches to create PCIe hierarchies.
Attach switches to root ports or downstream switch ports to create PCIe hierarchies.

Examples:
# Connect switch0 directly to root port rp0
# Connect switch0 (with 4 downstream switch ports) directly to root port rp0
--pcie-switch rp0:switch0,num_downstream_ports=4

# Connect switch1 to downstream port 0 of switch0
# Connect switch1 (with 2 downstream switch ports) to downstream port 0 of switch0
--pcie-switch switch0-downstream-0:switch1,num_downstream_ports=2

# Create a 3-level hierarchy: rp0 -> switch0 -> switch1 -> switch2
--pcie-switch rp0:switch0
--pcie-switch switch0-downstream-0:switch1
--pcie-switch switch1-downstream-1:switch2

syntax: <port_name>:<name>[,opt=arg,...]
# Enable hotplug on all downstream switch ports of switch0
--pcie-switch rp0:switch0,hotplug

port_name can be:
- Root port name (e.g., "rp0") to connect directly to a root port
- Downstream port name (e.g., "switch0-downstream-1") to connect to another switch
Syntax: <port_name>:<name>[,opt,opt=arg,...]

options:
`num_downstream_ports=<value>` number of downstream ports, default 4
port_name can be:
- Root port name (e.g., "rp0") to connect directly to a root port
- Downstream port name (e.g., "switch0-downstream-1") to connect to another switch

Options:
`hotplug` enable hotplug support for all downstream switch ports
`num_downstream_ports=<value>` number of downstream ports, default 4
"#)]
#[clap(long, conflicts_with("pcat"))]
pub pcie_switch: Vec<GenericPcieSwitchCli>,
Expand Down Expand Up @@ -1534,6 +1552,7 @@ impl FromStr for PcieRootComplexCli {
pub struct PcieRootPortCli {
pub root_complex_name: String,
pub name: String,
pub hotplug: bool,
}

impl FromStr for PcieRootPortCli {
Expand All @@ -1554,13 +1573,20 @@ impl FromStr for PcieRootPortCli {
anyhow::bail!("unexpected token: '{extra}'")
}

if let Some(extra) = opts.next() {
anyhow::bail!("unexpected token: '{extra}'")
let mut hotplug = false;

// Parse optional flags
for opt in opts {
match opt {
"hotplug" => hotplug = true,
_ => anyhow::bail!("unexpected option: '{opt}'"),
}
}

Ok(PcieRootPortCli {
root_complex_name: rc_name.to_string(),
name: rp_name.to_string(),
hotplug,
})
}
}
Expand All @@ -1570,6 +1596,7 @@ pub struct GenericPcieSwitchCli {
pub port_name: String,
pub name: String,
pub num_downstream_ports: u8,
pub hotplug: bool,
}

impl FromStr for GenericPcieSwitchCli {
Expand All @@ -1591,20 +1618,26 @@ impl FromStr for GenericPcieSwitchCli {
}

let mut num_downstream_ports = 4u8; // Default value
let mut hotplug = false;

for opt in opts {
let mut kv = opt.split('=');
let key = kv.next().context("expected option name")?;
let value = kv.next().context("expected option value")?;

if let Some(extra) = kv.next() {
anyhow::bail!("unexpected token: '{extra}'")
}

match key {
"num_downstream_ports" => {
let value = kv.next().context("expected option value")?;
if let Some(extra) = kv.next() {
anyhow::bail!("unexpected token: '{extra}'")
}
num_downstream_ports = value.parse().context("invalid num_downstream_ports")?;
}
"hotplug" => {
if kv.next().is_some() {
anyhow::bail!("hotplug option does not take a value")
}
hotplug = true;
}
_ => anyhow::bail!("unknown option: '{key}'"),
}
}
Expand All @@ -1613,6 +1646,7 @@ impl FromStr for GenericPcieSwitchCli {
port_name: port_name.to_string(),
name: switch_name.to_string(),
num_downstream_ports,
hotplug,
})
}
}
Expand Down Expand Up @@ -2237,15 +2271,27 @@ mod tests {
PcieRootPortCli::from_str("rc0:rc0rp0").unwrap(),
PcieRootPortCli {
root_complex_name: "rc0".to_string(),
name: "rc0rp0".to_string()
name: "rc0rp0".to_string(),
hotplug: false,
}
);

assert_eq!(
PcieRootPortCli::from_str("my_rc:port2").unwrap(),
PcieRootPortCli {
root_complex_name: "my_rc".to_string(),
name: "port2".to_string()
name: "port2".to_string(),
hotplug: false,
}
);

// Test with hotplug flag
assert_eq!(
PcieRootPortCli::from_str("my_rc:port2,hotplug").unwrap(),
PcieRootPortCli {
root_complex_name: "my_rc".to_string(),
name: "port2".to_string(),
hotplug: true,
}
);

Expand All @@ -2254,7 +2300,7 @@ mod tests {
assert!(PcieRootPortCli::from_str("rp0").is_err());
assert!(PcieRootPortCli::from_str("rp0,opt").is_err());
assert!(PcieRootPortCli::from_str("rc0:rp0:rp3").is_err());
assert!(PcieRootPortCli::from_str("rc0:rp0,rp3").is_err());
assert!(PcieRootPortCli::from_str("rc0:rp0,invalid_option").is_err());
}

#[test]
Expand All @@ -2265,6 +2311,7 @@ mod tests {
port_name: "rp0".to_string(),
name: "switch0".to_string(),
num_downstream_ports: 4,
hotplug: false,
}
);

Expand All @@ -2274,6 +2321,7 @@ mod tests {
port_name: "port1".to_string(),
name: "my_switch".to_string(),
num_downstream_ports: 4,
hotplug: false,
}
);

Expand All @@ -2283,6 +2331,7 @@ mod tests {
port_name: "rp2".to_string(),
name: "sw".to_string(),
num_downstream_ports: 8,
hotplug: false,
}
);

Expand All @@ -2293,6 +2342,29 @@ mod tests {
port_name: "switch0-downstream-1".to_string(),
name: "child_switch".to_string(),
num_downstream_ports: 4,
hotplug: false,
}
);

// Test hotplug flag
assert_eq!(
GenericPcieSwitchCli::from_str("rp0:switch0,hotplug").unwrap(),
GenericPcieSwitchCli {
port_name: "rp0".to_string(),
name: "switch0".to_string(),
num_downstream_ports: 4,
hotplug: true,
}
);

// Test hotplug with num_downstream_ports
assert_eq!(
GenericPcieSwitchCli::from_str("rp0:switch0,num_downstream_ports=8,hotplug").unwrap(),
GenericPcieSwitchCli {
port_name: "rp0".to_string(),
name: "switch0".to_string(),
num_downstream_ports: 8,
hotplug: true,
}
);

Expand All @@ -2303,5 +2375,6 @@ mod tests {
assert!(GenericPcieSwitchCli::from_str("rp0:switch0,invalid_opt=value").is_err());
assert!(GenericPcieSwitchCli::from_str("rp0:switch0,num_downstream_ports=bad").is_err());
assert!(GenericPcieSwitchCli::from_str("rp0:switch0,num_downstream_ports=").is_err());
assert!(GenericPcieSwitchCli::from_str("rp0:switch0,invalid_flag").is_err());
}
}
2 changes: 2 additions & 0 deletions openvmm/openvmm_entry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ fn build_switch_list(all_switches: &[cli_args::GenericPcieSwitchCli]) -> Vec<Pci
name: switch_cli.name.clone(),
num_downstream_ports: switch_cli.num_downstream_ports,
parent_port: switch_cli.port_name.clone(),
hotplug: switch_cli.hotplug,
})
.collect()
}
Expand Down Expand Up @@ -720,6 +721,7 @@ fn vm_config_from_command_line(
.filter(|port_cli| port_cli.root_complex_name == cli.name)
.map(|port_cli| PcieRootPortConfig {
name: port_cli.name.clone(),
hotplug: port_cli.hotplug,
})
.collect();

Expand Down
17 changes: 17 additions & 0 deletions vm/devices/pci/pci_core/src/capabilities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@

pub use self::read_only::ReadOnlyCapability;

use crate::spec::caps::CapabilityId;
use inspect::Inspect;
use vmcore::save_restore::ProtobufSaveRestore;

pub mod msi_cap;
pub mod msix;
pub mod pci_express;
pub mod read_only;
Expand All @@ -17,6 +19,9 @@ pub trait PciCapability: Send + Sync + Inspect + ProtobufSaveRestore {
/// A descriptive label for use in Save/Restore + Inspect output
fn label(&self) -> &str;

/// Returns the PCI capability ID for this capability
fn capability_id(&self) -> CapabilityId;

/// Length of the capability structure
fn len(&self) -> usize;

Expand All @@ -28,4 +33,16 @@ pub trait PciCapability: Send + Sync + Inspect + ProtobufSaveRestore {

/// Reset the capability
fn reset(&mut self);

// Specific downcast methods for known capability types

/// Downcast to PCI Express capability
fn as_pci_express(&self) -> Option<&pci_express::PciExpressCapability> {
None
}

/// Downcast to PCI Express capability (mutable)
fn as_pci_express_mut(&mut self) -> Option<&mut pci_express::PciExpressCapability> {
None
}
}
Loading