public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
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





  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
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal