From: sashiko-bot@kernel.org
To: "Szymon Durawa" <szymon.durawa@linux.intel.com>
Cc: linux-pci@vger.kernel.org
Subject: Re: [PATCH v10 7/7] PCI: vmd: Add support for second rootbus under VMD
Date: Fri, 06 Nov 2026 17:23:32 +0000 [thread overview]
Message-ID: <sashiko-outbox-201916@kernel.org> (raw)
In-Reply-To: <20261106184256.4130-8-szymon.durawa@linux.intel.com>
Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [High] PCI: vmd: overlapping busn_res and unisolated CFGBAR in
vmd_create_bus()
- [Medium] PCI: vmd: missing VMD_BUSRANGE bounds check in
vmd_configure_cfgbar()
- [Medium] PCI: vmd: wrong relative bus number for BUS1 in
vmd_acpi_find_companion()
- [Medium] PCI: vmd: leak of srcu_struct on error paths in
vmd_enable_domain()
--
commit e9682a58316a29ebd9d43933c4903d99f8a94e4b
Author: Szymon Durawa <szymon.durawa@linux.intel.com>
PCI: vmd: Add support for second rootbus under VMD
This commit adds support for a second VMD root bus (BUS1) exposed via Bus
Restriction type 3 on Intel Arrow Lake and later platforms, partitioning
CFGBAR, MEMBAR1, and MEMBAR2 across two emulated PCI domains.
> diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
> index 5733faf972d10..6831d0a5c443a 100644
> --- a/drivers/pci/controller/vmd.c
> +++ b/drivers/pci/controller/vmd.c
[ ... ]
> @@ -989,6 +1133,62 @@ static void vmd_configure_cfgbar(struct vmd_dev *vmd)
> .end = busn_end,
> .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
> };
> +
> + if (vmd->bus1_rootbus) {
> + int ret;
> + u16 bus0_range = 0;
> + u16 bus1_range = 0;
> + u8 bus0_start, bus0_end;
> + u8 bus1_start, bus1_end;
> +
> + ret = pci_read_config_word(vmd->dev, VMD_BUSRANGE0, &bus0_range);
> + if (ret) {
> + pci_err(vmd->dev, "Failed to read VMD_BUSRANGE0: %d\n", ret);
> + return -EIO;
> + }
> +
> + ret = pci_read_config_word(vmd->dev, VMD_BUSRANGE1, &bus1_range);
> + if (ret) {
> + pci_err(vmd->dev, "Failed to read VMD_BUSRANGE1: %d\n", ret);
> + return -EIO;
> + }
> +
> + bus0_start = VMD_BUS_START(bus0_range);
> + bus0_end = VMD_BUS_END(bus0_range);
> + bus1_start = VMD_BUS_START(bus1_range);
> + bus1_end = VMD_BUS_END(bus1_range);
> +
> + if (bus0_start > bus0_end || bus1_start > bus1_end) {
> + pci_err(vmd->dev,
> + "Invalid bus range(s): BUS0 [%02x-%02x], BUS1 [%02x-%02x]\n",
> + bus0_start, bus0_end, bus1_start, bus1_end);
> + return -EINVAL;
> + }
> +
> + if (!(bus0_end < bus1_start || bus1_end < bus0_start)) {
> + pci_err(vmd->dev,
> + "Overlapping bus ranges: BUS0 [%02x-%02x], BUS1 [%02x-%02x]\n",
> + bus0_start, bus0_end, bus1_start, bus1_end);
> + return -EINVAL;
> + }
> +
> + /*
> + * Resize BUS0 CFGBAR range to make space for BUS1
> + * owned devices by adjusting range end with value stored in
> + * VMD_BUSRANGE0 register.
> + */
> + vmd->resources[VMD_RES_CFGBAR].start = bus0_start;
> + vmd->resources[VMD_RES_CFGBAR].end = bus0_end;
> +
> + vmd->resources[VMD_RES_BUS1_CFGBAR] = (struct resource){
> + .name = "VMD CFGBAR BUS1",
> + .start = bus1_start,
> + .end = bus1_end,
> + .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
> + };
> + }
> +
> + return 0;
> }
[Severity: Medium]
Does vmd_configure_cfgbar() need to validate that bus0_start and bus1_start
are above vmd->busn_start[VMD_BUS_1] (0xe1) and that bus0_end and bus1_end
do not exceed busn_end?
If firmware leaves VMD_BUSRANGE0 or VMD_BUSRANGE1 at 0x0000 when one root
bus has no subordinate devices, or reports an end bus beyond the VMD_CFGBAR
aperture, the checks above still pass and overwrite the bus resources with
an inverted range such as [0xe0..0x00] or an out-of-bounds bus number.
[ ... ]
> @@ -1040,55 +1241,132 @@ static int vmd_configure_membar1_membar2(struct vmd_dev *vmd,
> static int vmd_create_bus(struct vmd_dev *vmd, enum vmd_rootbus bus_number,
> struct pci_sysdata *sd, resource_size_t *offset,
> bool vmd_in_guest, u8 primary)
> {
> + u8 cfgbar = bus_number * 3;
> + u8 membar1 = cfgbar + 1;
> + u8 membar2 = cfgbar + 2;
> + struct pci_bus *vmd_bus;
> LIST_HEAD(resources);
>
> - pci_add_resource(&resources, &vmd->resources[VMD_RES_CFGBAR]);
> - pci_add_resource_offset(&resources, &vmd->resources[VMD_RES_MBAR_1],
> + pci_add_resource(&resources, &vmd->resources[cfgbar]);
> + pci_add_resource_offset(&resources, &vmd->resources[membar1],
> offset[0]);
> - pci_add_resource_offset(&resources, &vmd->resources[VMD_RES_MBAR_2],
> + pci_add_resource_offset(&resources, &vmd->resources[membar2],
> offset[1]);
>
> - vmd->bus[VMD_BUS_0] = pci_create_root_bus(&vmd->dev->dev,
> - vmd->busn_start[VMD_BUS_0],
> - &vmd_ops, sd, &resources);
> - if (!vmd->bus[VMD_BUS_0]) {
> + vmd_bus = pci_create_root_bus(&vmd->dev->dev,
> + vmd->busn_start[bus_number], &vmd_ops, sd,
> + &resources);
[Severity: High]
Can VMD_BUS_0 and VMD_BUS_1 allocate and scan each other's ECAM bus numbers
when a root port bridge is unconfigured?
vmd_configure_cfgbar() sets vmd->resources[VMD_RES_CFGBAR] to
[bus0_start..bus0_end] (for example [0xe2..0xf0]) and
vmd->resources[VMD_RES_BUS1_CFGBAR] to [bus1_start..bus1_end] (for example
[0xf1..0xff]). However, pci_create_root_bus() -> pci_register_host_bridge()
ignores res->start and inserts bus->busn_res starting at bus->number:
drivers/pci/probe.c:pci_register_host_bridge() {
...
resource_list_for_each_entry(window, &bridge->windows) {
res = window->res;
if (resource_type(res) == IORESOURCE_BUS)
pci_bus_insert_busn_res(bus, bus->number, res->end);
}
...
}
As a result, VMD_BUS_0 gets busn_res [0xe0..0xf0] (which includes 0xe1, the
root bus of VMD_BUS_1) and VMD_BUS_1 gets busn_res [0xe1..0xff] (which
includes 0xe2..0xf0, the subordinate bus range of VMD_BUS_0), while
vmd_cfg_addr() maps bus->number directly into the shared vmd->cfgbar
without checking which domain owns that bus number. When
pci_scan_child_bus_extend() encounters an unconfigured bridge on VMD_BUS_1
(or on VMD_BUS_0 before max passes 0xe0), Pass 1 assigns max + 1 (0xe2 on
VMD_BUS_1, or 0xe1 on VMD_BUS_0), scanning and reprogramming devices that
belong to the other VMD root bus.
[ ... ]
> @@ -1213,11 +1511,38 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
>
> sd->node = pcibus_to_node(vmd->dev->bus);
>
> - ret = vmd_create_bus(vmd, sd, offset, vmd_in_guest);
> + if (vmd->bus1_rootbus) {
> + vmd->sysdata[VMD_BUS_1].vmd_dev = vmd->dev;
> + vmd->sysdata[VMD_BUS_1].node = pcibus_to_node(vmd->dev->bus);
> + vmd->sysdata[VMD_BUS_1].domain = pci_bus_find_emul_domain_nr(0, 0x10000, INT_MAX);
> + if (vmd->sysdata[VMD_BUS_1].domain < 0) {
> + pci_warn(vmd->dev,
> + "Can't allocate BUS1 domain: %d, continuing with BUS0 only\n",
> + vmd->sysdata[VMD_BUS_1].domain);
> + vmd_disable_bus1(vmd);
> + }
> + }
> +
> + ret = vmd_attach_resources(vmd);
> + if (ret) {
> + pci_bus_release_emul_domain_nr(sd->domain);
> + if (vmd->bus1_rootbus && vmd->sysdata[VMD_BUS_1].domain >= 0)
> + pci_bus_release_emul_domain_nr(vmd->sysdata[VMD_BUS_1].domain);
> +
> + vmd_remove_irq_domain(vmd);
> + return ret;
> + }
> +
> + ret = vmd_create_bus(vmd, VMD_BUS_0, sd, offset, vmd_in_guest,
> + VMD_PRIMARY_BUS0);
> if (ret) {
> pci_err(vmd->dev, "Can't create bus: %d\n", ret);
> pci_bus_release_emul_domain_nr(sd->domain);
> + if (vmd->bus1_rootbus && vmd->sysdata[VMD_BUS_1].domain >= 0)
> + pci_bus_release_emul_domain_nr(vmd->sysdata[VMD_BUS_1].domain);
> +
> vmd_remove_irq_domain(vmd);
> + vmd_detach_resources(vmd);
> return ret;
> }
[Severity: Medium]
Do these error paths leak the per-vector srcu_struct instances initialized
by vmd_alloc_irqs()?
When MSI remapping is enabled, vmd_alloc_irqs() calls init_srcu_struct() on
each vmd->irqs[i].srcu, which allocates per-CPU memory not managed by
devres. If vmd_attach_resources() or vmd_create_bus() fails, returning
without calling vmd_cleanup_srcu(vmd) leaks that memory and leaves active
srcu_struct objects in devm-allocated vmd->irqs memory.
> WARN(sysfs_create_link(&vmd->dev->dev.kobj,
> &vmd->bus[VMD_BUS_0]->dev.kobj, "domain"),
> "Can't create symlink to domain\n");
>
> + if (vmd->bus1_rootbus && vmd->sysdata[VMD_BUS_1].domain >= 0) {
> + ret = vmd_create_bus(vmd, VMD_BUS_1, &vmd->sysdata[VMD_BUS_1], offset,
> + vmd_in_guest, VMD_PRIMARY_BUS1);
> + if (ret) {
> + pci_warn(vmd->dev,
> + "Can't create BUS1: %d, continuing with BUS0 only\n",
> + ret);
> +
> + release_resource(&vmd->resources[VMD_RES_BUS1_MBAR_2]);
> + release_resource(&vmd->resources[VMD_RES_BUS1_MBAR_1]);
> + pci_bus_release_emul_domain_nr(vmd->sysdata[VMD_BUS_1].domain);
> + vmd_disable_bus1(vmd);
> + }
> +
> + if (vmd->bus1_rootbus && vmd->sysdata[VMD_BUS_1].domain >= 0) {
> + WARN(sysfs_create_link(&vmd->dev->dev.kobj,
> + &vmd->bus[VMD_BUS_1]->dev.kobj,
> + "domain1"),
> + "Can't create symlink to domain1\n");
> + }
> + }
> +
> vmd_bus_enumeration(vmd->bus[VMD_BUS_0], features);
>
> + if (vmd->bus1_rootbus && vmd->sysdata[VMD_BUS_1].domain >= 0)
> + vmd_bus_enumeration(vmd->bus[VMD_BUS_1], features);
[Severity: Medium]
Does vmd_acpi_find_companion() compute the wrong relative bus number when
enumerating VMD_BUS_1?
drivers/pci/controller/vmd.c:vmd_acpi_find_companion() {
...
bridge = pci_find_host_bridge(pci_dev->bus);
busnr = pci_dev->bus->number - bridge->bus->number;
...
addr = (busnr << 24) | ((u32)pci_dev->devfn << 16) | 0x8000FFFFU;
return acpi_find_child_device(ACPI_COMPANION(bridge->dev.parent), addr,
false);
}
Both host bridges share &vmd->dev->dev as bridge->dev.parent, where the
relative bus number in the ACPI _ADR is the offset from VMD_BUS_0's start
bus (0xe0). Because bridge->bus->number for VMD_BUS_1 is 0xe1, subtracting
bridge->bus->number yields busnr == 0 for root ports on 0xe1 (matching
VMD_BUS_0's ACPI companion objects) and shifts all VMD_BUS_1 subordinate bus
_ADR lookups down by one.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261106184256.4130-1-szymon.durawa@linux.intel.com?part=7
next prev parent reply other threads:[~2026-11-06 17:23 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 18:42 [PATCH v10 0/7] VMD add second root bus support Szymon Durawa
2026-11-06 18:42 ` [PATCH v10 1/7] PCI: vmd: Add vmd_bus_enumeration() helper function Szymon Durawa
2026-11-06 16:26 ` sashiko-bot
2026-11-06 18:42 ` [PATCH v10 2/7] PCI: vmd: Add vmd_configure_cfgbar() " Szymon Durawa
2026-11-06 16:33 ` sashiko-bot
2026-11-06 18:42 ` [PATCH v10 3/7] PCI: vmd: Add vmd_configure_membar() and vmd_configure_membar1_membar2() Szymon Durawa
2026-11-06 16:44 ` sashiko-bot
2026-11-06 18:42 ` [PATCH v10 4/7] PCI: vmd: Add vmd_create_bus() Szymon Durawa
2026-11-06 16:49 ` sashiko-bot
2026-11-06 18:42 ` [PATCH v10 5/7] PCI: vmd: Replace hardcoded values with enum and defines Szymon Durawa
2026-11-06 16:51 ` sashiko-bot
2026-11-06 18:42 ` [PATCH v10 6/7] PCI: vmd: Convert bus, busn_start and sysdata to an array Szymon Durawa
2026-11-06 16:57 ` sashiko-bot
2026-11-06 18:42 ` [PATCH v10 7/7] PCI: vmd: Add support for second rootbus under VMD Szymon Durawa
2026-11-06 17:23 ` sashiko-bot [this message]
2026-11-07 17:06 ` [PATCH v10 0/7] VMD add second root bus support Manivannan Sadhasivam
2026-11-11 0:32 ` Vladimir Nedoshivin
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=sashiko-outbox-201916@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=szymon.durawa@linux.intel.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