Subject: xen/dom0: Reserve devices for guest use
From: http://xenbits.xensource.com/linux-2.6.18-xen.hg (tip 898:ca12928cdafe)
Patch-mainline: n/a

jb: Added support for reassign_resources=all (bnc#574224).
jb: Used kzalloc() instead of all kmalloc()+memset() pairs.
Acked-by: jbeulich@novell.com

--- head-2010-04-29.orig/Documentation/kernel-parameters.txt	2010-04-29 09:29:51.000000000 +0200
+++ head-2010-04-29/Documentation/kernel-parameters.txt	2010-04-29 09:30:30.000000000 +0200
@@ -834,6 +834,24 @@ and is between 256 and 4096 characters. 
 	gpt		[EFI] Forces disk with valid GPT signature but
 			invalid Protective MBR to be treated as GPT.
 
+	guestdev=	[PCI,ACPI,XEN]
+			Format: {<device path>|<sbdf>}][,{<device path>|<sbdf>}[,...]]
+			Format of device path: <hid>[:<uid>]-<dev>.<func>[-<dev>.<func>[,...]][+iomul]
+			Format of sbdf: [<segment>:]<bus>:<dev>.<func>[+iomul]
+			Specifies PCI device for guest domain.
+			If PCI-PCI bridge is specified, all PCI devices
+			behind PCI-PCI bridge are reserved.
+			+iomul means that this PCI function will share
+			IO ports with other +iomul functions under same
+			switch. NOTE: if +iomul is specfied, all the functions
+			of the device will share IO ports.
+
+	guestiomuldev=	[PCI,ACPI,XEN]
+			Format: [sbd][,<sbd>][,...]
+			Format of sbdf: [<segment>:]<bus>:<dev>
+			Note: function shouldn't be specified.
+			Specifies PCI device for IO port multiplexing driver.
+
 	gvp11=		[HW,SCSI]
 
 	hashdist=	[KNL,NUMA] Large hashes allocated during boot
@@ -2183,6 +2201,10 @@ and is between 256 and 4096 characters. 
 			Run specified binary instead of /init from the ramdisk,
 			used for early userspace startup. See initrd.
 
+	reassign_resources	[PCI,ACPI,XEN]
+			Use guestdev= parameter to reassign device's
+			resources, or specify =all here.
+
 	reboot=		[BUGS=X86-32,BUGS=ARM,BUGS=IA-64] Rebooting mode
 			Format: <reboot_mode>[,<reboot_mode2>[,...]]
 			See arch/*/kernel/reboot.c or arch/*/kernel/process.c
--- head-2010-04-29.orig/drivers/acpi/pci_root.c	2010-04-29 09:29:51.000000000 +0200
+++ head-2010-04-29/drivers/acpi/pci_root.c	2010-04-15 09:39:25.000000000 +0200
@@ -419,6 +419,40 @@ out:
 }
 EXPORT_SYMBOL(acpi_pci_osc_control_set);
 
+#ifdef CONFIG_PCI_GUESTDEV
+#include <linux/sysfs.h>
+
+static ssize_t seg_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct list_head *entry;
+
+	list_for_each(entry, &acpi_pci_roots) {
+		struct acpi_pci_root *root;
+		root = list_entry(entry, struct acpi_pci_root, node);
+		if (&root->device->dev == dev)
+			return sprintf(buf, "%04x\n", root->segment);
+	}
+	return 0;
+}
+static DEVICE_ATTR(seg, 0444, seg_show, NULL);
+
+static ssize_t bbn_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct list_head *entry;
+
+	list_for_each(entry, &acpi_pci_roots) {
+		struct acpi_pci_root *root;
+		root = list_entry(entry, struct acpi_pci_root, node);
+		if (&root->device->dev == dev)
+			return sprintf(buf, "%02x\n", root->bus_nr);
+	}
+	return 0;
+}
+static DEVICE_ATTR(bbn, 0444, bbn_show, NULL);
+#endif
+
 static int __devinit acpi_pci_root_add(struct acpi_device *device)
 {
 	unsigned long long segment, bus;
@@ -530,6 +564,13 @@ static int __devinit acpi_pci_root_add(s
 	if (flags != base_flags)
 		acpi_pci_osc_support(root, flags);
 
+#ifdef CONFIG_PCI_GUESTDEV
+	if (device_create_file(&device->dev, &dev_attr_seg))
+		dev_warn(&device->dev, "could not create seg attr\n");
+	if (device_create_file(&device->dev, &dev_attr_bbn))
+		dev_warn(&device->dev, "could not create bbn attr\n");
+#endif
+
 	pci_acpi_add_bus_pm_notifier(device, root->bus);
 	if (device->wakeup.flags.run_wake)
 		device_set_run_wake(root->bus->bridge, true);
@@ -575,3 +616,31 @@ static int __init acpi_pci_root_init(voi
 }
 
 subsys_initcall(acpi_pci_root_init);
+
+#ifdef CONFIG_PCI_GUESTDEV
+int acpi_pci_get_root_seg_bbn(char *hid, char *uid, int *seg, int *bbn)
+{
+	struct list_head *entry;
+
+	list_for_each(entry, &acpi_pci_roots) {
+		struct acpi_pci_root *root;
+
+		root = list_entry(entry, struct acpi_pci_root, node);
+		if (strcmp(acpi_device_hid(root->device), hid))
+			continue;
+
+		if (!root->device->pnp.unique_id) {
+			if (strlen(uid))
+				continue;
+		} else {
+			if (strcmp(root->device->pnp.unique_id, uid))
+				continue;
+		}
+
+		*seg = (int)root->segment;
+		*bbn = (int)root->bus_nr;
+		return TRUE;
+	}
+	return FALSE;
+}
+#endif
--- head-2010-04-29.orig/drivers/acpi/scan.c	2010-04-29 09:29:51.000000000 +0200
+++ head-2010-04-29/drivers/acpi/scan.c	2010-04-15 09:39:27.000000000 +0200
@@ -170,6 +170,16 @@ acpi_device_hid_show(struct device *dev,
 }
 static DEVICE_ATTR(hid, 0444, acpi_device_hid_show, NULL);
 
+#ifdef CONFIG_PCI_GUESTDEV
+static ssize_t
+acpi_device_uid_show(struct device *dev, struct device_attribute *attr, char *buf) {
+	struct acpi_device *acpi_dev = to_acpi_device(dev);
+
+	return sprintf(buf, "%s\n", acpi_dev->pnp.unique_id);
+}
+static DEVICE_ATTR(uid, 0444, acpi_device_uid_show, NULL);
+#endif
+
 static ssize_t
 acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *buf) {
 	struct acpi_device *acpi_dev = to_acpi_device(dev);
@@ -210,6 +220,13 @@ static int acpi_device_setup_files(struc
 	if (result)
 		goto end;
 
+#ifdef CONFIG_PCI_GUESTDEV
+	if(dev->pnp.unique_id) {
+		result = device_create_file(&dev->dev, &dev_attr_uid);
+		if(result)
+			goto end;
+	}
+#endif
         /*
          * If device has _EJ0, 'eject' file is created that is used to trigger
          * hot-removal function from userland.
@@ -273,6 +290,9 @@ static void acpi_free_ids(struct acpi_de
 		kfree(id->id);
 		kfree(id);
 	}
+#ifdef CONFIG_PCI_GUESTDEV
+	kfree(device->pnp.unique_id);
+#endif
 }
 
 static void acpi_device_release(struct device *dev)
@@ -1096,6 +1116,11 @@ static void acpi_device_set_id(struct ac
 			for (i = 0; i < cid_list->count; i++)
 				acpi_add_id(device, cid_list->ids[i].string);
 		}
+#ifdef CONFIG_PCI_GUESTDEV
+		if (info->valid & ACPI_VALID_UID)
+			device->pnp.unique_id = kstrdup(info->unique_id.string,
+							GFP_KERNEL);
+#endif
 		if (info->valid & ACPI_VALID_ADR) {
 			device->pnp.bus_address = info->address;
 			device->flags.bus_address = 1;
--- head-2010-04-29.orig/drivers/pci/Kconfig	2010-04-29 09:29:51.000000000 +0200
+++ head-2010-04-29/drivers/pci/Kconfig	2010-03-24 13:55:21.000000000 +0100
@@ -31,6 +31,20 @@ config PCI_DEBUG
 
 	  When in doubt, say N.
 
+config PCI_GUESTDEV
+	bool "PCI Device Reservation for Passthrough"
+	depends on PCI && ACPI && XEN
+	default y
+	help
+	  Say Y here if you want to reserve PCI device for passthrough.
+
+config PCI_IOMULTI
+	bool "PCI Device IO Multiplex for Passthrough"
+	depends on PCI && ACPI && XEN
+	default y
+	help
+	  Say Y here if you need io multiplexing.
+
 config PCI_STUB
 	tristate "PCI Stub driver"
 	depends on PCI
--- head-2010-04-29.orig/drivers/pci/Makefile	2010-04-29 09:29:51.000000000 +0200
+++ head-2010-04-29/drivers/pci/Makefile	2010-03-24 13:55:21.000000000 +0100
@@ -7,6 +7,8 @@ obj-y		+= access.o bus.o probe.o remove.
 			irq.o vpd.o
 obj-$(CONFIG_PROC_FS) += proc.o
 obj-$(CONFIG_SYSFS) += slot.o
+obj-$(CONFIG_PCI_GUESTDEV) += guestdev.o
+obj-$(CONFIG_PCI_IOMULTI) += iomulti.o
 
 obj-$(CONFIG_PCI_QUIRKS) += quirks.o
 
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ head-2010-04-29/drivers/pci/guestdev.c	2010-04-28 15:57:49.000000000 +0200
@@ -0,0 +1,887 @@
+/*
+ * Copyright (c) 2008, 2009 NEC Corporation.
+ * Copyright (c) 2009 Isaku Yamahata
+ *                    VA Linux Systems Japan K.K.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ * Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/acpi.h>
+#include <asm/setup.h>
+
+#define HID_LEN 8
+#define UID_LEN 8
+#define DEV_LEN 2
+#define FUNC_LEN 1
+#define DEV_NUM_MAX 31
+#define FUNC_NUM_MAX 7
+#define INVALID_SEG (-1)
+#define INVALID_BBN (-1)
+#define GUESTDEV_STR_MAX 128
+
+#define GUESTDEV_FLAG_TYPE_MASK 0x3
+#define GUESTDEV_FLAG_DEVICEPATH 0x1
+#define GUESTDEV_FLAG_SBDF 0x2
+
+#define GUESTDEV_OPT_IOMUL	0x1
+
+struct guestdev {
+	int flags;
+	int options;
+	struct list_head root_list;
+	union {
+		struct devicepath {
+			char hid[HID_LEN + 1];
+			char uid[UID_LEN + 1];
+			int seg;
+			int bbn;
+			struct devicepath_node *child;
+		} devicepath;
+		struct sbdf {
+			int seg;
+			int bus;
+			int dev;
+			int func;
+		} sbdf;
+	} u;
+};
+
+struct devicepath_node {
+	int dev;
+	int func;
+	struct devicepath_node *child;
+};
+
+struct pcidev_sbdf {
+	int seg;
+	int bus;
+	struct pcidev_sbdf_node *child;
+};
+
+struct pcidev_sbdf_node {
+	int dev;
+	int func;
+	struct pcidev_sbdf_node *child;
+};
+
+static char guestdev_param[COMMAND_LINE_SIZE];
+static LIST_HEAD(guestdev_list);
+
+/* Get hid and uid */
+static int __init pci_get_hid_uid(char *str, char *hid, char *uid)
+{
+	char *sp, *ep;
+	int len;
+
+	sp = str;
+	ep = strchr(sp, ':');
+	if (!ep) {
+		ep = strchr(sp, '-');
+		if (!ep)
+			goto format_err_end;
+	}
+	/* hid length */
+	len = ep - sp;
+	if (len <= 0 || HID_LEN < len)
+		goto format_err_end;
+
+	strlcpy(hid, sp, len);
+
+	if (*ep == '-') { /* no uid */
+		uid[0] = '\0';
+		return TRUE;
+	}
+
+	sp = ep + 1;
+	ep = strchr(sp, '-');
+	if (!ep)
+		ep = strchr(sp, '\0');
+
+	/* uid length */
+	len = ep - sp;
+	if (len <= 0 || UID_LEN < len)
+		goto format_err_end;
+
+	strlcpy(uid, sp, len);
+	return TRUE;
+
+format_err_end:
+	return FALSE;
+}
+
+/* Get device and function */
+static int __init pci_get_dev_func(char *str, int *dev, int *func)
+{
+	if (sscanf(str, "%02x.%01x", dev, func) != 2)
+		goto format_err_end;
+
+	if (*dev < 0 || DEV_NUM_MAX < *dev)
+		goto format_err_end;
+
+	if (*func < 0 || FUNC_NUM_MAX < *func)
+		goto format_err_end;
+
+	return TRUE;
+
+format_err_end:
+	return FALSE;
+}
+
+/* Check extended guestdev parameter format error */
+static int __init pci_check_extended_guestdev_format(char *str)
+{
+	int flg;
+	char *p;
+
+	/* Check extended format */
+	if (strpbrk(str, "(|)") == NULL)
+		return TRUE;
+
+	flg = 0;
+	p = str;
+	while (*p) {
+		switch (*p) {
+		case '(':
+			/* Check nesting error */
+			if (flg != 0)
+				goto format_err_end;
+			flg = 1;
+			/* Check position of '(' is head or
+			   previos charactor of '(' is not '-'. */
+			if (p == str || *(p - 1) != '-')
+				goto format_err_end;
+			break;
+		case ')':
+			/* Check nesting error */
+			if (flg != 1)
+				goto format_err_end;
+			flg = 0;
+			/* Check next charactor of ')' is not '\0' */
+			if (*(p + 1) != '\0')
+				goto format_err_end;
+			break;
+		case '|':
+			/* Check position of '|' is outside of '(' and ')' */
+			if (flg != 1)
+				goto format_err_end;
+			break;
+		default:
+			break;
+		}
+		p++;
+	}
+	/* Check number of '(' and ')' are not equal */
+	if (flg != 0)
+		goto format_err_end;
+	return TRUE;
+
+format_err_end:
+	printk(KERN_ERR
+		"PCI: The format of the guestdev parameter is illegal. [%s]\n",
+		str);
+	return FALSE;
+}
+
+/* Make guestdev strings */
+static void pci_make_guestdev_str(struct guestdev *gdev,
+					char *gdev_str, int buf_size)
+{
+	struct devicepath_node *node;
+	int count;
+
+	switch (gdev->flags & GUESTDEV_FLAG_TYPE_MASK) {
+	case GUESTDEV_FLAG_DEVICEPATH:
+		memset(gdev_str, 0, buf_size);
+
+		if (strlen(gdev->u.devicepath.uid))
+			count = snprintf(gdev_str, buf_size, "%s:%s",
+						gdev->u.devicepath.hid,
+						gdev->u.devicepath.uid);
+		else
+			count = snprintf(gdev_str, buf_size, "%s",
+						 gdev->u.devicepath.hid);
+		if (count < 0)
+			return;
+
+		node = gdev->u.devicepath.child;
+		while (node) {
+			gdev_str += count;
+			buf_size -= count;
+			if (buf_size <= 0)
+				return;
+			count = snprintf(gdev_str, buf_size, "-%02x.%01x",
+				node->dev, node->func);
+			if (count < 0)
+				return;
+			node = node->child;
+		}
+		break;
+	case GUESTDEV_FLAG_SBDF:
+		snprintf(gdev_str, buf_size, "%04x:%02x:%02x.%01x",
+					gdev->u.sbdf.seg, gdev->u.sbdf.bus,
+					gdev->u.sbdf.dev, gdev->u.sbdf.func);
+		break;
+	default:
+		BUG();
+	}
+}
+
+/* Free guestdev and nodes */
+static void __init pci_free_guestdev(struct guestdev *gdev)
+{
+	struct devicepath_node *node, *next;
+
+	if (!gdev)
+		return;
+	if (gdev->flags & GUESTDEV_FLAG_DEVICEPATH) {
+		node = gdev->u.devicepath.child;
+		while (node) {
+			next = node->child;
+			kfree(node);
+			node = next;
+		}
+	}
+	list_del(&gdev->root_list);
+	kfree(gdev);
+}
+
+/* Copy guestdev and nodes */
+struct guestdev __init *pci_copy_guestdev(struct guestdev *gdev_src)
+{
+	struct guestdev *gdev;
+	struct devicepath_node *node, *node_src, *node_upper;
+
+	BUG_ON(!(gdev_src->flags & GUESTDEV_FLAG_DEVICEPATH));
+
+	gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
+	if (!gdev)
+		goto allocate_err_end;
+
+	INIT_LIST_HEAD(&gdev->root_list);
+	gdev->flags = gdev_src->flags;
+	gdev->options = gdev_src->options;
+	strcpy(gdev->u.devicepath.hid, gdev_src->u.devicepath.hid);
+	strcpy(gdev->u.devicepath.uid, gdev_src->u.devicepath.uid);
+	gdev->u.devicepath.seg = gdev_src->u.devicepath.seg;
+	gdev->u.devicepath.bbn = gdev_src->u.devicepath.bbn;
+
+	node_upper = NULL;
+
+	node_src = gdev_src->u.devicepath.child;
+	while (node_src) {
+		node = kzalloc(sizeof(*node), GFP_KERNEL);
+		if (!node)
+			goto allocate_err_end;
+		node->dev = node_src->dev;
+		node->func = node_src->func;
+		if (!node_upper)
+			gdev->u.devicepath.child = node;
+		else
+			node_upper->child = node;
+		node_upper = node;
+		node_src = node_src->child;
+	}
+
+	return gdev;
+
+allocate_err_end:
+	if (gdev)
+		pci_free_guestdev(gdev);
+	printk(KERN_ERR "PCI: Failed to allocate memory.\n");
+	return NULL;
+}
+
+/* Make guestdev from path strings */
+static int __init pci_make_devicepath_guestdev(char *path_str, int options)
+{
+	char hid[HID_LEN + 1], uid[UID_LEN + 1];
+	char *sp, *ep;
+	struct guestdev *gdev, *gdev_org;
+	struct devicepath_node *node, *node_tmp;
+	int dev, func, ret_val;
+
+	ret_val = 0;
+	gdev = gdev_org = NULL;
+	sp = path_str;
+	/* Look for end of hid:uid'-' */
+	ep = strchr(sp, '-');
+	/* Only hid, uid. (No dev, func) */
+	if (!ep)
+		goto format_err_end;
+
+	memset(hid, 0 ,sizeof(hid));
+	memset(uid, 0, sizeof(uid));
+	if (!pci_get_hid_uid(sp, hid, uid))
+		goto format_err_end;
+
+	gdev_org = kzalloc(sizeof(*gdev_org), GFP_KERNEL);
+	if (!gdev_org)
+		goto allocate_err_end;
+	INIT_LIST_HEAD(&gdev_org->root_list);
+	gdev_org->flags = GUESTDEV_FLAG_DEVICEPATH;
+	gdev_org->options = options;
+	strcpy(gdev_org->u.devicepath.hid, hid);
+	strcpy(gdev_org->u.devicepath.uid, uid);
+	gdev_org->u.devicepath.seg = INVALID_SEG;
+	gdev_org->u.devicepath.bbn = INVALID_BBN;
+
+	gdev = gdev_org;
+
+	sp = ep + 1;
+	ep = sp;
+	do {
+		if (*sp == '(') {
+			sp++;
+			if (strchr(sp, '|')) {
+				gdev = pci_copy_guestdev(gdev_org);
+				if (!gdev) {
+					ret_val = -ENOMEM;
+					goto end;
+				}
+			}
+			continue;
+		}
+		if (gdev && pci_get_dev_func(sp, &dev, &func)) {
+			node = kzalloc(sizeof(*node), GFP_KERNEL);
+			if (!node)
+				goto allocate_err_end;
+			node->dev = dev;
+			node->func = func;
+			/* add node to end of guestdev */
+			if (gdev->u.devicepath.child) {
+				node_tmp = gdev->u.devicepath.child;
+				while (node_tmp->child) {
+					node_tmp = node_tmp->child;
+				}
+				node_tmp->child = node;
+			} else
+				gdev->u.devicepath.child = node;
+		} else if (gdev) {
+			printk(KERN_ERR
+				"PCI: Can't obtain dev# and #func# from %s.\n",
+				sp);
+			ret_val = -EINVAL;
+			if (gdev == gdev_org)
+				goto end;
+			pci_free_guestdev(gdev);
+			gdev = NULL;
+		}
+
+		ep = strpbrk(sp, "-|)");
+		if (!ep)
+			ep = strchr(sp, '\0');
+		/* Is *ep '|' OR ')' OR '\0' ? */
+		if (*ep != '-') {
+			if (gdev)
+				list_add_tail(&gdev->root_list, &guestdev_list);
+			if (*ep == '|') {
+				/* Between '|' and '|' ? */
+				if (strchr(ep + 1, '|')) {
+					gdev = pci_copy_guestdev(gdev_org);
+					if (!gdev) {
+						ret_val = -ENOMEM;
+						goto end;
+					}
+				} else {
+					gdev = gdev_org;
+					gdev_org = NULL;
+				}
+			} else {
+				gdev_org = NULL;
+				gdev = NULL;
+			}
+		}
+		if (*ep == ')')
+			ep++;
+		sp = ep + 1;
+	} while (*ep != '\0');
+
+	goto end;
+
+format_err_end:
+	printk(KERN_ERR
+		"PCI: The format of the guestdev parameter is illegal. [%s]\n",
+		path_str);
+	ret_val = -EINVAL;
+	goto end;
+
+allocate_err_end:
+	printk(KERN_ERR "PCI: Failed to allocate memory.\n");
+	ret_val = -ENOMEM;
+	goto end;
+
+end:
+	if (gdev_org && (gdev_org != gdev))
+		pci_free_guestdev(gdev_org);
+	if (gdev)
+		pci_free_guestdev(gdev);
+	return ret_val;
+}
+
+static int __init pci_make_sbdf_guestdev(char* str, int options)
+{
+	struct guestdev *gdev;
+	int seg, bus, dev, func;
+
+	if (sscanf(str, "%x:%x:%x.%x", &seg, &bus, &dev, &func) != 4) {
+		seg = 0;
+		if (sscanf(str, "%x:%x.%x", &bus, &dev, &func) != 3)
+			return -EINVAL;
+	}
+	gdev = kmalloc(sizeof(*gdev), GFP_KERNEL);
+	if (!gdev) {
+		printk(KERN_ERR "PCI: Failed to allocate memory.\n");
+		return -ENOMEM;
+	}
+	INIT_LIST_HEAD(&gdev->root_list);
+	gdev->flags = GUESTDEV_FLAG_SBDF;
+	gdev->options = options;
+	gdev->u.sbdf.seg = seg;
+	gdev->u.sbdf.bus = bus;
+	gdev->u.sbdf.dev = dev;
+	gdev->u.sbdf.func = func;
+	list_add_tail(&gdev->root_list, &guestdev_list);
+	return 0;
+}
+
+static int __init pci_parse_options(const char *str)
+{
+	int options = 0;
+	char *ep;
+
+	while (str) {
+		str++;
+		ep = strchr(str, '+');
+		if (ep)
+			ep = '\0';	/* Chop */
+
+		if (!strcmp(str, "iomul"))
+			options |= GUESTDEV_OPT_IOMUL;
+
+		str = ep;
+	}
+	return options;
+}
+
+/* Parse guestdev parameter */
+static int __init pci_parse_guestdev(void)
+{
+	int len;
+	char *sp, *ep, *op;
+	int options;
+	struct list_head *head;
+	struct guestdev *gdev;
+	char path_str[GUESTDEV_STR_MAX];
+	int ret_val = 0;
+
+	len = strlen(guestdev_param);
+	if (len == 0)
+		return 0;
+
+	sp = guestdev_param;
+
+	do {
+		ep = strchr(sp, ',');
+		/* Chop */
+		if (ep)
+			*ep = '\0';
+		options = 0;
+		op = strchr(sp, '+');
+		if (op && (!ep || op < ep)) {
+			options = pci_parse_options(op);
+			*op = '\0';	/* Chop */
+		}
+		ret_val = pci_make_sbdf_guestdev(sp, options);
+		if (ret_val == -EINVAL) {
+			if (pci_check_extended_guestdev_format(sp)) {
+				ret_val = pci_make_devicepath_guestdev(
+					sp, options);
+				if (ret_val && ret_val != -EINVAL)
+					break;
+			}
+		} else if (ret_val)
+			break;
+
+		if (ep)
+			ep++;
+		sp = ep;
+	} while (ep);
+
+	list_for_each(head, &guestdev_list) {
+		gdev = list_entry(head, struct guestdev, root_list);
+		pci_make_guestdev_str(gdev, path_str, GUESTDEV_STR_MAX);
+		printk(KERN_DEBUG
+			"PCI: %s has been reserved for guest domain.\n",
+			path_str);
+	}
+	return 0;
+}
+
+arch_initcall(pci_parse_guestdev);
+
+/* Get command line */
+static int __init pci_guestdev_setup(char *str)
+{
+	if (strlen(str) >= COMMAND_LINE_SIZE)
+		return 0;
+	strlcpy(guestdev_param, str, sizeof(guestdev_param));
+	return 1;
+}
+
+__setup("guestdev=", pci_guestdev_setup);
+
+/* Free sbdf and nodes */
+static void pci_free_sbdf(struct pcidev_sbdf *sbdf)
+{
+	struct pcidev_sbdf_node *node, *next;
+
+	node = sbdf->child;
+	while (node) {
+		next = node->child;
+		kfree(node);
+		node = next;
+	}
+	/* Skip kfree(sbdf) */
+}
+
+/* Does PCI device belong to sub tree specified by guestdev with device path? */
+typedef int (*pci_node_match_t)(const struct devicepath_node *gdev_node,
+				const struct pcidev_sbdf_node *sbdf_node,
+				int options);
+
+static int pci_node_match(const struct devicepath_node *gdev_node,
+			  const struct pcidev_sbdf_node *sbdf_node,
+			  int options_unused)
+{
+	return (gdev_node->dev == sbdf_node->dev &&
+		gdev_node->func == sbdf_node->func);
+}
+
+static int pci_is_in_devicepath_sub_tree(struct guestdev *gdev,
+					 struct pcidev_sbdf *sbdf,
+					 pci_node_match_t match)
+{
+	int seg, bbn;
+	struct devicepath_node *gdev_node;
+	struct pcidev_sbdf_node *sbdf_node;
+
+	if (!gdev || !sbdf)
+		return FALSE;
+
+	BUG_ON(!(gdev->flags & GUESTDEV_FLAG_DEVICEPATH));
+
+	/* Compare seg and bbn */
+	if (gdev->u.devicepath.seg == INVALID_SEG ||
+	    gdev->u.devicepath.bbn == INVALID_BBN) {
+		if (acpi_pci_get_root_seg_bbn(gdev->u.devicepath.hid,
+		    gdev->u.devicepath.uid, &seg, &bbn)) {
+			gdev->u.devicepath.seg = seg;
+			gdev->u.devicepath.bbn = bbn;
+		} else
+			return FALSE;
+	}
+
+	if (gdev->u.devicepath.seg != sbdf->seg ||
+	    gdev->u.devicepath.bbn != sbdf->bus)
+		return FALSE;
+
+	gdev_node = gdev->u.devicepath.child;
+	sbdf_node = sbdf->child;
+
+	/* Compare dev and func */
+	while (gdev_node) {
+		if (!sbdf_node)
+			return FALSE;
+		if (!match(gdev_node, sbdf_node, gdev->options))
+			return FALSE;
+		gdev_node = gdev_node->child;
+		sbdf_node = sbdf_node->child;
+	}
+	return TRUE;
+}
+
+/* Get sbdf from device */
+static int pci_get_sbdf_from_pcidev(
+	struct pci_dev *dev, struct pcidev_sbdf *sbdf)
+{
+	struct pcidev_sbdf_node *node;
+
+	if (!dev)
+		return FALSE;
+
+	for(;;) {
+		node = kzalloc(sizeof(*node), GFP_KERNEL);
+		if (!node) {
+			printk(KERN_ERR "PCI: Failed to allocate memory.\n");
+			goto err_end;
+		}
+		node->dev = PCI_SLOT(dev->devfn);
+		node->func = PCI_FUNC(dev->devfn);
+
+		if (!sbdf->child)
+			sbdf->child = node;
+		else {
+			node->child = sbdf->child;
+			sbdf->child = node;
+		}
+		if (!dev->bus)
+			goto err_end;
+		if (!dev->bus->self)
+			break;
+		dev = dev->bus->self;
+	}
+	if (sscanf(dev_name(&dev->dev), "%04x:%02x", &sbdf->seg, &sbdf->bus) != 2)
+		goto err_end;
+	return TRUE;
+
+err_end:
+	pci_free_sbdf(sbdf);
+	return FALSE;
+}
+
+/* Does PCI device belong to sub tree specified by guestdev with sbdf? */
+typedef int (*pci_sbdf_match_t)(const struct guestdev *gdev,
+				const  struct pci_dev *dev);
+
+static int pci_sbdf_match(const struct guestdev *gdev,
+			  const struct pci_dev *dev)
+{
+	int seg, bus;
+
+	if (sscanf(dev_name(&dev->dev), "%04x:%02x", &seg, &bus) != 2)
+		return FALSE;
+
+	return gdev->u.sbdf.seg == seg &&
+		gdev->u.sbdf.bus == bus &&
+		gdev->u.sbdf.dev == PCI_SLOT(dev->devfn) &&
+		gdev->u.sbdf.func == PCI_FUNC(dev->devfn);
+}
+
+static int pci_is_in_sbdf_sub_tree(struct guestdev *gdev, struct pci_dev *dev,
+				   pci_sbdf_match_t match)
+{
+	BUG_ON(!(gdev->flags & GUESTDEV_FLAG_SBDF));
+	for (;;) {
+		if (match(gdev, dev))
+			return TRUE;
+		if (!dev->bus || !dev->bus->self)
+			break;
+		dev = dev->bus->self;
+	}
+	return FALSE;
+}
+
+/* Does PCI device belong to sub tree specified by guestdev parameter? */
+static int __pci_is_guestdev(struct pci_dev *dev, pci_node_match_t node_match,
+			     pci_sbdf_match_t sbdf_match)
+{
+	struct guestdev *gdev;
+	struct pcidev_sbdf pcidev_sbdf, *sbdf = NULL;
+	struct list_head *head;
+	int result = FALSE;
+
+	if (!dev)
+		return FALSE;
+
+	list_for_each(head, &guestdev_list) {
+		gdev = list_entry(head, struct guestdev, root_list);
+		switch (gdev->flags & GUESTDEV_FLAG_TYPE_MASK) {
+		case GUESTDEV_FLAG_DEVICEPATH:
+			if (sbdf == NULL) {
+				sbdf = &pcidev_sbdf;
+				memset(sbdf, 0 ,sizeof(*sbdf));
+				if (!pci_get_sbdf_from_pcidev(dev, sbdf))
+					goto out;
+			}
+			if (pci_is_in_devicepath_sub_tree(gdev, sbdf,
+							  node_match)) {
+				result = TRUE;
+				goto out;
+			}
+			break;
+		case GUESTDEV_FLAG_SBDF:
+			if (pci_is_in_sbdf_sub_tree(gdev, dev, sbdf_match)) {
+				result = TRUE;
+				goto out;
+			}
+			break;
+		default:
+			BUG();
+		}
+	}
+out:
+	if (sbdf)
+		pci_free_sbdf(sbdf);
+	return result;
+}
+
+int pci_is_guestdev(struct pci_dev *dev)
+{
+	return __pci_is_guestdev(dev, pci_node_match, pci_sbdf_match);
+}
+EXPORT_SYMBOL_GPL(pci_is_guestdev);
+
+static int reassign_resources;
+
+static int __init pci_set_reassign_resources(char *str)
+{
+	if (str && !strcmp(str, "all"))
+		reassign_resources = -1;
+	else
+		reassign_resources = 1;
+
+	return 1;
+}
+__setup("reassign_resources", pci_set_reassign_resources);
+
+int pci_is_guestdev_to_reassign(struct pci_dev *dev)
+{
+	if (reassign_resources < 0)
+		return TRUE;
+	if (reassign_resources)
+		return pci_is_guestdev(dev);
+	return FALSE;
+}
+
+#ifdef CONFIG_PCI_IOMULTI
+static int pci_iomul_node_match(const struct devicepath_node *gdev_node,
+				const struct pcidev_sbdf_node *sbdf_node,
+				int options)
+{
+	return (options & GUESTDEV_OPT_IOMUL) &&
+		((gdev_node->child != NULL &&
+		  sbdf_node->child != NULL &&
+		  gdev_node->dev == sbdf_node->dev &&
+		  gdev_node->func == sbdf_node->func) ||
+		 (gdev_node->child == NULL &&
+		  sbdf_node->child == NULL &&
+		  gdev_node->dev == sbdf_node->dev));
+}
+
+static int pci_iomul_sbdf_match(const struct guestdev *gdev,
+				const struct pci_dev *dev)
+{
+	int seg, bus;
+
+	if (sscanf(dev_name(&dev->dev), "%04x:%02x", &seg, &bus) != 2)
+		return FALSE;
+
+	return (gdev->options & GUESTDEV_OPT_IOMUL) &&
+		gdev->u.sbdf.seg == seg &&
+		gdev->u.sbdf.bus == bus &&
+		gdev->u.sbdf.dev == PCI_SLOT(dev->devfn);
+}
+
+int pci_is_iomuldev(struct pci_dev *dev)
+{
+	return __pci_is_guestdev(dev,
+				 pci_iomul_node_match, pci_iomul_sbdf_match);
+}
+#endif /* CONFIG_PCI_IOMULTI */
+
+/* Check whether the devicepath exists under the pci root bus */
+static int __init pci_check_devicepath_exists(
+		struct guestdev *gdev, struct pci_bus *bus)
+{
+	struct devicepath_node *node;
+	struct pci_dev *dev;
+
+	BUG_ON(!(gdev->flags & GUESTDEV_FLAG_DEVICEPATH));
+
+	node = gdev->u.devicepath.child;
+	while (node) {
+		if (!bus)
+			return FALSE;
+		dev = pci_get_slot(bus, PCI_DEVFN(node->dev, node->func));
+		if (!dev)
+			return FALSE;
+		bus = dev->subordinate;
+		node = node->child;
+		pci_dev_put(dev);
+	}
+	return TRUE;
+}
+
+/* Check whether the guestdev exists in the PCI device tree */
+static int __init pci_check_guestdev_exists(void)
+{
+	struct list_head *head;
+	struct guestdev *gdev;
+	int seg, bbn;
+	struct pci_bus *bus;
+	struct pci_dev *dev;
+	char path_str[GUESTDEV_STR_MAX];
+
+	list_for_each(head, &guestdev_list) {
+		gdev = list_entry(head, struct guestdev, root_list);
+		switch (gdev->flags & GUESTDEV_FLAG_TYPE_MASK) {
+		case GUESTDEV_FLAG_DEVICEPATH:
+			if (gdev->u.devicepath.seg == INVALID_SEG ||
+				gdev->u.devicepath.bbn == INVALID_BBN) {
+				if (acpi_pci_get_root_seg_bbn(
+					gdev->u.devicepath.hid,
+					gdev->u.devicepath.uid, &seg, &bbn)) {
+					gdev->u.devicepath.seg = seg;
+					gdev->u.devicepath.bbn = bbn;
+				} else {
+					pci_make_guestdev_str(gdev,
+						path_str, GUESTDEV_STR_MAX);
+					printk(KERN_INFO
+					"PCI: Device does not exist. %s\n",
+					path_str);
+					continue;
+				}
+			}
+
+			bus = pci_find_bus(gdev->u.devicepath.seg,
+						gdev->u.devicepath.bbn);
+			if (!bus ||
+				!pci_check_devicepath_exists(gdev, bus)) {
+				pci_make_guestdev_str(gdev, path_str,
+					GUESTDEV_STR_MAX);
+				printk(KERN_INFO
+					"PCI: Device does not exist. %s\n",
+					path_str);
+			}
+			break;
+		case GUESTDEV_FLAG_SBDF:
+			bus = pci_find_bus(gdev->u.sbdf.seg, gdev->u.sbdf.bus);
+			if (bus) {
+				dev = pci_get_slot(bus,
+					PCI_DEVFN(gdev->u.sbdf.dev,
+							gdev->u.sbdf.func));
+				if (dev) {
+					pci_dev_put(dev);
+					continue;
+				}
+			}
+			pci_make_guestdev_str(gdev, path_str, GUESTDEV_STR_MAX);
+			printk(KERN_INFO "PCI: Device does not exist. %s\n",
+								path_str);
+			break;
+		default:
+			BUG();
+		}
+	}
+	return 0;
+}
+
+fs_initcall(pci_check_guestdev_exists);
+
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ head-2010-04-29/drivers/pci/iomulti.c	2010-03-24 13:55:21.000000000 +0100
@@ -0,0 +1,1415 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright (c) 2009 Isaku Yamahata
+ *                    VA Linux Systems Japan K.K.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/sort.h>
+
+#include <asm/setup.h>
+#include <asm/uaccess.h>
+
+#include "pci.h"
+#include "iomulti.h"
+
+#define PCI_NUM_BARS		6
+#define PCI_BUS_MAX		255
+#define PCI_DEV_MAX		31
+#define PCI_FUNC_MAX		7
+#define PCI_NUM_FUNC		8
+
+/* see pci_resource_len */
+static inline resource_size_t pci_iomul_len(const struct resource* r)
+{
+	if (r->start == 0 && r->start == r->end)
+		return 0;
+	return r->end - r->start + 1;
+}
+
+#define ROUND_UP(x, a)		(((x) + (a) - 1) & ~((a) - 1))
+/* stolen from pbus_size_io() */
+static unsigned long pdev_size_io(struct pci_dev *pdev)
+{
+	unsigned long size = 0, size1 = 0;
+	int i;
+
+	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+		struct resource *r = &pdev->resource[i];
+		unsigned long r_size;
+
+		if (!(r->flags & IORESOURCE_IO))
+			continue;
+
+		r_size = r->end - r->start + 1;
+
+		if (r_size < 0x400)
+			/* Might be re-aligned for ISA */
+			size += r_size;
+		else
+			size1 += r_size;
+	}
+
+/* To be fixed in 2.5: we should have sort of HAVE_ISA
+   flag in the struct pci_bus. */
+#if defined(CONFIG_ISA) || defined(CONFIG_EISA)
+	size = (size & 0xff) + ((size & ~0xffUL) << 2);
+#endif
+	size = ROUND_UP(size + size1, 4096);
+	return size;
+}
+
+/*
+ * primary bus number of PCI-PCI bridge in switch on which
+ * this slots sits.
+ * i.e. the primary bus number of PCI-PCI bridge of downstream port
+ *      or root port in switch.
+ *      the secondary bus number of PCI-PCI bridge of upstream port
+ *      in switch.
+ */
+static inline unsigned char pci_dev_switch_busnr(struct pci_dev *pdev)
+{
+	if (pci_find_capability(pdev, PCI_CAP_ID_EXP))
+		return pdev->bus->primary;
+	return pdev->bus->number;
+}
+
+struct pci_iomul_func {
+	int		segment;
+	uint8_t		bus;
+	uint8_t		devfn;
+
+	/* only start and end are used */
+	unsigned long	io_size;
+	uint8_t		io_bar;
+	struct resource	resource[PCI_NUM_BARS];
+	struct resource dummy_parent;
+};
+
+struct pci_iomul_switch {
+	struct list_head	list;	/* bus_list_lock protects */
+
+	/*
+	 * This lock the following entry and following
+	 * pci_iomul_slot/pci_iomul_func.
+	 */
+	struct mutex		lock;
+	struct kref		kref;
+
+	struct resource		io_resource;
+	struct resource		*io_region;
+	unsigned int		count;
+	struct pci_dev		*current_pdev;
+
+	int			segment;
+	uint8_t			bus;
+
+	uint32_t		io_base;
+	uint32_t		io_limit;
+
+	/* func which has the largeset io size*/
+	struct pci_iomul_func	*func;
+
+	struct list_head	slots;
+};
+
+struct pci_iomul_slot {
+	struct list_head	sibling;
+	struct kref		kref;
+	/*
+	 * busnr
+	 * when pcie, the primary busnr of the PCI-PCI bridge on which
+	 * this devices sits.
+	 */
+	uint8_t			switch_busnr;
+	struct resource		dummy_parent[PCI_NUM_RESOURCES - PCI_BRIDGE_RESOURCES];
+
+	/* device */
+	int			segment;
+	uint8_t			bus;
+	uint8_t			dev;
+
+	struct pci_iomul_func	*func[PCI_NUM_FUNC];
+};
+
+static LIST_HEAD(switch_list);
+static DEFINE_MUTEX(switch_list_lock);
+
+/*****************************************************************************/
+static int inline pci_iomul_switch_io_allocated(
+	const struct pci_iomul_switch *sw)
+{
+	return !(sw->io_base == 0 || sw->io_base > sw->io_limit);
+}
+
+static struct pci_iomul_switch *pci_iomul_find_switch_locked(int segment,
+							     uint8_t bus)
+{
+	struct pci_iomul_switch *sw;
+
+	BUG_ON(!mutex_is_locked(&switch_list_lock));
+	list_for_each_entry(sw, &switch_list, list) {
+		if (sw->segment == segment && sw->bus == bus)
+			return sw;
+	}
+	return NULL;
+}
+
+static struct pci_iomul_slot *pci_iomul_find_slot_locked(
+	struct pci_iomul_switch *sw, uint8_t busnr, uint8_t dev)
+{
+	struct pci_iomul_slot *slot;
+
+	BUG_ON(!mutex_is_locked(&sw->lock));
+	list_for_each_entry(slot, &sw->slots, sibling) {
+		if (slot->bus == busnr && slot->dev == dev)
+			return slot;
+	}
+	return NULL;
+}
+
+static void pci_iomul_switch_get(struct pci_iomul_switch *sw);
+/* on successfull exit, sw->lock is locked for use slot and
+ * refrence count of sw is incremented.
+ */
+static void pci_iomul_get_lock_switch(struct pci_dev *pdev,
+				      struct pci_iomul_switch **swp,
+				      struct pci_iomul_slot **slot)
+{
+	mutex_lock(&switch_list_lock);
+
+	*swp = pci_iomul_find_switch_locked(pci_domain_nr(pdev->bus),
+					    pci_dev_switch_busnr(pdev));
+	if (*swp == NULL) {
+		*slot = NULL;
+		goto out;
+	}
+
+	mutex_lock(&(*swp)->lock);
+	*slot = pci_iomul_find_slot_locked(*swp, pdev->bus->number,
+					   PCI_SLOT(pdev->devfn));
+	if (*slot == NULL) {
+		mutex_unlock(&(*swp)->lock);
+		*swp = NULL;
+	} else {
+		pci_iomul_switch_get(*swp);
+	}
+out:
+	mutex_unlock(&switch_list_lock);
+}
+
+static struct pci_iomul_switch *pci_iomul_switch_alloc(int segment,
+						       uint8_t bus)
+{
+	struct pci_iomul_switch *sw;
+
+	BUG_ON(!mutex_is_locked(&switch_list_lock));
+
+	sw = kmalloc(sizeof(*sw), GFP_KERNEL);
+
+	mutex_init(&sw->lock);
+	kref_init(&sw->kref);
+	sw->io_region = NULL;
+	sw->count = 0;
+	sw->current_pdev = NULL;
+	sw->segment = segment;
+	sw->bus = bus;
+	sw->io_base = 0;
+	sw->io_limit = 0;
+	sw->func = NULL;
+	INIT_LIST_HEAD(&sw->slots);
+
+	return sw;
+}
+
+static void pci_iomul_switch_add_locked(struct pci_iomul_switch *sw)
+{
+	BUG_ON(!mutex_is_locked(&switch_list_lock));
+	list_add(&sw->list, &switch_list);
+}
+
+#ifdef CONFIG_HOTPLUG_PCI
+static void pci_iomul_switch_del_locked(struct pci_iomul_switch *sw)
+{
+	BUG_ON(!mutex_is_locked(&switch_list_lock));
+	list_del(&sw->list);
+}
+#endif
+
+static void pci_iomul_switch_get(struct pci_iomul_switch *sw)
+{
+	kref_get(&sw->kref);
+}
+
+static void pci_iomul_switch_release(struct kref *kref)
+{
+	struct pci_iomul_switch *sw = container_of(kref,
+						   struct pci_iomul_switch,
+						   kref);
+	kfree(sw);
+}
+
+static void pci_iomul_switch_put(struct pci_iomul_switch *sw)
+{
+	kref_put(&sw->kref, &pci_iomul_switch_release);
+}
+
+static int __devinit pci_iomul_slot_init(struct pci_dev *pdev,
+					 struct pci_iomul_slot *slot)
+{
+	u16 rpcap;
+	u16 cap;
+
+	rpcap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+	if (!rpcap) {
+		/* pci device isn't supported */
+		printk(KERN_INFO
+		       "PCI: sharing io port of non PCIe device %s "
+		       "isn't supported. ignoring.\n",
+		       pci_name(pdev));
+		return -ENOSYS;
+	}
+
+        pci_read_config_word(pdev, rpcap + PCI_CAP_FLAGS, &cap);
+	switch ((cap & PCI_EXP_FLAGS_TYPE) >> 4) {
+	case PCI_EXP_TYPE_RC_END:
+		printk(KERN_INFO
+		       "PCI: io port sharing of root complex integrated "
+		       "endpoint %s isn't supported. ignoring.\n",
+		       pci_name(pdev));
+		return -ENOSYS;
+	case PCI_EXP_TYPE_ENDPOINT:
+	case PCI_EXP_TYPE_LEG_END:
+		break;
+	default:
+		printk(KERN_INFO
+		       "PCI: io port sharing of non endpoint %s "
+		       "doesn't make sense. ignoring.\n",
+		       pci_name(pdev));
+		return -EINVAL;
+	}
+
+	kref_init(&slot->kref);
+	slot->switch_busnr = pci_dev_switch_busnr(pdev);
+	slot->segment = pci_domain_nr(pdev->bus);
+	slot->bus = pdev->bus->number;
+	slot->dev = PCI_SLOT(pdev->devfn);
+
+	return 0;
+}
+
+static struct pci_iomul_slot *__devinit
+pci_iomul_slot_alloc(struct pci_dev *pdev)
+{
+	struct pci_iomul_slot *slot;
+
+	slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+	if (slot == NULL)
+		return NULL;
+
+	if (pci_iomul_slot_init(pdev, slot) != 0) {
+		kfree(slot);
+		return NULL;
+	}
+	return slot;
+}
+
+static void pci_iomul_slot_add_locked(struct pci_iomul_switch *sw,
+				      struct pci_iomul_slot *slot)
+{
+	BUG_ON(!mutex_is_locked(&sw->lock));
+	list_add(&slot->sibling, &sw->slots);
+}
+
+#ifdef CONFIG_HOTPLUG_PCI
+static void pci_iomul_slot_del_locked(struct pci_iomul_switch *sw,
+				       struct pci_iomul_slot *slot)
+{
+	BUG_ON(!mutex_is_locked(&sw->lock));
+	list_del(&slot->sibling);
+}
+#endif
+
+static void pci_iomul_slot_get(struct pci_iomul_slot *slot)
+{
+	kref_get(&slot->kref);
+}
+
+static void pci_iomul_slot_release(struct kref *kref)
+{
+	struct pci_iomul_slot *slot = container_of(kref, struct pci_iomul_slot,
+						   kref);
+	kfree(slot);
+}
+
+static void pci_iomul_slot_put(struct pci_iomul_slot *slot)
+{
+	kref_put(&slot->kref, &pci_iomul_slot_release);
+}
+
+/*****************************************************************************/
+static int pci_get_sbd(const char *str,
+		       int *segment__, uint8_t *bus__, uint8_t *dev__)
+{
+	int segment;
+	int bus;
+	int dev;
+
+	if (sscanf(str, "%x:%x:%x", &segment, &bus, &dev) != 3) {
+		if (sscanf(str, "%x:%x", &bus, &dev) == 2)
+			segment = 0;
+		else
+			return -EINVAL;
+	}
+
+	if (segment < 0 || INT_MAX <= segment)
+		return -EINVAL;
+	if (bus < 0 || PCI_BUS_MAX < bus)
+		return -EINVAL;
+	if (dev < 0 || PCI_DEV_MAX < dev)
+		return -EINVAL;
+
+	*segment__ = segment;
+	*bus__ = bus;
+	*dev__ = dev;
+	return 0;
+}
+
+static char iomul_param[COMMAND_LINE_SIZE];
+#define TOKEN_MAX	10	/* SSSS:BB:DD length is 10 */
+static int pci_is_iomul_dev_param(struct pci_dev *pdev)
+{
+        int len;
+        char *p;
+	char *next_str;
+
+	for (p = &iomul_param[0]; *p != '\0'; p = next_str + 1) {
+		next_str = strchr(p, ',');
+		if (next_str != NULL)
+			len = next_str - p;
+		else
+			len = strlen(p);
+
+		if (len > 0 && len <= TOKEN_MAX) {
+			char tmp[TOKEN_MAX+1];
+			int seg;
+			uint8_t bus;
+			uint8_t dev;
+
+			strlcpy(tmp, p, len);
+			if (pci_get_sbd(tmp, &seg, &bus, &dev) == 0 &&
+			    pci_domain_nr(pdev->bus) == seg &&
+			    pdev->bus->number == bus &&
+			    PCI_SLOT(pdev->devfn) == dev)
+				return 1;
+		}
+		if (next_str == NULL)
+			break;
+	}
+
+	/* check guestcev=<device>+iomul option */
+	return pci_is_iomuldev(pdev);
+}
+
+/*
+ * Format: [<segment>:]<bus>:<dev>[,[<segment>:]<bus>:<dev>[,...]
+ */
+static int __init pci_iomul_param_setup(char *str)
+{
+	if (strlen(str) >= COMMAND_LINE_SIZE)
+		return 0;
+
+	/* parse it after pci bus scanning */
+	strlcpy(iomul_param, str, sizeof(iomul_param));
+	return 1;
+}
+__setup("guestiomuldev=", pci_iomul_param_setup);
+
+/*****************************************************************************/
+static void __devinit pci_iomul_set_bridge_io_window(struct pci_dev *bridge,
+						     uint32_t io_base,
+						     uint32_t io_limit)
+{
+	uint16_t l;
+	uint32_t upper16;
+
+	io_base >>= 12;
+	io_base <<= 4;
+	io_limit >>= 12;
+	io_limit <<= 4;
+	l = (io_base & 0xff) | ((io_limit & 0xff) << 8);
+	upper16 = ((io_base & 0xffff00) >> 8) |
+		(((io_limit & 0xffff00) >> 8) << 16);
+
+        /* Temporarily disable the I/O range before updating PCI_IO_BASE. */
+        pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff);
+        /* Update lower 16 bits of I/O base/limit. */
+        pci_write_config_word(bridge, PCI_IO_BASE, l);
+        /* Update upper 16 bits of I/O base/limit. */
+        pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, upper16);
+}
+
+static void __devinit pci_disable_bridge_io_window(struct pci_dev *bridge)
+{
+	/* set base = 0xffffff limit = 0x0 */
+	pci_iomul_set_bridge_io_window(bridge, 0xffffff, 0);
+}
+
+static int __devinit pci_iomul_func_scan(struct pci_dev *pdev,
+					 struct pci_iomul_slot *slot,
+					 uint8_t func)
+{
+	struct pci_iomul_func *f;
+	unsigned int i;
+
+	f = kzalloc(sizeof(*f), GFP_KERNEL);
+	if (f == NULL)
+		return -ENOMEM;
+
+	f->segment = slot->segment;
+	f->bus = slot->bus;
+	f->devfn = PCI_DEVFN(slot->dev, func);
+	f->io_size = pdev_size_io(pdev);
+
+	for (i = 0; i < PCI_NUM_BARS; i++) {
+		if (!(pci_resource_flags(pdev, i) & IORESOURCE_IO))
+			continue;
+		if (pci_resource_len(pdev, i) == 0)
+			continue;
+
+		f->io_bar |= 1 << i;
+		f->resource[i] = pdev->resource[i];
+	}
+
+	if (f->io_bar)
+		slot->func[func] = f;
+	else
+		kfree(f);
+	return 0;
+}
+
+/*
+ * This is tricky part.
+ * fake PCI resource assignment routines by setting flags to 0.
+ * PCI resource allocate routines think the resource should
+ * be allocated by checking flags. 0 means this resource isn't used.
+ * See pbus_size_io() and pdev_sort_resources().
+ *
+ * After allocated resources, flags (IORESOURCE_IO) is exported
+ * to other part including user process.
+ * So we have to set flags to IORESOURCE_IO, but at the same time
+ * we must prevent those resources from reassigning when pci hot plug.
+ * To achieve that, set r->parent to dummy resource.
+ */
+static void __devinit pci_iomul_disable_resource(struct resource *r)
+{
+	/* don't allocate this resource */
+	r->flags = 0;
+}
+
+static void __devinit pci_iomul_reenable_resource(
+	struct resource *dummy_parent, struct resource *r)
+{
+	int ret;
+
+	dummy_parent->start = r->start;
+	dummy_parent->end = r->end;
+	dummy_parent->flags = r->flags;
+	dummy_parent->name = "PCI IOMUL dummy resource";
+
+	ret = request_resource(dummy_parent, r);
+	BUG_ON(ret);
+}
+
+static void __devinit pci_iomul_fixup_ioresource(struct pci_dev *pdev,
+						 struct pci_iomul_func *func,
+						 int reassign, int dealloc)
+{
+	uint8_t i;
+	struct resource *r;
+
+	printk(KERN_INFO "PCI: deallocating io resource[%s]. io size 0x%lx\n",
+	       pci_name(pdev), func->io_size);
+	for (i = 0; i < PCI_NUM_BARS; i++) {
+		r = &pdev->resource[i];
+		if (!(func->io_bar & (1 << i)))
+			continue;
+
+		if (reassign) {
+			r->end -= r->start;
+			r->start = 0;
+			pci_update_resource(pdev, i);
+			func->resource[i] = *r;
+		}
+
+		if (dealloc)
+			/* don't allocate this resource */
+			pci_iomul_disable_resource(r);
+	}
+
+	/* parent PCI-PCI bridge */
+	if (!reassign)
+		return;
+	pdev = pdev->bus->self;
+	if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
+		return;
+	pci_disable_bridge_io_window(pdev);
+	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+		r = &pdev->resource[i];
+		if (!(r->flags & IORESOURCE_IO))
+			continue;
+
+		r->end -= r->start;
+		r->start = 0;
+		if (i < PCI_BRIDGE_RESOURCES)
+			pci_update_resource(pdev, i);
+	}
+}
+
+static void __devinit __quirk_iomul_dealloc_ioresource(
+	struct pci_iomul_switch *sw,
+	struct pci_dev *pdev, struct pci_iomul_slot *slot)
+{
+	struct pci_iomul_func *f;
+	struct pci_iomul_func *__f;
+
+	if (pci_iomul_func_scan(pdev, slot, PCI_FUNC(pdev->devfn)) != 0)
+		return;
+
+	f = slot->func[PCI_FUNC(pdev->devfn)];
+	if (f == NULL)
+		return;
+
+	__f = sw->func;
+	/* sw->io_base == 0 means that we are called at boot time.
+	 * != 0 means that we are called by php after boot. */
+	if (sw->io_base == 0 &&
+	    (__f == NULL || __f->io_size < f->io_size)) {
+		if (__f != NULL) {
+			struct pci_bus *__pbus;
+			struct pci_dev *__pdev;
+
+			__pbus = pci_find_bus(__f->segment, __f->bus);
+			BUG_ON(__pbus == NULL);
+			__pdev = pci_get_slot(__pbus, __f->devfn);
+			BUG_ON(__pdev == NULL);
+			pci_iomul_fixup_ioresource(__pdev, __f, 0, 1);
+			pci_dev_put(__pdev);
+		}
+
+		pci_iomul_fixup_ioresource(pdev, f, 1, 0);
+		sw->func = f;
+	} else {
+		pci_iomul_fixup_ioresource(pdev, f, 1, 1);
+	}
+}
+
+static void __devinit quirk_iomul_dealloc_ioresource(struct pci_dev *pdev)
+{
+	struct pci_iomul_switch *sw;
+	struct pci_iomul_slot *slot;
+
+	if (pdev->hdr_type != PCI_HEADER_TYPE_NORMAL)
+		return;
+	if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
+		return;	/* PCI Host Bridge isn't a target device */
+	if (!pci_is_iomul_dev_param(pdev))
+		return;
+
+	mutex_lock(&switch_list_lock);
+	sw = pci_iomul_find_switch_locked(pci_domain_nr(pdev->bus),
+					  pci_dev_switch_busnr(pdev));
+	if (sw == NULL) {
+		sw = pci_iomul_switch_alloc(pci_domain_nr(pdev->bus),
+					    pci_dev_switch_busnr(pdev));
+		if (sw == NULL) {
+			mutex_unlock(&switch_list_lock);
+			printk(KERN_WARNING
+			       "PCI: can't allocate memory "
+			       "for sw of IO mulplexing %s", pci_name(pdev));
+			return;
+		}
+		pci_iomul_switch_add_locked(sw);
+	}
+	pci_iomul_switch_get(sw);
+	mutex_unlock(&switch_list_lock);
+
+	mutex_lock(&sw->lock);
+	slot = pci_iomul_find_slot_locked(sw, pdev->bus->number,
+					  PCI_SLOT(pdev->devfn));
+	if (slot == NULL) {
+		slot = pci_iomul_slot_alloc(pdev);
+		if (slot == NULL) {
+			mutex_unlock(&sw->lock);
+			pci_iomul_switch_put(sw);
+			printk(KERN_WARNING "PCI: can't allocate memory "
+			       "for IO mulplexing %s", pci_name(pdev));
+			return;
+		}
+		pci_iomul_slot_add_locked(sw, slot);
+	}
+
+	printk(KERN_INFO "PCI: disable device and release io resource[%s].\n",
+	       pci_name(pdev));
+	pci_disable_device(pdev);
+
+	__quirk_iomul_dealloc_ioresource(sw, pdev, slot);
+
+	mutex_unlock(&sw->lock);
+	pci_iomul_switch_put(sw);
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID,
+			 quirk_iomul_dealloc_ioresource);
+
+static void __devinit pci_iomul_read_bridge_io(struct pci_iomul_switch *sw)
+{
+	struct pci_iomul_func *f = sw->func;
+
+	struct pci_bus *pbus;
+	struct pci_dev *pdev;
+	struct pci_dev *bridge;
+
+	uint16_t l;
+	uint16_t base_upper16;
+	uint16_t limit_upper16;
+	uint32_t io_base;
+	uint32_t io_limit;
+
+	pbus = pci_find_bus(f->segment, f->bus);
+	BUG_ON(pbus == NULL);
+
+	pdev = pci_get_slot(pbus, f->devfn);
+	BUG_ON(pdev == NULL);
+
+	bridge = pdev->bus->self;
+	pci_read_config_word(bridge, PCI_IO_BASE, &l);
+	pci_read_config_word(bridge, PCI_IO_BASE_UPPER16, &base_upper16);
+	pci_read_config_word(bridge, PCI_IO_LIMIT_UPPER16, &limit_upper16);
+
+	io_base = (l & 0xf0) | ((uint32_t)base_upper16 << 8);
+	io_base <<= 8;
+	io_limit = (l >> 8) | ((uint32_t)limit_upper16 << 8);
+	io_limit <<= 8;
+	io_limit |= 0xfff;
+
+	sw->io_base = io_base;
+	sw->io_limit = io_limit;
+
+	pci_dev_put(pdev);
+	printk(KERN_INFO "PCI: bridge %s base 0x%x limit 0x%x\n",
+	       pci_name(bridge), sw->io_base, sw->io_limit);
+}
+
+static void __devinit pci_iomul_setup_brige(struct pci_dev *bridge,
+					    uint32_t io_base,
+					    uint32_t io_limit)
+{
+	uint16_t cmd;
+
+	if ((bridge->class >> 8) == PCI_CLASS_BRIDGE_HOST)
+		return;
+
+	pci_iomul_set_bridge_io_window(bridge, io_base, io_limit);
+
+	/* and forcibly enables IO */
+	pci_read_config_word(bridge, PCI_COMMAND, &cmd);
+	if (!(cmd & PCI_COMMAND_IO)) {
+		cmd |= PCI_COMMAND_IO;
+                printk(KERN_INFO "PCI: Forcibly Enabling IO %s\n",
+		       pci_name(bridge));
+                pci_write_config_word(bridge, PCI_COMMAND, cmd);
+	}
+}
+
+struct __bar {
+	unsigned long size;
+	uint8_t bar;
+};
+
+/* decending order */
+static int __devinit pci_iomul_bar_cmp(const void *lhs__, const void *rhs__)
+{
+	const struct __bar *lhs = (struct __bar*)lhs__;
+	const struct __bar *rhs = (struct __bar*)rhs__;
+	return - (lhs->size - rhs->size);
+}
+
+static void __devinit pci_iomul_setup_dev(struct pci_dev *pdev,
+					  struct pci_iomul_func *f,
+					  uint32_t io_base)
+{
+	struct __bar bars[PCI_NUM_BARS];
+	int i;
+	uint8_t num_bars = 0;
+	struct resource *r;
+
+	printk(KERN_INFO "PCI: Forcibly assign IO %s from 0x%x\n",
+	       pci_name(pdev), io_base);
+
+	for (i = 0; i < PCI_NUM_BARS; i++) {
+		if (!(f->io_bar & (1 << i)))
+			continue;
+
+		r = &f->resource[i];
+		bars[num_bars].size = pci_iomul_len(r);
+		bars[num_bars].bar = i;
+
+		num_bars++;
+	}
+
+	sort(bars, num_bars, sizeof(bars[0]), &pci_iomul_bar_cmp, NULL);
+
+	for (i = 0; i < num_bars; i++) {
+		struct resource *fr = &f->resource[bars[i].bar];
+		r = &pdev->resource[bars[i].bar];
+
+		BUG_ON(r->start != 0);
+		r->start += io_base;
+		r->end += io_base;
+
+		fr->start = r->start;
+		fr->end = r->end;
+
+		/* pci_update_resource() check flags. */
+		r->flags = fr->flags;
+		pci_update_resource(pdev, bars[i].bar);
+		pci_iomul_reenable_resource(&f->dummy_parent, r);
+
+		io_base += bars[i].size;
+	}
+}
+
+static void __devinit pci_iomul_release_io_resource(
+	struct pci_dev *pdev, struct pci_iomul_switch *sw,
+	struct pci_iomul_slot *slot, struct pci_iomul_func *f)
+{
+	int i;
+	struct resource *r;
+
+	for (i = 0; i < PCI_NUM_BARS; i++) {
+		if (pci_resource_flags(pdev, i) & IORESOURCE_IO &&
+		    pdev->resource[i].parent != NULL) {
+			r = &pdev->resource[i];
+			f->resource[i] = *r;
+			release_resource(r);
+			pci_iomul_reenable_resource(&f->dummy_parent, r);
+		}
+	}
+
+	/* parent PCI-PCI bridge */
+	pdev = pdev->bus->self;
+	if ((pdev->class >> 8) != PCI_CLASS_BRIDGE_HOST) {
+		for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) {
+			struct resource *parent = pdev->resource[i].parent;
+
+			if (pci_resource_flags(pdev, i) & IORESOURCE_IO &&
+			    parent != NULL) {
+				r = &pdev->resource[i];
+
+				sw->io_resource.flags = r->flags;
+				sw->io_resource.start = sw->io_base;
+				sw->io_resource.end = sw->io_limit;
+				sw->io_resource.name = "PCI IO Multiplexer";
+
+				release_resource(r);
+				pci_iomul_reenable_resource(
+					&slot->dummy_parent[i - PCI_BRIDGE_RESOURCES], r);
+
+				if (request_resource(parent,
+						     &sw->io_resource))
+					printk(KERN_ERR
+					       "PCI IOMul: can't allocate "
+					       "resource. [0x%x, 0x%x]",
+					       sw->io_base, sw->io_limit);
+			}
+		}
+	}
+}
+
+static void __devinit quirk_iomul_reassign_ioresource(struct pci_dev *pdev)
+{
+	struct pci_iomul_switch *sw;
+	struct pci_iomul_slot *slot;
+	struct pci_iomul_func *sf;
+	struct pci_iomul_func *f;
+
+	pci_iomul_get_lock_switch(pdev, &sw, &slot);
+	if (sw == NULL || slot == NULL)
+		return;
+
+	if (sw->io_base == 0)
+		pci_iomul_read_bridge_io(sw);
+	if (!pci_iomul_switch_io_allocated(sw))
+		goto out;
+
+	sf = sw->func;
+	f = slot->func[PCI_FUNC(pdev->devfn)];
+	if (f == NULL)
+		/* (sf == NULL || f == NULL) case
+		 * can happen when all the specified devices
+		 * don't have io space
+		 */
+		goto out;
+
+	if (sf != NULL &&
+	    (pci_domain_nr(pdev->bus) != sf->segment ||
+	     pdev->bus->number != sf->bus ||
+	     PCI_SLOT(pdev->devfn) != PCI_SLOT(sf->devfn)) &&
+	    PCI_FUNC(pdev->devfn) == 0) {
+		pci_iomul_setup_brige(pdev->bus->self,
+				      sw->io_base, sw->io_limit);
+	}
+
+	BUG_ON(f->io_size > sw->io_limit - sw->io_base + 1);
+	if (/* f == sf */
+            sf != NULL &&
+	    pci_domain_nr(pdev->bus) == sf->segment &&
+	    pdev->bus->number == sf->bus &&
+	    pdev->devfn == sf->devfn)
+		pci_iomul_release_io_resource(pdev, sw, slot, f);
+	else
+		pci_iomul_setup_dev(pdev, f, sw->io_base);
+
+out:
+	mutex_unlock(&sw->lock);
+	pci_iomul_switch_put(sw);
+}
+
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID,
+			quirk_iomul_reassign_ioresource);
+
+/*****************************************************************************/
+#ifdef CONFIG_HOTPLUG_PCI
+static int __devinit __pci_iomul_notifier_del_device(struct pci_dev *pdev)
+{
+	struct pci_iomul_switch *sw;
+	struct pci_iomul_slot *slot;
+	int i;
+
+	pci_iomul_get_lock_switch(pdev, &sw, &slot);
+	if (sw == NULL || slot == NULL)
+		return 0;
+
+	if (sw->func == slot->func[PCI_FUNC(pdev->devfn)])
+		sw->func = NULL;
+	kfree(slot->func[PCI_FUNC(pdev->devfn)]);
+	slot->func[PCI_FUNC(pdev->devfn)] = NULL;
+	for (i = 0; i < PCI_NUM_FUNC; i++) {
+		if (slot->func[i] != NULL)
+			goto out;
+	}
+
+	pci_iomul_slot_del_locked(sw, slot);
+	pci_iomul_slot_put(slot);
+
+out:
+	mutex_unlock(&sw->lock);
+	pci_iomul_switch_put(sw);
+	return 0;
+}
+
+static int __devinit __pci_iomul_notifier_del_switch(struct pci_dev *pdev)
+{
+	struct pci_iomul_switch *sw;
+
+	mutex_lock(&switch_list_lock);
+	sw = pci_iomul_find_switch_locked(pci_domain_nr(pdev->bus),
+					  pdev->bus->number);
+	if (sw == NULL)
+		goto out;
+
+	pci_iomul_switch_del_locked(sw);
+
+	mutex_lock(&sw->lock);
+	if (sw->io_resource.parent)
+		release_resource(&sw->io_resource);
+	sw->io_base = 0;	/* to tell this switch is removed */
+	sw->io_limit = 0;
+	BUG_ON(!list_empty(&sw->slots));
+	mutex_unlock(&sw->lock);
+
+out:
+	mutex_unlock(&switch_list_lock);
+	pci_iomul_switch_put(sw);
+	return 0;
+}
+
+static int __devinit pci_iomul_notifier_del_device(struct pci_dev *pdev)
+{
+	int ret;
+	switch (pdev->hdr_type) {
+	case PCI_HEADER_TYPE_NORMAL:
+		ret = __pci_iomul_notifier_del_device(pdev);
+		break;
+	case PCI_HEADER_TYPE_BRIDGE:
+		ret = __pci_iomul_notifier_del_switch(pdev);
+		break;
+	default:
+                printk(KERN_WARNING "PCI IOMUL: "
+		       "device %s has unknown header type %02x, ignoring.\n",
+		       pci_name(pdev), pdev->hdr_type);
+		ret = -EIO;
+		break;
+	}
+	return ret;
+}
+
+static int __devinit pci_iomul_notifier(struct notifier_block *nb,
+					unsigned long action, void *data)
+{
+        struct device *dev = data;
+        struct pci_dev *pdev = to_pci_dev(dev);
+
+        switch (action) {
+	case BUS_NOTIFY_ADD_DEVICE:
+		quirk_iomul_reassign_ioresource(pdev);
+		break;
+	case BUS_NOTIFY_DEL_DEVICE:
+		return pci_iomul_notifier_del_device(pdev);
+	default:
+		/* nothing */
+		break;
+	}
+
+	return 0;
+}
+
+static struct notifier_block pci_iomul_nb = {
+        .notifier_call = pci_iomul_notifier,
+};
+
+static int __init pci_iomul_hotplug_init(void)
+{
+        bus_register_notifier(&pci_bus_type, &pci_iomul_nb);
+	return 0;
+}
+
+late_initcall(pci_iomul_hotplug_init);
+#endif
+
+/*****************************************************************************/
+struct pci_iomul_data {
+	struct mutex lock;
+
+	struct pci_dev *pdev;
+	struct pci_iomul_switch *sw;
+	struct pci_iomul_slot *slot;	/* slot::kref */
+	struct pci_iomul_func **func;	/* when dereferencing,
+					   sw->lock is necessary */
+};
+
+static int pci_iomul_func_ioport(struct pci_iomul_func *func,
+				 uint8_t bar, uint64_t offset, int *port)
+{
+	if (!(func->io_bar & (1 << bar)))
+		return -EINVAL;
+
+	*port = func->resource[bar].start + offset;
+	if (*port < func->resource[bar].start ||
+	    *port > func->resource[bar].end)
+		return -EINVAL;
+
+	return 0;
+}
+
+static inline int pci_iomul_valid(struct pci_iomul_data *iomul)
+{
+	BUG_ON(!mutex_is_locked(&iomul->lock));
+	BUG_ON(!mutex_is_locked(&iomul->sw->lock));
+	return pci_iomul_switch_io_allocated(iomul->sw) &&
+		*iomul->func != NULL;
+}
+
+static void __pci_iomul_enable_io(struct pci_dev *pdev)
+{
+	uint16_t cmd;
+
+	pci_dev_get(pdev);
+	pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+	cmd |= PCI_COMMAND_IO;
+	pci_write_config_word(pdev, PCI_COMMAND, cmd);
+}
+
+static void __pci_iomul_disable_io(struct pci_iomul_data *iomul,
+				   struct pci_dev *pdev)
+{
+	uint16_t cmd;
+
+	if (!pci_iomul_valid(iomul))
+		return;
+
+	pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+	cmd &= ~PCI_COMMAND_IO;
+	pci_write_config_word(pdev, PCI_COMMAND, cmd);
+	pci_dev_put(pdev);
+}
+
+static int pci_iomul_open(struct inode *inode, struct file *filp)
+{
+	struct pci_iomul_data *iomul;
+	iomul = kmalloc(sizeof(*iomul), GFP_KERNEL);
+	if (iomul == NULL)
+		return -ENOMEM;
+
+	mutex_init(&iomul->lock);
+	iomul->pdev = NULL;
+	iomul->sw = NULL;
+	iomul->slot = NULL;
+	iomul->func = NULL;
+	filp->private_data = (void*)iomul;
+
+	return 0;
+}
+
+static int pci_iomul_release(struct inode *inode, struct file *filp)
+{
+	struct pci_iomul_data *iomul =
+		(struct pci_iomul_data*)filp->private_data;
+	struct pci_iomul_switch *sw;
+	struct pci_iomul_slot *slot = NULL;
+
+	mutex_lock(&iomul->lock);
+	sw = iomul->sw;
+	slot = iomul->slot;
+	if (iomul->pdev != NULL) {
+		if (sw != NULL) {
+			mutex_lock(&sw->lock);
+			if (sw->current_pdev == iomul->pdev) {
+				__pci_iomul_disable_io(iomul,
+						       sw->current_pdev);
+				sw->current_pdev = NULL;
+			}
+			sw->count--;
+			if (sw->count == 0) {
+				release_region(sw->io_region->start, sw->io_region->end - sw->io_region->start + 1);
+				sw->io_region = NULL;
+			}
+			mutex_unlock(&sw->lock);
+		}
+		pci_dev_put(iomul->pdev);
+	}
+	mutex_unlock(&iomul->lock);
+
+	if (slot != NULL)
+		pci_iomul_slot_put(slot);
+	if (sw != NULL)
+		pci_iomul_switch_put(sw);
+	kfree(iomul);
+	return 0;
+}
+
+static long pci_iomul_setup(struct pci_iomul_data *iomul,
+			    struct pci_iomul_setup __user *arg)
+{
+	long error = 0;
+	struct pci_iomul_setup setup;
+	struct pci_iomul_switch *sw = NULL;
+	struct pci_iomul_slot *slot;
+	struct pci_bus *pbus;
+	struct pci_dev *pdev;
+
+	if (copy_from_user(&setup, arg, sizeof(setup)))
+		return -EFAULT;
+
+	pbus = pci_find_bus(setup.segment, setup.bus);
+	if (pbus == NULL)
+		return -ENODEV;
+	pdev = pci_get_slot(pbus, setup.dev);
+	if (pdev == NULL)
+		return -ENODEV;
+
+	mutex_lock(&iomul->lock);
+	if (iomul->sw != NULL) {
+		error = -EBUSY;
+		goto out0;
+	}
+
+	pci_iomul_get_lock_switch(pdev, &sw, &slot);
+	if (sw == NULL || slot == NULL) {
+		error = -ENODEV;
+		goto out0;
+	}
+	if (!pci_iomul_switch_io_allocated(sw)) {
+		error = -ENODEV;
+		goto out;
+	}
+
+	if (slot->func[setup.func] == NULL) {
+		error = -ENODEV;
+		goto out;
+	}
+
+	if (sw->count == 0) {
+		BUG_ON(sw->io_region != NULL);
+		sw->io_region =
+			request_region(sw->io_base,
+				       sw->io_limit - sw->io_base + 1,
+				       "PCI IO Multiplexer driver");
+		if (sw->io_region == NULL) {
+			mutex_unlock(&sw->lock);
+			error = -EBUSY;
+			goto out;
+		}
+	}
+	sw->count++;
+	pci_iomul_slot_get(slot);
+
+	iomul->pdev = pdev;
+	iomul->sw = sw;
+	iomul->slot = slot;
+	iomul->func = &slot->func[setup.func];
+
+out:
+	mutex_unlock(&sw->lock);
+out0:
+	mutex_unlock(&iomul->lock);
+	if (error != 0) {
+		if (sw != NULL)
+			pci_iomul_switch_put(sw);
+		pci_dev_put(pdev);
+	}
+	return error;
+}
+
+static int pci_iomul_lock(struct pci_iomul_data *iomul,
+			  struct pci_iomul_switch **sw,
+			  struct pci_iomul_func **func)
+{
+	mutex_lock(&iomul->lock);
+	*sw = iomul->sw;
+	if (*sw == NULL) {
+		mutex_unlock(&iomul->lock);
+		return -ENODEV;
+	}
+	mutex_lock(&(*sw)->lock);
+	if (!pci_iomul_valid(iomul)) {
+		mutex_unlock(&(*sw)->lock);
+		mutex_unlock(&iomul->lock);
+		return -ENODEV;
+	}
+	*func = *iomul->func;
+
+	return 0;
+}
+
+static long pci_iomul_disable_io(struct pci_iomul_data *iomul)
+{
+	long error = 0;
+	struct pci_iomul_switch *sw;
+	struct pci_iomul_func *dummy_func;
+	struct pci_dev *pdev;
+
+	if (pci_iomul_lock(iomul, &sw, &dummy_func) < 0)
+		return -ENODEV;
+
+	pdev = iomul->pdev;
+	if (pdev == NULL)
+		error = -ENODEV;
+
+	if (pdev != NULL && sw->current_pdev == pdev) {
+		__pci_iomul_disable_io(iomul, pdev);
+		sw->current_pdev = NULL;
+	}
+
+	mutex_unlock(&sw->lock);
+	mutex_unlock(&iomul->lock);
+	return error;
+}
+
+static void pci_iomul_switch_to(
+	struct pci_iomul_data *iomul, struct pci_iomul_switch *sw,
+	struct pci_dev *next_pdev)
+{
+	if (sw->current_pdev == next_pdev)
+		/* nothing to do */
+		return;
+
+	if (sw->current_pdev != NULL)
+		__pci_iomul_disable_io(iomul, sw->current_pdev);
+
+	__pci_iomul_enable_io(next_pdev);
+	sw->current_pdev = next_pdev;
+}
+
+static long pci_iomul_in(struct pci_iomul_data *iomul,
+			 struct pci_iomul_in __user *arg)
+{
+	struct pci_iomul_in in;
+	struct pci_iomul_switch *sw;
+	struct pci_iomul_func *func;
+
+	long error = 0;
+	int port;
+	uint32_t value = 0;
+
+	if (copy_from_user(&in, arg, sizeof(in)))
+		return -EFAULT;
+
+	if (pci_iomul_lock(iomul, &sw, &func) < 0)
+		return -ENODEV;
+
+	error = pci_iomul_func_ioport(func, in.bar, in.offset, &port);
+	if (error)
+		goto out;
+
+	pci_iomul_switch_to(iomul, sw, iomul->pdev);
+	switch (in.size) {
+	case 4:
+		value = inl(port);
+		break;
+	case 2:
+		value = inw(port);
+		break;
+	case 1:
+		value = inb(port);
+		break;
+	default:
+		error = -EINVAL;
+		break;
+	}
+
+out:
+	mutex_unlock(&sw->lock);
+	mutex_unlock(&iomul->lock);
+
+	if (error == 0 && put_user(value, &arg->value))
+		return -EFAULT;
+	return error;
+}
+
+static long pci_iomul_out(struct pci_iomul_data *iomul,
+			  struct pci_iomul_out __user *arg)
+{
+	struct pci_iomul_in out;
+	struct pci_iomul_switch *sw;
+	struct pci_iomul_func *func;
+
+	long error = 0;
+	int port;
+
+	if (copy_from_user(&out, arg, sizeof(out)))
+		return -EFAULT;
+
+	if (pci_iomul_lock(iomul, &sw, &func) < 0)
+		return -ENODEV;
+
+	error = pci_iomul_func_ioport(func, out.bar, out.offset, &port);
+	if (error)
+		goto out;
+
+	pci_iomul_switch_to(iomul, sw, iomul->pdev);
+	switch (out.size) {
+	case 4:
+		outl(out.value, port);
+		break;
+	case 2:
+		outw(out.value, port);
+		break;
+	case 1:
+		outb(out.value, port);
+		break;
+	default:
+		error = -EINVAL;
+		break;
+	}
+
+out:
+	mutex_unlock(&sw->lock);
+	mutex_unlock(&iomul->lock);
+	return error;
+}
+
+static long pci_iomul_ioctl(struct file *filp,
+			    unsigned int cmd, unsigned long arg)
+{
+	long error;
+	struct pci_iomul_data *iomul =
+		(struct pci_iomul_data*)filp->private_data;
+
+	if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
+		return -EPERM;
+
+	switch (cmd) {
+	case PCI_IOMUL_SETUP:
+		error = pci_iomul_setup(iomul,
+					(struct pci_iomul_setup __user *)arg);
+		break;
+	case PCI_IOMUL_DISABLE_IO:
+		error = pci_iomul_disable_io(iomul);
+		break;
+	case PCI_IOMUL_IN:
+		error = pci_iomul_in(iomul, (struct pci_iomul_in __user *)arg);
+		break;
+	case PCI_IOMUL_OUT:
+		error = pci_iomul_out(iomul,
+				      (struct pci_iomul_out __user *)arg);
+		break;
+	default:
+		error = -ENOSYS;
+		break;
+	}
+
+	return error;
+}
+
+static const struct file_operations pci_iomul_fops = {
+	.owner = THIS_MODULE,
+
+	.open = pci_iomul_open, /* nonseekable_open */
+	.release = pci_iomul_release,
+
+	.unlocked_ioctl = pci_iomul_ioctl,
+};
+
+static struct miscdevice pci_iomul_miscdev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "pci_iomul",
+	.fops = &pci_iomul_fops,
+};
+
+static int pci_iomul_init(void)
+{
+	int error;
+	error = misc_register(&pci_iomul_miscdev);
+	if (error != 0) {
+		printk(KERN_ALERT "Couldn't register /dev/misc/pci_iomul");
+		return error;
+	}
+	printk("PCI IO multiplexer device installed.\n");
+	return 0;
+}
+
+#if 0
+static void pci_iomul_cleanup(void)
+{
+	misc_deregister(&pci_iomul_miscdev);
+}
+#endif
+
+/*
+ * This must be called after pci fixup final which is called by
+ * device_initcall(pci_init).
+ */
+late_initcall(pci_iomul_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Isaku Yamahata <yamahata@valinux.co.jp>");
+MODULE_DESCRIPTION("PCI IO space multiplexing driver");
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ head-2010-04-29/drivers/pci/iomulti.h	2010-03-24 13:55:21.000000000 +0100
@@ -0,0 +1,51 @@
+#ifndef PCI_IOMULTI_H
+#define PCI_IOMULTI_H
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright (c) 2009 Isaku Yamahata
+ *                    VA Linux Systems Japan K.K.
+ *
+ */
+
+struct pci_iomul_setup {
+	uint16_t	segment;
+	uint8_t		bus;
+	uint8_t		dev;
+	uint8_t		func;
+};
+
+struct pci_iomul_in {
+	uint8_t		bar;
+	uint64_t	offset;
+
+	uint8_t		size;
+	uint32_t	value;
+};
+
+struct pci_iomul_out {
+	uint8_t		bar;
+	uint64_t	offset;
+
+	uint8_t		size;
+	uint32_t	value;
+};
+
+#define PCI_IOMUL_SETUP		_IOW ('P', 0, struct pci_iomul_setup)
+#define PCI_IOMUL_DISABLE_IO	_IO  ('P', 1)
+#define PCI_IOMUL_IN		_IOWR('P', 2, struct pci_iomul_in)
+#define PCI_IOMUL_OUT		_IOW ('P', 3, struct pci_iomul_out)
+
+#endif /* PCI_IOMULTI_H */
--- head-2010-04-29.orig/drivers/pci/pci.c	2010-04-29 09:29:51.000000000 +0200
+++ head-2010-04-29/drivers/pci/pci.c	2010-04-29 09:30:41.000000000 +0200
@@ -2901,6 +2901,13 @@ resource_size_t pci_specified_resource_a
  */
 int pci_is_reassigndev(struct pci_dev *dev)
 {
+#ifdef CONFIG_PCI_GUESTDEV
+	int result;
+
+	result = pci_is_guestdev_to_reassign(dev);
+	if (result)
+		return result;
+#endif /* CONFIG_PCI_GUESTDEV */
 	return (pci_specified_resource_alignment(dev) != 0);
 }
 
--- head-2010-04-29.orig/drivers/pci/pci.h	2010-04-29 09:29:51.000000000 +0200
+++ head-2010-04-29/drivers/pci/pci.h	2010-03-24 13:55:21.000000000 +0100
@@ -337,4 +337,11 @@ static inline int pci_dev_specific_reset
 }
 #endif
 
+#ifdef CONFIG_PCI_GUESTDEV
+extern int pci_is_guestdev_to_reassign(struct pci_dev *dev);
+extern int pci_is_iomuldev(struct pci_dev *dev);
+#else
+#define pci_is_iomuldev(dev)	0
+#endif
+
 #endif /* DRIVERS_PCI_H */
--- head-2010-04-29.orig/include/linux/acpi.h	2010-04-29 09:29:51.000000000 +0200
+++ head-2010-04-29/include/linux/acpi.h	2010-03-24 13:55:21.000000000 +0100
@@ -247,6 +247,8 @@ int acpi_check_region(resource_size_t st
 int acpi_check_mem_region(resource_size_t start, resource_size_t n,
 		      const char *name);
 
+int acpi_pci_get_root_seg_bbn(char *hid, char *uid, int *seg, int *bbn);
+
 #ifdef CONFIG_PM_SLEEP
 void __init acpi_no_s4_hw_signature(void);
 void __init acpi_old_suspend_ordering(void);
--- head-2010-04-29.orig/include/linux/pci.h	2010-04-29 09:29:51.000000000 +0200
+++ head-2010-04-29/include/linux/pci.h	2010-03-24 13:55:21.000000000 +0100
@@ -1504,5 +1504,11 @@ int pci_vpd_find_tag(const u8 *buf, unsi
 int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off,
 			      unsigned int len, const char *kw);
 
+#ifdef CONFIG_PCI_GUESTDEV
+int pci_is_guestdev(struct pci_dev *dev);
+#else
+#define pci_is_guestdev(dev)	0
+#endif
+
 #endif /* __KERNEL__ */
 #endif /* LINUX_PCI_H */
