Message ID | 1403662641-28526-2-git-send-email-tiejun.chen@intel.com |
---|---|
State | New |
Headers | show |
Il 25/06/2014 04:17, Tiejun Chen ha scritto: > +static int get_vgabios(unsigned char *buf, XenHostPCIDevice *dev) > +{ > + char rom_file[64]; > + FILE *fp; > + uint8_t val; > + struct stat st; > + uint16_t magic = 0; > + int ret = 0; > + > + snprintf(rom_file, sizeof(rom_file), > + "/sys/bus/pci/devices/%04x:%02x:%02x.%d/rom", > + dev->domain, dev->bus, dev->dev, > + dev->func); > + > + if (stat(rom_file, &st)) { > + return -ENODEV; > + } > + > + if (access(rom_file, F_OK)) { > + XEN_PT_ERR(NULL, "pci-assign: Insufficient privileges for %s", > + rom_file); > + return -ENODEV; > + } These two checks (stat and access) are useless. > + /* Write "1" to the ROM file to enable it */ > + fp = fopen(rom_file, "r+"); > + if (fp == NULL) { > + return -EACCES; > + } > + val = 1; > + if (fwrite(&val, 1, 1, fp) != 1) { > + XEN_PT_LOG("%s\n", "Failed to enable pci-sysfs rom file"); > + ret = -EIO; > + goto close_rom; > + } > + fseek(fp, 0, SEEK_SET); > + > + /* > + * Check if it a real bios extension. > + * The magic number is 0xAA55. > + */ > + if (!fread(&magic, sizeof(magic), 1, fp)) { > + XEN_PT_ERR(NULL, "VGA: can't get magic.\n"); > + ret = -ENODEV; > + goto close_rom; > + } > + if (magic != 0xAA55) { > + XEN_PT_ERR(NULL, "VGA: wrong magic %x.\n", magic); > + ret = -ENODEV; > + goto close_rom; > + } > + fseek(fp, 0, SEEK_SET); > + > + if (!fread(buf, 1, st.st_size, fp)) { > + XEN_PT_ERR(NULL, "VGA: pci-assign: Cannot read from host %s", rom_file); > + XEN_PT_LOG(NULL, "VGA: Device option ROM contents are probably invalid " > + "(check dmesg).\nSkip option ROM probe with rombar=0, " > + "or load from file with romfile=\n"); > + } > + Why is this graphics passthrough specific? Should QEMU's Xen PCI passthrough read ROM BAR contents from network cards too, for example? Paolo
On Wed, Jun 25, 2014 at 10:17:17AM +0800, Tiejun Chen wrote: > basic gfx passthrough support: > - add a vga type for gfx passthrough > - retrieve VGA bios from sysfs, then load it to guest at 0xC0000 > - register/unregister legacy VGA I/O ports and MMIOs for passthrough GFX > > The original patch is from Weidong Han <weidong.han@intel.com> > > Signed-off-by: Yang Zhang <yang.z.zhang@Intel.com> > Signed-off-by: Tiejun Chen <tiejun.chen@intel.com> > Cc: Weidong Han <weidong.han@intel.com> ROM loading code is duplicated from assigned_dev_load_option_rom. Would it be a problem for you to create a memory region holding the ROM? You won't need cpu_physical_memory_rw then, either, or need the VGA_BIOS_DEFAULT_SIZE hack. > --- > v5: > > * Just rebase. > > v4: > > * Fix some typos in the patch head description. > * Improve some comments. > * Given that xen_pt_register_vga_regions()/xen_pt_unregister_vga_regions() > are called unconditionally, so we just return 0 there. > * Remove one spurious change. > > v3: > > * Fix some typos. > * Add more comments to make that readable. > * Improve some return paths. > > v2: > > * retrieve VGA bios from sysfs properly. > * redefine some function name. > > hw/xen/Makefile.objs | 2 +- > hw/xen/xen-host-pci-device.c | 5 + > hw/xen/xen-host-pci-device.h | 1 + > hw/xen/xen_pt.c | 10 ++ > hw/xen/xen_pt.h | 4 + > hw/xen/xen_pt_graphics.c | 232 +++++++++++++++++++++++++++++++++++++++++++ > qemu-options.hx | 9 ++ > vl.c | 10 ++ > 8 files changed, 272 insertions(+), 1 deletion(-) > create mode 100644 hw/xen/xen_pt_graphics.c > > diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs > index a0ca0aa..77b7dae 100644 > --- a/hw/xen/Makefile.objs > +++ b/hw/xen/Makefile.objs > @@ -2,4 +2,4 @@ > common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o > > obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o > -obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o > +obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o xen_pt_graphics.o > diff --git a/hw/xen/xen-host-pci-device.c b/hw/xen/xen-host-pci-device.c > index 743b37b..a54b7de 100644 > --- a/hw/xen/xen-host-pci-device.c > +++ b/hw/xen/xen-host-pci-device.c > @@ -376,6 +376,11 @@ int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, > goto error; > } > d->irq = v; > + rc = xen_host_pci_get_hex_value(d, "class", &v); > + if (rc) { > + goto error; > + } > + d->class_code = v; > d->is_virtfn = xen_host_pci_dev_is_virtfn(d); > > return 0; > diff --git a/hw/xen/xen-host-pci-device.h b/hw/xen/xen-host-pci-device.h > index c2486f0..f1e1c30 100644 > --- a/hw/xen/xen-host-pci-device.h > +++ b/hw/xen/xen-host-pci-device.h > @@ -25,6 +25,7 @@ typedef struct XenHostPCIDevice { > > uint16_t vendor_id; > uint16_t device_id; > + uint32_t class_code; > int irq; > > XenHostPCIIORegion io_regions[PCI_NUM_REGIONS - 1]; > diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c > index be4220b..dac4238 100644 > --- a/hw/xen/xen_pt.c > +++ b/hw/xen/xen_pt.c > @@ -450,6 +450,7 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s) > d->rom.size, d->rom.base_addr); > } > > + xen_pt_register_vga_regions(d); > return 0; > } > > @@ -470,6 +471,8 @@ static void xen_pt_unregister_regions(XenPCIPassthroughState *s) > if (d->rom.base_addr && d->rom.size) { > memory_region_destroy(&s->rom); > } > + > + xen_pt_unregister_vga_regions(d); > } > > /* region mapping */ > @@ -693,6 +696,13 @@ static int xen_pt_initfn(PCIDevice *d) > /* Handle real device's MMIO/PIO BARs */ > xen_pt_register_regions(s); > > + /* Setup VGA bios for passthrough GFX */ > + if (xen_pt_setup_vga(&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; > + } > + > /* reinitialize each config register to be emulated */ > if (xen_pt_config_init(s)) { > XEN_PT_ERR(d, "PCI Config space initialisation failed.\n"); > diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h > index 942dc60..4d3a18d 100644 > --- a/hw/xen/xen_pt.h > +++ b/hw/xen/xen_pt.h > @@ -298,5 +298,9 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar) > return s->msix && s->msix->bar_index == bar; > } > > +extern int xen_has_gfx_passthru; > +int xen_pt_register_vga_regions(XenHostPCIDevice *dev); > +int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev); > +int xen_pt_setup_vga(XenHostPCIDevice *dev); > > #endif /* !XEN_PT_H */ > diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c > new file mode 100644 > index 0000000..461e526 > --- /dev/null > +++ b/hw/xen/xen_pt_graphics.c > @@ -0,0 +1,232 @@ > +/* > + * graphics passthrough > + */ > +#include "xen_pt.h" > +#include "xen-host-pci-device.h" > +#include "hw/xen/xen_backend.h" > + > +static int is_vga_passthrough(XenHostPCIDevice *dev) > +{ > + return (xen_has_gfx_passthru > + && ((dev->class_code >> 0x8) == PCI_CLASS_DISPLAY_VGA)); > +} > + > +typedef struct VGARegion { > + int type; /* Memory or port I/O */ > + uint64_t guest_base_addr; > + uint64_t machine_base_addr; > + uint64_t size; /* size of the region */ > + int rc; > +} VGARegion; > + > +#define IORESOURCE_IO 0x00000100 > +#define IORESOURCE_MEM 0x00000200 > + > +static struct VGARegion vga_args[] = { > + { > + .type = IORESOURCE_IO, > + .guest_base_addr = 0x3B0, > + .machine_base_addr = 0x3B0, > + .size = 0xC, > + .rc = -1, > + }, > + { > + .type = IORESOURCE_IO, > + .guest_base_addr = 0x3C0, > + .machine_base_addr = 0x3C0, > + .size = 0x20, > + .rc = -1, > + }, > + { > + .type = IORESOURCE_MEM, > + .guest_base_addr = 0xa0000 >> XC_PAGE_SHIFT, > + .machine_base_addr = 0xa0000 >> XC_PAGE_SHIFT, > + .size = 0x20, > + .rc = -1, > + }, > +}; > + > +/* > + * register VGA resources for the domain with assigned gfx > + */ > +int xen_pt_register_vga_regions(XenHostPCIDevice *dev) > +{ > + int i = 0; > + > + if (!is_vga_passthrough(dev)) { > + return 0; > + } > + > + for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) { > + if (vga_args[i].type == IORESOURCE_IO) { > + vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid, > + vga_args[i].guest_base_addr, > + vga_args[i].machine_base_addr, > + vga_args[i].size, DPCI_ADD_MAPPING); > + } else { > + vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid, > + vga_args[i].guest_base_addr, > + vga_args[i].machine_base_addr, > + vga_args[i].size, DPCI_ADD_MAPPING); > + } > + > + if (vga_args[i].rc) { > + XEN_PT_ERR(NULL, "VGA %s mapping failed! (rc: %i)\n", > + vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory", > + vga_args[i].rc); > + return vga_args[i].rc; > + } > + } > + > + return 0; > +} > + > +/* > + * unregister VGA resources for the domain with assigned gfx > + */ > +int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev) > +{ > + int i = 0; > + > + if (!is_vga_passthrough(dev)) { > + return 0; > + } > + > + for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) { > + if (vga_args[i].type == IORESOURCE_IO) { > + vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid, > + vga_args[i].guest_base_addr, > + vga_args[i].machine_base_addr, > + vga_args[i].size, DPCI_REMOVE_MAPPING); > + } else { > + vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid, > + vga_args[i].guest_base_addr, > + vga_args[i].machine_base_addr, > + vga_args[i].size, DPCI_REMOVE_MAPPING); > + } > + > + if (vga_args[i].rc) { > + XEN_PT_ERR(NULL, "VGA %s unmapping failed! (rc: %i)\n", > + vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory", > + vga_args[i].rc); > + return vga_args[i].rc; > + } > + } > + > + return 0; > +} > + > +static int get_vgabios(unsigned char *buf, XenHostPCIDevice *dev) > +{ > + char rom_file[64]; > + FILE *fp; > + uint8_t val; > + struct stat st; > + uint16_t magic = 0; > + int ret = 0; > + > + snprintf(rom_file, sizeof(rom_file), > + "/sys/bus/pci/devices/%04x:%02x:%02x.%d/rom", > + dev->domain, dev->bus, dev->dev, > + dev->func); > + > + if (stat(rom_file, &st)) { > + return -ENODEV; > + } > + > + if (access(rom_file, F_OK)) { > + XEN_PT_ERR(NULL, "pci-assign: Insufficient privileges for %s", > + rom_file); > + return -ENODEV; > + } > + > + /* Write "1" to the ROM file to enable it */ > + fp = fopen(rom_file, "r+"); > + if (fp == NULL) { > + return -EACCES; > + } > + val = 1; > + if (fwrite(&val, 1, 1, fp) != 1) { > + XEN_PT_LOG("%s\n", "Failed to enable pci-sysfs rom file"); > + ret = -EIO; > + goto close_rom; > + } > + fseek(fp, 0, SEEK_SET); > + > + /* > + * Check if it a real bios extension. > + * The magic number is 0xAA55. > + */ > + if (!fread(&magic, sizeof(magic), 1, fp)) { > + XEN_PT_ERR(NULL, "VGA: can't get magic.\n"); > + ret = -ENODEV; > + goto close_rom; > + } > + if (magic != 0xAA55) { > + XEN_PT_ERR(NULL, "VGA: wrong magic %x.\n", magic); > + ret = -ENODEV; > + goto close_rom; > + } > + fseek(fp, 0, SEEK_SET); > + > + if (!fread(buf, 1, st.st_size, fp)) { > + XEN_PT_ERR(NULL, "VGA: pci-assign: Cannot read from host %s", rom_file); > + XEN_PT_LOG(NULL, "VGA: Device option ROM contents are probably invalid " > + "(check dmesg).\nSkip option ROM probe with rombar=0, " > + "or load from file with romfile=\n"); > + } > + > +close_rom: > + /* Write "0" to disable ROM */ > + fseek(fp, 0, SEEK_SET); > + val = 0; > + if (!fwrite(&val, 1, 1, fp)) { > + ret = -EIO; > + XEN_PT_LOG("%s\n", "Failed to disable pci-sysfs rom file"); > + } > + fclose(fp); > + return ret; > +} > + > +/* Allocated 128K for the vga bios */ > +#define VGA_BIOS_DEFAULT_SIZE (0x20000) > + > +int xen_pt_setup_vga(XenHostPCIDevice *dev) > +{ > + unsigned char *bios = NULL; > + int bios_size = 0; > + char *c = NULL; > + char checksum = 0; > + int rc = 0; > + > + if (!is_vga_passthrough(dev)) { > + return rc; > + } > + > + bios = g_malloc0(VGA_BIOS_DEFAULT_SIZE); > + > + bios_size = get_vgabios(bios, dev); > + if (bios_size) { > + XEN_PT_ERR(NULL, "VGA: Error %d getting VBIOS!\n", bios_size); > + if (bios_size < 0) { > + XEN_PT_ERR(NULL, "VGA: Error (%s).\n", strerror(bios_size)); > + } > + rc = -1; > + goto out; > + } > + > + /* Adjust the bios checksum */ It's easy to see that you do this here, but *why* do you do it? That's what the comment should explain. Do you see many ROMs with an invalid checksum in the field? > + for (c = (char *)bios; c < ((char *)bios + bios_size); c++) { > + checksum += *c; > + } > + if (checksum) { > + bios[bios_size - 1] -= checksum; > + XEN_PT_LOG(NULL, "vga bios checksum is adjusted!\n"); > + } > + > + /* Currently we fixed this address as a primary. */ > + cpu_physical_memory_rw(0xc0000, bios, bios_size, 1); > +out: > + g_free(bios); > + return rc; > +} > diff --git a/qemu-options.hx b/qemu-options.hx > index ff76ad4..1c77baa 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -1066,6 +1066,15 @@ STEXI > Rotate graphical output some deg left (only PXA LCD). > ETEXI > > +DEF("gfx_passthru", 0, QEMU_OPTION_gfx_passthru, > + "-gfx_passthru enable Intel IGD passthrough by XEN\n", > + QEMU_ARCH_ALL) > +STEXI > +@item -gfx_passthru > +@findex -gfx_passthru > +Enable Intel IGD passthrough by XEN > +ETEXI > + > DEF("vga", HAS_ARG, QEMU_OPTION_vga, > "-vga [std|cirrus|vmware|qxl|xenfb|tcx|cg3|none]\n" > " select video card type\n", QEMU_ARCH_ALL) > diff --git a/vl.c b/vl.c > index a1686ef..c5b572d 100644 > --- a/vl.c > +++ b/vl.c > @@ -1418,6 +1418,13 @@ static void configure_msg(QemuOpts *opts) > enable_timestamp_msg = qemu_opt_get_bool(opts, "timestamp", true); > } > > +/* We still need this for compatibility with XEN. */ > +int xen_has_gfx_passthru; > +static void xen_gfx_passthru(const char *optarg) > +{ > + xen_has_gfx_passthru = 1; > +} > + > /***********************************************************/ > /* USB devices */ > > @@ -3945,6 +3952,9 @@ int main(int argc, char **argv, char **envp) > exit(1); > } > break; > + case QEMU_OPTION_gfx_passthru: > + xen_gfx_passthru(optarg); > + break; > default: > os_parse_cmd_args(popt->index, optarg); > } > -- > 1.9.1
On 2014/6/25 14:21, Paolo Bonzini wrote: > Il 25/06/2014 04:17, Tiejun Chen ha scritto: >> +static int get_vgabios(unsigned char *buf, XenHostPCIDevice *dev) >> +{ >> + char rom_file[64]; >> + FILE *fp; >> + uint8_t val; >> + struct stat st; >> + uint16_t magic = 0; >> + int ret = 0; >> + >> + snprintf(rom_file, sizeof(rom_file), >> + "/sys/bus/pci/devices/%04x:%02x:%02x.%d/rom", >> + dev->domain, dev->bus, dev->dev, >> + dev->func); >> + >> + if (stat(rom_file, &st)) { >> + return -ENODEV; >> + } >> + >> + if (access(rom_file, F_OK)) { >> + XEN_PT_ERR(NULL, "pci-assign: Insufficient privileges for %s", >> + rom_file); >> + return -ENODEV; >> + } > > These two checks (stat and access) are useless. Will remove this. > >> + /* Write "1" to the ROM file to enable it */ >> + fp = fopen(rom_file, "r+"); >> + if (fp == NULL) { >> + return -EACCES; >> + } >> + val = 1; >> + if (fwrite(&val, 1, 1, fp) != 1) { >> + XEN_PT_LOG("%s\n", "Failed to enable pci-sysfs rom file"); >> + ret = -EIO; >> + goto close_rom; >> + } >> + fseek(fp, 0, SEEK_SET); >> + >> + /* >> + * Check if it a real bios extension. >> + * The magic number is 0xAA55. >> + */ >> + if (!fread(&magic, sizeof(magic), 1, fp)) { >> + XEN_PT_ERR(NULL, "VGA: can't get magic.\n"); >> + ret = -ENODEV; >> + goto close_rom; >> + } >> + if (magic != 0xAA55) { >> + XEN_PT_ERR(NULL, "VGA: wrong magic %x.\n", magic); >> + ret = -ENODEV; >> + goto close_rom; >> + } >> + fseek(fp, 0, SEEK_SET); >> + >> + if (!fread(buf, 1, st.st_size, fp)) { >> + XEN_PT_ERR(NULL, "VGA: pci-assign: Cannot read from host %s", >> rom_file); >> + XEN_PT_LOG(NULL, "VGA: Device option ROM contents are >> probably invalid " >> + "(check dmesg).\nSkip option ROM probe with >> rombar=0, " >> + "or load from file with romfile=\n"); >> + } >> + > > Why is this graphics passthrough specific? Should QEMU's Xen PCI > passthrough read ROM BAR contents from network cards too, for example? > I think this should not be same simply since we need to call VBIOS from RAM, not a ROM BAR of the device. Thanks Tiejun
On 2014/6/25 14:35, Michael S. Tsirkin wrote: > On Wed, Jun 25, 2014 at 10:17:17AM +0800, Tiejun Chen wrote: >> basic gfx passthrough support: >> - add a vga type for gfx passthrough >> - retrieve VGA bios from sysfs, then load it to guest at 0xC0000 >> - register/unregister legacy VGA I/O ports and MMIOs for passthrough GFX >> >> The original patch is from Weidong Han <weidong.han@intel.com> >> >> Signed-off-by: Yang Zhang <yang.z.zhang@Intel.com> >> Signed-off-by: Tiejun Chen <tiejun.chen@intel.com> >> Cc: Weidong Han <weidong.han@intel.com> > > ROM loading code is duplicated from assigned_dev_load_option_rom. Yes. Do you hint we need to split this? > Would it be a problem for you to create a memory region holding > the ROM? Sorry, could you tell me what should do exactly? > You won't need cpu_physical_memory_rw then, either, or need How to make sure this is fixed at 0xc0000? Thanks Tiejun > the VGA_BIOS_DEFAULT_SIZE hack. > > > > >> --- >> v5: >> >> * Just rebase. >> >> v4: >> >> * Fix some typos in the patch head description. >> * Improve some comments. >> * Given that xen_pt_register_vga_regions()/xen_pt_unregister_vga_regions() >> are called unconditionally, so we just return 0 there. >> * Remove one spurious change. >> >> v3: >> >> * Fix some typos. >> * Add more comments to make that readable. >> * Improve some return paths. >> >> v2: >> >> * retrieve VGA bios from sysfs properly. >> * redefine some function name. >> >> hw/xen/Makefile.objs | 2 +- >> hw/xen/xen-host-pci-device.c | 5 + >> hw/xen/xen-host-pci-device.h | 1 + >> hw/xen/xen_pt.c | 10 ++ >> hw/xen/xen_pt.h | 4 + >> hw/xen/xen_pt_graphics.c | 232 +++++++++++++++++++++++++++++++++++++++++++ >> qemu-options.hx | 9 ++ >> vl.c | 10 ++ >> 8 files changed, 272 insertions(+), 1 deletion(-) >> create mode 100644 hw/xen/xen_pt_graphics.c >> >> diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs >> index a0ca0aa..77b7dae 100644 >> --- a/hw/xen/Makefile.objs >> +++ b/hw/xen/Makefile.objs >> @@ -2,4 +2,4 @@ >> common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o >> >> obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o >> -obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o >> +obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o xen_pt_graphics.o >> diff --git a/hw/xen/xen-host-pci-device.c b/hw/xen/xen-host-pci-device.c >> index 743b37b..a54b7de 100644 >> --- a/hw/xen/xen-host-pci-device.c >> +++ b/hw/xen/xen-host-pci-device.c >> @@ -376,6 +376,11 @@ int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, >> goto error; >> } >> d->irq = v; >> + rc = xen_host_pci_get_hex_value(d, "class", &v); >> + if (rc) { >> + goto error; >> + } >> + d->class_code = v; >> d->is_virtfn = xen_host_pci_dev_is_virtfn(d); >> >> return 0; >> diff --git a/hw/xen/xen-host-pci-device.h b/hw/xen/xen-host-pci-device.h >> index c2486f0..f1e1c30 100644 >> --- a/hw/xen/xen-host-pci-device.h >> +++ b/hw/xen/xen-host-pci-device.h >> @@ -25,6 +25,7 @@ typedef struct XenHostPCIDevice { >> >> uint16_t vendor_id; >> uint16_t device_id; >> + uint32_t class_code; >> int irq; >> >> XenHostPCIIORegion io_regions[PCI_NUM_REGIONS - 1]; >> diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c >> index be4220b..dac4238 100644 >> --- a/hw/xen/xen_pt.c >> +++ b/hw/xen/xen_pt.c >> @@ -450,6 +450,7 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s) >> d->rom.size, d->rom.base_addr); >> } >> >> + xen_pt_register_vga_regions(d); >> return 0; >> } >> >> @@ -470,6 +471,8 @@ static void xen_pt_unregister_regions(XenPCIPassthroughState *s) >> if (d->rom.base_addr && d->rom.size) { >> memory_region_destroy(&s->rom); >> } >> + >> + xen_pt_unregister_vga_regions(d); >> } >> >> /* region mapping */ >> @@ -693,6 +696,13 @@ static int xen_pt_initfn(PCIDevice *d) >> /* Handle real device's MMIO/PIO BARs */ >> xen_pt_register_regions(s); >> >> + /* Setup VGA bios for passthrough GFX */ >> + if (xen_pt_setup_vga(&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; >> + } >> + >> /* reinitialize each config register to be emulated */ >> if (xen_pt_config_init(s)) { >> XEN_PT_ERR(d, "PCI Config space initialisation failed.\n"); >> diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h >> index 942dc60..4d3a18d 100644 >> --- a/hw/xen/xen_pt.h >> +++ b/hw/xen/xen_pt.h >> @@ -298,5 +298,9 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar) >> return s->msix && s->msix->bar_index == bar; >> } >> >> +extern int xen_has_gfx_passthru; >> +int xen_pt_register_vga_regions(XenHostPCIDevice *dev); >> +int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev); >> +int xen_pt_setup_vga(XenHostPCIDevice *dev); >> >> #endif /* !XEN_PT_H */ >> diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c >> new file mode 100644 >> index 0000000..461e526 >> --- /dev/null >> +++ b/hw/xen/xen_pt_graphics.c >> @@ -0,0 +1,232 @@ >> +/* >> + * graphics passthrough >> + */ >> +#include "xen_pt.h" >> +#include "xen-host-pci-device.h" >> +#include "hw/xen/xen_backend.h" >> + >> +static int is_vga_passthrough(XenHostPCIDevice *dev) >> +{ >> + return (xen_has_gfx_passthru >> + && ((dev->class_code >> 0x8) == PCI_CLASS_DISPLAY_VGA)); >> +} >> + >> +typedef struct VGARegion { >> + int type; /* Memory or port I/O */ >> + uint64_t guest_base_addr; >> + uint64_t machine_base_addr; >> + uint64_t size; /* size of the region */ >> + int rc; >> +} VGARegion; >> + >> +#define IORESOURCE_IO 0x00000100 >> +#define IORESOURCE_MEM 0x00000200 >> + >> +static struct VGARegion vga_args[] = { >> + { >> + .type = IORESOURCE_IO, >> + .guest_base_addr = 0x3B0, >> + .machine_base_addr = 0x3B0, >> + .size = 0xC, >> + .rc = -1, >> + }, >> + { >> + .type = IORESOURCE_IO, >> + .guest_base_addr = 0x3C0, >> + .machine_base_addr = 0x3C0, >> + .size = 0x20, >> + .rc = -1, >> + }, >> + { >> + .type = IORESOURCE_MEM, >> + .guest_base_addr = 0xa0000 >> XC_PAGE_SHIFT, >> + .machine_base_addr = 0xa0000 >> XC_PAGE_SHIFT, >> + .size = 0x20, >> + .rc = -1, >> + }, >> +}; >> + >> +/* >> + * register VGA resources for the domain with assigned gfx >> + */ >> +int xen_pt_register_vga_regions(XenHostPCIDevice *dev) >> +{ >> + int i = 0; >> + >> + if (!is_vga_passthrough(dev)) { >> + return 0; >> + } >> + >> + for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) { >> + if (vga_args[i].type == IORESOURCE_IO) { >> + vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid, >> + vga_args[i].guest_base_addr, >> + vga_args[i].machine_base_addr, >> + vga_args[i].size, DPCI_ADD_MAPPING); >> + } else { >> + vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid, >> + vga_args[i].guest_base_addr, >> + vga_args[i].machine_base_addr, >> + vga_args[i].size, DPCI_ADD_MAPPING); >> + } >> + >> + if (vga_args[i].rc) { >> + XEN_PT_ERR(NULL, "VGA %s mapping failed! (rc: %i)\n", >> + vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory", >> + vga_args[i].rc); >> + return vga_args[i].rc; >> + } >> + } >> + >> + return 0; >> +} >> + >> +/* >> + * unregister VGA resources for the domain with assigned gfx >> + */ >> +int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev) >> +{ >> + int i = 0; >> + >> + if (!is_vga_passthrough(dev)) { >> + return 0; >> + } >> + >> + for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) { >> + if (vga_args[i].type == IORESOURCE_IO) { >> + vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid, >> + vga_args[i].guest_base_addr, >> + vga_args[i].machine_base_addr, >> + vga_args[i].size, DPCI_REMOVE_MAPPING); >> + } else { >> + vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid, >> + vga_args[i].guest_base_addr, >> + vga_args[i].machine_base_addr, >> + vga_args[i].size, DPCI_REMOVE_MAPPING); >> + } >> + >> + if (vga_args[i].rc) { >> + XEN_PT_ERR(NULL, "VGA %s unmapping failed! (rc: %i)\n", >> + vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory", >> + vga_args[i].rc); >> + return vga_args[i].rc; >> + } >> + } >> + >> + return 0; >> +} >> + >> +static int get_vgabios(unsigned char *buf, XenHostPCIDevice *dev) >> +{ >> + char rom_file[64]; >> + FILE *fp; >> + uint8_t val; >> + struct stat st; >> + uint16_t magic = 0; >> + int ret = 0; >> + >> + snprintf(rom_file, sizeof(rom_file), >> + "/sys/bus/pci/devices/%04x:%02x:%02x.%d/rom", >> + dev->domain, dev->bus, dev->dev, >> + dev->func); >> + >> + if (stat(rom_file, &st)) { >> + return -ENODEV; >> + } >> + >> + if (access(rom_file, F_OK)) { >> + XEN_PT_ERR(NULL, "pci-assign: Insufficient privileges for %s", >> + rom_file); >> + return -ENODEV; >> + } >> + >> + /* Write "1" to the ROM file to enable it */ >> + fp = fopen(rom_file, "r+"); >> + if (fp == NULL) { >> + return -EACCES; >> + } >> + val = 1; >> + if (fwrite(&val, 1, 1, fp) != 1) { >> + XEN_PT_LOG("%s\n", "Failed to enable pci-sysfs rom file"); >> + ret = -EIO; >> + goto close_rom; >> + } >> + fseek(fp, 0, SEEK_SET); >> + >> + /* >> + * Check if it a real bios extension. >> + * The magic number is 0xAA55. >> + */ >> + if (!fread(&magic, sizeof(magic), 1, fp)) { >> + XEN_PT_ERR(NULL, "VGA: can't get magic.\n"); >> + ret = -ENODEV; >> + goto close_rom; >> + } >> + if (magic != 0xAA55) { >> + XEN_PT_ERR(NULL, "VGA: wrong magic %x.\n", magic); >> + ret = -ENODEV; >> + goto close_rom; >> + } >> + fseek(fp, 0, SEEK_SET); >> + >> + if (!fread(buf, 1, st.st_size, fp)) { >> + XEN_PT_ERR(NULL, "VGA: pci-assign: Cannot read from host %s", rom_file); >> + XEN_PT_LOG(NULL, "VGA: Device option ROM contents are probably invalid " >> + "(check dmesg).\nSkip option ROM probe with rombar=0, " >> + "or load from file with romfile=\n"); >> + } >> + >> +close_rom: >> + /* Write "0" to disable ROM */ >> + fseek(fp, 0, SEEK_SET); >> + val = 0; >> + if (!fwrite(&val, 1, 1, fp)) { >> + ret = -EIO; >> + XEN_PT_LOG("%s\n", "Failed to disable pci-sysfs rom file"); >> + } >> + fclose(fp); >> + return ret; >> +} >> + >> +/* Allocated 128K for the vga bios */ >> +#define VGA_BIOS_DEFAULT_SIZE (0x20000) >> + >> +int xen_pt_setup_vga(XenHostPCIDevice *dev) >> +{ >> + unsigned char *bios = NULL; >> + int bios_size = 0; >> + char *c = NULL; >> + char checksum = 0; >> + int rc = 0; >> + >> + if (!is_vga_passthrough(dev)) { >> + return rc; >> + } >> + >> + bios = g_malloc0(VGA_BIOS_DEFAULT_SIZE); >> + >> + bios_size = get_vgabios(bios, dev); >> + if (bios_size) { >> + XEN_PT_ERR(NULL, "VGA: Error %d getting VBIOS!\n", bios_size); >> + if (bios_size < 0) { >> + XEN_PT_ERR(NULL, "VGA: Error (%s).\n", strerror(bios_size)); >> + } >> + rc = -1; >> + goto out; >> + } >> + >> + /* Adjust the bios checksum */ > > It's easy to see that you do this here, but *why* do you do it? > That's what the comment should explain. > Do you see many ROMs with an invalid checksum > in the field? > >> + for (c = (char *)bios; c < ((char *)bios + bios_size); c++) { >> + checksum += *c; >> + } >> + if (checksum) { >> + bios[bios_size - 1] -= checksum; >> + XEN_PT_LOG(NULL, "vga bios checksum is adjusted!\n"); >> + } >> + >> + /* Currently we fixed this address as a primary. */ >> + cpu_physical_memory_rw(0xc0000, bios, bios_size, 1); >> +out: >> + g_free(bios); >> + return rc; >> +} >> diff --git a/qemu-options.hx b/qemu-options.hx >> index ff76ad4..1c77baa 100644 >> --- a/qemu-options.hx >> +++ b/qemu-options.hx >> @@ -1066,6 +1066,15 @@ STEXI >> Rotate graphical output some deg left (only PXA LCD). >> ETEXI >> >> +DEF("gfx_passthru", 0, QEMU_OPTION_gfx_passthru, >> + "-gfx_passthru enable Intel IGD passthrough by XEN\n", >> + QEMU_ARCH_ALL) >> +STEXI >> +@item -gfx_passthru >> +@findex -gfx_passthru >> +Enable Intel IGD passthrough by XEN >> +ETEXI >> + >> DEF("vga", HAS_ARG, QEMU_OPTION_vga, >> "-vga [std|cirrus|vmware|qxl|xenfb|tcx|cg3|none]\n" >> " select video card type\n", QEMU_ARCH_ALL) >> diff --git a/vl.c b/vl.c >> index a1686ef..c5b572d 100644 >> --- a/vl.c >> +++ b/vl.c >> @@ -1418,6 +1418,13 @@ static void configure_msg(QemuOpts *opts) >> enable_timestamp_msg = qemu_opt_get_bool(opts, "timestamp", true); >> } >> >> +/* We still need this for compatibility with XEN. */ >> +int xen_has_gfx_passthru; >> +static void xen_gfx_passthru(const char *optarg) >> +{ >> + xen_has_gfx_passthru = 1; >> +} >> + >> /***********************************************************/ >> /* USB devices */ >> >> @@ -3945,6 +3952,9 @@ int main(int argc, char **argv, char **envp) >> exit(1); >> } >> break; >> + case QEMU_OPTION_gfx_passthru: >> + xen_gfx_passthru(optarg); >> + break; >> default: >> os_parse_cmd_args(popt->index, optarg); >> } >> -- >> 1.9.1 > >
On Wed, Jun 25, 2014 at 05:06:37PM +0800, Chen, Tiejun wrote: > On 2014/6/25 14:35, Michael S. Tsirkin wrote: > >On Wed, Jun 25, 2014 at 10:17:17AM +0800, Tiejun Chen wrote: > >>basic gfx passthrough support: > >>- add a vga type for gfx passthrough > >>- retrieve VGA bios from sysfs, then load it to guest at 0xC0000 > >>- register/unregister legacy VGA I/O ports and MMIOs for passthrough GFX > >> > >>The original patch is from Weidong Han <weidong.han@intel.com> > >> > >>Signed-off-by: Yang Zhang <yang.z.zhang@Intel.com> > >>Signed-off-by: Tiejun Chen <tiejun.chen@intel.com> > >>Cc: Weidong Han <weidong.han@intel.com> > > > >ROM loading code is duplicated from assigned_dev_load_option_rom. > > Yes. Do you hint we need to split this? Absolutely. These two drivers need to start sharing code. > >Would it be a problem for you to create a memory region holding > >the ROM? > > Sorry, could you tell me what should do exactly? Split common code out and reuse :) > >You won't need cpu_physical_memory_rw then, either, or need > > How to make sure this is fixed at 0xc0000? > > Thanks > Tiejun I guess you just add it as subregion of system memory at this offset, maybe with a higher priority than RAM. Right, Paolo? > >the VGA_BIOS_DEFAULT_SIZE hack. > > > > > > > > > >>--- > >>v5: > >> > >>* Just rebase. > >> > >>v4: > >> > >>* Fix some typos in the patch head description. > >>* Improve some comments. > >>* Given that xen_pt_register_vga_regions()/xen_pt_unregister_vga_regions() > >> are called unconditionally, so we just return 0 there. > >>* Remove one spurious change. > >> > >>v3: > >> > >>* Fix some typos. > >>* Add more comments to make that readable. > >>* Improve some return paths. > >> > >>v2: > >> > >>* retrieve VGA bios from sysfs properly. > >>* redefine some function name. > >> > >> hw/xen/Makefile.objs | 2 +- > >> hw/xen/xen-host-pci-device.c | 5 + > >> hw/xen/xen-host-pci-device.h | 1 + > >> hw/xen/xen_pt.c | 10 ++ > >> hw/xen/xen_pt.h | 4 + > >> hw/xen/xen_pt_graphics.c | 232 +++++++++++++++++++++++++++++++++++++++++++ > >> qemu-options.hx | 9 ++ > >> vl.c | 10 ++ > >> 8 files changed, 272 insertions(+), 1 deletion(-) > >> create mode 100644 hw/xen/xen_pt_graphics.c > >> > >>diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs > >>index a0ca0aa..77b7dae 100644 > >>--- a/hw/xen/Makefile.objs > >>+++ b/hw/xen/Makefile.objs > >>@@ -2,4 +2,4 @@ > >> common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o > >> > >> obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o > >>-obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o > >>+obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o xen_pt_graphics.o > >>diff --git a/hw/xen/xen-host-pci-device.c b/hw/xen/xen-host-pci-device.c > >>index 743b37b..a54b7de 100644 > >>--- a/hw/xen/xen-host-pci-device.c > >>+++ b/hw/xen/xen-host-pci-device.c > >>@@ -376,6 +376,11 @@ int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, > >> goto error; > >> } > >> d->irq = v; > >>+ rc = xen_host_pci_get_hex_value(d, "class", &v); > >>+ if (rc) { > >>+ goto error; > >>+ } > >>+ d->class_code = v; > >> d->is_virtfn = xen_host_pci_dev_is_virtfn(d); > >> > >> return 0; > >>diff --git a/hw/xen/xen-host-pci-device.h b/hw/xen/xen-host-pci-device.h > >>index c2486f0..f1e1c30 100644 > >>--- a/hw/xen/xen-host-pci-device.h > >>+++ b/hw/xen/xen-host-pci-device.h > >>@@ -25,6 +25,7 @@ typedef struct XenHostPCIDevice { > >> > >> uint16_t vendor_id; > >> uint16_t device_id; > >>+ uint32_t class_code; > >> int irq; > >> > >> XenHostPCIIORegion io_regions[PCI_NUM_REGIONS - 1]; > >>diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c > >>index be4220b..dac4238 100644 > >>--- a/hw/xen/xen_pt.c > >>+++ b/hw/xen/xen_pt.c > >>@@ -450,6 +450,7 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s) > >> d->rom.size, d->rom.base_addr); > >> } > >> > >>+ xen_pt_register_vga_regions(d); > >> return 0; > >> } > >> > >>@@ -470,6 +471,8 @@ static void xen_pt_unregister_regions(XenPCIPassthroughState *s) > >> if (d->rom.base_addr && d->rom.size) { > >> memory_region_destroy(&s->rom); > >> } > >>+ > >>+ xen_pt_unregister_vga_regions(d); > >> } > >> > >> /* region mapping */ > >>@@ -693,6 +696,13 @@ static int xen_pt_initfn(PCIDevice *d) > >> /* Handle real device's MMIO/PIO BARs */ > >> xen_pt_register_regions(s); > >> > >>+ /* Setup VGA bios for passthrough GFX */ > >>+ if (xen_pt_setup_vga(&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; > >>+ } > >>+ > >> /* reinitialize each config register to be emulated */ > >> if (xen_pt_config_init(s)) { > >> XEN_PT_ERR(d, "PCI Config space initialisation failed.\n"); > >>diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h > >>index 942dc60..4d3a18d 100644 > >>--- a/hw/xen/xen_pt.h > >>+++ b/hw/xen/xen_pt.h > >>@@ -298,5 +298,9 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar) > >> return s->msix && s->msix->bar_index == bar; > >> } > >> > >>+extern int xen_has_gfx_passthru; > >>+int xen_pt_register_vga_regions(XenHostPCIDevice *dev); > >>+int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev); > >>+int xen_pt_setup_vga(XenHostPCIDevice *dev); > >> > >> #endif /* !XEN_PT_H */ > >>diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c > >>new file mode 100644 > >>index 0000000..461e526 > >>--- /dev/null > >>+++ b/hw/xen/xen_pt_graphics.c > >>@@ -0,0 +1,232 @@ > >>+/* > >>+ * graphics passthrough > >>+ */ > >>+#include "xen_pt.h" > >>+#include "xen-host-pci-device.h" > >>+#include "hw/xen/xen_backend.h" > >>+ > >>+static int is_vga_passthrough(XenHostPCIDevice *dev) > >>+{ > >>+ return (xen_has_gfx_passthru > >>+ && ((dev->class_code >> 0x8) == PCI_CLASS_DISPLAY_VGA)); > >>+} > >>+ > >>+typedef struct VGARegion { > >>+ int type; /* Memory or port I/O */ > >>+ uint64_t guest_base_addr; > >>+ uint64_t machine_base_addr; > >>+ uint64_t size; /* size of the region */ > >>+ int rc; > >>+} VGARegion; > >>+ > >>+#define IORESOURCE_IO 0x00000100 > >>+#define IORESOURCE_MEM 0x00000200 > >>+ > >>+static struct VGARegion vga_args[] = { > >>+ { > >>+ .type = IORESOURCE_IO, > >>+ .guest_base_addr = 0x3B0, > >>+ .machine_base_addr = 0x3B0, > >>+ .size = 0xC, > >>+ .rc = -1, > >>+ }, > >>+ { > >>+ .type = IORESOURCE_IO, > >>+ .guest_base_addr = 0x3C0, > >>+ .machine_base_addr = 0x3C0, > >>+ .size = 0x20, > >>+ .rc = -1, > >>+ }, > >>+ { > >>+ .type = IORESOURCE_MEM, > >>+ .guest_base_addr = 0xa0000 >> XC_PAGE_SHIFT, > >>+ .machine_base_addr = 0xa0000 >> XC_PAGE_SHIFT, > >>+ .size = 0x20, > >>+ .rc = -1, > >>+ }, > >>+}; > >>+ > >>+/* > >>+ * register VGA resources for the domain with assigned gfx > >>+ */ > >>+int xen_pt_register_vga_regions(XenHostPCIDevice *dev) > >>+{ > >>+ int i = 0; > >>+ > >>+ if (!is_vga_passthrough(dev)) { > >>+ return 0; > >>+ } > >>+ > >>+ for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) { > >>+ if (vga_args[i].type == IORESOURCE_IO) { > >>+ vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid, > >>+ vga_args[i].guest_base_addr, > >>+ vga_args[i].machine_base_addr, > >>+ vga_args[i].size, DPCI_ADD_MAPPING); > >>+ } else { > >>+ vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid, > >>+ vga_args[i].guest_base_addr, > >>+ vga_args[i].machine_base_addr, > >>+ vga_args[i].size, DPCI_ADD_MAPPING); > >>+ } > >>+ > >>+ if (vga_args[i].rc) { > >>+ XEN_PT_ERR(NULL, "VGA %s mapping failed! (rc: %i)\n", > >>+ vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory", > >>+ vga_args[i].rc); > >>+ return vga_args[i].rc; > >>+ } > >>+ } > >>+ > >>+ return 0; > >>+} > >>+ > >>+/* > >>+ * unregister VGA resources for the domain with assigned gfx > >>+ */ > >>+int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev) > >>+{ > >>+ int i = 0; > >>+ > >>+ if (!is_vga_passthrough(dev)) { > >>+ return 0; > >>+ } > >>+ > >>+ for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) { > >>+ if (vga_args[i].type == IORESOURCE_IO) { > >>+ vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid, > >>+ vga_args[i].guest_base_addr, > >>+ vga_args[i].machine_base_addr, > >>+ vga_args[i].size, DPCI_REMOVE_MAPPING); > >>+ } else { > >>+ vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid, > >>+ vga_args[i].guest_base_addr, > >>+ vga_args[i].machine_base_addr, > >>+ vga_args[i].size, DPCI_REMOVE_MAPPING); > >>+ } > >>+ > >>+ if (vga_args[i].rc) { > >>+ XEN_PT_ERR(NULL, "VGA %s unmapping failed! (rc: %i)\n", > >>+ vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory", > >>+ vga_args[i].rc); > >>+ return vga_args[i].rc; > >>+ } > >>+ } > >>+ > >>+ return 0; > >>+} > >>+ > >>+static int get_vgabios(unsigned char *buf, XenHostPCIDevice *dev) > >>+{ > >>+ char rom_file[64]; > >>+ FILE *fp; > >>+ uint8_t val; > >>+ struct stat st; > >>+ uint16_t magic = 0; > >>+ int ret = 0; > >>+ > >>+ snprintf(rom_file, sizeof(rom_file), > >>+ "/sys/bus/pci/devices/%04x:%02x:%02x.%d/rom", > >>+ dev->domain, dev->bus, dev->dev, > >>+ dev->func); > >>+ > >>+ if (stat(rom_file, &st)) { > >>+ return -ENODEV; > >>+ } > >>+ > >>+ if (access(rom_file, F_OK)) { > >>+ XEN_PT_ERR(NULL, "pci-assign: Insufficient privileges for %s", > >>+ rom_file); > >>+ return -ENODEV; > >>+ } > >>+ > >>+ /* Write "1" to the ROM file to enable it */ > >>+ fp = fopen(rom_file, "r+"); > >>+ if (fp == NULL) { > >>+ return -EACCES; > >>+ } > >>+ val = 1; > >>+ if (fwrite(&val, 1, 1, fp) != 1) { > >>+ XEN_PT_LOG("%s\n", "Failed to enable pci-sysfs rom file"); > >>+ ret = -EIO; > >>+ goto close_rom; > >>+ } > >>+ fseek(fp, 0, SEEK_SET); > >>+ > >>+ /* > >>+ * Check if it a real bios extension. > >>+ * The magic number is 0xAA55. > >>+ */ > >>+ if (!fread(&magic, sizeof(magic), 1, fp)) { > >>+ XEN_PT_ERR(NULL, "VGA: can't get magic.\n"); > >>+ ret = -ENODEV; > >>+ goto close_rom; > >>+ } > >>+ if (magic != 0xAA55) { > >>+ XEN_PT_ERR(NULL, "VGA: wrong magic %x.\n", magic); > >>+ ret = -ENODEV; > >>+ goto close_rom; > >>+ } > >>+ fseek(fp, 0, SEEK_SET); > >>+ > >>+ if (!fread(buf, 1, st.st_size, fp)) { > >>+ XEN_PT_ERR(NULL, "VGA: pci-assign: Cannot read from host %s", rom_file); > >>+ XEN_PT_LOG(NULL, "VGA: Device option ROM contents are probably invalid " > >>+ "(check dmesg).\nSkip option ROM probe with rombar=0, " > >>+ "or load from file with romfile=\n"); > >>+ } > >>+ > >>+close_rom: > >>+ /* Write "0" to disable ROM */ > >>+ fseek(fp, 0, SEEK_SET); > >>+ val = 0; > >>+ if (!fwrite(&val, 1, 1, fp)) { > >>+ ret = -EIO; > >>+ XEN_PT_LOG("%s\n", "Failed to disable pci-sysfs rom file"); > >>+ } > >>+ fclose(fp); > >>+ return ret; > >>+} > >>+ > >>+/* Allocated 128K for the vga bios */ > >>+#define VGA_BIOS_DEFAULT_SIZE (0x20000) > >>+ > >>+int xen_pt_setup_vga(XenHostPCIDevice *dev) > >>+{ > >>+ unsigned char *bios = NULL; > >>+ int bios_size = 0; > >>+ char *c = NULL; > >>+ char checksum = 0; > >>+ int rc = 0; > >>+ > >>+ if (!is_vga_passthrough(dev)) { > >>+ return rc; > >>+ } > >>+ > >>+ bios = g_malloc0(VGA_BIOS_DEFAULT_SIZE); > >>+ > >>+ bios_size = get_vgabios(bios, dev); > >>+ if (bios_size) { > >>+ XEN_PT_ERR(NULL, "VGA: Error %d getting VBIOS!\n", bios_size); > >>+ if (bios_size < 0) { > >>+ XEN_PT_ERR(NULL, "VGA: Error (%s).\n", strerror(bios_size)); > >>+ } > >>+ rc = -1; > >>+ goto out; > >>+ } > >>+ > >>+ /* Adjust the bios checksum */ > > > >It's easy to see that you do this here, but *why* do you do it? > >That's what the comment should explain. > >Do you see many ROMs with an invalid checksum > >in the field? > > > >>+ for (c = (char *)bios; c < ((char *)bios + bios_size); c++) { > >>+ checksum += *c; > >>+ } > >>+ if (checksum) { > >>+ bios[bios_size - 1] -= checksum; > >>+ XEN_PT_LOG(NULL, "vga bios checksum is adjusted!\n"); > >>+ } > >>+ > >>+ /* Currently we fixed this address as a primary. */ > >>+ cpu_physical_memory_rw(0xc0000, bios, bios_size, 1); > >>+out: > >>+ g_free(bios); > >>+ return rc; > >>+} > >>diff --git a/qemu-options.hx b/qemu-options.hx > >>index ff76ad4..1c77baa 100644 > >>--- a/qemu-options.hx > >>+++ b/qemu-options.hx > >>@@ -1066,6 +1066,15 @@ STEXI > >> Rotate graphical output some deg left (only PXA LCD). > >> ETEXI > >> > >>+DEF("gfx_passthru", 0, QEMU_OPTION_gfx_passthru, > >>+ "-gfx_passthru enable Intel IGD passthrough by XEN\n", > >>+ QEMU_ARCH_ALL) > >>+STEXI > >>+@item -gfx_passthru > >>+@findex -gfx_passthru > >>+Enable Intel IGD passthrough by XEN > >>+ETEXI > >>+ > >> DEF("vga", HAS_ARG, QEMU_OPTION_vga, > >> "-vga [std|cirrus|vmware|qxl|xenfb|tcx|cg3|none]\n" > >> " select video card type\n", QEMU_ARCH_ALL) > >>diff --git a/vl.c b/vl.c > >>index a1686ef..c5b572d 100644 > >>--- a/vl.c > >>+++ b/vl.c > >>@@ -1418,6 +1418,13 @@ static void configure_msg(QemuOpts *opts) > >> enable_timestamp_msg = qemu_opt_get_bool(opts, "timestamp", true); > >> } > >> > >>+/* We still need this for compatibility with XEN. */ > >>+int xen_has_gfx_passthru; > >>+static void xen_gfx_passthru(const char *optarg) > >>+{ > >>+ xen_has_gfx_passthru = 1; > >>+} > >>+ > >> /***********************************************************/ > >> /* USB devices */ > >> > >>@@ -3945,6 +3952,9 @@ int main(int argc, char **argv, char **envp) > >> exit(1); > >> } > >> break; > >>+ case QEMU_OPTION_gfx_passthru: > >>+ xen_gfx_passthru(optarg); > >>+ break; > >> default: > >> os_parse_cmd_args(popt->index, optarg); > >> } > >>-- > >>1.9.1 > > > >
diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs index a0ca0aa..77b7dae 100644 --- a/hw/xen/Makefile.objs +++ b/hw/xen/Makefile.objs @@ -2,4 +2,4 @@ common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o -obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o +obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_msi.o xen_pt_graphics.o diff --git a/hw/xen/xen-host-pci-device.c b/hw/xen/xen-host-pci-device.c index 743b37b..a54b7de 100644 --- a/hw/xen/xen-host-pci-device.c +++ b/hw/xen/xen-host-pci-device.c @@ -376,6 +376,11 @@ int xen_host_pci_device_get(XenHostPCIDevice *d, uint16_t domain, goto error; } d->irq = v; + rc = xen_host_pci_get_hex_value(d, "class", &v); + if (rc) { + goto error; + } + d->class_code = v; d->is_virtfn = xen_host_pci_dev_is_virtfn(d); return 0; diff --git a/hw/xen/xen-host-pci-device.h b/hw/xen/xen-host-pci-device.h index c2486f0..f1e1c30 100644 --- a/hw/xen/xen-host-pci-device.h +++ b/hw/xen/xen-host-pci-device.h @@ -25,6 +25,7 @@ typedef struct XenHostPCIDevice { uint16_t vendor_id; uint16_t device_id; + uint32_t class_code; int irq; XenHostPCIIORegion io_regions[PCI_NUM_REGIONS - 1]; diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index be4220b..dac4238 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -450,6 +450,7 @@ static int xen_pt_register_regions(XenPCIPassthroughState *s) d->rom.size, d->rom.base_addr); } + xen_pt_register_vga_regions(d); return 0; } @@ -470,6 +471,8 @@ static void xen_pt_unregister_regions(XenPCIPassthroughState *s) if (d->rom.base_addr && d->rom.size) { memory_region_destroy(&s->rom); } + + xen_pt_unregister_vga_regions(d); } /* region mapping */ @@ -693,6 +696,13 @@ static int xen_pt_initfn(PCIDevice *d) /* Handle real device's MMIO/PIO BARs */ xen_pt_register_regions(s); + /* Setup VGA bios for passthrough GFX */ + if (xen_pt_setup_vga(&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; + } + /* reinitialize each config register to be emulated */ if (xen_pt_config_init(s)) { XEN_PT_ERR(d, "PCI Config space initialisation failed.\n"); diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index 942dc60..4d3a18d 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -298,5 +298,9 @@ static inline bool xen_pt_has_msix_mapping(XenPCIPassthroughState *s, int bar) return s->msix && s->msix->bar_index == bar; } +extern int xen_has_gfx_passthru; +int xen_pt_register_vga_regions(XenHostPCIDevice *dev); +int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev); +int xen_pt_setup_vga(XenHostPCIDevice *dev); #endif /* !XEN_PT_H */ diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c new file mode 100644 index 0000000..461e526 --- /dev/null +++ b/hw/xen/xen_pt_graphics.c @@ -0,0 +1,232 @@ +/* + * graphics passthrough + */ +#include "xen_pt.h" +#include "xen-host-pci-device.h" +#include "hw/xen/xen_backend.h" + +static int is_vga_passthrough(XenHostPCIDevice *dev) +{ + return (xen_has_gfx_passthru + && ((dev->class_code >> 0x8) == PCI_CLASS_DISPLAY_VGA)); +} + +typedef struct VGARegion { + int type; /* Memory or port I/O */ + uint64_t guest_base_addr; + uint64_t machine_base_addr; + uint64_t size; /* size of the region */ + int rc; +} VGARegion; + +#define IORESOURCE_IO 0x00000100 +#define IORESOURCE_MEM 0x00000200 + +static struct VGARegion vga_args[] = { + { + .type = IORESOURCE_IO, + .guest_base_addr = 0x3B0, + .machine_base_addr = 0x3B0, + .size = 0xC, + .rc = -1, + }, + { + .type = IORESOURCE_IO, + .guest_base_addr = 0x3C0, + .machine_base_addr = 0x3C0, + .size = 0x20, + .rc = -1, + }, + { + .type = IORESOURCE_MEM, + .guest_base_addr = 0xa0000 >> XC_PAGE_SHIFT, + .machine_base_addr = 0xa0000 >> XC_PAGE_SHIFT, + .size = 0x20, + .rc = -1, + }, +}; + +/* + * register VGA resources for the domain with assigned gfx + */ +int xen_pt_register_vga_regions(XenHostPCIDevice *dev) +{ + int i = 0; + + if (!is_vga_passthrough(dev)) { + return 0; + } + + for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) { + if (vga_args[i].type == IORESOURCE_IO) { + vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid, + vga_args[i].guest_base_addr, + vga_args[i].machine_base_addr, + vga_args[i].size, DPCI_ADD_MAPPING); + } else { + vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid, + vga_args[i].guest_base_addr, + vga_args[i].machine_base_addr, + vga_args[i].size, DPCI_ADD_MAPPING); + } + + if (vga_args[i].rc) { + XEN_PT_ERR(NULL, "VGA %s mapping failed! (rc: %i)\n", + vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory", + vga_args[i].rc); + return vga_args[i].rc; + } + } + + return 0; +} + +/* + * unregister VGA resources for the domain with assigned gfx + */ +int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev) +{ + int i = 0; + + if (!is_vga_passthrough(dev)) { + return 0; + } + + for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) { + if (vga_args[i].type == IORESOURCE_IO) { + vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid, + vga_args[i].guest_base_addr, + vga_args[i].machine_base_addr, + vga_args[i].size, DPCI_REMOVE_MAPPING); + } else { + vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid, + vga_args[i].guest_base_addr, + vga_args[i].machine_base_addr, + vga_args[i].size, DPCI_REMOVE_MAPPING); + } + + if (vga_args[i].rc) { + XEN_PT_ERR(NULL, "VGA %s unmapping failed! (rc: %i)\n", + vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory", + vga_args[i].rc); + return vga_args[i].rc; + } + } + + return 0; +} + +static int get_vgabios(unsigned char *buf, XenHostPCIDevice *dev) +{ + char rom_file[64]; + FILE *fp; + uint8_t val; + struct stat st; + uint16_t magic = 0; + int ret = 0; + + snprintf(rom_file, sizeof(rom_file), + "/sys/bus/pci/devices/%04x:%02x:%02x.%d/rom", + dev->domain, dev->bus, dev->dev, + dev->func); + + if (stat(rom_file, &st)) { + return -ENODEV; + } + + if (access(rom_file, F_OK)) { + XEN_PT_ERR(NULL, "pci-assign: Insufficient privileges for %s", + rom_file); + return -ENODEV; + } + + /* Write "1" to the ROM file to enable it */ + fp = fopen(rom_file, "r+"); + if (fp == NULL) { + return -EACCES; + } + val = 1; + if (fwrite(&val, 1, 1, fp) != 1) { + XEN_PT_LOG("%s\n", "Failed to enable pci-sysfs rom file"); + ret = -EIO; + goto close_rom; + } + fseek(fp, 0, SEEK_SET); + + /* + * Check if it a real bios extension. + * The magic number is 0xAA55. + */ + if (!fread(&magic, sizeof(magic), 1, fp)) { + XEN_PT_ERR(NULL, "VGA: can't get magic.\n"); + ret = -ENODEV; + goto close_rom; + } + if (magic != 0xAA55) { + XEN_PT_ERR(NULL, "VGA: wrong magic %x.\n", magic); + ret = -ENODEV; + goto close_rom; + } + fseek(fp, 0, SEEK_SET); + + if (!fread(buf, 1, st.st_size, fp)) { + XEN_PT_ERR(NULL, "VGA: pci-assign: Cannot read from host %s", rom_file); + XEN_PT_LOG(NULL, "VGA: Device option ROM contents are probably invalid " + "(check dmesg).\nSkip option ROM probe with rombar=0, " + "or load from file with romfile=\n"); + } + +close_rom: + /* Write "0" to disable ROM */ + fseek(fp, 0, SEEK_SET); + val = 0; + if (!fwrite(&val, 1, 1, fp)) { + ret = -EIO; + XEN_PT_LOG("%s\n", "Failed to disable pci-sysfs rom file"); + } + fclose(fp); + return ret; +} + +/* Allocated 128K for the vga bios */ +#define VGA_BIOS_DEFAULT_SIZE (0x20000) + +int xen_pt_setup_vga(XenHostPCIDevice *dev) +{ + unsigned char *bios = NULL; + int bios_size = 0; + char *c = NULL; + char checksum = 0; + int rc = 0; + + if (!is_vga_passthrough(dev)) { + return rc; + } + + bios = g_malloc0(VGA_BIOS_DEFAULT_SIZE); + + bios_size = get_vgabios(bios, dev); + if (bios_size) { + XEN_PT_ERR(NULL, "VGA: Error %d getting VBIOS!\n", bios_size); + if (bios_size < 0) { + XEN_PT_ERR(NULL, "VGA: Error (%s).\n", strerror(bios_size)); + } + rc = -1; + goto out; + } + + /* Adjust the bios checksum */ + for (c = (char *)bios; c < ((char *)bios + bios_size); c++) { + checksum += *c; + } + if (checksum) { + bios[bios_size - 1] -= checksum; + XEN_PT_LOG(NULL, "vga bios checksum is adjusted!\n"); + } + + /* Currently we fixed this address as a primary. */ + cpu_physical_memory_rw(0xc0000, bios, bios_size, 1); +out: + g_free(bios); + return rc; +} diff --git a/qemu-options.hx b/qemu-options.hx index ff76ad4..1c77baa 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1066,6 +1066,15 @@ STEXI Rotate graphical output some deg left (only PXA LCD). ETEXI +DEF("gfx_passthru", 0, QEMU_OPTION_gfx_passthru, + "-gfx_passthru enable Intel IGD passthrough by XEN\n", + QEMU_ARCH_ALL) +STEXI +@item -gfx_passthru +@findex -gfx_passthru +Enable Intel IGD passthrough by XEN +ETEXI + DEF("vga", HAS_ARG, QEMU_OPTION_vga, "-vga [std|cirrus|vmware|qxl|xenfb|tcx|cg3|none]\n" " select video card type\n", QEMU_ARCH_ALL) diff --git a/vl.c b/vl.c index a1686ef..c5b572d 100644 --- a/vl.c +++ b/vl.c @@ -1418,6 +1418,13 @@ static void configure_msg(QemuOpts *opts) enable_timestamp_msg = qemu_opt_get_bool(opts, "timestamp", true); } +/* We still need this for compatibility with XEN. */ +int xen_has_gfx_passthru; +static void xen_gfx_passthru(const char *optarg) +{ + xen_has_gfx_passthru = 1; +} + /***********************************************************/ /* USB devices */ @@ -3945,6 +3952,9 @@ int main(int argc, char **argv, char **envp) exit(1); } break; + case QEMU_OPTION_gfx_passthru: + xen_gfx_passthru(optarg); + break; default: os_parse_cmd_args(popt->index, optarg); }