@@ -32,6 +32,16 @@ static int vpci_mmio_read(struct vcpu *v, mmio_info_t *info,
/* data is needed to prevent a pointer cast on 32bit */
unsigned long data;
+ /*
+ * For the passed through devices we need to map their virtual SBDF
+ * to the physical PCI device being passed through.
+ */
+ if ( !bridge && !vpci_translate_virtual_device(v->domain, &sbdf) )
+ {
+ *r = ~0ul;
+ return 1;
+ }
+
if ( vpci_ecam_read(sbdf, ECAM_REG_OFFSET(info->gpa),
1U << info->dabt.size, &data) )
{
@@ -50,6 +60,13 @@ static int vpci_mmio_write(struct vcpu *v, mmio_info_t *info,
struct pci_host_bridge *bridge = p;
pci_sbdf_t sbdf = vpci_sbdf_from_gpa(bridge, info->gpa);
+ /*
+ * For the passed through devices we need to map their virtual SBDF
+ * to the physical PCI device being passed through.
+ */
+ if ( !bridge && !vpci_translate_virtual_device(v->domain, &sbdf) )
+ return 1;
+
return vpci_ecam_write(sbdf, ECAM_REG_OFFSET(info->gpa),
1U << info->dabt.size, r);
}
@@ -180,6 +180,44 @@ static void vpci_remove_virtual_device(const struct pci_dev *pdev)
write_unlock(&pdev->domain->vpci_rwlock);
}
+/*
+ * Find the physical device which is mapped to the virtual device
+ * and translate virtual SBDF to the physical one.
+ */
+bool vpci_translate_virtual_device(struct domain *d, pci_sbdf_t *sbdf)
+{
+ struct pci_dev *pdev;
+
+ ASSERT(!is_hardware_domain(d));
+
+ read_lock(&d->vpci_rwlock);
+ pcidevs_lock();
+ for_each_pdev( d, pdev )
+ {
+ bool found;
+
+ if ( !pdev->vpci )
+ continue;
+
+ spin_lock(&pdev->vpci->lock);
+ found = pdev->vpci && (pdev->vpci->guest_sbdf.sbdf == sbdf->sbdf);
+ spin_unlock(&pdev->vpci->lock);
+
+ if ( found )
+ {
+ /* Replace guest SBDF with the physical one. */
+ *sbdf = pdev->sbdf;
+ pcidevs_unlock();
+ read_unlock(&d->vpci_rwlock);
+ return true;
+ }
+ }
+
+ pcidevs_unlock();
+ read_unlock(&d->vpci_rwlock);
+ return false;
+}
+
/* Notify vPCI that device is assigned to guest. */
int vpci_assign_device(struct pci_dev *pdev)
{
@@ -291,6 +291,7 @@ static inline bool __must_check vpci_process_pending(struct vcpu *v)
/* Notify vPCI that device is assigned/de-assigned to/from guest. */
int vpci_assign_device(struct pci_dev *pdev);
void vpci_deassign_device(struct pci_dev *pdev);
+bool vpci_translate_virtual_device(struct domain *d, pci_sbdf_t *sbdf);
#else
static inline int vpci_assign_device(struct pci_dev *pdev)
{
@@ -300,6 +301,12 @@ static inline int vpci_assign_device(struct pci_dev *pdev)
static inline void vpci_deassign_device(struct pci_dev *pdev)
{
};
+
+static inline bool vpci_translate_virtual_device(struct domain *d,
+ pci_sbdf_t *sbdf)
+{
+ return false;
+}
#endif
#endif