From: Dominik Csapak <d.csapak@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH pve-qemu-server-rs v2 2/9] pci: legacy: generate pci bridges
Date: Thu, 15 Oct 2026 10:10:24 +0200 [thread overview]
Message-ID: <20261015081041.743422-3-d.csapak@proxmox.com> (raw)
In-Reply-To: <20261015081041.743422-1-d.csapak@proxmox.com>
Besides the address of a single device, we also need the list of
bridges a guest has to be started with. Return them from
`pci_bus_devices`, which is intended to return all devices that form a
bus, since those must come before the devices using them on the
commandline.
The list itself follows the perl implementation:
* q35 machines already have the bridges up to 3 in their machine
config
* pci.3 is only needed for virtio-scsi-single
* pci.4 is only needed for scsi controllers with an index > 1, or
starting with machine version 11.1
Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
pve-qemu-server-pci/src/layout/legacy.rs | 109 ++++++++++++++++++++++-
pve-qemu-server-pci/src/lib.rs | 9 ++
2 files changed, 117 insertions(+), 1 deletion(-)
diff --git a/pve-qemu-server-pci/src/layout/legacy.rs b/pve-qemu-server-pci/src/layout/legacy.rs
index e6693ca..6d5cc81 100644
--- a/pve-qemu-server-pci/src/layout/legacy.rs
+++ b/pve-qemu-server-pci/src/layout/legacy.rs
@@ -331,6 +331,43 @@ pub(crate) fn pci_bridge_for_device(id: &str) -> Option<Bus> {
Some(addr.bus)
}
+/// Renders the bridge devices a guest needs, see
+/// [`crate::pci_bus_devices`].
+pub(crate) fn pci_bus_devices(hw: &HardwareConfig) -> Result<Vec<String>, LayoutError> {
+ let mut res = Vec::new();
+
+ // some SCSI controllers can only have 7 disks each, so scsi14 and upwards
+ // need scsihw2 and above, which live on pci.4
+ let include_pci4 = hw.version_at_least(11, 1, None) || hw.max_scsihw > 1;
+
+ for i in 1..=4 {
+ // q35.cfg already includes the bridges up to three
+ if i < 4 && hw.mainboard == Mainboard::Q35 {
+ continue;
+ }
+
+ if i == 3 && !hw.virtio_scsi_single() {
+ continue;
+ }
+
+ if i == 4 && !include_pci4 {
+ continue;
+ }
+
+ let id = if i == 2 && hw.legacy_igd {
+ format!("pci.{i}-igd")
+ } else {
+ format!("pci.{i}")
+ };
+
+ let addr = address(hw, &id)?.to_qemu_addr();
+ res.push("-device".to_string());
+ res.push(format!("pci-bridge,id=pci.{i},chassis_nr={i}{addr}"));
+ }
+
+ Ok(res)
+}
+
/// Returns if the caller wants a PCIE address instead of a PCI one based on the id.
fn is_pcie_id(id: &str) -> bool {
id.starts_with("hostpcie") || id == "vga" || id == "ivshmem"
@@ -363,7 +400,9 @@ pub(crate) fn address(hw: &HardwareConfig, id: &str) -> Result<PciAddress, Layou
pub(crate) mod test {
use std::collections::HashSet;
- use super::{LEGACY_PCIE_LAYOUT, get_legacy_pci_layout, pci_bridge_for_device};
+ use super::{
+ LEGACY_PCIE_LAYOUT, get_legacy_pci_layout, pci_bridge_for_device, pci_bus_devices,
+ };
use crate::hardware_config::Mainboard;
use crate::layout::legacy::is_pcie_id;
use crate::{Bus, Device, HardwareConfig, Layout, PciAddress};
@@ -625,4 +664,72 @@ pub(crate) mod test {
assert_eq!(pci_bridge_for_device("no-such-device"), None);
}
+
+ fn hw_config(
+ q35: bool,
+ scsihw: Option<&str>,
+ max_scsihw: u8,
+ legacy_igd: bool,
+ ) -> HardwareConfig {
+ HardwareConfig::new(
+ "x86_64",
+ format!("pc-{}-9.2-pve0", if q35 { "q35" } else { "i440fx" }),
+ scsihw.map(str::to_string),
+ max_scsihw,
+ legacy_igd,
+ None,
+ 9,
+ 2,
+ None,
+ )
+ }
+
+ fn bridge(nr: u8, addr: &str) -> [String; 2] {
+ [
+ "-device".to_string(),
+ format!("pci-bridge,id=pci.{nr},chassis_nr={nr}{addr}"),
+ ]
+ }
+
+ #[test]
+ fn test_pci_bus_devices() {
+ let pci1 = bridge(1, ",bus=pci.0,addr=0x1e");
+ let pci2 = bridge(2, ",bus=pci.0,addr=0x1f");
+ let pci3 = bridge(3, ",bus=pci.0,addr=0x5");
+ let pci4 = bridge(4, ",bus=pci.1,addr=0x1c");
+
+ let test = |hw: HardwareConfig, expected: &[&[String; 2]]| {
+ let expected: Vec<String> = expected.iter().flat_map(|b| b.iter().cloned()).collect();
+ assert_eq!(pci_bus_devices(&hw), Ok(expected));
+ };
+
+ test(hw_config(false, None, 0, false), &[&pci1, &pci2]);
+
+ // q35 already brings the bridges up to three along
+ test(hw_config(true, None, 0, false), &[]);
+
+ // every SCSI disk gets its own controller on pci.3
+ test(
+ hw_config(false, Some("virtio-scsi-single"), 0, false),
+ &[&pci1, &pci2, &pci3],
+ );
+
+ // scsihw2 and above live on pci.4
+ test(hw_config(false, Some("lsi"), 1, false), &[&pci1, &pci2]);
+ test(
+ hw_config(false, Some("lsi"), 2, false),
+ &[&pci1, &pci2, &pci4],
+ );
+ test(hw_config(true, Some("lsi"), 2, false), &[&pci4]);
+
+ // pci.4 is always added from machine version 11.1 on
+ let mut new_machine = hw_config(false, None, 0, false);
+ new_machine.machine_version = (11, 1, None);
+ test(new_machine, &[&pci1, &pci2, &pci4]);
+
+ // with legacy IGD passthrough pci.2 moves behind pci.1
+ let mut expected_igd = pci2.clone();
+ expected_igd[1] = "pci-bridge,id=pci.2,chassis_nr=2,bus=pci.1,addr=0x1e".to_string();
+ test(hw_config(false, None, 0, true), &[&pci1, &expected_igd]);
+ }
}
diff --git a/pve-qemu-server-pci/src/lib.rs b/pve-qemu-server-pci/src/lib.rs
index 43a3886..fc982ce 100644
--- a/pve-qemu-server-pci/src/lib.rs
+++ b/pve-qemu-server-pci/src/lib.rs
@@ -27,6 +27,15 @@ pub fn pci_bridge_for_device(id: &str) -> Option<Bus> {
layout::legacy::pci_bridge_for_device(id)
}
+/// Returns the QEMU arguments for all devices that form the buses `hw`
+/// needs, as alternating `-device` flags and their values.
+///
+/// They are ordered so that every bus is created before the devices on it,
+/// and have to be passed before any other device that sits on these buses.
+pub fn pci_bus_devices(hw: &HardwareConfig) -> Result<Vec<String>, LayoutError> {
+ layout::legacy::pci_bus_devices(hw)
+}
+
/// Returns the address of the device with the configuration id `id`.
pub fn address(hw: &HardwareConfig, id: &str) -> Result<PciAddress, LayoutError> {
layout::legacy::address(hw, id)
--
2.47.3
next prev parent reply other threads:[~2026-10-15 8:10 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 8:10 [RFC proxmox-perl-rs/qemu-server/qemu-server-rs v2 0/9] pci-handling rewrite (part 1/2/3) Dominik Csapak
2026-10-15 8:10 ` [PATCH pve-qemu-server-rs v2 1/9] add pve-qemu-server-pci crate for guest PCI address generation Dominik Csapak
2026-10-15 12:16 ` Wolfgang Bumiller
2026-10-15 8:10 ` Dominik Csapak [this message]
2026-10-15 8:10 ` [PATCH pve-qemu-server-rs v2 3/9] pci: legacy: generate q35 chipset devices and hostpcie root ports Dominik Csapak
2026-10-15 8:10 ` [PATCH pve-qemu-server-rs v2 4/9] pci: layout: add grouped PCI and PCIe layouts Dominik Csapak
2026-10-15 8:10 ` [PATCH proxmox-perl-rs v2 5/9] pve: add bindings for `pve-qemu-server-pci` crate Dominik Csapak
2026-10-15 11:47 ` Wolfgang Bumiller
2026-10-15 8:10 ` [PATCH qemu-server v2 6/9] pci: use PVE::RS::PCI bindings for addresses and bus devices Dominik Csapak
2026-10-15 8:10 ` [PATCH qemu-server v2 7/9] tree-wide: use HardwareConfig for the machine version checks Dominik Csapak
2026-10-15 8:10 ` [PATCH qemu-server v2 8/9] cfg2cmd: q35: generate chipset devices with machine version 11.1+pve1 Dominik Csapak
2026-10-15 8:10 ` [PATCH qemu-server v2 9/9] machine: add 'pci-layout' option Dominik Csapak
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261015081041.743422-3-d.csapak@proxmox.com \
--to=d.csapak@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox