[SRU][R][PATCH 1/2] UBUNTU: SAUCE: PCI: vmd: Add second rootbus support
Vladimir Nedoshivin
gvozd188 at mail.ru
Wed Sep 9 17:12:56 UTC 2026
From: Szymon Durawa <szymon.durawa at linux.intel.com>
BugLink: https://bugs.launchpad.net/bugs/2166325
Intel VMD currently exposes devices through a single root bus. Starting
with Intel Arrow Lake, VMD may expose an additional root bus and devices
behind that bus are otherwise not enumerated correctly.
Add support for the second VMD root bus, including its configuration and
MMIO resources, bus ranges and the non-zero BUS1 root bus number.
This is based on the Intel VMD second-rootbus work posted upstream by
Szymon Durawa.
Signed-off-by: Szymon Durawa <szymon.durawa at linux.intel.com>
Signed-off-by: Vladimir Nedoshivin <gvozd188 at mail.ru>
---
drivers/pci/controller/vmd.c | 484 +++++++++++++++++++++++++++--------
1 file changed, 371 insertions(+), 113 deletions(-)
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index bcd0eca..59fe330 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
+#define VMD_RESTRICT_0_BUS_START 0
+#define VMD_RESTRICT_1_BUS_START 128
+#define VMD_RESTRICT_2_BUS_START 224
+#define VMD_RESTRICT_3_BUS_START 225
+
#define PCI_REG_VMCAP 0x40
#define BUS_RESTRICT_CAP(vmcap) (vmcap & 0x1)
#define PCI_REG_VMCONFIG 0x44
@@ -37,6 +42,36 @@
#define MB2_SHADOW_OFFSET 0x2000
#define MB2_SHADOW_SIZE 16
+#define VMD_PRIMARY_BUS0 0x00
+#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) ((busr >> 8) & 0xff)
+#define VMD_BUS_START(busr) (busr & 0x00ff)
+
+/*
+ * 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_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
+};
+
enum vmd_features {
/*
* Device may contain registers which hint the physical location of the
@@ -77,6 +112,12 @@ enum vmd_features {
* proper power management of the SoC.
*/
VMD_FEAT_BIOS_PM_QUIRK = (1 << 5),
+
+ /*
+ * Starting from Intel Arrow Lake, VMD devices have their VMD rootports
+ * connected to additional BUS1 rootport.
+ */
+ VMD_FEAT_HAS_BUS1_ROOTBUS = (1 << 6)
};
#define VMD_BIOS_PM_QUIRK_LTR 0x1003 /* 3145728 ns */
@@ -84,7 +125,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);
@@ -108,6 +150,7 @@ struct vmd_irq {
struct vmd_irq_list *irq;
bool enabled;
unsigned int virq;
+ void __iomem *flush_addr;
};
/**
@@ -119,6 +162,7 @@ struct vmd_irq {
* @virq: The underlying VMD Linux interrupt number
*/
struct vmd_irq_list {
+ struct vmd_dev *vmd;
struct list_head irq_list;
struct srcu_struct srcu;
unsigned int count;
@@ -135,13 +179,14 @@ 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;
+ struct pci_bus *bus[VMD_BUS_COUNT];
+ u8 busn_start[VMD_BUS_COUNT];
u8 first_vec;
char *name;
int instance;
+ bool bus1_rootbus;
};
static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus)
@@ -392,8 +437,22 @@ static void vmd_remove_irq_domain(struct vmd_dev *vmd)
static void __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus,
unsigned int devfn, int reg, int len)
{
- unsigned int busnr_ecam = bus->number - vmd->busn_start;
- u32 offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg);
+ unsigned char bus_number;
+ unsigned int busnr_ecam;
+ u32 offset;
+
+ /*
+ * VMD workaraund: for BUS1, bus->number is set to VMD_PRIMARY_BUS1
+ * (see comment under vmd_create_bus() for BUS1) but original value
+ * is 225 which is stored in vmd->busn_start[VMD_BUS_1].
+ */
+ if (vmd->bus1_rootbus && bus->number == VMD_PRIMARY_BUS1)
+ bus_number = vmd->busn_start[VMD_BUS_1];
+ else
+ bus_number = bus->number;
+
+ busnr_ecam = bus_number - vmd->busn_start[VMD_BUS_0];
+ offset = PCIE_ECAM_OFFSET(busnr_ecam, devfn, reg);
if (offset + len >= resource_size(&vmd->dev->resource[VMD_CFGBAR]))
return NULL;
@@ -520,11 +579,16 @@ static inline void vmd_acpi_end(void) { }
static void vmd_domain_reset(struct vmd_dev *vmd)
{
- u16 bus, max_buses = resource_size(&vmd->resources[0]);
+ u16 bus, bus_cnt = resource_size(&vmd->resources[VMD_RES_CFGBAR]);
u8 dev, functions, fn, hdr_type;
char __iomem *base;
- for (bus = 0; bus < max_buses; bus++) {
+ if (vmd->bus1_rootbus) {
+ bus_cnt += resource_size(&vmd->resources[VMD_RES_BUS1_CFGBAR]);
+ bus_cnt += 2;
+ }
+
+ for (bus = 0; bus < bus_cnt; bus++) {
for (dev = 0; dev < 32; dev++) {
base = vmd->cfgbar + PCIE_ECAM_OFFSET(bus,
PCI_DEVFN(dev, 0), 0);
@@ -568,14 +632,26 @@ 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];
+
+ if (vmd->bus1_rootbus) {
+ vmd->resources[VMD_RES_MBAR_1].sibling =
+ &vmd->resources[VMD_RES_BUS1_MBAR_1];
+ vmd->resources[VMD_RES_MBAR_2].sibling =
+ &vmd->resources[VMD_RES_BUS1_MBAR_2];
+ }
}
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->resources[VMD_RES_MBAR_1].sibling = NULL;
+ vmd->resources[VMD_RES_MBAR_2].sibling = NULL;
+ }
}
static int vmd_get_phys_offsets(struct vmd_dev *vmd, bool native_hint,
@@ -632,7 +708,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;
@@ -643,13 +719,26 @@ 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_BUS_0] = VMD_RESTRICT_0_BUS_START;
break;
case 1:
- vmd->busn_start = 128;
+ vmd->busn_start[VMD_BUS_0] = VMD_RESTRICT_1_BUS_START;
break;
case 2:
- vmd->busn_start = 224;
+ 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 Rootbus is not supported, aborting.\n",
+ BUS_RESTRICT_CFG(reg));
+ return -ENODEV;
+ }
+
+ /* BUS0 start number */
+ vmd->busn_start[VMD_BUS_0] = VMD_RESTRICT_2_BUS_START;
+ /* BUS1 start number */
+ 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",
@@ -773,19 +862,217 @@ out_state_change:
return 0;
}
-static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
+static void vmd_configure_cfgbar(struct vmd_dev *vmd)
+{
+ struct resource *res = &vmd->dev->resource[VMD_CFGBAR];
+
+ vmd->resources[VMD_RES_CFGBAR] = (struct resource){
+ .name = "VMD CFGBAR",
+ .start = vmd->busn_start[VMD_BUS_0],
+ .end = vmd->busn_start[VMD_BUS_0] +
+ (resource_size(res) >> 20) - 1,
+ .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
+ };
+
+ if (vmd->bus1_rootbus) {
+ u16 bus0_range = 0;
+ u16 bus1_range = 0;
+
+ pci_read_config_word(vmd->dev, VMD_BUSRANGE0, &bus0_range);
+ pci_read_config_word(vmd->dev, VMD_BUSRANGE1, &bus1_range);
+
+ /*
+ * 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 = VMD_BUS_START(bus0_range);
+ vmd->resources[VMD_RES_CFGBAR].end = VMD_BUS_END(bus0_range);
+
+ vmd->resources[VMD_RES_BUS1_CFGBAR] = (struct resource){
+ .name = "VMD CFGBAR BUS1",
+ .start = VMD_BUS_START(bus1_range),
+ .end = VMD_BUS_END(bus1_range),
+ .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
+ .parent = res,
+ };
+ }
+}
+
+/*
+ * 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.
+ */
+static void vmd_configure_membar(struct vmd_dev *vmd,
+ enum vmd_resource resource_number,
+ u8 membar_number, resource_size_t start_offset,
+ resource_size_t end_offset)
{
- struct pci_sysdata *sd = &vmd->sysdata;
- struct resource *res;
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;
+
+ vmd->resources[resource_number] = (struct resource){
+ .name = kasprintf(
+ GFP_KERNEL, "VMD MEMBAR%d %s", membar_number / 2,
+ resource_number > VMD_RES_MBAR_2 ? "BUS1" : ""),
+ .start = res->start + start_offset,
+ .end = res->end - end_offset,
+ .flags = flags,
+ .parent = res,
+ };
+}
+
+static void vmd_configure_membar1_membar2(struct vmd_dev *vmd,
+ resource_size_t mbar2_ofs)
+{
+ if (vmd->bus1_rootbus) {
+ u32 bus1_mbar1_ofs = 0;
+ u64 bus1_mbar2_ofs = 0;
+ u32 reg;
+
+ pci_read_config_dword(vmd->dev, VMD_MEMBAR1_OFFSET,
+ &bus1_mbar1_ofs);
+
+ pci_read_config_dword(vmd->dev, VMD_MEMBAR2_OFFSET1, ®);
+ bus1_mbar2_ofs = reg;
+
+ pci_read_config_dword(vmd->dev, VMD_MEMBAR2_OFFSET2, ®);
+ bus1_mbar2_ofs |= (u64)reg << 32;
+
+ /*
+ * 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
+ */
+ vmd_configure_membar(vmd, VMD_RES_MBAR_1, VMD_MEMBAR1, 0,
+ bus1_mbar1_ofs);
+ vmd_configure_membar(vmd, VMD_RES_MBAR_2, VMD_MEMBAR2,
+ mbar2_ofs, bus1_mbar2_ofs - mbar2_ofs);
+
+ vmd_configure_membar(vmd, VMD_RES_BUS1_MBAR_1, VMD_MEMBAR1,
+ bus1_mbar1_ofs, 0);
+ vmd_configure_membar(vmd, VMD_RES_BUS1_MBAR_2, VMD_MEMBAR2,
+ mbar2_ofs + bus1_mbar2_ofs, 0);
+ } else {
+ vmd_configure_membar(vmd, VMD_RES_MBAR_1, VMD_MEMBAR1, 0, 0);
+ vmd_configure_membar(vmd, VMD_RES_MBAR_2, VMD_MEMBAR2,
+ mbar2_ofs, 0);
+ }
+}
+
+static int vmd_create_bus(struct vmd_dev *vmd, enum vmd_rootbus bus_number,
+ struct pci_sysdata *sd, resource_size_t *offset)
+{
+ u8 cfgbar = bus_number * 3;
+ u8 membar1 = cfgbar + 1;
+ u8 membar2 = cfgbar + 2;
+ struct pci_bus *vmd_bus;
LIST_HEAD(resources);
- resource_size_t offset[2] = {0};
- resource_size_t membar2_offset = 0x2000;
+
+ pci_add_resource(&resources, &vmd->resources[cfgbar]);
+ pci_add_resource_offset(&resources, &vmd->resources[membar1],
+ offset[0]);
+ pci_add_resource_offset(&resources, &vmd->resources[membar2],
+ offset[1]);
+
+ 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);
+
+ if (bus_number == VMD_PRIMARY_BUS0)
+ vmd_remove_irq_domain(vmd);
+ return -ENODEV;
+ }
+
+ 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));
+
+ vmd->bus[bus_number] = vmd_bus;
+
+ 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);
+
+ 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
+ * 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;
+ resource_size_t offset[2] = {0};
+ resource_size_t membar2_offset = 0x2000;
+ int ret;
+
/*
* Shadow registers may exist in certain VMD device ids which allow
* guests to correctly assign host physical addresses to the root ports
@@ -808,18 +1095,12 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
* 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;
}
- res = &vmd->dev->resource[VMD_CFGBAR];
- vmd->resources[0] = (struct resource) {
- .name = "VMD CFGBAR",
- .start = vmd->busn_start,
- .end = vmd->busn_start + (resource_size(res) >> 20) - 1,
- .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED,
- };
+ vmd_configure_cfgbar(vmd);
/*
* If the window is below 4GB, clear IORESOURCE_MEM_64 so we can
@@ -833,36 +1114,12 @@ 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,
- };
+ vmd_configure_membar1_membar2(vmd, membar2_offset);
/*
* Currently MSI remapping must be enabled in guest passthrough mode
@@ -885,17 +1142,12 @@ 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;
/*
* Emulated domains start at 0x10000 to not clash with ACPI _SEG
- * domains. Per ACPI r6.0, sec 6.5.6, _SEG returns an integer, of
+ * domains. Per ACPI r6.0, sec 6.5.6, _SEG returns an integer, of
* which the lower 16 bits are the PCI Segment Group (domain) number.
- * Other bits are currently reserved.
*/
sd->domain = pci_bus_find_emul_domain_nr(0, 0x10000, INT_MAX);
if (sd->domain < 0)
@@ -905,70 +1157,63 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
pci_lock_rescan_remove();
- vmd->bus = pci_create_root_bus(&vmd->dev->dev, vmd->busn_start,
- &vmd_ops, sd, &resources);
- if (!vmd->bus) {
+ ret = vmd_create_bus(vmd, VMD_BUS_0, sd, offset);
+ if (ret) {
+ pci_err(vmd->dev, "Can't create bus: %d\n", ret);
pci_bus_release_emul_domain_nr(sd->domain);
-
pci_unlock_rescan_remove();
-
- pci_free_resource_list(&resources);
- vmd_remove_irq_domain(vmd);
- return -ENODEV;
+ return ret;
}
- vmd_copy_host_bridge_flags(pci_find_host_bridge(vmd->dev->bus),
- to_pci_host_bridge(vmd->bus->bridge));
+ WARN(sysfs_create_link(&vmd->dev->dev.kobj,
+ &vmd->bus[VMD_BUS_0]->dev.kobj, "domain"),
+ "Can't create symlink to domain\n");
- 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");
+ vmd_bus_enumeration(vmd->bus[VMD_BUS_0], features);
- vmd_acpi_begin();
+ if (vmd->bus1_rootbus) {
+ ret = vmd_create_bus(vmd, VMD_BUS_1, sd, offset);
+ if (ret) {
+ pci_err(vmd->dev, "Can't create BUS1: %d\n", ret);
- pci_scan_child_bus(vmd->bus);
- vmd_domain_reset(vmd);
+ sysfs_remove_link(&vmd->dev->dev.kobj, "domain");
+ pci_stop_root_bus(vmd->bus[VMD_BUS_0]);
+ pci_remove_root_bus(vmd->bus[VMD_BUS_0]);
+ vmd->bus[VMD_BUS_0] = NULL;
- /* 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);
+ vmd_detach_resources(vmd);
+ vmd_remove_irq_domain(vmd);
+ pci_bus_release_emul_domain_nr(sd->domain);
+ pci_unlock_rescan_remove();
- break;
+ return ret;
}
- }
-
- pci_assign_unassigned_bus_resources(vmd->bus);
- pci_walk_bus(vmd->bus, vmd_pm_enable_quirk, &features);
+ /*
+ * Primary bus is not set by pci_create_root_bus(), it is
+ * updated here
+ */
+ vmd->bus[VMD_BUS_1]->primary = VMD_PRIMARY_BUS1;
- /*
- * 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);
+ /*
+ * This is a workaround for pci_scan_bridge_extend() code.
+ * It detects that non-zero (0x80) primary bus number doesn't
+ * match the bus it's sitting on. As a result rootbus number is
+ * deconfigured in the first pass of pci_scan_bridge() to be
+ * re-assigned to 0x0 in the second pass.
+ * Update vmd->bus[VMD_BUS_1]->number and
+ * vmd->bus[VMD_BUS_1]->primary to the same value, which
+ * bypasses bus number reconfiguration.
+ */
+ vmd->bus[VMD_BUS_1]->number = VMD_PRIMARY_BUS1;
- pci_bus_add_devices(vmd->bus);
+ WARN(sysfs_create_link(&vmd->dev->dev.kobj,
+ &vmd->bus[VMD_BUS_1]->dev.kobj,
+ "domain1"),
+ "Can't create symlink to domain1\n");
- vmd_acpi_end();
+ vmd_bus_enumeration(vmd->bus[VMD_BUS_1], features);
+ }
pci_unlock_rescan_remove();
return 0;
@@ -1065,9 +1310,22 @@ 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]);
+
+ /* CFGBARs are static, do not require releasing memory */
+ kfree(vmd->resources[VMD_RES_MBAR_1].name);
+ kfree(vmd->resources[VMD_RES_MBAR_2].name);
+
+ if (vmd->bus1_rootbus) {
+ 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]);
+ kfree(vmd->resources[VMD_RES_BUS1_MBAR_1].name);
+ kfree(vmd->resources[VMD_RES_BUS1_MBAR_2].name);
+ }
+
vmd_cleanup_srcu(vmd);
vmd_detach_resources(vmd);
vmd_remove_irq_domain(vmd);
@@ -1160,4 +1418,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.53.0
More information about the kernel-team
mailing list