all lists on 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal