summarylogtreecommitdiffstats
path: root/0005-ZEN-PCI-Add-Intel-remapped-NVMe-device-support.patch
diff options
context:
space:
mode:
Diffstat (limited to '0005-ZEN-PCI-Add-Intel-remapped-NVMe-device-support.patch')
-rw-r--r--0005-ZEN-PCI-Add-Intel-remapped-NVMe-device-support.patch637
1 files changed, 637 insertions, 0 deletions
diff --git a/0005-ZEN-PCI-Add-Intel-remapped-NVMe-device-support.patch b/0005-ZEN-PCI-Add-Intel-remapped-NVMe-device-support.patch
new file mode 100644
index 000000000000..f8f08f65ed54
--- /dev/null
+++ b/0005-ZEN-PCI-Add-Intel-remapped-NVMe-device-support.patch
@@ -0,0 +1,637 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Daniel Drake <drake@endlessm.com>
+Date: Tue, 4 Jun 2019 14:51:21 +0800
+Subject: [PATCH] ZEN: PCI: Add Intel remapped NVMe device support
+
+Contains:
+ - PCI: Add Intel remapped NVMe device support
+
+ Consumer products that are configured by default to run the Intel SATA AHCI
+ controller in "RAID" or "Intel RST Premium With Intel Optane System
+ Acceleration" mode are becoming increasingly prevalent.
+
+ Unde this mode, NVMe devices are remapped into the SATA device and become
+ hidden from the PCI bus, which means that Linux users cannot access their
+ storage devices unless they go into the firmware setup menu to revert back
+ to AHCI mode - assuming such option is available. Lack of support for this
+ mode is also causing complications for vendors who distribute Linux.
+
+ Add support for the remapped NVMe mode by creating a virtual PCI bus,
+ where the AHCI and NVMe devices are presented separately, allowing the
+ ahci and nvme drivers to bind in the normal way.
+
+ Unfortunately the NVMe device configuration space is inaccesible under
+ this scheme, so we provide a fake one, and hope that no DeviceID-based
+ quirks are needed. The interrupt is shared between the AHCI and NVMe
+ devices.
+
+ Allow pci_real_dma_dev() to traverse back to the real DMA device from
+ the PCI devices created on our virtual bus, in case the iommu driver
+ will be involved with data transfers here.
+
+ The existing ahci driver is modified to not claim devices where remapped
+ NVMe devices are present, allowing this new driver to step in.
+
+ The details of the remapping scheme came from patches previously
+ posted by Dan Williams and the resulting discussion.
+
+ https://phabricator.endlessm.com/T24358
+ https://phabricator.endlessm.com/T29119
+
+ Signed-off-by: Daniel Drake <drake@endlessm.com>
+
+ - PCI: Fix order of remapped NVMe devices
+---
+ arch/x86/include/asm/pci.h | 6 +
+ arch/x86/pci/common.c | 7 +-
+ drivers/ata/ahci.c | 23 +-
+ drivers/pci/controller/Makefile | 6 +
+ drivers/pci/controller/intel-nvme-remap.c | 462 ++++++++++++++++++++++
+ 5 files changed, 488 insertions(+), 16 deletions(-)
+ create mode 100644 drivers/pci/controller/intel-nvme-remap.c
+
+diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h
+index f3fd5928bcbb58d29ad1ad9fa02acf527669272f..f7488c2b203cf6b90cd257cfd81334230ebd69c1 100644
+--- a/arch/x86/include/asm/pci.h
++++ b/arch/x86/include/asm/pci.h
+@@ -27,6 +27,7 @@ struct pci_sysdata {
+ #if IS_ENABLED(CONFIG_VMD)
+ struct pci_dev *vmd_dev; /* VMD Device if in Intel VMD domain */
+ #endif
++ struct pci_dev *nvme_remap_dev; /* AHCI Device if NVME remapped bus */
+ };
+
+ extern int pci_routeirq;
+@@ -70,6 +71,11 @@ static inline bool is_vmd(struct pci_bus *bus)
+ #define is_vmd(bus) false
+ #endif /* CONFIG_VMD */
+
++static inline bool is_nvme_remap(struct pci_bus *bus)
++{
++ return to_pci_sysdata(bus)->nvme_remap_dev != NULL;
++}
++
+ /* Can be used to override the logic in pci_scan_bus for skipping
+ already-configured bus numbers - to be used for buggy BIOSes
+ or architectures with incomplete PCI setup by the loader */
+diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c
+index ddb798603201ef79dd6c554d10b746a936ec2a84..7c20387d82029ac485f42c5fbc7e26545a536d3c 100644
+--- a/arch/x86/pci/common.c
++++ b/arch/x86/pci/common.c
+@@ -723,12 +723,15 @@ int pci_ext_cfg_avail(void)
+ return 0;
+ }
+
+-#if IS_ENABLED(CONFIG_VMD)
+ struct pci_dev *pci_real_dma_dev(struct pci_dev *dev)
+ {
++#if IS_ENABLED(CONFIG_VMD)
+ if (is_vmd(dev->bus))
+ return to_pci_sysdata(dev->bus)->vmd_dev;
++#endif
++
++ if (is_nvme_remap(dev->bus))
++ return to_pci_sysdata(dev->bus)->nvme_remap_dev;
+
+ return dev;
+ }
+-#endif
+diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
+index c1eca72b4575df73f32dc24ddc23d460cf5de581..974d877659113afc952c4ce617c44095423f480a 100644
+--- a/drivers/ata/ahci.c
++++ b/drivers/ata/ahci.c
+@@ -1503,7 +1503,7 @@ static irqreturn_t ahci_thunderx_irq_handler(int irq, void *dev_instance)
+ }
+ #endif
+
+-static void ahci_remap_check(struct pci_dev *pdev, int bar,
++static int ahci_remap_check(struct pci_dev *pdev, int bar,
+ struct ahci_host_priv *hpriv)
+ {
+ int i;
+@@ -1516,7 +1516,7 @@ static void ahci_remap_check(struct pci_dev *pdev, int bar,
+ pci_resource_len(pdev, bar) < SZ_512K ||
+ bar != AHCI_PCI_BAR_STANDARD ||
+ !(readl(hpriv->mmio + AHCI_VSCAP) & 1))
+- return;
++ return 0;
+
+ cap = readq(hpriv->mmio + AHCI_REMAP_CAP);
+ for (i = 0; i < AHCI_MAX_REMAP; i++) {
+@@ -1531,18 +1531,11 @@ static void ahci_remap_check(struct pci_dev *pdev, int bar,
+ }
+
+ if (!hpriv->remapped_nvme)
+- return;
+-
+- dev_warn(&pdev->dev, "Found %u remapped NVMe devices.\n",
+- hpriv->remapped_nvme);
+- dev_warn(&pdev->dev,
+- "Switch your BIOS from RAID to AHCI mode to use them.\n");
++ return 0;
+
+- /*
+- * Don't rely on the msi-x capability in the remap case,
+- * share the legacy interrupt across ahci and remapped devices.
+- */
+- hpriv->flags |= AHCI_HFLAG_NO_MSI;
++ /* Abort probe, allowing intel-nvme-remap to step in when available */
++ dev_info(&pdev->dev, "Device will be handled by intel-nvme-remap.\n");
++ return -ENODEV;
+ }
+
+ static int ahci_get_irq_vector(struct ata_host *host, int port)
+@@ -1765,7 +1758,9 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+ hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar];
+
+ /* detect remapped nvme devices */
+- ahci_remap_check(pdev, ahci_pci_bar, hpriv);
++ rc = ahci_remap_check(pdev, ahci_pci_bar, hpriv);
++ if (rc)
++ return rc;
+
+ sysfs_add_file_to_group(&pdev->dev.kobj,
+ &dev_attr_remapped_nvme.attr,
+diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
+index 37c8663de7fe1ff7c9c948cd39f4b6ce1a912f5b..897d19f92edeb123826596db8d3e65949cfe6678 100644
+--- a/drivers/pci/controller/Makefile
++++ b/drivers/pci/controller/Makefile
+@@ -1,4 +1,10 @@
+ # SPDX-License-Identifier: GPL-2.0
++ifdef CONFIG_X86_64
++ifdef CONFIG_SATA_AHCI
++obj-y += intel-nvme-remap.o
++endif
++endif
++
+ obj-$(CONFIG_PCIE_CADENCE) += cadence/
+ obj-$(CONFIG_PCI_FTPCI100) += pci-ftpci100.o
+ obj-$(CONFIG_PCI_IXP4XX) += pci-ixp4xx.o
+diff --git a/drivers/pci/controller/intel-nvme-remap.c b/drivers/pci/controller/intel-nvme-remap.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..e105e6f5cc91d1b99db4c9b1c79b500bb4beb22a
+--- /dev/null
++++ b/drivers/pci/controller/intel-nvme-remap.c
+@@ -0,0 +1,462 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Intel remapped NVMe device support.
++ *
++ * Copyright (c) 2019 Endless Mobile, Inc.
++ * Author: Daniel Drake <drake@endlessm.com>
++ *
++ * Some products ship by default with the SATA controller in "RAID" or
++ * "Intel RST Premium With Intel Optane System Acceleration" mode. Under this
++ * mode, which we refer to as "remapped NVMe" mode, any installed NVMe
++ * devices disappear from the PCI bus, and instead their I/O memory becomes
++ * available within the AHCI device BARs.
++ *
++ * This scheme is understood to be a way of avoiding usage of the standard
++ * Windows NVMe driver under that OS, instead mandating usage of Intel's
++ * driver instead, which has better power management, and presumably offers
++ * some RAID/disk-caching solutions too.
++ *
++ * Here in this driver, we support the remapped NVMe mode by claiming the
++ * AHCI device and creating a fake PCIe root port. On the new bus, the
++ * original AHCI device is exposed with only minor tweaks. Then, fake PCI
++ * devices corresponding to the remapped NVMe devices are created. The usual
++ * ahci and nvme drivers are then expected to bind to these devices and
++ * operate as normal.
++ *
++ * The PCI configuration space for the NVMe devices is completely
++ * unavailable, so we fake a minimal one and hope for the best.
++ *
++ * Interrupts are shared between the AHCI and NVMe devices. For simplicity,
++ * we only support the legacy interrupt here, although MSI support
++ * could potentially be added later.
++ */
++
++#define MODULE_NAME "intel-nvme-remap"
++
++#include <linux/ahci-remap.h>
++#include <linux/irq.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/pci.h>
++
++#define AHCI_PCI_BAR_STANDARD 5
++
++struct nvme_remap_dev {
++ struct pci_dev *dev; /* AHCI device */
++ struct pci_bus *bus; /* our fake PCI bus */
++ struct pci_sysdata sysdata;
++ int irq_base; /* our fake interrupts */
++
++ /*
++ * When we detect an all-ones write to a BAR register, this flag
++ * is set, so that we return the BAR size on the next read (a
++ * standard PCI behaviour).
++ * This includes the assumption that an all-ones BAR write is
++ * immediately followed by a read of the same register.
++ */
++ bool bar_sizing;
++
++ /*
++ * Resources copied from the AHCI device, to be regarded as
++ * resources on our fake bus.
++ */
++ struct resource ahci_resources[PCI_NUM_RESOURCES];
++
++ /* Resources corresponding to the NVMe devices. */
++ struct resource remapped_dev_mem[AHCI_MAX_REMAP];
++
++ /* Number of remapped NVMe devices found. */
++ int num_remapped_devices;
++};
++
++static inline struct nvme_remap_dev *nrdev_from_bus(struct pci_bus *bus)
++{
++ return container_of(bus->sysdata, struct nvme_remap_dev, sysdata);
++}
++
++
++/******** PCI configuration space **********/
++
++/*
++ * Helper macros for tweaking returned contents of PCI configuration space.
++ *
++ * value contains len bytes of data read from reg.
++ * If fixup_reg is included in that range, fix up the contents of that
++ * register to fixed_value.
++ */
++#define NR_FIX8(fixup_reg, fixed_value) do { \
++ if (reg <= fixup_reg && fixup_reg < reg + len) \
++ ((u8 *) value)[fixup_reg - reg] = (u8) (fixed_value); \
++ } while (0)
++
++#define NR_FIX16(fixup_reg, fixed_value) do { \
++ NR_FIX8(fixup_reg, fixed_value); \
++ NR_FIX8(fixup_reg + 1, fixed_value >> 8); \
++ } while (0)
++
++#define NR_FIX24(fixup_reg, fixed_value) do { \
++ NR_FIX8(fixup_reg, fixed_value); \
++ NR_FIX8(fixup_reg + 1, fixed_value >> 8); \
++ NR_FIX8(fixup_reg + 2, fixed_value >> 16); \
++ } while (0)
++
++#define NR_FIX32(fixup_reg, fixed_value) do { \
++ NR_FIX16(fixup_reg, (u16) fixed_value); \
++ NR_FIX16(fixup_reg + 2, fixed_value >> 16); \
++ } while (0)
++
++/*
++ * Read PCI config space of the slot 0 (AHCI) device.
++ * We pass through the read request to the underlying device, but
++ * tweak the results in some cases.
++ */
++static int nvme_remap_pci_read_slot0(struct pci_bus *bus, int reg,
++ int len, u32 *value)
++{
++ struct nvme_remap_dev *nrdev = nrdev_from_bus(bus);
++ struct pci_bus *ahci_dev_bus = nrdev->dev->bus;
++ int ret;
++
++ ret = ahci_dev_bus->ops->read(ahci_dev_bus, nrdev->dev->devfn,
++ reg, len, value);
++ if (ret)
++ return ret;
++
++ /*
++ * Adjust the device class, to prevent this driver from attempting to
++ * additionally probe the device we're simulating here.
++ */
++ NR_FIX24(PCI_CLASS_PROG, PCI_CLASS_STORAGE_SATA_AHCI);
++
++ /*
++ * Unset interrupt pin, otherwise ACPI tries to find routing
++ * info for our virtual IRQ, fails, and complains.
++ */
++ NR_FIX8(PCI_INTERRUPT_PIN, 0);
++
++ /*
++ * Truncate the AHCI BAR to not include the region that covers the
++ * hidden devices. This will cause the ahci driver to successfully
++ * probe th new device (instead of handing it over to this driver).
++ */
++ if (nrdev->bar_sizing) {
++ NR_FIX32(PCI_BASE_ADDRESS_5, ~(SZ_16K - 1));
++ nrdev->bar_sizing = false;
++ }
++
++ return PCIBIOS_SUCCESSFUL;
++}
++
++/*
++ * Read PCI config space of a remapped device.
++ * Since the original PCI config space is inaccessible, we provide a minimal,
++ * fake config space instead.
++ */
++static int nvme_remap_pci_read_remapped(struct pci_bus *bus, unsigned int port,
++ int reg, int len, u32 *value)
++{
++ struct nvme_remap_dev *nrdev = nrdev_from_bus(bus);
++ struct resource *remapped_mem;
++
++ if (port > nrdev->num_remapped_devices)
++ return PCIBIOS_DEVICE_NOT_FOUND;
++
++ *value = 0;
++ remapped_mem = &nrdev->remapped_dev_mem[port - 1];
++
++ /* Set a Vendor ID, otherwise Linux assumes no device is present */
++ NR_FIX16(PCI_VENDOR_ID, PCI_VENDOR_ID_INTEL);
++
++ /* Always appear on & bus mastering */
++ NR_FIX16(PCI_COMMAND, PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
++
++ /* Set class so that nvme driver probes us */
++ NR_FIX24(PCI_CLASS_PROG, PCI_CLASS_STORAGE_EXPRESS);
++
++ if (nrdev->bar_sizing) {
++ NR_FIX32(PCI_BASE_ADDRESS_0,
++ ~(resource_size(remapped_mem) - 1));
++ nrdev->bar_sizing = false;
++ } else {
++ resource_size_t mem_start = remapped_mem->start;
++
++ mem_start |= PCI_BASE_ADDRESS_MEM_TYPE_64;
++ NR_FIX32(PCI_BASE_ADDRESS_0, mem_start);
++ mem_start >>= 32;
++ NR_FIX32(PCI_BASE_ADDRESS_1, mem_start);
++ }
++
++ return PCIBIOS_SUCCESSFUL;
++}
++
++/* Read PCI configuration space. */
++static int nvme_remap_pci_read(struct pci_bus *bus, unsigned int devfn,
++ int reg, int len, u32 *value)
++{
++ if (PCI_SLOT(devfn) == 0)
++ return nvme_remap_pci_read_slot0(bus, reg, len, value);
++ else
++ return nvme_remap_pci_read_remapped(bus, PCI_SLOT(devfn),
++ reg, len, value);
++}
++
++/*
++ * Write PCI config space of the slot 0 (AHCI) device.
++ * Apart from the special case of BAR sizing, we disable all writes.
++ * Otherwise, the ahci driver could make changes (e.g. unset PCI bus master)
++ * that would affect the operation of the NVMe devices.
++ */
++static int nvme_remap_pci_write_slot0(struct pci_bus *bus, int reg,
++ int len, u32 value)
++{
++ struct nvme_remap_dev *nrdev = nrdev_from_bus(bus);
++ struct pci_bus *ahci_dev_bus = nrdev->dev->bus;
++
++ if (reg >= PCI_BASE_ADDRESS_0 && reg <= PCI_BASE_ADDRESS_5) {
++ /*
++ * Writing all-ones to a BAR means that the size of the
++ * memory region is being checked. Flag this so that we can
++ * reply with an appropriate size on the next read.
++ */
++ if (value == ~0)
++ nrdev->bar_sizing = true;
++
++ return ahci_dev_bus->ops->write(ahci_dev_bus,
++ nrdev->dev->devfn,
++ reg, len, value);
++ }
++
++ return PCIBIOS_SET_FAILED;
++}
++
++/*
++ * Write PCI config space of a remapped device.
++ * Since the original PCI config space is inaccessible, we reject all
++ * writes, except for the special case of BAR probing.
++ */
++static int nvme_remap_pci_write_remapped(struct pci_bus *bus,
++ unsigned int port,
++ int reg, int len, u32 value)
++{
++ struct nvme_remap_dev *nrdev = nrdev_from_bus(bus);
++
++ if (port > nrdev->num_remapped_devices)
++ return PCIBIOS_DEVICE_NOT_FOUND;
++
++ /*
++ * Writing all-ones to a BAR means that the size of the memory
++ * region is being checked. Flag this so that we can reply with
++ * an appropriate size on the next read.
++ */
++ if (value == ~0 && reg >= PCI_BASE_ADDRESS_0
++ && reg <= PCI_BASE_ADDRESS_5) {
++ nrdev->bar_sizing = true;
++ return PCIBIOS_SUCCESSFUL;
++ }
++
++ return PCIBIOS_SET_FAILED;
++}
++
++/* Write PCI configuration space. */
++static int nvme_remap_pci_write(struct pci_bus *bus, unsigned int devfn,
++ int reg, int len, u32 value)
++{
++ if (PCI_SLOT(devfn) == 0)
++ return nvme_remap_pci_write_slot0(bus, reg, len, value);
++ else
++ return nvme_remap_pci_write_remapped(bus, PCI_SLOT(devfn),
++ reg, len, value);
++}
++
++static struct pci_ops nvme_remap_pci_ops = {
++ .read = nvme_remap_pci_read,
++ .write = nvme_remap_pci_write,
++};
++
++
++/******** Initialization & exit **********/
++
++/*
++ * Find a PCI domain ID to use for our fake bus.
++ * Start at 0x10000 to not clash with ACPI _SEG domains (16 bits).
++ */
++static int find_free_domain(void)
++{
++ int domain = 0xffff;
++ struct pci_bus *bus = NULL;
++
++ while ((bus = pci_find_next_bus(bus)) != NULL)
++ domain = max_t(int, domain, pci_domain_nr(bus));
++
++ return domain + 1;
++}
++
++static int find_remapped_devices(struct nvme_remap_dev *nrdev,
++ struct list_head *resources)
++{
++ void __iomem *mmio;
++ int i, count = 0;
++ u32 cap;
++
++ mmio = pcim_iomap(nrdev->dev, AHCI_PCI_BAR_STANDARD,
++ pci_resource_len(nrdev->dev,
++ AHCI_PCI_BAR_STANDARD));
++ if (!mmio)
++ return -ENODEV;
++
++ /* Check if this device might have remapped nvme devices. */
++ if (pci_resource_len(nrdev->dev, AHCI_PCI_BAR_STANDARD) < SZ_512K ||
++ !(readl(mmio + AHCI_VSCAP) & 1))
++ return -ENODEV;
++
++ cap = readq(mmio + AHCI_REMAP_CAP);
++ for (i = AHCI_MAX_REMAP-1; i >= 0; i--) {
++ struct resource *remapped_mem;
++
++ if ((cap & (1 << i)) == 0)
++ continue;
++ if (readl(mmio + ahci_remap_dcc(i))
++ != PCI_CLASS_STORAGE_EXPRESS)
++ continue;
++
++ /* We've found a remapped device */
++ remapped_mem = &nrdev->remapped_dev_mem[count++];
++ remapped_mem->start =
++ pci_resource_start(nrdev->dev, AHCI_PCI_BAR_STANDARD)
++ + ahci_remap_base(i);
++ remapped_mem->end = remapped_mem->start
++ + AHCI_REMAP_N_SIZE - 1;
++ remapped_mem->flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED;
++ pci_add_resource(resources, remapped_mem);
++ }
++
++ pcim_iounmap(nrdev->dev, mmio);
++
++ if (count == 0)
++ return -ENODEV;
++
++ nrdev->num_remapped_devices = count;
++ dev_info(&nrdev->dev->dev, "Found %d remapped NVMe devices\n",
++ nrdev->num_remapped_devices);
++ return 0;
++}
++
++static void nvme_remap_remove_root_bus(void *data)
++{
++ struct pci_bus *bus = data;
++
++ pci_stop_root_bus(bus);
++ pci_remove_root_bus(bus);
++}
++
++static int nvme_remap_probe(struct pci_dev *dev,
++ const struct pci_device_id *id)
++{
++ struct nvme_remap_dev *nrdev;
++ LIST_HEAD(resources);
++ int i;
++ int ret;
++ struct pci_dev *child;
++
++ nrdev = devm_kzalloc(&dev->dev, sizeof(*nrdev), GFP_KERNEL);
++ nrdev->sysdata.domain = find_free_domain();
++ nrdev->sysdata.nvme_remap_dev = dev;
++ nrdev->dev = dev;
++ pci_set_drvdata(dev, nrdev);
++
++ ret = pcim_enable_device(dev);
++ if (ret < 0)
++ return ret;
++
++ pci_set_master(dev);
++
++ ret = find_remapped_devices(nrdev, &resources);
++ if (ret)
++ return ret;
++
++ /* Add resources from the original AHCI device */
++ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
++ struct resource *res = &dev->resource[i];
++
++ if (res->start) {
++ struct resource *nr_res = &nrdev->ahci_resources[i];
++
++ nr_res->start = res->start;
++ nr_res->end = res->end;
++ nr_res->flags = res->flags;
++ pci_add_resource(&resources, nr_res);
++ }
++ }
++
++ /* Create virtual interrupts */
++ nrdev->irq_base = devm_irq_alloc_descs(&dev->dev, -1, 0,
++ nrdev->num_remapped_devices + 1,
++ 0);
++ if (nrdev->irq_base < 0)
++ return nrdev->irq_base;
++
++ /* Create and populate PCI bus */
++ nrdev->bus = pci_create_root_bus(&dev->dev, 0, &nvme_remap_pci_ops,
++ &nrdev->sysdata, &resources);
++ if (!nrdev->bus)
++ return -ENODEV;
++
++ if (devm_add_action_or_reset(&dev->dev, nvme_remap_remove_root_bus,
++ nrdev->bus))
++ return -ENOMEM;
++
++ /* We don't support sharing MSI interrupts between these devices */
++ nrdev->bus->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
++
++ pci_scan_child_bus(nrdev->bus);
++
++ list_for_each_entry(child, &nrdev->bus->devices, bus_list) {
++ /*
++ * Prevent PCI core from trying to move memory BARs around.
++ * The hidden NVMe devices are at fixed locations.
++ */
++ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
++ struct resource *res = &child->resource[i];
++
++ if (res->flags & IORESOURCE_MEM)
++ res->flags |= IORESOURCE_PCI_FIXED;
++ }
++
++ /* Share the legacy IRQ between all devices */
++ child->irq = dev->irq;
++ }
++
++ pci_assign_unassigned_bus_resources(nrdev->bus);
++ pci_bus_add_devices(nrdev->bus);
++
++ return 0;
++}
++
++static const struct pci_device_id nvme_remap_ids[] = {
++ /*
++ * Match all Intel RAID controllers.
++ *
++ * There's overlap here with the set of devices detected by the ahci
++ * driver, but ahci will only successfully probe when there
++ * *aren't* any remapped NVMe devices, and this driver will only
++ * successfully probe when there *are* remapped NVMe devices that
++ * need handling.
++ */
++ {
++ PCI_VDEVICE(INTEL, PCI_ANY_ID),
++ .class = PCI_CLASS_STORAGE_RAID << 8,
++ .class_mask = 0xffffff00,
++ },
++ {0,}
++};
++MODULE_DEVICE_TABLE(pci, nvme_remap_ids);
++
++static struct pci_driver nvme_remap_drv = {
++ .name = MODULE_NAME,
++ .id_table = nvme_remap_ids,
++ .probe = nvme_remap_probe,
++};
++module_pci_driver(nvme_remap_drv);
++
++MODULE_AUTHOR("Daniel Drake <drake@endlessm.com>");
++MODULE_LICENSE("GPL v2");