* [PATCH v9 1/7] PCI: vmd: Add vmd_bus_enumeration() helper function
2026-10-26 20:54 [PATCH v9 0/7] VMD add second root bus support Szymon Durawa
@ 2026-10-26 20:54 ` Szymon Durawa
2026-10-26 20:54 ` [PATCH v9 2/7] PCI: vmd: Add vmd_configure_cfgbar() " Szymon Durawa
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Szymon Durawa @ 2026-10-26 20:54 UTC (permalink / raw)
To: nirmal.patel, bhelgaas, robh, mani, kwilczynski, lpieralisi,
asutosh.pathak, szymon.durawa
Cc: linux-pci, linux-kernel
Move the vmd bus enumeration code to a new helper vmd_bus_enumeration().
No functional changes.
Suggested-by: Nirmal Patel <nirmal.patel@linux.intel.com>
Reviewed-by: Asutosh Pathak <asutosh.pathak@linux.intel.com>
Signed-off-by: Szymon Durawa <szymon.durawa@linux.intel.com>
---
drivers/pci/controller/vmd.c | 89 ++++++++++++++++++++----------------
1 file changed, 49 insertions(+), 40 deletions(-)
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index 241023ecf677..57d5e917dc45 100644
--- a/drivers/pci/controller/vmd.c
+++ b/drivers/pci/controller/vmd.c
@@ -905,6 +905,54 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
return 0;
}
+static void vmd_bus_enumeration(struct pci_bus *bus, unsigned long features)
+{
+ struct pci_bus *child;
+ struct pci_dev *dev;
+ int ret;
+
+ vmd_acpi_begin();
+
+ pci_scan_child_bus(bus);
+ vmd_domain_reset(vmd_from_bus(bus));
+
+ /*
+ * When Intel VMD is enabled, the OS does not discover the Root Ports
+ * owned by Intel VMD within the MMCFG space. pci_reset_bus() applies
+ * a reset to the parent of the PCI device supplied as argument. This
+ * is why we pass a child device, so the reset can be triggered at
+ * the Intel bridge level and propagated to all the children in the
+ * hierarchy.
+ */
+ list_for_each_entry(child, &bus->children, node) {
+ if (!list_empty(&child->devices)) {
+ dev = list_first_entry(&child->devices, struct pci_dev,
+ bus_list);
+ ret = pci_reset_bus(dev);
+ if (ret)
+ pci_warn(dev, "can't reset device: %d\n", ret);
+
+ break;
+ }
+ }
+
+ pci_assign_unassigned_bus_resources(bus);
+
+ pci_walk_bus(bus, vmd_pm_enable_quirk, &features);
+
+ /*
+ * VMD root buses are virtual and don't return true on pci_is_pcie()
+ * and will fail pcie_bus_configure_settings() early. It can instead be
+ * run on each of the real root ports.
+ */
+ list_for_each_entry(child, &bus->children, node)
+ pcie_bus_configure_settings(child);
+
+ pci_bus_add_devices(bus);
+
+ vmd_acpi_end();
+}
+
static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
{
struct pci_sysdata *sd = &vmd->sysdata;
@@ -915,8 +963,6 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
resource_size_t offset[2] = {0};
resource_size_t membar2_offset = 0x2000;
resource_size_t busn_end;
- struct pci_bus *child;
- struct pci_dev *dev;
bool vmd_in_guest;
int ret;
@@ -1047,45 +1093,8 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj,
"domain"), "Can't create symlink to domain\n");
- vmd_acpi_begin();
-
- pci_scan_child_bus(vmd->bus);
- vmd_domain_reset(vmd);
+ vmd_bus_enumeration(vmd->bus, features);
- /* When Intel VMD is enabled, the OS does not discover the Root Ports
- * owned by Intel VMD within the MMCFG space. pci_reset_bus() applies
- * a reset to the parent of the PCI device supplied as argument. This
- * is why we pass a child device, so the reset can be triggered at
- * the Intel bridge level and propagated to all the children in the
- * hierarchy.
- */
- list_for_each_entry(child, &vmd->bus->children, node) {
- if (!list_empty(&child->devices)) {
- dev = list_first_entry(&child->devices,
- struct pci_dev, bus_list);
- ret = pci_reset_bus(dev);
- if (ret)
- pci_warn(dev, "can't reset device: %d\n", ret);
-
- break;
- }
- }
-
- pci_assign_unassigned_bus_resources(vmd->bus);
-
- pci_walk_bus(vmd->bus, vmd_pm_enable_quirk, &features);
-
- /*
- * VMD root buses are virtual and don't return true on pci_is_pcie()
- * and will fail pcie_bus_configure_settings() early. It can instead be
- * run on each of the real root ports.
- */
- list_for_each_entry(child, &vmd->bus->children, node)
- pcie_bus_configure_settings(child);
-
- pci_bus_add_devices(vmd->bus);
-
- vmd_acpi_end();
return 0;
}
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v9 2/7] PCI: vmd: Add vmd_configure_cfgbar() helper function
2026-10-26 20:54 [PATCH v9 0/7] VMD add second root bus support Szymon Durawa
2026-10-26 20:54 ` [PATCH v9 1/7] PCI: vmd: Add vmd_bus_enumeration() helper function Szymon Durawa
@ 2026-10-26 20:54 ` Szymon Durawa
2026-10-26 20:54 ` [PATCH v9 3/7] PCI: vmd: Add vmd_configure_membar() and vmd_configure_membar1_membar2() Szymon Durawa
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Szymon Durawa @ 2026-10-26 20:54 UTC (permalink / raw)
To: nirmal.patel, bhelgaas, robh, mani, kwilczynski, lpieralisi,
asutosh.pathak, szymon.durawa
Cc: linux-pci, linux-kernel
Move the VMD CFGBAR initialization code to a new helper
vmd_configure_cfgbar(). No functional changes.
Suggested-by: Nirmal Patel <nirmal.patel@linux.intel.com>
Reviewed-by: Asutosh Pathak <asutosh.pathak@linux.intel.com>
Signed-off-by: Szymon Durawa <szymon.durawa@linux.intel.com>
---
drivers/pci/controller/vmd.c | 28 +++++++++++++++++-----------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index 57d5e917dc45..2a8c10604c82 100644
--- a/drivers/pci/controller/vmd.c
+++ b/drivers/pci/controller/vmd.c
@@ -905,6 +905,22 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
return 0;
}
+static void vmd_configure_cfgbar(struct vmd_dev *vmd)
+{
+ struct resource *res = &vmd->dev->resource[VMD_CFGBAR];
+ resource_size_t busn_end;
+
+ /* Do not let resource[0] end go out of bound.*/
+ busn_end = vmd->busn_start + (resource_size(res) >> 20) - 1;
+ busn_end = min_t(resource_size_t, busn_end, 0xff);
+ vmd->resources[0] = (struct resource) {
+ .name = "VMD CFGBAR",
+ .start = vmd->busn_start,
+ .end = busn_end,
+ .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
+ };
+}
+
static void vmd_bus_enumeration(struct pci_bus *bus, unsigned long features)
{
struct pci_bus *child;
@@ -962,7 +978,6 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
LIST_HEAD(resources);
resource_size_t offset[2] = {0};
resource_size_t membar2_offset = 0x2000;
- resource_size_t busn_end;
bool vmd_in_guest;
int ret;
@@ -971,16 +986,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
if (ret)
return ret;
- /* Do not let resource[0] end go out of bounds */
- res = &vmd->dev->resource[VMD_CFGBAR];
- busn_end = vmd->busn_start + (resource_size(res) >> 20) - 1;
- busn_end = min_t(resource_size_t, busn_end, 0xff);
- vmd->resources[0] = (struct resource) {
- .name = "VMD CFGBAR",
- .start = vmd->busn_start,
- .end = busn_end,
- .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
- };
+ vmd_configure_cfgbar(vmd);
/*
* If the window is below 4GB, clear IORESOURCE_MEM_64 so we can
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v9 3/7] PCI: vmd: Add vmd_configure_membar() and vmd_configure_membar1_membar2()
2026-10-26 20:54 [PATCH v9 0/7] VMD add second root bus support Szymon Durawa
2026-10-26 20:54 ` [PATCH v9 1/7] PCI: vmd: Add vmd_bus_enumeration() helper function Szymon Durawa
2026-10-26 20:54 ` [PATCH v9 2/7] PCI: vmd: Add vmd_configure_cfgbar() " Szymon Durawa
@ 2026-10-26 20:54 ` Szymon Durawa
2026-10-26 20:54 ` [PATCH v9 4/7] PCI: vmd: Add vmd_create_bus() Szymon Durawa
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Szymon Durawa @ 2026-10-26 20:54 UTC (permalink / raw)
To: nirmal.patel, bhelgaas, robh, mani, kwilczynski, lpieralisi,
asutosh.pathak, szymon.durawa
Cc: linux-pci, linux-kernel
Move the MEMBAR1 and MEMBAR2 registry initialization code to new helpers
vmd_configure_membar() and vmd_configure_membar1_membar2().
The refactor preserves MEMBAR address/offset/flags programming, but
resource name construction is now generated in the helper ("VMD MEMBAR%d")
instead of using two separate string literals.
Suggested-by: Nirmal Patel <nirmal.patel@linux.intel.com>
Reviewed-by: Asutosh Pathak <asutosh.pathak@linux.intel.com>
Signed-off-by: Szymon Durawa <szymon.durawa@linux.intel.com>
---
drivers/pci/controller/vmd.c | 98 +++++++++++++++++++++++++-----------
1 file changed, 68 insertions(+), 30 deletions(-)
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index 2a8c10604c82..ed6d03656885 100644
--- a/drivers/pci/controller/vmd.c
+++ b/drivers/pci/controller/vmd.c
@@ -921,6 +921,69 @@ static void vmd_configure_cfgbar(struct vmd_dev *vmd)
};
}
+/*
+ * vmd_configure_membar - Configure VMD MemBAR register, which points
+ * to MMIO address assigned by the OS or BIOS.
+ * @vmd: the VMD device
+ * @resource_number: resource buffer number to be filled in
+ * @membar_number: number of the MemBAR
+ * @start_offset: 4K aligned offset applied to start of VMD’s MEMBAR MMIO space
+ * @end_offset: 4K aligned offset applied to end of VMD’s MEMBAR MMIO space
+ *
+ * Function fills resource buffer inside the VMD structure.
+ *
+ * Return: 0 on success, -ENOMEM on allocation failure.
+ */
+static int vmd_configure_membar(struct vmd_dev *vmd, u8 resource_number,
+ u8 membar_number, resource_size_t start_offset,
+ resource_size_t end_offset)
+{
+ char *name;
+ u32 upper_bits;
+ unsigned long flags;
+
+ struct resource *res = &vmd->dev->resource[membar_number];
+
+ upper_bits = upper_32_bits(res->end);
+ flags = res->flags & ~IORESOURCE_SIZEALIGN;
+ if (!upper_bits)
+ flags &= ~IORESOURCE_MEM_64;
+
+ name = devm_kasprintf(&vmd->dev->dev, GFP_KERNEL, "VMD MEMBAR%d",
+ resource_number);
+ if (!name)
+ return -ENOMEM;
+
+ vmd->resources[resource_number] = (struct resource){
+ .name = name,
+ .start = res->start + start_offset,
+ .end = res->end - end_offset,
+ .flags = flags,
+ .parent = res,
+ };
+
+ return 0;
+}
+
+static int vmd_configure_membar1_membar2(struct vmd_dev *vmd,
+ resource_size_t mbar2_ofs)
+{
+ int ret;
+
+ ret = vmd_configure_membar(vmd, 1, VMD_MEMBAR1, 0, 0);
+ if (ret)
+ return ret;
+
+ ret = vmd_configure_membar(vmd, 2, VMD_MEMBAR2, mbar2_ofs, 0);
+ if (ret) {
+ devm_kfree(&vmd->dev->dev, (void *)vmd->resources[1].name);
+ memset(&vmd->resources[1], 0, sizeof(vmd->resources[1]));
+ return ret;
+ }
+
+ return 0;
+}
+
static void vmd_bus_enumeration(struct pci_bus *bus, unsigned long features)
{
struct pci_bus *child;
@@ -972,9 +1035,6 @@ static void vmd_bus_enumeration(struct pci_bus *bus, unsigned long features)
static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
{
struct pci_sysdata *sd = &vmd->sysdata;
- struct resource *res;
- u32 upper_bits;
- unsigned long flags;
LIST_HEAD(resources);
resource_size_t offset[2] = {0};
resource_size_t membar2_offset = 0x2000;
@@ -1000,36 +1060,14 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
*
* The only way we could use a 64-bit non-prefetchable MEMBAR is
* if its address is <4GB so that we can convert it to a 32-bit
- * resource. To be visible to the host OS, all VMD endpoints must
+ * resource. To be visible to the host OS, all VMD endpoints must
* be initially configured by platform BIOS, which includes setting
- * up these resources. We can assume the device is configured
+ * up these resources. We can assume the device is configured
* according to the platform needs.
*/
- res = &vmd->dev->resource[VMD_MEMBAR1];
- upper_bits = upper_32_bits(res->end);
- flags = res->flags & ~IORESOURCE_SIZEALIGN;
- if (!upper_bits)
- flags &= ~IORESOURCE_MEM_64;
- vmd->resources[1] = (struct resource) {
- .name = "VMD MEMBAR1",
- .start = res->start,
- .end = res->end,
- .flags = flags,
- .parent = res,
- };
-
- res = &vmd->dev->resource[VMD_MEMBAR2];
- upper_bits = upper_32_bits(res->end);
- flags = res->flags & ~IORESOURCE_SIZEALIGN;
- if (!upper_bits)
- flags &= ~IORESOURCE_MEM_64;
- vmd->resources[2] = (struct resource) {
- .name = "VMD MEMBAR2",
- .start = res->start + membar2_offset,
- .end = res->end,
- .flags = flags,
- .parent = res,
- };
+ ret = vmd_configure_membar1_membar2(vmd, membar2_offset);
+ if (ret)
+ return ret;
/* Non-zero offset means guest/direct assign view. */
vmd_in_guest = offset[0] || offset[1];
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v9 4/7] PCI: vmd: Add vmd_create_bus()
2026-10-26 20:54 [PATCH v9 0/7] VMD add second root bus support Szymon Durawa
` (2 preceding siblings ...)
2026-10-26 20:54 ` [PATCH v9 3/7] PCI: vmd: Add vmd_configure_membar() and vmd_configure_membar1_membar2() Szymon Durawa
@ 2026-10-26 20:54 ` Szymon Durawa
2026-10-26 20:54 ` [PATCH v9 5/7] PCI: vmd: Replace hardcoded values with enum and defines Szymon Durawa
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Szymon Durawa @ 2026-10-26 20:54 UTC (permalink / raw)
To: nirmal.patel, bhelgaas, robh, mani, kwilczynski, lpieralisi,
asutosh.pathak, szymon.durawa
Cc: linux-pci, linux-kernel
Move the VMD bus initialization code to a new helper vmd_create_bus().
No functional changes.
Suggested-by: Nirmal Patel <nirmal.patel@linux.intel.com>
Reviewed-by: Asutosh Pathak <asutosh.pathak@linux.intel.com>
Signed-off-by: Szymon Durawa <szymon.durawa@linux.intel.com>
---
drivers/pci/controller/vmd.c | 63 ++++++++++++++++++++++--------------
1 file changed, 38 insertions(+), 25 deletions(-)
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index ed6d03656885..8b59b369f126 100644
--- a/drivers/pci/controller/vmd.c
+++ b/drivers/pci/controller/vmd.c
@@ -984,6 +984,40 @@ static int vmd_configure_membar1_membar2(struct vmd_dev *vmd,
return 0;
}
+static int vmd_create_bus(struct vmd_dev *vmd, struct pci_sysdata *sd,
+ resource_size_t *offset, bool vmd_in_guest)
+{
+ LIST_HEAD(resources);
+
+ pci_add_resource(&resources, &vmd->resources[0]);
+ pci_add_resource_offset(&resources, &vmd->resources[1], offset[0]);
+ pci_add_resource_offset(&resources, &vmd->resources[2], offset[1]);
+
+ vmd->bus = pci_create_root_bus(&vmd->dev->dev, vmd->busn_start,
+ &vmd_ops, sd, &resources);
+ if (!vmd->bus) {
+ pci_free_resource_list(&resources);
+ return -ENODEV;
+ }
+
+ /*
+ * Don't copy _OSC control flags from root bridge if running in a VM, as
+ * they don't reflect the physical root bridge capabilities.
+ */
+ if (!vmd_in_guest)
+ vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
+ to_pci_host_bridge(vmd->bus->bridge));
+
+ vmd_attach_resources(vmd);
+ if (vmd->irq_domain)
+ dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain);
+ else
+ dev_set_msi_domain(&vmd->bus->dev,
+ dev_get_msi_domain(&vmd->dev->dev));
+
+ return 0;
+}
+
static void vmd_bus_enumeration(struct pci_bus *bus, unsigned long features)
{
struct pci_bus *child;
@@ -1035,7 +1069,6 @@ static void vmd_bus_enumeration(struct pci_bus *bus, unsigned long features)
static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
{
struct pci_sysdata *sd = &vmd->sysdata;
- LIST_HEAD(resources);
resource_size_t offset[2] = {0};
resource_size_t membar2_offset = 0x2000;
bool vmd_in_guest;
@@ -1092,10 +1125,6 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
vmd_set_msi_remapping(vmd, false);
}
- pci_add_resource(&resources, &vmd->resources[0]);
- pci_add_resource_offset(&resources, &vmd->resources[1], offset[0]);
- pci_add_resource_offset(&resources, &vmd->resources[2], offset[1]);
-
sd->vmd_dev = vmd->dev;
/*
@@ -1110,30 +1139,14 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
sd->node = pcibus_to_node(vmd->dev->bus);
- vmd->bus = pci_create_root_bus(&vmd->dev->dev, vmd->busn_start,
- &vmd_ops, sd, &resources);
- if (!vmd->bus) {
+ ret = vmd_create_bus(vmd, sd, offset, vmd_in_guest);
+ if (ret) {
+ pci_err(vmd->dev, "Can't create bus: %d\n", ret);
pci_bus_release_emul_domain_nr(sd->domain);
- pci_free_resource_list(&resources);
vmd_remove_irq_domain(vmd);
- return -ENODEV;
+ return ret;
}
- /*
- * Don't copy _OSC control flags from root bridge if running in a VM, as
- * they don't reflect the physical root bridge capabilities.
- */
- if (!vmd_in_guest)
- vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
- to_pci_host_bridge(vmd->bus->bridge));
-
- vmd_attach_resources(vmd);
- if (vmd->irq_domain)
- dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain);
- else
- dev_set_msi_domain(&vmd->bus->dev,
- dev_get_msi_domain(&vmd->dev->dev));
-
WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj,
"domain"), "Can't create symlink to domain\n");
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v9 5/7] PCI: vmd: Replace hardcoded values with enum and defines
2026-10-26 20:54 [PATCH v9 0/7] VMD add second root bus support Szymon Durawa
` (3 preceding siblings ...)
2026-10-26 20:54 ` [PATCH v9 4/7] PCI: vmd: Add vmd_create_bus() Szymon Durawa
@ 2026-10-26 20:54 ` Szymon Durawa
2026-10-26 20:54 ` [PATCH v9 6/7] PCI: vmd: Convert bus, busn_start and sysdata to an array Szymon Durawa
2026-10-26 20:55 ` [PATCH v9 7/7] PCI: vmd: Add support for second rootbus under VMD Szymon Durawa
6 siblings, 0 replies; 8+ messages in thread
From: Szymon Durawa @ 2026-10-26 20:54 UTC (permalink / raw)
To: nirmal.patel, bhelgaas, robh, mani, kwilczynski, lpieralisi,
asutosh.pathak, szymon.durawa
Cc: linux-pci, linux-kernel
Add enum vmd_resource type to replace hardcoded values. Add defines for
vmd bus start number based on VMD restriction value. No functional
changes.
Suggested-by: Nirmal Patel <nirmal.patel@linux.intel.com>
Reviewed-by: Asutosh Pathak <asutosh.pathak@linux.intel.com>
Signed-off-by: Szymon Durawa <szymon.durawa@linux.intel.com>
---
drivers/pci/controller/vmd.c | 52 +++++++++++++++++++++++-------------
1 file changed, 34 insertions(+), 18 deletions(-)
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index 8b59b369f126..f85dd2d7c4af 100644
--- a/drivers/pci/controller/vmd.c
+++ b/drivers/pci/controller/vmd.c
@@ -26,6 +26,11 @@
#define VMD_MEMBAR1 2
#define VMD_MEMBAR2 4
+/* VMD restriction value determines secondary start bus number */
+#define VMD_RESTRICT_0_BUS_START 0x0
+#define VMD_RESTRICT_1_BUS_START 0x80
+#define VMD_RESTRICT_2_BUS_START 0xE0
+
#define PCI_REG_VMCAP 0x40
#define BUS_RESTRICT_CAP(vmcap) (vmcap & 0x1)
#define PCI_REG_VMCONFIG 0x44
@@ -43,6 +48,13 @@
#define BASE_ID_REG_28C1 0x2840
#define MEMBAR2_OFFSET_28C1 0x30d0
+enum vmd_resource {
+ VMD_RES_CFGBAR = 0, /* VMD Bus0 Config BAR */
+ VMD_RES_MBAR_1, /* VMD Bus0 Resource MemBAR 1 */
+ VMD_RES_MBAR_2, /* VMD Bus0 Resource MemBAR 2 */
+ VMD_RES_COUNT
+};
+
enum vmd_features {
/*
* Device may contain registers which hint the physical location of the
@@ -150,7 +162,7 @@ struct vmd_dev {
struct vmd_irq_list *irqs;
struct pci_sysdata sysdata;
- struct resource resources[3];
+ struct resource resources[VMD_RES_COUNT];
struct irq_domain *irq_domain;
struct pci_bus *bus;
u8 busn_start;
@@ -554,7 +566,7 @@ static resource_size_t vmd_cfgbar_ecam_space(struct vmd_dev *vmd)
unsigned int ecam_start;
cfgbar_buses = resource_size(&vmd->dev->resource[VMD_CFGBAR]) >> 20;
- ecam_start = vmd_bus_to_ecam(vmd, vmd->resources[0].start);
+ ecam_start = vmd_bus_to_ecam(vmd, vmd->resources[VMD_RES_CFGBAR].start);
if (ecam_start >= cfgbar_buses)
return 0;
@@ -562,14 +574,15 @@ static resource_size_t vmd_cfgbar_ecam_space(struct vmd_dev *vmd)
}
static void vmd_domain_reset(struct vmd_dev *vmd)
{
- u16 bus, max_buses = resource_size(&vmd->resources[0]);
+ u16 bus, max_buses = resource_size(&vmd->resources[VMD_RES_CFGBAR]);
u8 dev, functions, fn, hdr_type;
unsigned int ecam_bus;
char __iomem *base;
max_buses = min_t(u16, max_buses, vmd_cfgbar_ecam_space(vmd));
for (bus = 0; bus < max_buses; bus++) {
- ecam_bus = vmd_bus_to_ecam(vmd, vmd->resources[0].start + bus);
+ ecam_bus = vmd_bus_to_ecam(vmd, vmd->resources[VMD_RES_CFGBAR].start +
+ bus);
for (dev = 0; dev < 32; dev++) {
base = vmd->cfgbar + PCIE_ECAM_OFFSET(ecam_bus,
PCI_DEVFN(dev, 0), 0);
@@ -613,8 +626,8 @@ static void vmd_domain_reset(struct vmd_dev *vmd)
static void vmd_attach_resources(struct vmd_dev *vmd)
{
- vmd->dev->resource[VMD_MEMBAR1].child = &vmd->resources[1];
- vmd->dev->resource[VMD_MEMBAR2].child = &vmd->resources[2];
+ vmd->dev->resource[VMD_MEMBAR1].child = &vmd->resources[VMD_RES_MBAR_1];
+ vmd->dev->resource[VMD_MEMBAR2].child = &vmd->resources[VMD_RES_MBAR_2];
}
static void vmd_detach_resources(struct vmd_dev *vmd)
@@ -690,14 +703,14 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd)
switch (BUS_RESTRICT_CFG(reg)) {
case 0:
- vmd->busn_start = 0;
+ vmd->busn_start = VMD_RESTRICT_0_BUS_START;
break;
case 1:
- vmd->busn_start = 128;
+ vmd->busn_start = VMD_RESTRICT_1_BUS_START;
break;
case 3:
case 2:
- vmd->busn_start = 224;
+ vmd->busn_start = VMD_RESTRICT_2_BUS_START;
break;
default:
pci_err(dev, "Unknown Bus Offset Setting (%d)\n",
@@ -910,10 +923,10 @@ static void vmd_configure_cfgbar(struct vmd_dev *vmd)
struct resource *res = &vmd->dev->resource[VMD_CFGBAR];
resource_size_t busn_end;
- /* Do not let resource[0] end go out of bound.*/
+ /* Do not let resource[VMD_RES_CFGBAR] end go out of bound.*/
busn_end = vmd->busn_start + (resource_size(res) >> 20) - 1;
busn_end = min_t(resource_size_t, busn_end, 0xff);
- vmd->resources[0] = (struct resource) {
+ vmd->resources[VMD_RES_CFGBAR] = (struct resource) {
.name = "VMD CFGBAR",
.start = vmd->busn_start,
.end = busn_end,
@@ -970,14 +983,15 @@ static int vmd_configure_membar1_membar2(struct vmd_dev *vmd,
{
int ret;
- ret = vmd_configure_membar(vmd, 1, VMD_MEMBAR1, 0, 0);
+ ret = vmd_configure_membar(vmd, VMD_RES_MBAR_1, VMD_MEMBAR1, 0, 0);
if (ret)
return ret;
- ret = vmd_configure_membar(vmd, 2, VMD_MEMBAR2, mbar2_ofs, 0);
+ ret = vmd_configure_membar(vmd, VMD_RES_MBAR_2, VMD_MEMBAR2, mbar2_ofs, 0);
if (ret) {
- devm_kfree(&vmd->dev->dev, (void *)vmd->resources[1].name);
- memset(&vmd->resources[1], 0, sizeof(vmd->resources[1]));
+ devm_kfree(&vmd->dev->dev, (void *)vmd->resources[VMD_RES_MBAR_1].name);
+ memset(&vmd->resources[VMD_RES_MBAR_1], 0,
+ sizeof(vmd->resources[VMD_RES_MBAR_1]));
return ret;
}
@@ -989,9 +1003,11 @@ static int vmd_create_bus(struct vmd_dev *vmd, struct pci_sysdata *sd,
{
LIST_HEAD(resources);
- pci_add_resource(&resources, &vmd->resources[0]);
- pci_add_resource_offset(&resources, &vmd->resources[1], offset[0]);
- pci_add_resource_offset(&resources, &vmd->resources[2], offset[1]);
+ pci_add_resource(&resources, &vmd->resources[VMD_RES_CFGBAR]);
+ pci_add_resource_offset(&resources, &vmd->resources[VMD_RES_MBAR_1],
+ offset[0]);
+ pci_add_resource_offset(&resources, &vmd->resources[VMD_RES_MBAR_2],
+ offset[1]);
vmd->bus = pci_create_root_bus(&vmd->dev->dev, vmd->busn_start,
&vmd_ops, sd, &resources);
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v9 6/7] PCI: vmd: Convert bus, busn_start and sysdata to an array
2026-10-26 20:54 [PATCH v9 0/7] VMD add second root bus support Szymon Durawa
` (4 preceding siblings ...)
2026-10-26 20:54 ` [PATCH v9 5/7] PCI: vmd: Replace hardcoded values with enum and defines Szymon Durawa
@ 2026-10-26 20:54 ` Szymon Durawa
2026-10-26 20:55 ` [PATCH v9 7/7] PCI: vmd: Add support for second rootbus under VMD Szymon Durawa
6 siblings, 0 replies; 8+ messages in thread
From: Szymon Durawa @ 2026-10-26 20:54 UTC (permalink / raw)
To: nirmal.patel, bhelgaas, robh, mani, kwilczynski, lpieralisi,
asutosh.pathak, szymon.durawa
Cc: linux-pci, linux-kernel
Convert bus, busn_start and sysdata in vmd_dev struct from scalar
to an array to support multiple VMD buses in the future.
No functional changes.
Suggested-by: Nirmal Patel <nirmal.patel@linux.intel.com>
Reviewed-by: Asutosh Pathak <asutosh.pathak@linux.intel.com>
Signed-off-by: Szymon Durawa <szymon.durawa@linux.intel.com>
---
drivers/pci/controller/vmd.c | 64 +++++++++++++++++++++---------------
1 file changed, 37 insertions(+), 27 deletions(-)
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index f85dd2d7c4af..1db974ed760f 100644
--- a/drivers/pci/controller/vmd.c
+++ b/drivers/pci/controller/vmd.c
@@ -55,6 +55,11 @@ enum vmd_resource {
VMD_RES_COUNT
};
+enum vmd_rootbus {
+ VMD_BUS_0 = 0,
+ VMD_BUS_COUNT
+};
+
enum vmd_features {
/*
* Device may contain registers which hint the physical location of the
@@ -161,11 +166,11 @@ struct vmd_dev {
int msix_count;
struct vmd_irq_list *irqs;
- struct pci_sysdata sysdata;
+ struct pci_sysdata sysdata[VMD_BUS_COUNT];
struct resource resources[VMD_RES_COUNT];
struct irq_domain *irq_domain;
- struct pci_bus *bus;
- u8 busn_start;
+ struct pci_bus *bus[VMD_BUS_COUNT];
+ u8 busn_start[VMD_BUS_COUNT];
u8 first_vec;
char *name;
int instance;
@@ -174,7 +179,10 @@ struct vmd_dev {
static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus)
{
- return container_of(bus->sysdata, struct vmd_dev, sysdata);
+ struct pci_sysdata *sd = bus->sysdata;
+
+ return pci_get_drvdata(sd->vmd_dev);
+
}
static inline unsigned int index_from_irqs(struct vmd_dev *vmd,
@@ -376,7 +384,7 @@ static int vmd_create_irq_domain(struct vmd_dev *vmd)
};
info.fwnode = irq_domain_alloc_named_id_fwnode("VMD-MSI",
- vmd->sysdata.domain);
+ vmd->sysdata[VMD_BUS_0].domain);
if (!info.fwnode)
return -ENODEV;
@@ -425,7 +433,7 @@ static unsigned int vmd_bus_to_ecam(struct vmd_dev *vmd, unsigned int busnr)
if (!!(vmd->features & VMD_FEAT_USE_BIOS_INFO))
return busnr;
- return busnr - vmd->busn_start;
+ return busnr - vmd->busn_start[VMD_BUS_0];
}
static void __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus,
@@ -703,14 +711,14 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd)
switch (BUS_RESTRICT_CFG(reg)) {
case 0:
- vmd->busn_start = VMD_RESTRICT_0_BUS_START;
+ vmd->busn_start[VMD_BUS_0] = VMD_RESTRICT_0_BUS_START;
break;
case 1:
- vmd->busn_start = VMD_RESTRICT_1_BUS_START;
+ vmd->busn_start[VMD_BUS_0] = VMD_RESTRICT_1_BUS_START;
break;
case 3:
case 2:
- vmd->busn_start = VMD_RESTRICT_2_BUS_START;
+ vmd->busn_start[VMD_BUS_0] = VMD_RESTRICT_2_BUS_START;
break;
default:
pci_err(dev, "Unknown Bus Offset Setting (%d)\n",
@@ -747,7 +755,7 @@ static int vmd_get_bus_info_from_bar4(struct vmd_dev *vmd,
bar4_2840 = readq(bar4 + BASE_ID_REG_28C1);
base_id = bar4_2840 & 0xFFFFFF;
base_bus = base_id >> 8;
- vmd->busn_start = base_bus;
+ vmd->busn_start[VMD_BUS_0] = base_bus;
/* Calculate offsets like vmd_get_phys_offsets() does */
if (phys1)
@@ -924,11 +932,11 @@ static void vmd_configure_cfgbar(struct vmd_dev *vmd)
resource_size_t busn_end;
/* Do not let resource[VMD_RES_CFGBAR] end go out of bound.*/
- busn_end = vmd->busn_start + (resource_size(res) >> 20) - 1;
+ busn_end = vmd->busn_start[VMD_BUS_0] + (resource_size(res) >> 20) - 1;
busn_end = min_t(resource_size_t, busn_end, 0xff);
vmd->resources[VMD_RES_CFGBAR] = (struct resource) {
.name = "VMD CFGBAR",
- .start = vmd->busn_start,
+ .start = vmd->busn_start[VMD_BUS_0],
.end = busn_end,
.flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
};
@@ -1009,9 +1017,10 @@ static int vmd_create_bus(struct vmd_dev *vmd, struct pci_sysdata *sd,
pci_add_resource_offset(&resources, &vmd->resources[VMD_RES_MBAR_2],
offset[1]);
- vmd->bus = pci_create_root_bus(&vmd->dev->dev, vmd->busn_start,
- &vmd_ops, sd, &resources);
- if (!vmd->bus) {
+ 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]) {
pci_free_resource_list(&resources);
return -ENODEV;
}
@@ -1022,13 +1031,13 @@ static int vmd_create_bus(struct vmd_dev *vmd, struct pci_sysdata *sd,
*/
if (!vmd_in_guest)
vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
- to_pci_host_bridge(vmd->bus->bridge));
+ to_pci_host_bridge(vmd->bus[VMD_BUS_0]->bridge));
vmd_attach_resources(vmd);
if (vmd->irq_domain)
- dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain);
+ dev_set_msi_domain(&vmd->bus[VMD_BUS_0]->dev, vmd->irq_domain);
else
- dev_set_msi_domain(&vmd->bus->dev,
+ dev_set_msi_domain(&vmd->bus[VMD_BUS_0]->dev,
dev_get_msi_domain(&vmd->dev->dev));
return 0;
@@ -1084,7 +1093,7 @@ static void vmd_bus_enumeration(struct pci_bus *bus, unsigned long features)
static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
{
- struct pci_sysdata *sd = &vmd->sysdata;
+ struct pci_sysdata *sd = &vmd->sysdata[VMD_BUS_0];
resource_size_t offset[2] = {0};
resource_size_t membar2_offset = 0x2000;
bool vmd_in_guest;
@@ -1163,10 +1172,11 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
return ret;
}
- WARN(sysfs_create_link(&vmd->dev->dev.kobj, &vmd->bus->dev.kobj,
- "domain"), "Can't create symlink to domain\n");
+ WARN(sysfs_create_link(&vmd->dev->dev.kobj,
+ &vmd->bus[VMD_BUS_0]->dev.kobj, "domain"),
+ "Can't create symlink to domain\n");
- vmd_bus_enumeration(vmd->bus, features);
+ vmd_bus_enumeration(vmd->bus[VMD_BUS_0], features);
return 0;
}
@@ -1203,7 +1213,7 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
return -ENOMEM;
vmd->dev = dev;
- vmd->sysdata.domain = PCI_DOMAIN_NR_NOT_SET;
+ vmd->sysdata[VMD_BUS_0].domain = PCI_DOMAIN_NR_NOT_SET;
vmd->features = features;
vmd->instance = ida_alloc(&vmd_instance_ida, GFP_KERNEL);
if (vmd->instance < 0)
@@ -1243,7 +1253,7 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
goto out_release_instance;
dev_info(&vmd->dev->dev, "Bound to PCI domain %04x\n",
- vmd->sysdata.domain);
+ vmd->sysdata[VMD_BUS_0].domain);
return 0;
out_release_instance:
@@ -1263,14 +1273,14 @@ static void vmd_remove(struct pci_dev *dev)
{
struct vmd_dev *vmd = pci_get_drvdata(dev);
- pci_stop_root_bus(vmd->bus);
+ pci_stop_root_bus(vmd->bus[VMD_BUS_0]);
sysfs_remove_link(&vmd->dev->dev.kobj, "domain");
- pci_remove_root_bus(vmd->bus);
+ pci_remove_root_bus(vmd->bus[VMD_BUS_0]);
vmd_cleanup_srcu(vmd);
vmd_detach_resources(vmd);
vmd_remove_irq_domain(vmd);
ida_free(&vmd_instance_ida, vmd->instance);
- pci_bus_release_emul_domain_nr(vmd->sysdata.domain);
+ pci_bus_release_emul_domain_nr(vmd->sysdata[VMD_BUS_0].domain);
}
static void vmd_shutdown(struct pci_dev *dev)
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v9 7/7] PCI: vmd: Add support for second rootbus under VMD
2026-10-26 20:54 [PATCH v9 0/7] VMD add second root bus support Szymon Durawa
` (5 preceding siblings ...)
2026-10-26 20:54 ` [PATCH v9 6/7] PCI: vmd: Convert bus, busn_start and sysdata to an array Szymon Durawa
@ 2026-10-26 20:55 ` Szymon Durawa
6 siblings, 0 replies; 8+ messages in thread
From: Szymon Durawa @ 2026-10-26 20:55 UTC (permalink / raw)
To: nirmal.patel, bhelgaas, robh, mani, kwilczynski, lpieralisi,
asutosh.pathak, szymon.durawa
Cc: linux-pci, linux-kernel
Starting from Intel Arrow Lake, VMD exposes a second root bus (BUS1),
reported via Bus Restriction type 3. BUS0 starts at bus 0xE0 and BUS1
at 0xE1; the subordinate bus ranges for each are provided by firmware
in VMD_BUSRANGE0/1. All three VMD BARs are shared between BUS0 and
BUS1: CFGBAR is split by those bus ranges, and MEMBAR1/MEMBAR2 are
split at the offsets in VMD_MEMBAR1_OFFSET and VMD_MEMBAR2_OFFSET1/2.
Add VMD_FEAT_HAS_BUS1_ROOTBUS for client VMDs and, when restriction
type 3 is reported, create and enumerate a second root bus in its own
emulated PCI domain so that its bus numbers and resources stay
separate from BUS0. A "domain1" sysfs link is added alongside the
existing "domain" link. If the BUS1 domain or root bus cannot be
created, the driver warns and continues with BUS0 only. Restriction
type 3 on a VMD without the feature is now rejected instead of being
treated as type 2.
The root ports on BUS1 have PCI_PRIMARY_BUS hardwired to 0x80. Emulate
reads of that register for Type 1 devices on the BUS1 root bus so it
reports the actual root bus number, and set bus->primary on both root
buses before scanning.
Suggested-by: Nirmal Patel <nirmal.patel@linux.intel.com>
Reviewed-by: Asutosh Pathak <asutosh.pathak@linux.intel.com>
Signed-off-by: Szymon Durawa <szymon.durawa@linux.intel.com>
---
drivers/pci/controller/vmd.c | 467 +++++++++++++++++++++++++++++++----
1 file changed, 416 insertions(+), 51 deletions(-)
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index 1db974ed760f..079fa51f6d07 100644
--- a/drivers/pci/controller/vmd.c
+++ b/drivers/pci/controller/vmd.c
@@ -4,6 +4,7 @@
* Copyright (c) 2015, Intel Corporation.
*/
+#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -30,6 +31,7 @@
#define VMD_RESTRICT_0_BUS_START 0x0
#define VMD_RESTRICT_1_BUS_START 0x80
#define VMD_RESTRICT_2_BUS_START 0xE0
+#define VMD_RESTRICT_3_BUS_START 0xE1
#define PCI_REG_VMCAP 0x40
#define BUS_RESTRICT_CAP(vmcap) (vmcap & 0x1)
@@ -48,15 +50,36 @@
#define BASE_ID_REG_28C1 0x2840
#define MEMBAR2_OFFSET_28C1 0x30d0
+/* Primary Bus Number for VMD devices on root bus 0 */
+#define VMD_PRIMARY_BUS0 0x00
+/* Primary Bus Number for VMD devices on root bus 1 */
+#define VMD_PRIMARY_BUS1 0x80
+
+#define VMD_BUSRANGE0 0xc8
+#define VMD_BUSRANGE1 0xcc
+#define VMD_MEMBAR1_OFFSET 0xd0
+#define VMD_MEMBAR2_OFFSET1 0xd8
+#define VMD_MEMBAR2_OFFSET2 0xdc
+#define VMD_BUS_END(busr) FIELD_GET(GENMASK(15, 8), busr)
+#define VMD_BUS_START(busr) FIELD_GET(GENMASK(7, 0), busr)
+
+/*
+ * Add VMD resources for BUS1, it will share the same MMIO space with
+ * previous VMD resources.
+ */
enum vmd_resource {
- VMD_RES_CFGBAR = 0, /* VMD Bus0 Config BAR */
- VMD_RES_MBAR_1, /* VMD Bus0 Resource MemBAR 1 */
- VMD_RES_MBAR_2, /* VMD Bus0 Resource MemBAR 2 */
+ VMD_RES_CFGBAR = 0, /* VMD Bus0 Config BAR */
+ VMD_RES_MBAR_1, /* VMD Bus0 Resource MemBAR 1 */
+ VMD_RES_MBAR_2, /* VMD Bus0 Resource MemBAR 2 */
+ VMD_RES_BUS1_CFGBAR, /* VMD Bus1 Config BAR */
+ VMD_RES_BUS1_MBAR_1, /* VMD Bus1 Resource MemBAR 1 */
+ VMD_RES_BUS1_MBAR_2, /* VMD Bus1 Resource MemBAR 2 */
VMD_RES_COUNT
};
enum vmd_rootbus {
VMD_BUS_0 = 0,
+ VMD_BUS_1,
VMD_BUS_COUNT
};
@@ -109,6 +132,12 @@ enum vmd_features {
* referred to as MEMBAR2 or MSI-X BAR.
*/
VMD_FEAT_USE_BIOS_INFO = (1 << 6),
+
+ /*
+ * Starting from Intel Arrow Lake, VMD devices have their VMD root ports
+ * on the additional BUS1 root bus.
+ */
+ VMD_FEAT_HAS_BUS1_ROOTBUS = (1 << 7)
};
#define VMD_BIOS_PM_QUIRK_LTR 0x1003 /* 3145728 ns */
@@ -116,7 +145,8 @@ enum vmd_features {
#define VMD_FEATS_CLIENT (VMD_FEAT_HAS_MEMBAR_SHADOW_VSCAP | \
VMD_FEAT_HAS_BUS_RESTRICTIONS | \
VMD_FEAT_OFFSET_FIRST_VECTOR | \
- VMD_FEAT_BIOS_PM_QUIRK)
+ VMD_FEAT_BIOS_PM_QUIRK | \
+ VMD_FEAT_HAS_BUS1_ROOTBUS)
static DEFINE_IDA(vmd_instance_ida);
@@ -175,6 +205,7 @@ struct vmd_dev {
char *name;
int instance;
unsigned long features;
+ bool bus1_rootbus;
};
static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus)
@@ -451,6 +482,36 @@ static void __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus,
return vmd->cfgbar + offset;
}
+static bool vmd_is_bus1_root_bridge(struct vmd_dev *vmd, struct pci_bus *bus,
+ unsigned int devfn)
+{
+ void __iomem *hdr_addr;
+ u8 hdr_type;
+
+ lockdep_assert_held(&vmd->cfg_lock);
+
+ if (!vmd->bus1_rootbus)
+ return false;
+
+ /* Limit strictly to BUS1 */
+ if (bus->sysdata != &vmd->sysdata[VMD_BUS_1])
+ return false;
+
+ if (!pci_is_root_bus(bus))
+ return false;
+
+ /*
+ * Offset 0x18 is PCI_PRIMARY_BUS only for Type 1 bridge headers.
+ * On Type 0 endpoints, 0x18 is BAR2. Only apply the quirk to bridges.
+ */
+ hdr_addr = vmd_cfg_addr(vmd, bus, devfn, PCI_HEADER_TYPE, 1);
+ if (!hdr_addr)
+ return false;
+
+ hdr_type = readb(hdr_addr) & PCI_HEADER_TYPE_MASK;
+ return hdr_type == PCI_HEADER_TYPE_BRIDGE;
+}
+
/*
* CPU may deadlock if config space is not serialized on some versions of this
* hardware, so all config space access is done under a spinlock.
@@ -460,6 +521,7 @@ static int vmd_pci_read(struct pci_bus *bus, unsigned int devfn, int reg,
{
struct vmd_dev *vmd = vmd_from_bus(bus);
void __iomem *addr = vmd_cfg_addr(vmd, bus, devfn, reg, len);
+ int ret = 0;
if (!addr)
return -EFAULT;
@@ -468,16 +530,34 @@ static int vmd_pci_read(struct pci_bus *bus, unsigned int devfn, int reg,
switch (len) {
case 1:
*value = readb(addr);
- return 0;
+ break;
case 2:
*value = readw(addr);
- return 0;
+ break;
case 4:
*value = readl(addr);
- return 0;
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
+
+ /*
+ * Quirk: Hardware PCIe root port bridges on BUS1 have
+ * PCI_PRIMARY_BUS hardwired to 0x80 (VMD_PRIMARY_BUS1).
+ *
+ * Emulate the primary bus number to match the actual allocated
+ * root bus number. This prevents the PCI core from detecting a
+ * bus mismatch without needing synthetic bus ranges.
+ */
+ if (!ret && reg <= PCI_PRIMARY_BUS && reg + len > PCI_PRIMARY_BUS &&
+ vmd_is_bus1_root_bridge(vmd, bus, devfn)) {
+ u8 *buf = (u8 *)value;
+
+ buf[PCI_PRIMARY_BUS - reg] = bus->number;
+ }
+
+ return ret;
}
/*
@@ -574,12 +654,19 @@ static resource_size_t vmd_cfgbar_ecam_space(struct vmd_dev *vmd)
unsigned int ecam_start;
cfgbar_buses = resource_size(&vmd->dev->resource[VMD_CFGBAR]) >> 20;
- ecam_start = vmd_bus_to_ecam(vmd, vmd->resources[VMD_RES_CFGBAR].start);
+
+ if (vmd->bus1_rootbus)
+ /* BUS1 splits the aperture, so reset scans start from BUS0. */
+ ecam_start = vmd_bus_to_ecam(vmd, vmd->busn_start[VMD_BUS_0]);
+ else
+ ecam_start = vmd_bus_to_ecam(vmd, vmd->resources[VMD_RES_CFGBAR].start);
+
if (ecam_start >= cfgbar_buses)
return 0;
return cfgbar_buses - ecam_start;
}
+
static void vmd_domain_reset(struct vmd_dev *vmd)
{
u16 bus, max_buses = resource_size(&vmd->resources[VMD_RES_CFGBAR]);
@@ -587,10 +674,13 @@ static void vmd_domain_reset(struct vmd_dev *vmd)
unsigned int ecam_bus;
char __iomem *base;
+ if (vmd->bus1_rootbus)
+ max_buses = resource_size(&vmd->dev->resource[VMD_CFGBAR]) >> 20;
+
max_buses = min_t(u16, max_buses, vmd_cfgbar_ecam_space(vmd));
for (bus = 0; bus < max_buses; bus++) {
- ecam_bus = vmd_bus_to_ecam(vmd, vmd->resources[VMD_RES_CFGBAR].start +
- bus);
+ /* Calculate the ECAM bus nr starting from the first bus of VMD_BUS_0 */
+ ecam_bus = vmd_bus_to_ecam(vmd, vmd->busn_start[VMD_BUS_0] + bus);
for (dev = 0; dev < 32; dev++) {
base = vmd->cfgbar + PCIE_ECAM_OFFSET(ecam_bus,
PCI_DEVFN(dev, 0), 0);
@@ -632,16 +722,53 @@ static void vmd_domain_reset(struct vmd_dev *vmd)
}
}
-static void vmd_attach_resources(struct vmd_dev *vmd)
+static int vmd_attach_resources(struct vmd_dev *vmd)
{
- vmd->dev->resource[VMD_MEMBAR1].child = &vmd->resources[VMD_RES_MBAR_1];
- vmd->dev->resource[VMD_MEMBAR2].child = &vmd->resources[VMD_RES_MBAR_2];
+ int ret;
+
+ ret = request_resource(&vmd->dev->resource[VMD_MEMBAR1],
+ &vmd->resources[VMD_RES_MBAR_1]);
+ if (ret)
+ return ret;
+
+ ret = request_resource(&vmd->dev->resource[VMD_MEMBAR2],
+ &vmd->resources[VMD_RES_MBAR_2]);
+ if (ret) {
+ release_resource(&vmd->resources[VMD_RES_MBAR_1]);
+ return ret;
+ }
+
+ if (vmd->bus1_rootbus && vmd->sysdata[VMD_BUS_1].domain >= 0) {
+ ret = request_resource(&vmd->dev->resource[VMD_MEMBAR1],
+ &vmd->resources[VMD_RES_BUS1_MBAR_1]);
+ if (ret) {
+ release_resource(&vmd->resources[VMD_RES_MBAR_1]);
+ release_resource(&vmd->resources[VMD_RES_MBAR_2]);
+ return ret;
+ }
+
+ ret = request_resource(&vmd->dev->resource[VMD_MEMBAR2],
+ &vmd->resources[VMD_RES_BUS1_MBAR_2]);
+ if (ret) {
+ release_resource(&vmd->resources[VMD_RES_MBAR_1]);
+ release_resource(&vmd->resources[VMD_RES_MBAR_2]);
+ release_resource(&vmd->resources[VMD_RES_BUS1_MBAR_1]);
+ return ret;
+ }
+ }
+
+ return 0;
}
static void vmd_detach_resources(struct vmd_dev *vmd)
{
- vmd->dev->resource[VMD_MEMBAR1].child = NULL;
- vmd->dev->resource[VMD_MEMBAR2].child = NULL;
+ if (vmd->bus1_rootbus && vmd->sysdata[VMD_BUS_1].domain >= 0) {
+ release_resource(&vmd->resources[VMD_RES_BUS1_MBAR_2]);
+ release_resource(&vmd->resources[VMD_RES_BUS1_MBAR_1]);
+ }
+
+ release_resource(&vmd->resources[VMD_RES_MBAR_2]);
+ release_resource(&vmd->resources[VMD_RES_MBAR_1]);
}
static int vmd_get_phys_offsets(struct vmd_dev *vmd, bool native_hint,
@@ -698,7 +825,7 @@ static int vmd_get_phys_offsets(struct vmd_dev *vmd, bool native_hint,
return 0;
}
-static int vmd_get_bus_number_start(struct vmd_dev *vmd)
+static int vmd_get_bus_number_start(struct vmd_dev *vmd, unsigned long features)
{
struct pci_dev *dev = vmd->dev;
u16 reg;
@@ -716,10 +843,27 @@ static int vmd_get_bus_number_start(struct vmd_dev *vmd)
case 1:
vmd->busn_start[VMD_BUS_0] = VMD_RESTRICT_1_BUS_START;
break;
- case 3:
case 2:
vmd->busn_start[VMD_BUS_0] = VMD_RESTRICT_2_BUS_START;
break;
+ case 3:
+ if (!(features & VMD_FEAT_HAS_BUS1_ROOTBUS)) {
+ pci_err(dev, "VMD Bus Restriction detected type %d, but BUS1 root bus is not supported, aborting.\n",
+ BUS_RESTRICT_CFG(reg));
+ return -ENODEV;
+ }
+
+ /*
+ * Per Intel VMD specification (e.g., Arrow Lake-S):
+ * - VMD (on root bus 0) secondary bus start number: 0xE0
+ * (devices span 0xE2..0xF0 via VMD_BUSRANGE0).
+ * - VMD (on root bus 1) secondary bus start number: 0xE1
+ * (devices span 0xF1..0xFF via VMD_BUSRANGE1).
+ */
+ vmd->busn_start[VMD_BUS_0] = VMD_RESTRICT_2_BUS_START;
+ vmd->busn_start[VMD_BUS_1] = VMD_RESTRICT_3_BUS_START;
+ vmd->bus1_rootbus = true;
+ break;
default:
pci_err(dev, "Unknown Bus Offset Setting (%d)\n",
BUS_RESTRICT_CFG(reg));
@@ -859,7 +1003,7 @@ static int vmd_prepare_offsets_and_bus(struct vmd_dev *vmd,
* limits the bus range to between 0-127, 128-255, or 224-255.
*/
if (features & VMD_FEAT_HAS_BUS_RESTRICTIONS) {
- ret = vmd_get_bus_number_start(vmd);
+ ret = vmd_get_bus_number_start(vmd, features);
if (ret)
return ret;
}
@@ -926,7 +1070,7 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
return 0;
}
-static void vmd_configure_cfgbar(struct vmd_dev *vmd)
+static int vmd_configure_cfgbar(struct vmd_dev *vmd)
{
struct resource *res = &vmd->dev->resource[VMD_CFGBAR];
resource_size_t busn_end;
@@ -940,6 +1084,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;
}
/*
@@ -970,8 +1170,9 @@ static int vmd_configure_membar(struct vmd_dev *vmd, u8 resource_number,
if (!upper_bits)
flags &= ~IORESOURCE_MEM_64;
- name = devm_kasprintf(&vmd->dev->dev, GFP_KERNEL, "VMD MEMBAR%d",
- resource_number);
+ name = devm_kasprintf(&vmd->dev->dev, GFP_KERNEL, "VMD MEMBAR%d %s",
+ membar_number / 2,
+ resource_number > VMD_RES_MBAR_2 ? "BUS1" : "");
if (!name)
return -ENOMEM;
@@ -991,55 +1192,132 @@ static int vmd_configure_membar1_membar2(struct vmd_dev *vmd,
{
int ret;
- ret = vmd_configure_membar(vmd, VMD_RES_MBAR_1, VMD_MEMBAR1, 0, 0);
- if (ret)
- return ret;
+ if (vmd->bus1_rootbus) {
+ u32 reg = 0;
+ u32 bus1_mbar1_ofs = 0;
+ u64 bus1_mbar2_ofs = 0;
+ resource_size_t mbar1_sz, mbar2_sz;
- ret = vmd_configure_membar(vmd, VMD_RES_MBAR_2, VMD_MEMBAR2, mbar2_ofs, 0);
- if (ret) {
- devm_kfree(&vmd->dev->dev, (void *)vmd->resources[VMD_RES_MBAR_1].name);
- memset(&vmd->resources[VMD_RES_MBAR_1], 0,
- sizeof(vmd->resources[VMD_RES_MBAR_1]));
- return ret;
+ mbar1_sz = resource_size(&vmd->dev->resource[VMD_MEMBAR1]);
+ mbar2_sz = resource_size(&vmd->dev->resource[VMD_MEMBAR2]);
+
+ ret = pci_read_config_dword(vmd->dev, VMD_MEMBAR1_OFFSET,
+ &bus1_mbar1_ofs);
+ if (ret)
+ return -EIO;
+
+ ret = pci_read_config_dword(vmd->dev, VMD_MEMBAR2_OFFSET1, ®);
+ if (ret)
+ return -EIO;
+ bus1_mbar2_ofs = reg;
+
+ ret = pci_read_config_dword(vmd->dev, VMD_MEMBAR2_OFFSET2, ®);
+ if (ret)
+ return -EIO;
+ bus1_mbar2_ofs |= (u64)reg << 32;
+
+ if (!bus1_mbar1_ofs || bus1_mbar1_ofs >= mbar1_sz) {
+ pci_err(vmd->dev, "Invalid MEMBAR1 offset %#llx (BAR size %#llx)\n",
+ (unsigned long long)bus1_mbar1_ofs,
+ (unsigned long long)mbar1_sz);
+ return -EINVAL;
+ }
+
+ if (!bus1_mbar2_ofs || mbar2_ofs >= mbar2_sz ||
+ bus1_mbar2_ofs <= mbar2_ofs || bus1_mbar2_ofs >= mbar2_sz) {
+ pci_err(vmd->dev,
+ "Invalid MEMBAR2 split %#llx (shadow offset %#llx, BAR size %#llx)\n",
+ (unsigned long long)bus1_mbar2_ofs,
+ (unsigned long long)mbar2_ofs,
+ (unsigned long long)mbar2_sz);
+ return -EINVAL;
+ }
+
+ /*
+ * Resize BUS MEMBAR1 and MEMBAR2 ranges to make space
+ * for BUS1 owned devices by adjusting range end with values
+ * stored in VMD_MEMBAR1_OFFSET and VMD_MEMBAR2_OFFSET registers
+ */
+ ret = vmd_configure_membar(vmd, VMD_RES_MBAR_1, VMD_MEMBAR1, 0,
+ mbar1_sz - bus1_mbar1_ofs);
+ if (ret)
+ return ret;
+
+ ret = vmd_configure_membar(vmd, VMD_RES_MBAR_2, VMD_MEMBAR2,
+ mbar2_ofs, mbar2_sz - bus1_mbar2_ofs);
+ if (ret)
+ return ret;
+
+ ret = vmd_configure_membar(vmd, VMD_RES_BUS1_MBAR_1, VMD_MEMBAR1,
+ bus1_mbar1_ofs, 0);
+ if (ret)
+ return ret;
+
+ ret = vmd_configure_membar(vmd, VMD_RES_BUS1_MBAR_2, VMD_MEMBAR2,
+ bus1_mbar2_ofs, 0);
+ if (ret)
+ return ret;
+ } else {
+ ret = vmd_configure_membar(vmd, VMD_RES_MBAR_1, VMD_MEMBAR1, 0, 0);
+ if (ret)
+ return ret;
+
+ ret = vmd_configure_membar(vmd, VMD_RES_MBAR_2, VMD_MEMBAR2,
+ mbar2_ofs, 0);
+ if (ret)
+ return ret;
}
return 0;
}
-static int vmd_create_bus(struct vmd_dev *vmd, struct pci_sysdata *sd,
- resource_size_t *offset, bool vmd_in_guest)
+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);
+
+ if (!vmd_bus) {
pci_free_resource_list(&resources);
return -ENODEV;
}
+ /*
+ * pci_create_root_bus() does not initialise bus->primary.
+ * Set it here before any scanning so bridge traversal logic
+ * sees the correct upstream bus number from the start.
+ */
+ vmd_bus->primary = primary;
+
/*
* Don't copy _OSC control flags from root bridge if running in a VM, as
* they don't reflect the physical root bridge capabilities.
*/
if (!vmd_in_guest)
vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
- to_pci_host_bridge(vmd->bus[VMD_BUS_0]->bridge));
-
- vmd_attach_resources(vmd);
+ to_pci_host_bridge(vmd_bus->bridge));
if (vmd->irq_domain)
- dev_set_msi_domain(&vmd->bus[VMD_BUS_0]->dev, vmd->irq_domain);
+ dev_set_msi_domain(&vmd_bus->dev, vmd->irq_domain);
else
- dev_set_msi_domain(&vmd->bus[VMD_BUS_0]->dev,
+ dev_set_msi_domain(&vmd_bus->dev,
dev_get_msi_domain(&vmd->dev->dev));
+ vmd->bus[bus_number] = vmd_bus;
+
return 0;
}
@@ -1052,7 +1330,15 @@ static void vmd_bus_enumeration(struct pci_bus *bus, unsigned long features)
vmd_acpi_begin();
pci_scan_child_bus(bus);
- vmd_domain_reset(vmd_from_bus(bus));
+
+ /*
+ * vmd_domain_reset() walks the full VMD CFGBAR aperture, so a single
+ * invocation from BUS0 resets bridge windows for the whole VMD domain,
+ * including BUS1. Running it again during BUS1 enumeration would
+ * re-clobber windows already assigned for BUS0.
+ */
+ if (bus->primary == VMD_PRIMARY_BUS0)
+ vmd_domain_reset(vmd_from_bus(bus));
/*
* When Intel VMD is enabled, the OS does not discover the Root Ports
@@ -1091,6 +1377,16 @@ static void vmd_bus_enumeration(struct pci_bus *bus, unsigned long features)
vmd_acpi_end();
}
+/* Drop BUS1 configuration and continue with BUS0 only. */
+static void vmd_disable_bus1(struct vmd_dev *vmd)
+{
+ vmd->resources[VMD_RES_BUS1_CFGBAR] = (struct resource){};
+ vmd->resources[VMD_RES_BUS1_MBAR_1] = (struct resource){};
+ vmd->resources[VMD_RES_BUS1_MBAR_2] = (struct resource){};
+ vmd->sysdata[VMD_BUS_1].domain = PCI_DOMAIN_NR_NOT_SET;
+ vmd->bus[VMD_BUS_1] = NULL;
+}
+
static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
{
struct pci_sysdata *sd = &vmd->sysdata[VMD_BUS_0];
@@ -1104,7 +1400,9 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
if (ret)
return ret;
- vmd_configure_cfgbar(vmd);
+ ret = vmd_configure_cfgbar(vmd);
+ if (ret)
+ return ret;
/*
* If the window is below 4GB, clear IORESOURCE_MEM_64 so we can
@@ -1164,11 +1462,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;
}
@@ -1176,8 +1501,33 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
&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);
+
return 0;
}
@@ -1214,6 +1564,7 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
vmd->dev = dev;
vmd->sysdata[VMD_BUS_0].domain = PCI_DOMAIN_NR_NOT_SET;
+ vmd->sysdata[VMD_BUS_1].domain = PCI_DOMAIN_NR_NOT_SET;
vmd->features = features;
vmd->instance = ida_alloc(&vmd_instance_ida, GFP_KERNEL);
if (vmd->instance < 0)
@@ -1252,8 +1603,14 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (err)
goto out_release_instance;
- dev_info(&vmd->dev->dev, "Bound to PCI domain %04x\n",
- vmd->sysdata[VMD_BUS_0].domain);
+ if (vmd->bus1_rootbus && vmd->sysdata[VMD_BUS_1].domain >= 0)
+ dev_info(&vmd->dev->dev, "Bound to PCI domains %04x and %04x\n",
+ vmd->sysdata[VMD_BUS_0].domain,
+ vmd->sysdata[VMD_BUS_1].domain);
+ else
+ dev_info(&vmd->dev->dev, "Bound to PCI domain %04x\n",
+ vmd->sysdata[VMD_BUS_0].domain);
+
return 0;
out_release_instance:
@@ -1276,6 +1633,14 @@ static void vmd_remove(struct pci_dev *dev)
pci_stop_root_bus(vmd->bus[VMD_BUS_0]);
sysfs_remove_link(&vmd->dev->dev.kobj, "domain");
pci_remove_root_bus(vmd->bus[VMD_BUS_0]);
+
+ if (vmd->bus1_rootbus && vmd->bus[VMD_BUS_1]) {
+ pci_stop_root_bus(vmd->bus[VMD_BUS_1]);
+ sysfs_remove_link(&vmd->dev->dev.kobj, "domain1");
+ pci_remove_root_bus(vmd->bus[VMD_BUS_1]);
+ pci_bus_release_emul_domain_nr(vmd->sysdata[VMD_BUS_1].domain);
+ }
+
vmd_cleanup_srcu(vmd);
vmd_detach_resources(vmd);
vmd_remove_irq_domain(vmd);
@@ -1376,4 +1741,4 @@ module_pci_driver(vmd_drv);
MODULE_AUTHOR("Intel Corporation");
MODULE_DESCRIPTION("Volume Management Device driver");
MODULE_LICENSE("GPL v2");
-MODULE_VERSION("0.6");
+MODULE_VERSION("0.7");
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread