<div dir="ltr">hw/misc/vfio.c<div><br></div><div>this patch adds:</div><div>* memory map intel opregion</div><div>* mirroring of bdsm to guest's device 0 not hosts.</div><div><br></div><div>patch</div><div>---------------------</div><div><br></div><div><div>diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c</div><div>index e88b610..54e549b 100644</div><div>--- a/hw/misc/vfio.c</div><div>+++ b/hw/misc/vfio.c</div><div>@@ -5,6 +5,7 @@</div><div>  *</div><div>  * Authors:</div><div>  *  Alex Williamson <<a href="mailto:alex.williamson@redhat.com">alex.williamson@redhat.com</a>></div><div>+ *  Andrew Barnes <<a href="mailto:andy@outsideglobe.com">andy@outsideglobe.com</a>> IGD Support</div><div>  *</div><div>  * This work is licensed under the terms of the GNU GPL, version 2.  See</div><div>  * the COPYING file in the top-level directory.</div><div>@@ -56,6 +57,45 @@</div><div> #define VFIO_ALLOW_KVM_MSI 1</div><div> #define VFIO_ALLOW_KVM_MSIX 1</div><div> </div><div>+/* A handy list of IGD device ID's */</div><div>+#define IS_IGD_HASWELL(id)            (id == 0x0402 \</div><div>+                                        || id == 0x0406 \</div><div>+                                        || id == 0x040a \</div><div>+                                        || id == 0x0412 \</div><div>+                                        || id == 0x0416 \</div><div>+                                        || id == 0x041a \</div><div>+                                        || id == 0x0a04 \</div><div>+                                        || id == 0x0a16 \</div><div>+                                        || id == 0x0a22 \</div><div>+                                        || id == 0x0a26 \</div><div>+                                        || id == 0x0a2a )</div><div>+#define IS_IGD_IVYBRIDGE(id)          (id == 0x0162 \</div><div>+                                        || id == 0x0166 \</div><div>+                                        || id == 0x016a \</div><div>+                                        || id == 0x0152 \</div><div>+                                        || id == 0x0156 \</div><div>+                                        || id == 0x015a )</div><div>+#define IS_IGD_SANDYBRIDGE(id)        (id == 0x0102 \</div><div>+                                        || id == 0x0106 \</div><div>+                                        || id == 0x0112 \</div><div>+                                        || id == 0x0116 \</div><div>+                                        || id == 0x0122 \</div><div>+                                        || id == 0x0126 \</div><div>+                                        || id ==0x010a )</div><div>+#define IS_IGD_IRONLAKE_CLARKDALE(id) (id == 0x0042 )</div><div>+#define IS_IGD_IRONLAKE_ARRANDALE(id) (id == 0x0046 )</div><div>+#define IS_IGD(id)                    (IS_IGD_IRONLAKE_CLARKDALE(id) \</div><div>+                                        || IS_IGD_IRONLAKE_ARRANDALE(id) \</div><div>+                                        || IS_IGD_SANDYBRIDGE(id) \</div><div>+                                        || IS_IGD_IVYBRIDGE(id) \</div><div>+                                        || IS_IGD_HASWELL(id) )</div><div>+#define IGD_BAR_MASK                  0xFFFFFFFFFFFF0000</div><div>+#define DMAR_OPERATION_TIMEOUT        ((s_time_t)((_ms) * 1000000ULL))</div><div>+</div><div>+#define PCI_CONFIG_INTEL_OPREGION       0xfc</div><div>+#define INTEL_OPREGION_PAGES            3</div><div>+#define INTEL_OPREGION_SIZE             INTEL_OPREGION_PAGES * TARGET_PAGE_SIZE</div><div>+</div><div> struct VFIODevice;</div><div> </div><div> typedef struct VFIOQuirk {</div><div>@@ -227,6 +267,8 @@ typedef struct VFIODevice {</div><div>     bool has_pm_reset;</div><div>     bool needs_reset;</div><div>     bool rom_read_failed;</div><div>+    MemoryRegion opregion; /* Intel opregion */</div><div>+    uint32_t host_opregion; /* Host address of opregion */</div><div> } VFIODevice;</div><div> </div><div> typedef struct VFIOGroup {</div><div>@@ -283,6 +325,18 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,</div><div>                                   uint32_t val, int len);</div><div> static void vfio_mmap_set_enabled(VFIODevice *vdev, bool enabled);</div><div> </div><div>+static void vfio_set_word_bits(uint8_t *buf, uint16_t val, uint16_t mask);</div><div>+static void vfio_add_emulated_word(VFIODevice *vdev, int pos,</div><div>+                                   uint16_t val, uint16_t mask);</div><div>+static void vfio_set_long_bits(uint8_t *buf, uint32_t val, uint32_t mask);</div><div>+static void vfio_add_emulated_long(VFIODevice *vdev, int pos,</div><div>+                                   uint32_t val, uint32_t mask);</div><div>+static void vfio_add_emulated_rw_long(VFIODevice *vdev, int pos,</div><div>+                                   uint32_t val, uint32_t mask);</div><div>+static void vfio_map_igdopregion(VFIODevice *vdev, uint32_t guest_opregion);</div><div>+</div><div>+static VFIODevice *igdvfio;</div><div>+</div><div> /*</div><div>  * Common VFIO interrupt disable</div><div>  */</div><div>@@ -2324,27 +2378,46 @@ static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len)</div><div>     VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);</div><div>     uint32_t emu_bits = 0, emu_val = 0, phys_val = 0, val;</div><div> </div><div>-    memcpy(&emu_bits, vdev->emulated_config_bits + addr, len);</div><div>-    emu_bits = le32_to_cpu(emu_bits);</div><div>-</div><div>-    if (emu_bits) {</div><div>-        emu_val = pci_default_read_config(pdev, addr, len);</div><div>+    /* BDSM mirror - BDSM can be read at either 0xb0 device 0, or 0x5c device 2.</div><div>+     * Redirect this mirror from host 0xb0 device 0 to guest 0xb0 device 0.*/</div><div>+    if (IS_IGD(pci_get_word(pdev->config + PCI_DEVICE_ID)) && ranges_overlap(addr,len,0x5c,4))</div><div>+    {</div><div>+        DPRINTF("%s Read Trapped (%04x:%02x:%02x.%x, @0x%x, 0x%x, len=0x%x)\n", __func__,</div><div>+                    vdev->host.domain, vdev->host.bus, vdev->host.slot,</div><div>+                                vdev->host.function, addr, val, len);</div><div>+        PCIBus *root = pci_find_primary_bus();</div><div>+        PCIDevice *q35 = pci_find_device(root,0,PCI_DEVFN(0, 0));</div><div>+        val = pci_default_read_config(q35, 0xb0, len);</div><div>     }</div><div> </div><div>-    if (~emu_bits & (0xffffffffU >> (32 - len * 8))) {</div><div>-        ssize_t ret;</div><div>+    else</div><div>+    {</div><div>+        memcpy(&emu_bits, vdev->emulated_config_bits + addr, len);</div><div>+        emu_bits = le32_to_cpu(emu_bits);</div><div>+</div><div>+        if (emu_bits) {</div><div>+            emu_val = pci_default_read_config(pdev, addr, len);</div><div>+            DPRINTF("%s emulated read: %x \n",__func__,emu_val);</div><div> </div><div>-        ret = pread(vdev->fd, &phys_val, len, vdev->config_offset + addr);</div><div>-        if (ret != len) {</div><div>-            error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x) failed: %m",</div><div>-                         __func__, vdev->host.domain, vdev->host.bus,</div><div>-                         vdev->host.slot, vdev->host.function, addr, len);</div><div>-            return -errno;</div><div>         }</div><div>-        phys_val = le32_to_cpu(phys_val);</div><div>-    }</div><div> </div><div>-    val = (emu_val & emu_bits) | (phys_val & ~emu_bits);</div><div>+        if (~emu_bits & (0xffffffffU >> (32 - len * 8))) {</div><div>+            ssize_t ret;</div><div>+</div><div>+            ret = pread(vdev->fd, &phys_val, len, vdev->config_offset + addr);</div><div>+            if (ret != len) {</div><div>+                error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x) failed: %m",</div><div>+                             __func__, vdev->host.domain, vdev->host.bus,</div><div>+                             vdev->host.slot, vdev->host.function, addr, len);</div><div>+                return -errno;</div><div>+            }</div><div>+            phys_val = le32_to_cpu(phys_val);</div><div>+            DPRINTF("%s direct read: %x \n",__func__,phys_val);</div><div>+</div><div>+        }</div><div>+</div><div>+        val = (emu_val & emu_bits) | (phys_val & ~emu_bits);</div><div>+    }</div><div> </div><div>     DPRINTF("%s(%04x:%02x:%02x.%x, @0x%x, len=0x%x) %x\n", __func__,</div><div>             vdev->host.domain, vdev->host.bus, vdev->host.slot,</div><div>@@ -2363,12 +2436,30 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,</div><div>             vdev->host.domain, vdev->host.bus, vdev->host.slot,</div><div>             vdev->host.function, addr, val, len);</div><div> </div><div>+    /* A write to OPREGION base address means that seabios has allocated a new memory region for OPREGION</div><div>+     * in the guest. */</div><div>+    if (IS_IGD(pci_get_word(pdev->config + PCI_DEVICE_ID)) && ranges_overlap(addr,len,PCI_CONFIG_INTEL_OPREGION,4))</div><div>+    {</div><div>+        DPRINTF("%s Write Trapped (%04x:%02x:%02x.%x, @0x%x, 0x%x, len=0x%x)\n", __func__,</div><div>+                    vdev->host.domain, vdev->host.bus, vdev->host.slot,</div><div>+                                vdev->host.function, addr, val, len);</div><div>+        //val = (val & 0xfffff000) | (vdev->host_opregion & 0xfff);</div><div>+        vfio_map_igdopregion(vdev,val);</div><div>+        goto defaultwrite;</div><div>+    }</div><div>+</div><div>     /* Write everything to VFIO, let it filter out what we can't write */</div><div>-    if (pwrite(vdev->fd, &val_le, len, vdev->config_offset + addr) != len) {</div><div>+    else if (pwrite(vdev->fd, &val_le, len, vdev->config_offset + addr) != len) {</div><div>         error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x, 0x%x) failed: %m",</div><div>                      __func__, vdev->host.domain, vdev->host.bus,</div><div>                      vdev->host.slot, vdev->host.function, addr, val, len);</div><div>     }</div><div>+    else</div><div>+    {</div><div>+        DPRINTF("%s Written to VFIO (%04x:%02x:%02x.%x, @0x%x, 0x%x, len=0x%x)\n", __func__,</div><div>+                  vdev->host.domain, vdev->host.bus, vdev->host.slot,</div><div>+                              vdev->host.function, addr, val, len);</div><div>+    }</div><div> </div><div>     /* MSI/MSI-X Enabling/Disabling */</div><div>     if (pdev->cap_present & QEMU_PCI_CAP_MSI &&</div><div>@@ -2405,7 +2496,11 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,</div><div>         }</div><div>     } else {</div><div>         /* Write everything to QEMU to keep emulated bits correct */</div><div>-        pci_default_write_config(pdev, addr, val, len);</div><div>+defaultwrite:</div><div>+       pci_default_write_config(pdev, addr, val, len);</div><div>+       DPRINTF("%s Default Write (%04x:%02x:%02x.%x, @0x%x, 0x%x, len=0x%x)\n", __func__,</div><div>+                   vdev->host.domain, vdev->host.bus, vdev->host.slot,</div><div>+                               vdev->host.function, addr, val, len);</div><div>     }</div><div> }</div><div> </div><div>@@ -3065,7 +3160,7 @@ static void vfio_set_word_bits(uint8_t *buf, uint16_t val, uint16_t mask)</div><div> {</div><div>     pci_set_word(buf, (pci_get_word(buf) & ~mask) | val);</div><div> }</div><div>-</div><div>+/* helper functions make read-only emulated registers! */</div><div> static void vfio_add_emulated_word(VFIODevice *vdev, int pos,</div><div>                                    uint16_t val, uint16_t mask)</div><div> {</div><div>@@ -3087,6 +3182,62 @@ static void vfio_add_emulated_long(VFIODevice *vdev, int pos,</div><div>     vfio_set_long_bits(vdev->emulated_config_bits + pos, mask, mask);</div><div> }</div><div> </div><div>+static void vfio_add_emulated_rw_long(VFIODevice *vdev, int pos,</div><div>+                                   uint32_t val, uint32_t mask)</div><div>+{</div><div>+    vfio_set_long_bits(vdev->pdev.config + pos, val, mask);</div><div>+    vfio_set_long_bits(vdev->pdev.wmask + pos, mask, mask);</div><div>+    vfio_set_long_bits(vdev->emulated_config_bits + pos, mask, mask);</div><div>+}</div><div>+</div><div>+/* Setup the mapping of the opregion ready for Seabios to allocate the guest location */</div><div>+static void vfio_setup_igdopregion(VFIODevice *vdev)</div><div>+{</div><div>+    PCIDevice *pdev = &vdev->pdev;</div><div>+    int fd;</div><div>+    char name[64];</div><div>+    void *map;</div><div>+</div><div>+    vdev->host_opregion = vfio_pci_read_config(pdev,PCI_CONFIG_INTEL_OPREGION,4);</div><div>+</div><div>+    DPRINTF("%s Setup IGD OpRegion: %x\n",__func__,vdev->host_opregion);</div><div>+</div><div>+    snprintf(name, sizeof(name), "VFIO %04x:%02x:%02x.%x IGDOPREGION mmap",</div><div>+             vdev->host.domain, vdev->host.bus, vdev->host.slot,</div><div>+             vdev->host.function);</div><div>+</div><div>+    fd = open("/dev/mem", O_RDWR);</div><div>+    map = mmap(NULL,INTEL_OPREGION_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,(vdev->host_opregion & ~0xfff));</div><div>+    if (map == MAP_FAILED)</div><div>+    {</div><div>+        map = NULL;</div><div>+        DPRINTF("%s Map IGD OpRegion: MAP_FAILED\n",__func__);</div><div>+    }</div><div>+    memory_region_init_ram_ptr(&vdev->opregion, OBJECT(vdev), name, INTEL_OPREGION_SIZE, map);</div><div>+}</div><div>+</div><div>+/* Seabios allocates the guest location for the opregion. This function then memmory maps</div><div>+ * host memory at that guest location */</div><div>+static void vfio_map_igdopregion(VFIODevice *vdev, uint32_t new_opregion)</div><div>+{</div><div>+    PCIDevice *pdev = &vdev->pdev;</div><div>+    MemoryRegion *guest_memory = get_system_memory();</div><div>+    uint32_t current_opregion = vfio_pci_read_config(pdev,PCI_CONFIG_INTEL_OPREGION,4);</div><div>+</div><div>+    if ( current_opregion != vdev->host_opregion )</div><div>+    {</div><div>+        // remap</div><div>+        DPRINTF("%s Delete IGD OpRegion: %x\n",__func__,(current_opregion & ~0xfff));</div><div>+        memory_region_del_subregion(guest_memory, &vdev->opregion);</div><div>+    }</div><div>+</div><div>+    DPRINTF("%s Map IGD OpRegion: %x -> %x\n",__func__,vdev->host_opregion,new_opregion);</div><div>+    memory_region_add_subregion(guest_memory, (new_opregion & ~0xfff), &vdev->opregion);</div><div>+</div><div>+    DPRINTF("%s Adding 0xfc to emulated bits\n", __func__);</div><div>+    vfio_add_emulated_rw_long(vdev, PCI_CONFIG_INTEL_OPREGION, new_opregion, 0xffffffff);</div><div>+}</div><div>+</div><div> static int vfio_setup_pcie_cap(VFIODevice *vdev, int pos, uint8_t size)</div><div> {</div><div>     uint16_t flags;</div><div>@@ -4028,7 +4179,8 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev)</div><div>     ret = ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info);</div><div>     if (ret) {</div><div>         /* This can fail for an old kernel or legacy PCI dev */</div><div>-        DPRINTF("VFIO_DEVICE_GET_IRQ_INFO failure: %m\n");</div><div>+        //DPRINTF("VFIO_DEVICE_GET_IRQ_INFO failure: %m\n");</div><div>+        DPRINTF("VFIO_DEVICE_GET_IRQ_INFO failure ret=%d\n", ret);</div><div>         ret = 0;</div><div>     } else if (irq_info.count == 1) {</div><div>         vdev->pci_aer = true;</div><div>@@ -4253,6 +4405,11 @@ static int vfio_initfn(PCIDevice *pdev)</div><div>     vdev->emulated_config_bits[PCI_HEADER_TYPE] =</div><div>                                               PCI_HEADER_TYPE_MULTI_FUNCTION;</div><div> </div><div>+    if (IS_IGD(pci_get_word(pdev->config + PCI_DEVICE_ID)))</div><div>+    {</div><div>+        vfio_setup_igdopregion(vdev);</div><div>+    }</div><div>+</div><div>     /* Restore or clear multifunction, this is always controlled by QEMU */</div><div>     if (vdev->pdev.cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {</div><div>         vdev->pdev.config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION;</div></div></div>