commit 3e37644fcde724433454440a61b22d3f7f9932bb
Author: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
Date: Fri Sep 4 16:21:25 2009 +0100
graphics passthrough with VT-d
This patch supports basic gfx passthrough on QEMU:
- disable emulated VGA adpater if there is passthroughed gfx
- retrieve VGA bios from host 0xC0000, then load it to guest 0xC0000
- register/unregister legacy VGA I/O ports and MMIOs for passthroughed gfx
Signed-off-by: Ben Lin <ben.y.lin@xxxxxxxxx>
Signed-off-by: Weidong Han <weidong.han@xxxxxxxxx>x
[PATCH 2/2 v2]; [1/2 already applied to xen-unstable -iwj]
---
hw/pass-through.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/pass-through.h | 6 ++
hw/pc.c | 51 ++++++++++--------
vl.c | 32 ++++++++++--
4 files changed, 220 insertions(+), 26 deletions(-)
diff --git a/hw/pass-through.c b/hw/pass-through.c
index 8d80755..a97368a 100644
--- a/hw/pass-through.c
+++ b/hw/pass-through.c
@@ -93,6 +93,8 @@
#include <unistd.h>
#include <sys/ioctl.h>
+extern int gfx_passthru;
+
struct php_dev {
struct pt_dev *pt_dev;
uint8_t valid;
@@ -1781,12 +1783,57 @@ static int pt_dev_is_virtfn(struct pci_dev *dev)
return rc;
}
+/*
+ * register VGA resources for the domain with assigned gfx
+ */
+static int register_vga_regions(struct pt_dev *real_device)
+{
+ int ret = 0;
+
+ ret |= xc_domain_ioport_mapping(xc_handle, domid, 0x3B0,
+ 0x3B0, 0xC, DPCI_ADD_MAPPING);
+
+ ret |= xc_domain_ioport_mapping(xc_handle, domid, 0x3C0,
+ 0x3C0, 0x20, DPCI_ADD_MAPPING);
+
+ ret |= xc_domain_memory_mapping(xc_handle, domid,
+ 0xa0000 >> XC_PAGE_SHIFT,
+ 0xa0000 >> XC_PAGE_SHIFT,
+ 0x20,
+ DPCI_ADD_MAPPING);
+
+ return ret;
+}
+
+/*
+ * unregister VGA resources for the domain with assigned gfx
+ */
+static int unregister_vga_regions(struct pt_dev *real_device)
+{
+ int ret = 0;
+
+ ret |= xc_domain_ioport_mapping(xc_handle, domid, 0x3B0,
+ 0x3B0, 0xC, DPCI_REMOVE_MAPPING);
+
+ ret |= xc_domain_ioport_mapping(xc_handle, domid, 0x3C0,
+ 0x3C0, 0x20, DPCI_REMOVE_MAPPING);
+
+ ret |= xc_domain_memory_mapping(xc_handle, domid,
+ 0xa0000 >> XC_PAGE_SHIFT,
+ 0xa0000 >> XC_PAGE_SHIFT,
+ 0x20,
+ DPCI_REMOVE_MAPPING);
+
+ return ret;
+}
+
static int pt_register_regions(struct pt_dev *assigned_device)
{
int i = 0;
uint32_t bar_data = 0;
struct pci_dev *pci_dev = assigned_device->pci_dev;
PCIDevice *d = &assigned_device->dev;
+ int ret;
/* Register PIO/MMIO BARs */
for ( i = 0; i < PCI_BAR_ENTRIES; i++ )
@@ -1842,6 +1889,16 @@ static int pt_register_regions(struct pt_dev
*assigned_device)
(uint32_t)(pci_dev->rom_size), (uint32_t)(pci_dev->rom_base_addr));
}
+ if ( gfx_passthru && (pci_dev->device_class == 0x0300) )
+ {
+ ret = register_vga_regions(assigned_device);
+ if ( ret != 0 )
+ {
+ PT_LOG("VGA region mapping failed\n");
+ return ret;
+ }
+ }
+
return 0;
}
@@ -1891,6 +1948,12 @@ static void pt_unregister_regions(struct pt_dev
*assigned_device)
}
+ if ( gfx_passthru && (assigned_device->pci_dev->device_class == 0x0300) )
+ {
+ ret = unregister_vga_regions(assigned_device);
+ if ( ret != 0 )
+ PT_LOG("VGA region unmapping failed\n");
+ }
}
static uint8_t find_cap_offset(struct pci_dev *pci_dev, uint8_t cap)
@@ -4013,6 +4076,89 @@ static int pt_pmcsr_reg_restore(struct pt_dev *ptdev,
return 0;
}
+static int get_vgabios(unsigned char *buf)
+{
+ int fd;
+ uint32_t bios_size = 0;
+ uint32_t start = 0xC0000;
+ uint16_t magic = 0;
+
+ if ( (fd = open("/dev/mem", O_RDONLY)) < 0 )
+ {
+ PT_LOG("Error: Can't open /dev/mem: %s\n", strerror(errno));
+ return 0;
+ }
+
+ /*
+ * Check if it a real bios extension.
+ * The magic number is 0xAA55.
+ */
+ if ( start != lseek(fd, start, SEEK_SET) )
+ goto out;
+ if ( read(fd, &magic, 2) != 2 )
+ goto out;
+ if ( magic != 0xAA55 )
+ goto out;
+
+ /* Find the size of the rom extension */
+ if ( start != lseek(fd, start, SEEK_SET) )
+ goto out;
+ if ( lseek(fd, 2, SEEK_CUR) != (start + 2) )
+ goto out;
+ if ( read(fd, &bios_size, 1) != 1 )
+ goto out;
+
+ /* This size is in 512 bytes */
+ bios_size *= 512;
+
+ /*
+ * Set the file to the begining of the rombios,
+ * to start the copy.
+ */
+ if ( start != lseek(fd, start, SEEK_SET) )
+ goto out;
+
+ if ( bios_size != read(fd, buf, bios_size))
+ bios_size = 0;
+
+out:
+ close(fd);
+ return bios_size;
+}
+
+static int setup_vga_pt(void)
+{
+ unsigned char *bios = NULL;
+ int bios_size = 0;
+ char *c = NULL;
+ char checksum = 0;
+ int rc = 0;
+
+ /* Allocated 64K for the vga bios */
+ if ( !(bios = malloc(64 * 1024)) )
+ return -1;
+
+ bios_size = get_vgabios(bios);
+ if ( bios_size == 0 || bios_size > 64 * 1024)
+ {
+ PT_LOG("vga bios size (0x%x) is invalid!\n", 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;
+
+ cpu_physical_memory_rw(0xc0000, bios, bios_size, 1);
+
+out:
+ free(bios);
+ return rc;
+}
+
static struct pt_dev * register_real_device(PCIBus *e_bus,
const char *e_dev_name, int e_devfn, uint8_t r_bus, uint8_t r_dev,
uint8_t r_func, uint32_t machine_irq, struct pci_access *pci_access,
@@ -4123,6 +4269,17 @@ static struct pt_dev * register_real_device(PCIBus
*e_bus,
/* Handle real device's MMIO/PIO BARs */
pt_register_regions(assigned_device);
+ /* Setup VGA bios for passthroughed gfx */
+ if ( gfx_passthru && (assigned_device->pci_dev->device_class == 0x0300) )
+ {
+ rc = setup_vga_pt();
+ if ( rc < 0 )
+ {
+ PT_LOG("Setup VGA BIOS of passthroughed gfx failed!\n");
+ return NULL;
+ }
+ }
+
/* reinitialize each config register to be emulated */
rc = pt_config_init(assigned_device);
if ( rc < 0 ) {
diff --git a/hw/pass-through.h b/hw/pass-through.h
index 028a03e..956e228 100644
--- a/hw/pass-through.h
+++ b/hw/pass-through.h
@@ -142,6 +142,12 @@ enum {
GRP_TYPE_EMU, /* emul reg group */
};
+enum {
+ GFX_NO_PASSTHRU = 0, /* No gfx pass-through */
+ GFX_IGD_PASSTHRU, /* IGD pass-through */
+ GFX_DISCRETE_PASSTHRU, /* Discrete gfx pass-through */
+};
+
#define PT_GET_EMUL_SIZE(flag, r_size) do { \
if (flag == PT_BAR_FLAG_MEM) {\
r_size = (((r_size) + XC_PAGE_SIZE - 1) & ~(XC_PAGE_SIZE - 1)); \
diff --git a/hw/pc.c b/hw/pc.c
index 129e9d9..53b59c0 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -41,6 +41,7 @@
#include "virtio-balloon.h"
#include "virtio-console.h"
#include "hpet_emul.h"
+#include "pass-through.h"
/* output Bochs bios info messages */
//#define DEBUG_BIOS
@@ -65,6 +66,8 @@ void tpm_tis_init(SetIRQFunc *set_irq, void *opaque, int irq);
extern uint8_t *acpi_tables;
extern size_t acpi_tables_len;
+extern int gfx_passthru;
+
static fdctrl_t *floppy_controller;
static RTCState *rtc_state;
static PITState *pit;
@@ -983,30 +986,34 @@ vga_bios_error:
register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL);
- if (cirrus_vga_enabled) {
- if (pci_enabled) {
- pci_cirrus_vga_init(pci_bus,
- phys_ram_base + vga_ram_addr,
- vga_ram_addr, vga_ram_size);
- } else {
- isa_cirrus_vga_init(phys_ram_base + vga_ram_addr,
- vga_ram_addr, vga_ram_size);
- }
+ if (gfx_passthru == GFX_NO_PASSTHRU) {
+ if (cirrus_vga_enabled) {
+ fprintf(logfile,"cirrus_vga_enabled\n");
+ if (pci_enabled) {
+ pci_cirrus_vga_init(pci_bus,
+ phys_ram_base + vga_ram_addr,
+ vga_ram_addr, vga_ram_size);
+ } else {
+ isa_cirrus_vga_init(phys_ram_base + vga_ram_addr,
+ vga_ram_addr, vga_ram_size);
+ }
#ifndef CONFIG_DM
- } else if (vmsvga_enabled) {
- if (pci_enabled)
- pci_vmsvga_init(pci_bus, phys_ram_base + vga_ram_addr,
- vga_ram_addr, vga_ram_size);
- else
- fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
+ } else if (vmsvga_enabled) {
+ if (pci_enabled)
+ pci_vmsvga_init(pci_bus, phys_ram_base + vga_ram_addr,
+ vga_ram_addr, vga_ram_size);
+ else
+ fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
#endif
- } else if (std_vga_enabled) {
- if (pci_enabled) {
- pci_vga_init(pci_bus, phys_ram_base + vga_ram_addr,
- vga_ram_addr, vga_ram_size, 0, 0);
- } else {
- isa_vga_init(phys_ram_base + vga_ram_addr,
- vga_ram_addr, vga_ram_size);
+ } else if (std_vga_enabled) {
+ fprintf(logfile,"std_vga_enabled\n");
+ if (pci_enabled) {
+ pci_vga_init(pci_bus, phys_ram_base + vga_ram_addr,
+ vga_ram_addr, vga_ram_size, 0, 0);
+ } else {
+ isa_vga_init(phys_ram_base + vga_ram_addr,
+ vga_ram_addr, vga_ram_size);
+ }
}
}
diff --git a/vl.c b/vl.c
index 06d9662..2f60aa5 100644
--- a/vl.c
+++ b/vl.c
@@ -48,6 +48,7 @@
#include <stdlib.h>
#include "qemu-xen.h"
+#include "hw/pass-through.h"
#include <unistd.h>
#include <fcntl.h>
@@ -213,6 +214,7 @@ static int rtc_date_offset = -1; /* -1 means no change */
int cirrus_vga_enabled = 1;
int std_vga_enabled = 0;
int vmsvga_enabled = 0;
+int gfx_passthru = 0;
#ifdef TARGET_SPARC
int graphic_width = 1024;
int graphic_height = 768;
@@ -4269,6 +4271,7 @@ enum {
/* Xen tree: */
QEMU_OPTION_disable_opengl,
QEMU_OPTION_direct_pci,
+ QEMU_OPTION_gfx_passthru,
QEMU_OPTION_pci_emulation,
QEMU_OPTION_vncunused,
QEMU_OPTION_videoram,
@@ -4447,6 +4450,7 @@ static const QEMUOption qemu_options[] = {
#endif
{ "acpi", 0, QEMU_OPTION_acpi }, /* deprecated, for xend compatibility */
{ "direct_pci", HAS_ARG, QEMU_OPTION_direct_pci },
+ { "gfx_passthru", HAS_ARG, QEMU_OPTION_gfx_passthru},
{ "pciemulation", HAS_ARG, QEMU_OPTION_pci_emulation },
{ "vncunused", 0, QEMU_OPTION_vncunused },
{ "vcpus", HAS_ARG, QEMU_OPTION_vcpus },
@@ -5485,6 +5489,22 @@ int main(int argc, char **argv, char **envp)
case QEMU_OPTION_runas:
run_as = optarg;
break;
+ case QEMU_OPTION_gfx_passthru:
+ gfx_passthru = atoi(optarg);
+ switch (gfx_passthru) {
+ case GFX_NO_PASSTHRU:
+ break;
+ case GFX_IGD_PASSTHRU:
+ fprintf(logfile, "IGD graphics card passthrough\n");
+ break;
+ case GFX_DISCRETE_PASSTHRU:
+ fprintf(logfile, "Discrete graphics card passthrough\n");
+ break;
+ default:
+ fprintf(stderr, "unsupported gfx_passthru option: %d\n",
+ gfx_passthru);
+ }
+ break;
}
}
}
@@ -5898,13 +5918,17 @@ int main(int argc, char **argv, char **envp)
exit(1);
xenstore_write_vncport(vnc_display_port);
}
+
+ if (gfx_passthru == GFX_NO_PASSTHRU)
+ {
#if defined(CONFIG_SDL)
- if (sdl || !vnc_display)
- sdl_display_init(ds, full_screen, no_frame,
opengl_enabled);
+ if (sdl || !vnc_display)
+ sdl_display_init(ds, full_screen, no_frame,
opengl_enabled);
#elif defined(CONFIG_COCOA)
- if (sdl || !vnc_display)
- cocoa_display_init(ds, full_screen);
+ if (sdl || !vnc_display)
+ cocoa_display_init(ds, full_screen);
#endif
+ }
}
}
dpy_resize(ds);
--
generated by git-patchbot for /home/xen/git/qemu-xen-unstable.git
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|