/* Highest slot of child device with resources allocated */
        int wslot_res_allocated;
+       bool use_calls; /* Use hypercalls to access mmio cfg space */
 
        /* hypercall arg, must not cross page boundary */
        struct hv_retarget_device_interrupt retarget_msi_interrupt_params;
        return PCI_DEVFN(slot_no.bits.dev, slot_no.bits.func);
 }
 
+static void hv_pci_read_mmio(struct device *dev, phys_addr_t gpa, int size, u32 *val)
+{
+       struct hv_mmio_read_input *in;
+       struct hv_mmio_read_output *out;
+       u64 ret;
+
+       /*
+        * Must be called with interrupts disabled so it is safe
+        * to use the per-cpu input argument page.  Use it for
+        * both input and output.
+        */
+       in = *this_cpu_ptr(hyperv_pcpu_input_arg);
+       out = *this_cpu_ptr(hyperv_pcpu_input_arg) + sizeof(*in);
+       in->gpa = gpa;
+       in->size = size;
+
+       ret = hv_do_hypercall(HVCALL_MMIO_READ, in, out);
+       if (hv_result_success(ret)) {
+               switch (size) {
+               case 1:
+                       *val = *(u8 *)(out->data);
+                       break;
+               case 2:
+                       *val = *(u16 *)(out->data);
+                       break;
+               default:
+                       *val = *(u32 *)(out->data);
+                       break;
+               }
+       } else
+               dev_err(dev, "MMIO read hypercall error %llx addr %llx size %d\n",
+                               ret, gpa, size);
+}
+
+static void hv_pci_write_mmio(struct device *dev, phys_addr_t gpa, int size, u32 val)
+{
+       struct hv_mmio_write_input *in;
+       u64 ret;
+
+       /*
+        * Must be called with interrupts disabled so it is safe
+        * to use the per-cpu input argument memory.
+        */
+       in = *this_cpu_ptr(hyperv_pcpu_input_arg);
+       in->gpa = gpa;
+       in->size = size;
+       switch (size) {
+       case 1:
+               *(u8 *)(in->data) = val;
+               break;
+       case 2:
+               *(u16 *)(in->data) = val;
+               break;
+       default:
+               *(u32 *)(in->data) = val;
+               break;
+       }
+
+       ret = hv_do_hypercall(HVCALL_MMIO_WRITE, in, NULL);
+       if (!hv_result_success(ret))
+               dev_err(dev, "MMIO write hypercall error %llx addr %llx size %d\n",
+                               ret, gpa, size);
+}
+
 /*
  * PCI Configuration Space for these root PCI buses is implemented as a pair
  * of pages in memory-mapped I/O space.  Writing to the first page chooses
 static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where,
                                     int size, u32 *val)
 {
+       struct hv_pcibus_device *hbus = hpdev->hbus;
+       struct device *dev = &hbus->hdev->device;
+       int offset = where + CFG_PAGE_OFFSET;
        unsigned long flags;
-       void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + where;
 
        /*
         * If the attempt is to read the IDs or the ROM BAR, simulate that.
                 */
                *val = 0;
        } else if (where + size <= CFG_PAGE_SIZE) {
-               spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
-               /* Choose the function to be read. (See comment above) */
-               writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
-               /* Make sure the function was chosen before we start reading. */
-               mb();
-               /* Read from that function's config space. */
-               switch (size) {
-               case 1:
-                       *val = readb(addr);
-                       break;
-               case 2:
-                       *val = readw(addr);
-                       break;
-               default:
-                       *val = readl(addr);
-                       break;
+
+               spin_lock_irqsave(&hbus->config_lock, flags);
+               if (hbus->use_calls) {
+                       phys_addr_t addr = hbus->mem_config->start + offset;
+
+                       hv_pci_write_mmio(dev, hbus->mem_config->start, 4,
+                                               hpdev->desc.win_slot.slot);
+                       hv_pci_read_mmio(dev, addr, size, val);
+               } else {
+                       void __iomem *addr = hbus->cfg_addr + offset;
+
+                       /* Choose the function to be read. (See comment above) */
+                       writel(hpdev->desc.win_slot.slot, hbus->cfg_addr);
+                       /* Make sure the function was chosen before reading. */
+                       mb();
+                       /* Read from that function's config space. */
+                       switch (size) {
+                       case 1:
+                               *val = readb(addr);
+                               break;
+                       case 2:
+                               *val = readw(addr);
+                               break;
+                       default:
+                               *val = readl(addr);
+                               break;
+                       }
+                       /*
+                        * Make sure the read was done before we release the
+                        * spinlock allowing consecutive reads/writes.
+                        */
+                       mb();
                }
-               /*
-                * Make sure the read was done before we release the spinlock
-                * allowing consecutive reads/writes.
-                */
-               mb();
-               spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
+               spin_unlock_irqrestore(&hbus->config_lock, flags);
        } else {
-               dev_err(&hpdev->hbus->hdev->device,
-                       "Attempt to read beyond a function's config space.\n");
+               dev_err(dev, "Attempt to read beyond a function's config space.\n");
        }
 }
 
 static u16 hv_pcifront_get_vendor_id(struct hv_pci_dev *hpdev)
 {
+       struct hv_pcibus_device *hbus = hpdev->hbus;
+       struct device *dev = &hbus->hdev->device;
+       u32 val;
        u16 ret;
        unsigned long flags;
-       void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET +
-                            PCI_VENDOR_ID;
 
-       spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
+       spin_lock_irqsave(&hbus->config_lock, flags);
 
-       /* Choose the function to be read. (See comment above) */
-       writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
-       /* Make sure the function was chosen before we start reading. */
-       mb();
-       /* Read from that function's config space. */
-       ret = readw(addr);
-       /*
-        * mb() is not required here, because the spin_unlock_irqrestore()
-        * is a barrier.
-        */
+       if (hbus->use_calls) {
+               phys_addr_t addr = hbus->mem_config->start +
+                                        CFG_PAGE_OFFSET + PCI_VENDOR_ID;
+
+               hv_pci_write_mmio(dev, hbus->mem_config->start, 4,
+                                       hpdev->desc.win_slot.slot);
+               hv_pci_read_mmio(dev, addr, 2, &val);
+               ret = val;  /* Truncates to 16 bits */
+       } else {
+               void __iomem *addr = hbus->cfg_addr + CFG_PAGE_OFFSET +
+                                            PCI_VENDOR_ID;
+               /* Choose the function to be read. (See comment above) */
+               writel(hpdev->desc.win_slot.slot, hbus->cfg_addr);
+               /* Make sure the function was chosen before we start reading. */
+               mb();
+               /* Read from that function's config space. */
+               ret = readw(addr);
+               /*
+                * mb() is not required here, because the
+                * spin_unlock_irqrestore() is a barrier.
+                */
+       }
 
-       spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
+       spin_unlock_irqrestore(&hbus->config_lock, flags);
 
        return ret;
 }
 static void _hv_pcifront_write_config(struct hv_pci_dev *hpdev, int where,
                                      int size, u32 val)
 {
+       struct hv_pcibus_device *hbus = hpdev->hbus;
+       struct device *dev = &hbus->hdev->device;
+       int offset = where + CFG_PAGE_OFFSET;
        unsigned long flags;
-       void __iomem *addr = hpdev->hbus->cfg_addr + CFG_PAGE_OFFSET + where;
 
        if (where >= PCI_SUBSYSTEM_VENDOR_ID &&
            where + size <= PCI_CAPABILITY_LIST) {
                /* SSIDs and ROM BARs are read-only */
        } else if (where >= PCI_COMMAND && where + size <= CFG_PAGE_SIZE) {
-               spin_lock_irqsave(&hpdev->hbus->config_lock, flags);
-               /* Choose the function to be written. (See comment above) */
-               writel(hpdev->desc.win_slot.slot, hpdev->hbus->cfg_addr);
-               /* Make sure the function was chosen before we start writing. */
-               wmb();
-               /* Write to that function's config space. */
-               switch (size) {
-               case 1:
-                       writeb(val, addr);
-                       break;
-               case 2:
-                       writew(val, addr);
-                       break;
-               default:
-                       writel(val, addr);
-                       break;
+               spin_lock_irqsave(&hbus->config_lock, flags);
+
+               if (hbus->use_calls) {
+                       phys_addr_t addr = hbus->mem_config->start + offset;
+
+                       hv_pci_write_mmio(dev, hbus->mem_config->start, 4,
+                                               hpdev->desc.win_slot.slot);
+                       hv_pci_write_mmio(dev, addr, size, val);
+               } else {
+                       void __iomem *addr = hbus->cfg_addr + offset;
+
+                       /* Choose the function to write. (See comment above) */
+                       writel(hpdev->desc.win_slot.slot, hbus->cfg_addr);
+                       /* Make sure the function was chosen before writing. */
+                       wmb();
+                       /* Write to that function's config space. */
+                       switch (size) {
+                       case 1:
+                               writeb(val, addr);
+                               break;
+                       case 2:
+                               writew(val, addr);
+                               break;
+                       default:
+                               writel(val, addr);
+                               break;
+                       }
+                       /*
+                        * Make sure the write was done before we release the
+                        * spinlock allowing consecutive reads/writes.
+                        */
+                       mb();
                }
-               /*
-                * Make sure the write was done before we release the spinlock
-                * allowing consecutive reads/writes.
-                */
-               mb();
-               spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
+               spin_unlock_irqrestore(&hbus->config_lock, flags);
        } else {
-               dev_err(&hpdev->hbus->hdev->device,
-                       "Attempt to write beyond a function's config space.\n");
+               dev_err(dev, "Attempt to write beyond a function's config space.\n");
        }
 }
 
        hbus->bridge->domain_nr = dom;
 #ifdef CONFIG_X86
        hbus->sysdata.domain = dom;
+       hbus->use_calls = !!(ms_hyperv.hints & HV_X64_USE_MMIO_HYPERCALLS);
 #elif defined(CONFIG_ARM64)
        /*
         * Set the PCI bus parent to be the corresponding VMbus
         * information to devices created on the bus.
         */
        hbus->sysdata.parent = hdev->device.parent;
+       hbus->use_calls = false;
 #endif
 
        hbus->hdev = hdev;