>From e2e2b6091b6d0075bc1d94472771571f69b451cd Mon Sep 17 00:00:00 2001
From: Weidong Han <weidong.han@xxxxxxxxx>
Date: Fri, 25 Sep 2009 18:01:30 +0800
Subject: [PATCH] support Intel integrated graphics passthrough
This patch supports passthrough Intel integrated graphics (IGD) to guest.
It needs to passthrough some registers of physical IGD and physical host
bridge for passthroughed IGD in guest.
Passthroughed IGD must use 00:02.0 in guest like in native, otherwise
the driver may fail to operate the IGD. This patch reserves the
00:02.0 in guest and assigns it IGD automatically.
Signed-off-by: Weidong Han <weidong.han@xxxxxxxxx>
---
hw/pass-through.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
hw/pass-through.h | 7 ++++
hw/pci.c | 26 ++++++++++++++++-
qemu-xen.h | 4 ++
vl.c | 6 ++++
xenstore.c | 47 +++++++++++++++++++++++++++++++
6 files changed, 168 insertions(+), 2 deletions(-)
diff --git a/hw/pass-through.c b/hw/pass-through.c
index a97368a..feaf49e 100644
--- a/hw/pass-through.c
+++ b/hw/pass-through.c
@@ -94,6 +94,7 @@
#include <sys/ioctl.h>
extern int gfx_passthru;
+int igd_passthru = 0;
struct php_dev {
struct pt_dev *pt_dev;
@@ -940,7 +941,15 @@ static int __insert_to_pci_devfn(int bus, int dev, int
func, int devfn,
PCIBus *e_bus = dpci_infos.e_bus;
int vslot;
- if ( devfn & AUTO_PHP_SLOT )
+ if ( igd_passthru && bus == 0x0 && dev == 0x2 && func == 0x0 )
+ {
+ /* passthroughed IGD must use 00:02.0 in guest */
+ if ( devfn != 0x10 )
+ devfn = 0x10;
+ if ( test_pci_devfn(devfn) || pci_devfn_in_use(e_bus, devfn) )
+ return -2;
+ }
+ else if ( devfn & AUTO_PHP_SLOT )
{
vslot = find_free_vslot();
if (vslot < 0)
@@ -2015,6 +2024,48 @@ static uint32_t find_ext_cap_offset(struct pci_dev
*pci_dev, uint32_t cap)
return 0;
}
+u8 pt_pci_host_read_byte(int bus, int dev, int fn, u32 addr)
+{
+ struct pci_dev *pci_dev;
+ u8 val;
+
+ pci_dev = pci_get_dev(dpci_infos.pci_access, 0, bus, dev, fn);
+ if ( !pci_dev )
+ return 0;
+
+ val = pci_read_byte(pci_dev, addr);
+ pci_free_dev(pci_dev);
+ return val;
+}
+
+u16 pt_pci_host_read_word(int bus, int dev, int fn, u32 addr)
+{
+ struct pci_dev *pci_dev;
+ u16 val;
+
+ pci_dev = pci_get_dev(dpci_infos.pci_access, 0, bus, dev, fn);
+ if ( !pci_dev )
+ return 0;
+
+ val = pci_read_word(pci_dev, addr);
+ pci_free_dev(pci_dev);
+ return val;
+}
+
+u32 pt_pci_host_read_long(int bus, int dev, int fn, u32 addr)
+{
+ struct pci_dev *pci_dev;
+ u32 val;
+
+ pci_dev = pci_get_dev(dpci_infos.pci_access, 0, bus, dev, fn);
+ if ( !pci_dev )
+ return 0;
+
+ val = pci_read_long(pci_dev, addr);
+ pci_free_dev(pci_dev);
+ return val;
+}
+
/* parse BAR */
static int pt_bar_reg_parse(
struct pt_dev *ptdev, struct pt_reg_info_tbl *reg)
@@ -4531,3 +4582,30 @@ uint8_t pci_intx(struct pt_dev *ptdev)
return 0;
return pci_read_intx(ptdev);
}
+
+/*
+ * Check if it's IGD passthrough
+ * This function will be called after xenstore initializion in qemu and
+ * before qemu device initialization
+ */
+void check_igd_passthrough(void)
+{
+ int num = xenstore_get_assigned_device_num();
+ int seg, bus, dev, func, devfn;
+ char *bdf_str = NULL, *opt;
+ int i;
+
+ if ( !gfx_passthru )
+ return;
+
+ for ( i = 0; i < num; i++ )
+ {
+ bdf_str = xenstore_get_assigned_device(i);
+
+ if ( !parse_bdf(&bdf_str, &seg, &bus, &dev, &func, &opt, &devfn) )
+ exit(1);
+
+ if ( bus == 0x00 && dev == 0x02 && func == 0x00 )
+ igd_passthru = 1;
+ }
+}
diff --git a/hw/pass-through.h b/hw/pass-through.h
index 028a03e..c0cdb90 100644
--- a/hw/pass-through.h
+++ b/hw/pass-through.h
@@ -404,5 +404,12 @@ static inline pciaddr_t pt_pci_base_addr(pciaddr_t base)
uint8_t pci_intx(struct pt_dev *ptdev);
+u8 pt_pci_host_read_byte(int bus, int dev, int fn, u32 addr);
+u16 pt_pci_host_read_word(int bus, int dev, int fn, u32 addr);
+u32 pt_pci_host_read_long(int bus, int dev, int fn, u32 addr);
+
+extern int igd_passthru;
+void check_igd_passthrough(void);
+
#endif /* __PASSTHROUGH_H__ */
diff --git a/hw/pci.c b/hw/pci.c
index d7c516e..ccf12a7 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -30,6 +30,7 @@
#include "exec-all.h"
#include "qemu-xen.h"
+#include "pass-through.h"
//#define DEBUG_PCI
@@ -230,6 +231,9 @@ PCIDevice *pci_register_device(PCIBus *bus, const char
*name,
if (devfn < 0) {
for(devfn = bus->devfn_min ; devfn < 256; devfn += 8) {
+ /* reserve 00:02.0 for passthroughed IGD */
+ if ( igd_passthru && devfn == 0x10 )
+ continue;
if ( !pci_devfn_in_use(bus, devfn) )
goto found;
}
@@ -611,7 +615,27 @@ uint32_t pci_data_read(void *opaque, uint32_t addr, int
len)
goto the_end;
}
config_addr = addr & 0xff;
- val = pci_dev->config_read(pci_dev, config_addr, len);
+
+ /* host bridge reads for IGD passthrough */
+ if ( igd_passthru && pci_dev->devfn == 0x00 )
+ {
+ val = pci_dev->config_read(pci_dev, config_addr, len);
+
+ if ( config_addr == 0x00 && len == 4 )
+ val = pt_pci_host_read_long(0, 0, 0, 0x00);
+ else if ( config_addr == 0x02 ) // Device ID
+ val = pt_pci_host_read_word(0, 0, 0, 0x02);
+ else if ( config_addr == 0x52 ) // GMCH Graphics Control Register
+ val = pt_pci_host_read_word(0, 0, 0, 0x52);
+ else if ( config_addr == 0xa0 ) // GMCH Top of Memory Register
+ val = pt_pci_host_read_word(0, 0, 0, 0xa0);
+ }
+ else if ( igd_passthru && pci_dev->devfn == 0x10 &&
+ config_addr == 0xfc ) // read on IGD device
+ val = 0; // use SMI to communicate with the system BIOS
+ else
+ val = pci_dev->config_read(pci_dev, config_addr, len);
+
#if defined(DEBUG_PCI)
printf("pci_config_read: %s: addr=%02x val=%08x len=%d\n",
pci_dev->name, config_addr, val, len);
diff --git a/qemu-xen.h b/qemu-xen.h
index 29b8161..1d48a4f 100644
--- a/qemu-xen.h
+++ b/qemu-xen.h
@@ -123,6 +123,10 @@ char *xenstore_read_battery_data(int battery_status);
int xenstore_refresh_battery_status(void);
int xenstore_pv_driver_build_blacklisted(uint16_t product_number,
uint32_t build_nr);
+#ifdef CONFIG_PASSTHROUGH
+int xenstore_get_assigned_device_num(void);
+char *xenstore_get_assigned_device(int index);
+#endif
/* xenfbfront.c */
int xenfb_pv_display_init(DisplayState *ds);
diff --git a/vl.c b/vl.c
index e916561..579fc2c 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>
@@ -5740,6 +5741,11 @@ int main(int argc, char **argv, char **envp)
xenstore_parse_domain_config(domid);
#endif /* CONFIG_STUBDOM */
+#ifdef CONFIG_PASSTHROUGH
+ if ( gfx_passthru )
+ check_igd_passthrough();
+#endif
+
/* we always create the cdrom drive, even if no disk is there */
#ifndef CONFIG_DM
diff --git a/xenstore.c b/xenstore.c
index e091259..b6e89f7 100644
--- a/xenstore.c
+++ b/xenstore.c
@@ -347,6 +347,53 @@ static const char *xenstore_get_guest_uuid(void)
return already_computed;
}
+#ifdef CONFIG_PASSTHROUGH
+int xenstore_get_assigned_device_num(void)
+{
+ char *path = NULL, *num_str = NULL;
+ int num_devs = 0;
+ unsigned int len;
+
+ if (pasprintf(&path,
+ "/local/domain/0/backend/pci/%u/0/num_devs", domid) == -1) {
+ fprintf(logfile, "get num_devs: out of memory.\n");
+ goto out;
+ }
+
+ num_str = xs_read(xsh, XBT_NULL, path, &len);
+ if (num_str == NULL) {
+ fprintf(logfile, "xs_read(): fail to get num_devs. %s.\n", path);
+ goto out;
+ }
+
+ num_devs = strtol(num_str, NULL, 16);
+
+out:
+ return num_devs;
+}
+
+char *xenstore_get_assigned_device(int index)
+{
+ char *path = NULL, *bdf_str = NULL;
+ unsigned int len;
+
+ if (pasprintf(&path, "/local/domain/0/backend/pci/%u/0/dev-%i",
+ domid, index) == -1) {
+ fprintf(logfile, "get assigned device: out of memory.\n");
+ goto out;
+ }
+
+ bdf_str = xs_read(xsh, XBT_NULL, path, &len);
+ if (bdf_str == NULL) {
+ fprintf(logfile, "xs_read(): fail to get assigned device. %s.\n",
path);
+ goto out;
+ }
+
+out:
+ return bdf_str;
+}
+#endif
+
#define PT_PCI_MSITRANSLATE_DEFAULT 1
#define PT_PCI_POWER_MANAGEMENT_DEFAULT 0
int direct_pci_msitranslate;
--
1.6.0.4
0001-IGD-passthrough-support.patch
Description: 0001-IGD-passthrough-support.patch
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|