diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 42380c3976..15b02cbd98 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -725,6 +725,16 @@ static int xen_pt_initfn(PCIDevice *d) s->memory_listener = xen_pt_memory_listener; s->io_listener = xen_pt_io_listener; + /* Setup VGA bios for passthrough GFX */ + if ((s->real_device.domain == 0) && (s->real_device.bus == 0) && + (s->real_device.dev == 2) && (s->real_device.func == 0)) { + if (xen_pt_setup_vga(s, &s->real_device) < 0) { + XEN_PT_ERR(d, "Setup VGA BIOS of passthrough GFX failed!\n"); + xen_host_pci_device_put(&s->real_device); + return -1; + } + } + /* Handle real device's MMIO/PIO BARs */ xen_pt_register_regions(s, &cmd); diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index 5eb3c52546..a33e95ceb2 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -305,6 +305,11 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar) return s->msix && s->msix->bar_index == bar; } +extern void *pci_assign_dev_load_option_rom(PCIDevice *dev, + struct Object *owner, int *size, + unsigned int domain, + unsigned int bus, unsigned int slot, + unsigned int function); extern bool has_igd_gfx_passthru; static inline bool is_igd_vga_passthrough(XenHostPCIDevice *dev) { diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c index 9b3df811b1..3232296bf8 100644 --- a/hw/xen/xen_pt_graphics.c +++ b/hw/xen/xen_pt_graphics.c @@ -109,3 +109,82 @@ int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev) return 0; } + +static void *get_vgabios(XenPCIPassthroughState *s, int *size, + XenHostPCIDevice *dev) +{ + return pci_assign_dev_load_option_rom(&s->dev, OBJECT(&s->dev), size, + dev->domain, dev->bus, + dev->dev, dev->func); +} + +/* Refer to Seabios. */ +struct rom_header { + uint16_t signature; + uint8_t size; + uint8_t initVector[4]; + uint8_t reserved[17]; + uint16_t pcioffset; + uint16_t pnpoffset; +} __attribute__((packed)); + +struct pci_data { + uint32_t signature; + uint16_t vendor; + uint16_t device; + uint16_t vitaldata; + uint16_t dlen; + uint8_t drevision; + uint8_t class_lo; + uint16_t class_hi; + uint16_t ilen; + uint16_t irevision; + uint8_t type; + uint8_t indicator; + uint16_t reserved; +} __attribute__((packed)); + +int xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev) +{ + unsigned char *bios = NULL; + struct rom_header *rom; + int bios_size; + char *c = NULL; + char checksum = 0; + uint32_t len = 0; + struct pci_data *pd = NULL; + + if (!is_igd_vga_passthrough(dev)) { + return -1; + } + + bios = get_vgabios(s, &bios_size, dev); + if (!bios) { + XEN_PT_ERR(&s->dev, "VGA: Can't getting VBIOS!\n"); + return -1; + } + + /* Currently we fixed this address as a primary. */ + rom = (struct rom_header *)bios; + pd = (void *)(bios + (unsigned char)rom->pcioffset); + + /* We may need to fixup Device Identification. */ + if (pd->device != s->real_device.device_id) { + pd->device = s->real_device.device_id; + + len = rom->size * 512; + /* Then adjust the bios checksum */ + for (c = (char *)bios; c < ((char *)bios + len); c++) { + checksum += *c; + } + if (checksum) { + bios[len - 1] -= checksum; + XEN_PT_LOG(&s->dev, "vga bios checksum is adjusted %x!\n", + checksum); + } + } + + /* Currently we fixed this address as a primary for legacy BIOS. */ + cpu_physical_memory_rw(0xc0000, bios, bios_size, 1); + return 0; +}