|
|
|
|
@@ -514,6 +514,7 @@ struct hv_pcibus_device {
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
@@ -1041,6 +1042,70 @@ static int wslot_to_devfn(u32 wslot)
|
|
|
|
|
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
|
|
|
|
|
@@ -1059,8 +1124,10 @@ static int wslot_to_devfn(u32 wslot)
|
|
|
|
|
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.
|
|
|
|
|
@@ -1088,56 +1155,79 @@ static void _hv_pcifront_read_config(struct hv_pci_dev *hpdev, int where,
|
|
|
|
|
*/
|
|
|
|
|
*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;
|
|
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&hpdev->hbus->config_lock, flags);
|
|
|
|
|
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(&hbus->config_lock, flags);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
@@ -1152,39 +1242,51 @@ static u16 hv_pcifront_get_vendor_id(struct hv_pci_dev *hpdev)
|
|
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -3563,6 +3665,7 @@ static int hv_pci_probe(struct hv_device *hdev,
|
|
|
|
|
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
|
|
|
|
|
@@ -3572,6 +3675,7 @@ static int hv_pci_probe(struct hv_device *hdev,
|
|
|
|
|
* information to devices created on the bus.
|
|
|
|
|
*/
|
|
|
|
|
hbus->sysdata.parent = hdev->device.parent;
|
|
|
|
|
hbus->use_calls = false;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
hbus->hdev = hdev;
|
|
|
|
|
|