From: Jean Guyader Date: Thu, 9 Apr 2009 15:20:56 +0000 (+0100) Subject: Add pciback FLR support. X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=50c08c0d3e499c5576e906ab5f0c9c234fba2493;p=xenclient%2Flinux-2.6.27-pq.git Add pciback FLR support. Add xen acpi wmi support. --- diff --git a/master/pciback-flr b/master/pciback-flr new file mode 100644 index 0000000..e39ef4b --- /dev/null +++ b/master/pciback-flr @@ -0,0 +1,1411 @@ +diff --git a/drivers/xen/pciback/controller.c b/drivers/xen/pciback/controller.c +index 294e48f..4c1aaf3 100644 +--- a/drivers/xen/pciback/controller.c ++++ b/drivers/xen/pciback/controller.c +@@ -208,7 +208,7 @@ void pciback_release_pci_dev(struct pciback_device *pdev, struct pci_dev *dev) + } + + spin_unlock_irqrestore(&dev_data->lock, flags); +- pcistub_put_pci_dev(found_dev); ++ pcistub_put_pci_dev(found_dev, 0); + } + + int pciback_init_devices(struct pciback_device *pdev) +@@ -396,7 +396,7 @@ void pciback_release_devices(struct pciback_device *pdev) + list_for_each_entry_safe(dev_entry, d, + &cntrl_entry->dev_list, list) { + list_del(&dev_entry->list); +- pcistub_put_pci_dev(dev_entry->dev); ++ pcistub_put_pci_dev(dev_entry->dev, 0); + kfree(dev_entry); + } + list_del(&cntrl_entry->list); +diff --git a/drivers/xen/pciback/passthrough.c b/drivers/xen/pciback/passthrough.c +index 9e7a0c4..b9948a5 100644 +--- a/drivers/xen/pciback/passthrough.c ++++ b/drivers/xen/pciback/passthrough.c +@@ -88,7 +88,7 @@ void pciback_release_pci_dev(struct pciback_device *pdev, struct pci_dev *dev) + spin_unlock_irqrestore(&dev_data->lock, flags); + + if (found_dev) +- pcistub_put_pci_dev(found_dev); ++ pcistub_put_pci_dev(found_dev, 1); + } + + int pciback_init_devices(struct pciback_device *pdev) +@@ -157,7 +157,7 @@ void pciback_release_devices(struct pciback_device *pdev) + + list_for_each_entry_safe(dev_entry, t, &dev_data->dev_list, list) { + list_del(&dev_entry->list); +- pcistub_put_pci_dev(dev_entry->dev); ++ pcistub_put_pci_dev(dev_entry->dev, 1); + kfree(dev_entry); + } + +diff --git a/drivers/xen/pciback/pci_stub.c b/drivers/xen/pciback/pci_stub.c +index 6345b39..486034f 100644 +--- a/drivers/xen/pciback/pci_stub.c ++++ b/drivers/xen/pciback/pci_stub.c +@@ -24,10 +24,28 @@ wait_queue_head_t aer_wait_queue; + * We want to avoid in middle of AER ops, pciback devices is being removed + */ + static DECLARE_RWSEM(pcistub_sem); +-module_param_named(hide, pci_devs_to_hide, charp, 0444); ++module_param_named(hide, pci_devs_to_hide, charp, S_IRUGO); ++ ++static char *pci_devs_use_sbr = NULL; ++module_param_named(sbr, pci_devs_use_sbr, charp, S_IRUGO); ++ ++static char *pci_devs_use_d3r = NULL; ++module_param_named(d3r, pci_devs_use_d3r, charp, S_IRUGO); ++ ++static char *pci_devs_no_flr = NULL; ++module_param_named(noflr, pci_devs_no_flr, charp, S_IRUGO); ++ ++/* Device id list holding different device type listings ++ * for hiding devices and reset logic. ++ */ ++#define PCIBACK_ID_TYPE_HIDE 1 ++#define PCIBACK_ID_TYPE_SBR 2 ++#define PCIBACK_ID_TYPE_D3R 3 ++#define PCIBACK_ID_TYPE_NOFLR 4 + + struct pcistub_device_id { + struct list_head slot_list; ++ int type; + int domain; + unsigned char bus; + unsigned int devfn; +@@ -56,6 +74,8 @@ static LIST_HEAD(pcistub_devices); + static int initialize_devices = 0; + static LIST_HEAD(seized_devices); + ++static int disable_all_flr = 0; ++ + static struct pcistub_device *pcistub_device_alloc(struct pci_dev *dev) + { + struct pcistub_device *psdev; +@@ -78,6 +98,23 @@ static struct pcistub_device *pcistub_device_alloc(struct pci_dev *dev) + return psdev; + } + ++static struct pciback_dev_data *pcistub_dev_data_alloc(struct pci_dev *dev) ++{ ++ struct pciback_dev_data *dev_data; ++ ++ dev_dbg(&dev->dev, "pcistub_dev_data_alloc\n"); ++ ++ dev_data = kzalloc(sizeof(*dev_data) + dev->cfg_size, GFP_ATOMIC); ++ if (!dev_data) ++ return NULL; ++ ++ pci_set_drvdata(dev, dev_data); ++ ++ dev_data->cfg_space = (u8*)(dev_data) + sizeof(*dev_data); ++ ++ return dev_data; ++} ++ + /* Don't call this directly as it's called by pcistub_device_put */ + static void pcistub_device_release(struct kref *kref) + { +@@ -200,7 +237,7 @@ struct pci_dev *pcistub_get_pci_dev(struct pciback_device *pdev, + return found_dev; + } + +-void pcistub_put_pci_dev(struct pci_dev *dev) ++void pcistub_put_pci_dev(struct pci_dev *dev, int do_flr) + { + struct pcistub_device *psdev, *found_psdev = NULL; + unsigned long flags; +@@ -220,6 +257,13 @@ void pcistub_put_pci_dev(struct pci_dev *dev) + * pcistub and pciback when AER is in processing + */ + down_write(&pcistub_sem); ++ ++ /* For pass-through devices, do an FLR (or approximate) for the device ++ * before it is put back and ready for the next domain ++ */ ++ if (!disable_all_flr && do_flr) ++ pciback_flr_device(dev); ++ + /* Cleanup our device + * (so it's ready for the next domain) + */ +@@ -235,6 +279,43 @@ void pcistub_put_pci_dev(struct pci_dev *dev) + up_write(&pcistub_sem); + } + ++struct pci_dev *pcistub_ref_pci_dev(struct pci_dev *dev) ++{ ++ struct pcistub_device *psdev; ++ struct pci_dev *found_dev = NULL; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&pcistub_devices_lock, flags); ++ ++ list_for_each_entry(psdev, &pcistub_devices, dev_list) { ++ if (psdev->dev == dev) { ++ pcistub_device_get(psdev); /* just a ref count */ ++ found_dev = psdev->dev; ++ break; ++ } ++ } ++ ++ spin_unlock_irqrestore(&pcistub_devices_lock, flags); ++ return found_dev; ++} ++ ++void pcistub_unref_pci_dev(struct pci_dev *dev) ++{ ++ struct pcistub_device *psdev; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&pcistub_devices_lock, flags); ++ ++ list_for_each_entry(psdev, &pcistub_devices, dev_list) { ++ if (psdev->dev == dev) { ++ pcistub_device_get(psdev); /* just an unref count */ ++ break; ++ } ++ } ++ ++ spin_unlock_irqrestore(&pcistub_devices_lock, flags); ++} ++ + static int __devinit pcistub_match_one(struct pci_dev *dev, + struct pcistub_device_id *pdev_id) + { +@@ -255,7 +336,7 @@ static int __devinit pcistub_match_one(struct pci_dev *dev, + return 0; + } + +-static int __devinit pcistub_match(struct pci_dev *dev) ++static int __devinit pcistub_match(struct pci_dev *dev, int type) + { + struct pcistub_device_id *pdev_id; + unsigned long flags; +@@ -263,6 +344,8 @@ static int __devinit pcistub_match(struct pci_dev *dev) + + spin_lock_irqsave(&device_ids_lock, flags); + list_for_each_entry(pdev_id, &pcistub_device_ids, slot_list) { ++ if (pdev_id->type != type) ++ continue; + if (pcistub_match_one(dev, pdev_id)) { + found = 1; + break; +@@ -285,12 +368,11 @@ static int __devinit pcistub_init_device(struct pci_dev *dev) + * would need to be called somewhere to free the memory allocated + * here and then to call kfree(pci_get_drvdata(psdev->dev)). + */ +- dev_data = kzalloc(sizeof(*dev_data), GFP_ATOMIC); ++ dev_data = pcistub_dev_data_alloc(dev); + if (!dev_data) { + err = -ENOMEM; + goto out; + } +- pci_set_drvdata(dev, dev_data); + + dev_dbg(&dev->dev, "initializing config\n"); + +@@ -317,6 +399,22 @@ static int __devinit pcistub_init_device(struct pci_dev *dev) + dev_dbg(&dev->dev, "reset device\n"); + pciback_reset_device(dev); + ++ /* Classify the device so we know if it is PCI/PCIe and if it is ++ * a bridge - this information is used for FLR logic. Also store ++ * values if SBR/D3R reset logic was requested. ++ */ ++ pciback_classify_device(dev); ++ dev_data->no_flr = pcistub_match(dev, PCIBACK_ID_TYPE_NOFLR); ++ if (!dev_data->no_flr) { ++ dev_data->use_sbr = pcistub_match(dev, PCIBACK_ID_TYPE_SBR); ++ dev_data->use_d3r = pcistub_match(dev, PCIBACK_ID_TYPE_D3R); ++ } ++ ++ /* Store the config space here where the device is off and ready to be ++ * exported before any FLRs or other resets are done ++ */ ++ pciback_store_config_space(dev); ++ + return 0; + + config_release: +@@ -414,7 +512,7 @@ static int __devinit pcistub_probe(struct pci_dev *dev, + + dev_dbg(&dev->dev, "probing...\n"); + +- if (pcistub_match(dev)) { ++ if (pcistub_match(dev, PCIBACK_ID_TYPE_HIDE)) { + + if (dev->hdr_type != PCI_HEADER_TYPE_NORMAL + && dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { +@@ -851,7 +949,7 @@ static inline int str_to_quirk(const char *buf, int *domain, int *bus, int + return -EINVAL; + } + +-static int pcistub_device_id_add(int domain, int bus, int slot, int func) ++static int pcistub_device_id_add(int domain, int bus, int slot, int func, int type) + { + struct pcistub_device_id *pci_dev_id; + unsigned long flags; +@@ -860,12 +958,13 @@ static int pcistub_device_id_add(int domain, int bus, int slot, int func) + if (!pci_dev_id) + return -ENOMEM; + ++ pci_dev_id->type = type; + pci_dev_id->domain = domain; + pci_dev_id->bus = bus; + pci_dev_id->devfn = PCI_DEVFN(slot, func); + +- pr_debug("pciback: wants to seize %04x:%02x:%02x.%01x\n", +- domain, bus, slot, func); ++ pr_debug("pciback: adding device ID type: %d for %04x:%02x:%02x.%01x\n", ++ type, domain, bus, slot, func); + + spin_lock_irqsave(&device_ids_lock, flags); + list_add_tail(&pci_dev_id->slot_list, &pcistub_device_ids); +@@ -874,7 +973,7 @@ static int pcistub_device_id_add(int domain, int bus, int slot, int func) + return 0; + } + +-static int pcistub_device_id_remove(int domain, int bus, int slot, int func) ++static int pcistub_device_id_remove(int domain, int bus, int slot, int func, int type) + { + struct pcistub_device_id *pci_dev_id, *t; + int devfn = PCI_DEVFN(slot, func); +@@ -884,7 +983,7 @@ static int pcistub_device_id_remove(int domain, int bus, int slot, int func) + spin_lock_irqsave(&device_ids_lock, flags); + list_for_each_entry_safe(pci_dev_id, t, &pcistub_device_ids, slot_list) { + +- if (pci_dev_id->domain == domain ++ if (pci_dev_id->type == type && pci_dev_id->domain == domain + && pci_dev_id->bus == bus && pci_dev_id->devfn == devfn) { + /* Don't break; here because it's possible the same + * slot could be in the list more than once +@@ -939,6 +1038,32 @@ static int pcistub_reg_add(int domain, int bus, int slot, int func, int reg, + return err; + } + ++static int pcistub_device_do_flr(int domain, int bus, int slot, int func) ++{ ++ int err = 0; ++ struct pcistub_device *psdev; ++ struct pci_dev *dev; ++ ++ psdev = pcistub_device_find(domain, bus, slot, func); ++ if (!psdev || !psdev->dev) { ++ err = -ENODEV; ++ goto out; ++ } ++ dev = psdev->dev; ++ ++ /* Do an FLR (or approximate) for the device on demand and ++ * reload config ++ */ ++ if (!disable_all_flr) { ++ pciback_flr_device(dev); ++ } ++ else ++ dev_dbg(&dev->dev, "FLR disabled for all devices\n"); ++ ++out: ++ return err; ++} ++ + static ssize_t pcistub_slot_add(struct device_driver *drv, const char *buf, + size_t count) + { +@@ -949,7 +1074,7 @@ static ssize_t pcistub_slot_add(struct device_driver *drv, const char *buf, + if (err) + goto out; + +- err = pcistub_device_id_add(domain, bus, slot, func); ++ err = pcistub_device_id_add(domain, bus, slot, func, PCIBACK_ID_TYPE_HIDE); + + out: + if (!err) +@@ -969,7 +1094,7 @@ static ssize_t pcistub_slot_remove(struct device_driver *drv, const char *buf, + if (err) + goto out; + +- err = pcistub_device_id_remove(domain, bus, slot, func); ++ err = pcistub_device_id_remove(domain, bus, slot, func, PCIBACK_ID_TYPE_HIDE); + + out: + if (!err) +@@ -987,6 +1112,10 @@ static ssize_t pcistub_slot_show(struct device_driver *drv, char *buf) + + spin_lock_irqsave(&device_ids_lock, flags); + list_for_each_entry(pci_dev_id, &pcistub_device_ids, slot_list) { ++ /* only want devices set for hide, not reset entries */ ++ if (pci_dev_id->type != PCIBACK_ID_TYPE_HIDE) ++ continue; ++ + if (count >= PAGE_SIZE) + break; + +@@ -1068,7 +1197,7 @@ static ssize_t pcistub_quirk_show(struct device_driver *drv, char *buf) + + DRIVER_ATTR(quirks, S_IRUSR | S_IWUSR, pcistub_quirk_show, pcistub_quirk_add); + +-static ssize_t permissive_add(struct device_driver *drv, const char *buf, ++static ssize_t pcistub_permissive_add(struct device_driver *drv, const char *buf, + size_t count) + { + int domain, bus, slot, func; +@@ -1109,7 +1238,7 @@ static ssize_t permissive_add(struct device_driver *drv, const char *buf, + return err; + } + +-static ssize_t permissive_show(struct device_driver *drv, char *buf) ++static ssize_t pcistub_permissive_show(struct device_driver *drv, char *buf) + { + struct pcistub_device *psdev; + struct pciback_dev_data *dev_data; +@@ -1132,7 +1261,68 @@ static ssize_t permissive_show(struct device_driver *drv, char *buf) + return count; + } + +-DRIVER_ATTR(permissive, S_IRUSR | S_IWUSR, permissive_show, permissive_add); ++DRIVER_ATTR(permissive, S_IRUSR | S_IWUSR, pcistub_permissive_show, pcistub_permissive_add); ++ ++static ssize_t pcistub_do_flr(struct device_driver *drv, const char *buf, ++ size_t count) ++{ ++ int domain, bus, slot, func; ++ int err; ++ ++ err = str_to_slot(buf, &domain, &bus, &slot, &func); ++ if (err) ++ goto out; ++ ++ err = pcistub_device_do_flr(domain, bus, slot, func); ++ ++out: ++ if (!err) ++ err = count; ++ return err; ++} ++ ++DRIVER_ATTR(do_flr, S_IWUSR, NULL, pcistub_do_flr); ++ ++static ssize_t pcistub_resets(struct device_driver *drv, const char *buf, ++ size_t count) ++{ ++ int domain, bus, slot, func; ++ int type, err = 0; ++ ++ /* string begins with reset type specifier sbr=|dr3=|noflr= */ ++ if (!strncmp(buf, "sbr=", 4)) { ++ type = PCIBACK_ID_TYPE_SBR; ++ buf += 4; ++ } else if (!strncmp(buf, "d3r=", 4)) { ++ type = PCIBACK_ID_TYPE_D3R; ++ buf += 4; ++ } else if (!strncmp(buf, "noflr=", 6)) { ++ type = PCIBACK_ID_TYPE_NOFLR; ++ buf += 6; ++ } else { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* check special wildcard noflr */ ++ if (type == PCIBACK_ID_TYPE_NOFLR && !strncmp(buf, "(*)", 3)) { ++ disable_all_flr = 1; ++ goto out; ++ } ++ ++ err = str_to_slot(buf, &domain, &bus, &slot, &func); ++ if (err) ++ goto out; ++ ++ err = pcistub_device_id_add(domain, bus, slot, func, type); ++ ++out: ++ if (!err) ++ err = count; ++ return err; ++} ++ ++DRIVER_ATTR(resets, S_IWUSR, NULL, pcistub_resets); + + #ifdef CONFIG_PCI_MSI + +@@ -1158,6 +1348,8 @@ static void pcistub_exit(void) + driver_remove_file(&pciback_pci_driver.driver, &driver_attr_slots); + driver_remove_file(&pciback_pci_driver.driver, &driver_attr_quirks); + driver_remove_file(&pciback_pci_driver.driver, &driver_attr_permissive); ++ driver_remove_file(&pciback_pci_driver.driver, &driver_attr_do_flr); ++ driver_remove_file(&pciback_pci_driver.driver, &driver_attr_resets); + + pci_unregister_driver(&pciback_pci_driver); + WARN_ON(unregister_msi_get_owner(pciback_get_owner)); +@@ -1165,35 +1357,27 @@ static void pcistub_exit(void) + + static int __init pcistub_init(void) + { +- int pos = 0; + int err = 0; +- int domain, bus, slot, func; +- int parsed; +- +- if (pci_devs_to_hide && *pci_devs_to_hide) { +- do { +- parsed = 0; +- +- err = sscanf(pci_devs_to_hide + pos, +- " (%x:%x:%x.%x) %n", +- &domain, &bus, &slot, &func, &parsed); +- if (err != 4) { +- domain = 0; +- err = sscanf(pci_devs_to_hide + pos, +- " (%x:%x.%x) %n", +- &bus, &slot, &func, &parsed); +- if (err != 3) +- goto parse_error; +- } +- +- err = pcistub_device_id_add(domain, bus, slot, func); +- if (err) +- goto out; + +- /* if parsed<=0, we've reached the end of the string */ +- pos += parsed; +- } while (parsed > 0 && pci_devs_to_hide[pos]); +- } ++ /* Parse device lists for hide, sbr, and d3r */ ++ err = pciback_parse_device_params(pci_devs_to_hide, PCIBACK_ID_TYPE_HIDE, pcistub_device_id_add); ++ if (err) ++ goto out; ++ ++ err = pciback_parse_device_params(pci_devs_use_sbr, PCIBACK_ID_TYPE_SBR, pcistub_device_id_add); ++ if (err) ++ goto out; ++ ++ err = pciback_parse_device_params(pci_devs_use_d3r, PCIBACK_ID_TYPE_D3R, pcistub_device_id_add); ++ if (err) ++ goto out; ++ ++ if (pci_devs_no_flr && *pci_devs_no_flr && !strncmp(pci_devs_no_flr, "(*)", 3)) ++ disable_all_flr = 1; /* check special wildcard noflr */ ++ else ++ err = pciback_parse_device_params(pci_devs_no_flr, PCIBACK_ID_TYPE_NOFLR, pcistub_device_id_add); ++ if (err) ++ goto out; + + /* If we're the first PCI Device Driver to register, we're the + * first one to get offered PCI devices as they become +@@ -1217,6 +1401,12 @@ static int __init pcistub_init(void) + if (!err) + err = driver_create_file(&pciback_pci_driver.driver, + &driver_attr_permissive); ++ if (!err) ++ err = driver_create_file(&pciback_pci_driver.driver, ++ &driver_attr_do_flr); ++ if (!err) ++ err = driver_create_file(&pciback_pci_driver.driver, ++ &driver_attr_resets); + + if (!err) + err = register_msi_get_owner(pciback_get_owner); +@@ -1225,11 +1415,6 @@ static int __init pcistub_init(void) + + out: + return err; +- +- parse_error: +- printk(KERN_ERR "pciback: Error parsing pci_devs_to_hide at \"%s\"\n", +- pci_devs_to_hide + pos); +- return -EINVAL; + } + + #ifndef MODULE +diff --git a/drivers/xen/pciback/pciback.h b/drivers/xen/pciback/pciback.h +index b2cb031..5cd967c 100644 +--- a/drivers/xen/pciback/pciback.h ++++ b/drivers/xen/pciback/pciback.h +@@ -25,6 +25,14 @@ struct pci_dev_entry { + #define _PCIB_op_pending (1) + #define PCIB_op_pending (1<<(_PCIB_op_pending)) + ++#define PCIBACK_TYPE_UNKNOWN 0 ++#define PCIBACK_TYPE_PCIe_ENDPOINT 1 ++#define PCIBACK_TYPE_PCIe_BRIDGE 2 ++#define PCIBACK_TYPE_PCI_BRIDGE 3 ++#define PCIBACK_TYPE_PCI 4 ++ ++#define DEV_CLASS_PCI_PCI_BRIDGE 0x0604 ++ + struct pciback_device { + void *pci_dev_data; + spinlock_t dev_lock; +@@ -48,6 +56,13 @@ struct pciback_dev_data { + struct list_head config_fields; + int permissive; + int warned_on_write; ++ u32 dev_type; ++ int no_flr; ++ int exp_flr_offset; ++ int af_flr_offset; ++ int use_sbr; ++ int use_d3r; ++ u8 *cfg_space; /* saved config space for device */ + }; + + /* Get/Put PCI Devices that are hidden from the PCI Backend Domain */ +@@ -56,11 +71,25 @@ struct pci_dev *pcistub_get_pci_dev_by_slot(struct pciback_device *pdev, + int slot, int func); + struct pci_dev *pcistub_get_pci_dev(struct pciback_device *pdev, + struct pci_dev *dev); +-void pcistub_put_pci_dev(struct pci_dev *dev); ++void pcistub_put_pci_dev(struct pci_dev *dev, int do_flr); ++ ++/* Reference/unreference PCI Devices and stubs without changing the state */ ++struct pci_dev *pcistub_ref_pci_dev(struct pci_dev *dev); ++void pcistub_unref_pci_dev(struct pci_dev *dev); ++ ++/* Store/reload config space for devices */ ++void pciback_store_config_space(struct pci_dev *dev); ++void pciback_reload_config_space(struct pci_dev *dev); + + /* Ensure a device is turned off or reset */ + void pciback_reset_device(struct pci_dev *pdev); + ++/* Do a function level reset (or approximage functionality) for device */ ++void pciback_flr_device(struct pci_dev *dev); ++ ++/* Helper to classify the device type */ ++void pciback_classify_device(struct pci_dev *dev); ++ + /* Access a virtual configuration space for a PCI device */ + int pciback_config_init(void); + int pciback_config_init_dev(struct pci_dev *dev); +@@ -102,6 +131,10 @@ void pciback_release_devices(struct pciback_device *pdev); + irqreturn_t pciback_handle_event(int irq, void *dev_id); + void pciback_do_op(struct work_struct *work); + ++/* Parse and load device specific module parameters */ ++int pciback_parse_device_params(const char *device_args, int type, ++ int (*add_func) (int domain, int bus, int slot, int func, int type)); ++ + int pciback_xenbus_register(void); + void pciback_xenbus_unregister(void); + +diff --git a/drivers/xen/pciback/pciback_ops.c b/drivers/xen/pciback/pciback_ops.c +index d6b9bf9..ff61476 100644 +--- a/drivers/xen/pciback/pciback_ops.c ++++ b/drivers/xen/pciback/pciback_ops.c +@@ -9,16 +9,184 @@ + #include + #include "pciback.h" + ++#define PCIBACK_VENDOR_INTEL 0x8086 ++#define PCIBACK_CLASS_ID_USB 0x0c03 ++#define PCIBACK_CLASS_ID_VGA 0x0300 ++#define PCIBACK_USB_FLRCTRL 0x4 ++ ++#define PCIBACK_IGFX_CAP09_OFFSET 0xa4 ++#define PCIBACK_IGFX_CAP13_OFFSET 0xa4 ++ ++#define PCIBACK_IGFX_MEDIARST 0x0d ++#define PCIBACK_IGFX_MEDIARST_OFFSET 0xc0 ++ + int verbose_request = 0; + module_param(verbose_request, int, 0644); + ++struct pcistub_sbr_entry { ++ struct list_head dev_list; ++ struct pci_dev *dev; ++}; ++ ++struct pcistub_sbr_list { ++ struct list_head dev_list; ++ struct pci_dev *bridge; ++ struct pci_dev *dev; ++ int find_all; ++ int err; ++}; ++ ++/* Used to store the config state so it can be restored after ++ * resets. ++ */ ++void pciback_store_config_space(struct pci_dev *dev) ++{ ++ struct pciback_dev_data *dev_data = pci_get_drvdata(dev); ++ u32 *ptr = (u32*)dev_data->cfg_space; ++ int i, count = dev->cfg_size/sizeof(u32); ++ ++ for (i = 0; i < count; i += sizeof(u32), ptr++) ++ pci_read_config_dword(dev, i, ptr); ++} ++ ++/* Used to reload the config state after resets. ++ */ ++void pciback_reload_config_space(struct pci_dev *dev) ++{ ++ struct pciback_dev_data *dev_data = pci_get_drvdata(dev); ++ u32 *ptr = (u32*)dev_data->cfg_space; ++ int i, val, count = dev->cfg_size/sizeof(u32); ++ ++ for (i = 0; i < count; i += sizeof(u32), ptr++) { ++ pci_read_config_dword(dev, i, &val); ++ if (val != *ptr) ++ pci_write_config_dword(dev, i, *ptr); ++ } ++} ++ ++static void pciback_walk_bus_cb(struct pci_dev *dev, void *userdata) ++{ ++ struct pcistub_sbr_list *list = (struct pcistub_sbr_list*)userdata; ++ struct pcistub_sbr_entry *entry; ++ struct pci_dev *dev_tmp; ++ ++ if (list->err != 0) ++ return; ++ ++ /* For PCIe endpoints we are only looking for co-assigned functions */ ++ if (!list->find_all && ++ (dev->bus->number != list->dev->bus->number || ++ PCI_SLOT(dev->devfn) != PCI_SLOT(list->dev->devfn))) ++ return; ++ ++ dev_tmp = pcistub_ref_pci_dev(dev); ++ if (dev_tmp == NULL) { ++ /* not controlled by pciback, fail */ ++ list->err = ENXIO; ++ return; ++ } ++ ++ entry = kzalloc(sizeof(*entry), GFP_ATOMIC); ++ if (entry == NULL) { ++ pcistub_unref_pci_dev(dev_tmp); ++ list->err = ENOMEM; ++ return; ++ } ++ ++ entry->dev = dev_tmp; ++ list_add_tail(&entry->dev_list, &list->dev_list); ++} ++ ++static void pciback_cleanup_sbr_list(struct pcistub_sbr_list *list) ++{ ++ struct pcistub_sbr_entry *entry; ++ ++ list_for_each_entry(entry, &list->dev_list, dev_list) { ++ pcistub_unref_pci_dev(entry->dev); ++ kfree(entry); ++ } ++} ++ ++/* Routine to find all devices and bridges that need to be reset ++ * during a secondary bus reset. For PCIe this is simply all the ++ * functions on the particular device. For PCI this is all devices ++ * and bridges below the topmost PCI/PCI-X bridge. Note for PCI, ++ * there is at least one something->PCI/PCI-X bridge to find since ++ * the device is not on the host bus 0 and is on a PCI bus. ++ */ ++static int pciback_get_sbr_list(struct pci_dev *dev, ++ struct pcistub_sbr_list *list, int pcie_endpoint) ++{ ++ struct pci_dev *bridge = dev->bus->self; ++ struct pci_dev *last = NULL; ++ int exp_pos; ++ u16 exp_caps = 0; ++ ++ list->err = 0; ++ list->dev = dev; ++ INIT_LIST_HEAD(&list->dev_list); ++ ++ if (!pcie_endpoint) { ++ while (bridge) { ++ /* Looking for the uppermost PCI/PCI-X bridge. If it is not PCIe then ++ * this is a PCI/PCI-X bridge. If it is PCIe then except the PCIe to ++ * PCI/PCI-X type 7, the rest of the bridge types are PCIe so the last ++ * bridge encountered was the topmost PCI/PCI-X bridge. ++ */ ++ exp_pos = pci_find_capability(bridge, PCI_CAP_ID_EXP); ++ if (exp_pos != 0) { ++ pci_read_config_word(bridge, exp_pos + PCI_EXP_FLAGS, &exp_caps); ++ if (((exp_caps & PCI_EXP_FLAGS_TYPE) >> 4) != PCI_EXP_TYPE_PCI_BRIDGE) ++ break; /* don't want it in the list if it is a PCIe bridge */ ++ } ++ last = bridge; ++ bridge = last->bus->self; ++ } ++ list->bridge = last; ++ list->find_all = 1; /* find all devices/bridges below the topmost */ ++ } ++ else { ++ if (bridge) { ++ /* For PCIe, SBR logic is limited to PCIe endpoints behind a root/switch ++ * port. ++ */ ++ exp_pos = pci_find_capability(bridge, PCI_CAP_ID_EXP); ++ if (likely(exp_pos != 0)) { ++ pci_read_config_word(bridge, exp_pos + PCI_EXP_FLAGS, &exp_caps); ++ exp_caps = ((exp_caps & PCI_EXP_FLAGS_TYPE) >> 4); ++ if (exp_caps == PCI_EXP_TYPE_ROOT_PORT || ++ exp_caps == PCI_EXP_TYPE_UPSTREAM || ++ exp_caps == PCI_EXP_TYPE_DOWNSTREAM) ++ last = bridge; ++ } ++ } ++ list->bridge = last; ++ list->find_all = 0; /* find just functions on this slot */ ++ } ++ ++ /* Sanity check, there may not be any appropriate bridge to reset */ ++ if (!list->bridge) { ++ dev_dbg(&dev->dev, "No appropriate bridge to reset\n"); ++ return ENXIO; ++ } ++ ++ pci_walk_bus(list->bridge->subordinate, pciback_walk_bus_cb, list); ++ ++ if (list->err) { ++ pciback_cleanup_sbr_list(list); ++ return list->err; ++ } ++ ++ return 0; ++} ++ + /* Ensure a device is "turned off" and ready to be exported. + * (Also see pciback_config_reset to ensure virtual configuration space is + * ready to be re-exported) + */ + void pciback_reset_device(struct pci_dev *dev) + { +- u16 cmd; ++ u16 cmd = 0; + + /* Disable devices (but not bridges) */ + if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) { +@@ -38,6 +206,425 @@ void pciback_reset_device(struct pci_dev *dev) + } + } + } ++ ++/* Do a PCIe type function level reset for a single function on this ++ * device. ++ */ ++static void pciback_do_pcie_flr(struct pci_dev *dev, int exp_pos) ++{ ++ u16 status = 0; ++ ++ dev_dbg(&dev->dev, "doing PCIe FLR\n"); ++ ++ pci_block_user_cfg_access(dev); ++ ++ /* Wait for Transaction Pending bit clean */ ++ msleep(100); ++ pci_read_config_word(dev, exp_pos + PCI_EXP_DEVSTA, &status); ++ if (status & PCI_EXP_DEVSTA_TRPND) { ++ dev_dbg(&dev->dev, "Busy after 100ms while trying to reset; sleeping for 1 second\n"); ++ ssleep(1); ++ pci_read_config_word(dev, exp_pos + PCI_EXP_DEVSTA, &status); ++ if (status & PCI_EXP_DEVSTA_TRPND) ++ dev_warn(&dev->dev, "Still busy after 1s; proceeding with reset anyway\n"); ++ } ++ ++ pci_write_config_word(dev, exp_pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR); ++ mdelay(200); ++ ++ pciback_reload_config_space(dev); ++ ++ pci_unblock_user_cfg_access(dev); ++} ++ ++/* Do a PCI type function level reset for a single function on this ++ * device. This uses the Advanced Features Capability extensions to ++ * the PCI spec. ++ */ ++static void pciback_do_pci_flr(struct pci_dev *dev, int af_pos, int clear_cmd) ++{ ++ u8 status = 0; ++ ++ dev_dbg(&dev->dev, "doing PCI FLR\n"); ++ ++ pci_block_user_cfg_access(dev); ++ ++ /* Clear the command register to prevent new transactions */ ++ if (clear_cmd) ++ pci_write_config_word(dev, PCI_COMMAND, 0); ++ ++ /* Wait for Transaction Pending bit clean */ ++ msleep(100); ++ pci_read_config_byte(dev, af_pos + PCI_AF_STA, &status); ++ if (status & PCI_AF_STA_TP) { ++ dev_dbg(&dev->dev, "Busy after 100ms while trying to reset; sleeping for 1 second\n"); ++ ssleep(1); ++ pci_read_config_byte(dev, af_pos + PCI_AF_STA, &status); ++ if (status & PCI_AF_STA_TP) ++ dev_warn(&dev->dev, "Still busy after 1s; proceeding with reset anyway\n"); ++ } ++ ++ pci_write_config_byte(dev, af_pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR); ++ mdelay(200); ++ ++ pciback_reload_config_space(dev); ++ ++ pci_unblock_user_cfg_access(dev); ++} ++ ++/* Vendor specific resets. These can be set in the vendor specific ++ * capabilities structures. Currently only the Intel USB and iGFX ++ * reset is supported. ++ */ ++static int pciback_do_vendor_specific_reset(struct pci_dev *dev) ++{ ++ struct pci_dev *gmch; ++ int vendor_pos, i; ++ u32 reg32 = 0; ++ u16 device_id, cmd; ++ u8 reg8 = 0; ++ ++ dev_dbg(&dev->dev, "doing vendor specific resets\n"); ++ ++ if (dev->vendor != PCIBACK_VENDOR_INTEL) ++ return -ENXIO; ++ ++ if ((dev->class >> 8) == PCIBACK_CLASS_ID_VGA) { ++ if (dev->bus->number != 0 || dev->devfn != PCI_DEVFN(2,0)) ++ return -ENXIO; ++ ++ /* Locate the GMCH (north bridge) and test for specific Intel devices */ ++ gmch = pci_get_bus_and_slot(0, PCI_DEVFN(0,0)); ++ if (!gmch) ++ return -ENXIO; ++ ++ device_id = gmch->device; ++ pci_dev_put(gmch); ++ ++ if (device_id != PCI_DEVICE_ID_INTEL_GMCHGM45) ++ return -ENXIO; ++ ++ /* Correct device and platform, assume vendor specific offset */ ++ pci_read_config_dword(dev, PCIBACK_IGFX_CAP09_OFFSET, ®32); ++ if ((reg32 & 0x000000FF) != PCI_CAP_ID_VNDR || ++ ((reg32 >> 16) & 0x000000FF) != 0x06 || ++ ((reg32 >> 24) & 0x000000F0) != 0x20) ++ return -ENXIO; ++ ++ vendor_pos = PCIBACK_IGFX_CAP09_OFFSET; ++ } else if ((dev->class >> 8) == PCIBACK_CLASS_ID_USB) { ++ vendor_pos = pci_find_capability(dev, PCI_CAP_ID_VNDR); ++ if (vendor_pos == 0) ++ return -ENXIO; ++ } ++ else ++ return -ENXIO; ++ ++ if ((dev->class >> 8) == PCIBACK_CLASS_ID_VGA) { ++ pci_write_config_byte(dev, PCIBACK_IGFX_MEDIARST_OFFSET, PCIBACK_IGFX_MEDIARST); ++ for (i = 0; i <= 10; i++) { ++ msleep(100); ++ pci_read_config_byte(dev, PCIBACK_IGFX_MEDIARST_OFFSET, ®8); ++ if ((reg8 & 0x01) == 0) ++ break; ++ if (i == 10) { ++ dev_warn(&dev->dev, "media not reset after 1s; skipping FLR\n"); ++ goto out; ++ } ++ } ++ ++ /* This specific reset will hang if the command register does not have ++ * memory space access enabled */ ++ pci_read_config_word(dev, PCI_COMMAND, &cmd); ++ pci_write_config_word(dev, PCI_COMMAND, (cmd | PCI_COMMAND_MEMORY)); ++ /* The rest is the same as a PCI AF FLR - use the same routine */ ++ pciback_do_pci_flr(dev, vendor_pos, 0); ++ pci_write_config_word(dev, PCI_COMMAND, cmd); ++ } else { ++ pci_block_user_cfg_access(dev); ++ ++ pci_write_config_byte(dev, vendor_pos + PCIBACK_USB_FLRCTRL, 1); ++ mdelay(200); ++ ++ pciback_reload_config_space(dev); ++ ++ pci_unblock_user_cfg_access(dev); ++ } ++ ++out: ++ return 0; ++} ++ ++/* Use a D0-D3-D0 device state transition to reset the device. This ++ * is a good enough reset for some devices (like NICs). ++ */ ++static int pciback_do_dstate_transition_reset(struct pci_dev *dev) ++{ ++ int pm_pos; ++ u32 pm_ctl = 0; ++ ++ pm_pos = pci_find_capability(dev, PCI_CAP_ID_PM); ++ if (pm_pos == 0) ++ return -ENXIO; ++ ++ dev_dbg(&dev->dev, "doing Dstate transition reset\n"); ++ ++ /* No_Soft_Reset - When set 1, this bit indicates that devices ++ * transitioning from D3hot to D0 because of PowerState commands ++ * do not perform an internal reset. ++ */ ++ pci_read_config_dword(dev, pm_pos + PCI_PM_CTRL, &pm_ctl); ++ if (pm_ctl & PCI_PM_CTRL_NO_SOFT_RESET) ++ return -ENXIO; ++ ++ pci_block_user_cfg_access(dev); ++ ++ pm_ctl &= ~PCI_PM_CTRL_STATE_MASK; ++ pm_ctl |= PCI_PM_CTRL_D3HOT; ++ pci_write_config_word(dev, pm_pos + PCI_PM_CTRL, pm_ctl); ++ mdelay(10); ++ ++ pm_ctl &= ~PCI_PM_CTRL_STATE_MASK; ++ pm_ctl |= PCI_PM_CTRL_D0; ++ pci_write_config_word(dev, pm_pos + PCI_PM_CTRL, pm_ctl); ++ mdelay(10); ++ ++ pciback_reload_config_space(dev); ++ ++ pci_unblock_user_cfg_access(dev); ++ ++ return 0; ++} ++ ++/* Do a secondary bus reset on a bridge. This is only done if all ++ * co-assignment rules are satisfied and if it was explicitly ++ * requested via pciback parameters. ++ */ ++static int pciback_do_secondary_bus_reset(struct pci_dev *dev, u32 dev_type) ++{ ++ struct pcistub_sbr_list sbr_list; ++ struct pcistub_sbr_entry *entry; ++ u16 pci_bctl = 0; ++ int err = 0; ++ ++ /* Call helper to get the device list needed for the device type. */ ++ err = pciback_get_sbr_list(dev, &sbr_list, ++ (dev_type == PCIBACK_TYPE_PCIe_ENDPOINT ? 1 : 0)); ++ if (err) { ++ dev_warn(&dev->dev, ++ "secondary bus reset failed for device - all functions need to be co-assigned - err: %d\n", err); ++ return err; ++ } ++ ++ pci_block_user_cfg_access(dev); ++ ++ /* Reset the secondary bus and restore the PCI space for all the devfn found above. ++ */ ++ pci_read_config_word(sbr_list.bridge, PCI_BRIDGE_CONTROL, &pci_bctl); ++ pci_write_config_word(sbr_list.bridge, PCI_BRIDGE_CONTROL, pci_bctl | PCI_BRIDGE_CTL_BUS_RESET); ++ msleep(200); ++ pci_write_config_word(sbr_list.bridge, PCI_BRIDGE_CONTROL, pci_bctl); ++ msleep(200); ++ ++ list_for_each_entry(entry, &sbr_list.dev_list, dev_list) { ++ pciback_reload_config_space(entry->dev); ++ } ++ ++ pci_unblock_user_cfg_access(dev); ++ ++ pciback_cleanup_sbr_list(&sbr_list); ++ ++ return 0; ++} ++ ++/* This function is used to do a function level reset on a singe ++ * device/function. FLRs must be done on devices before they are ++ * unassigned from one domain and passed through to another. The ++ * preferred method is to do an actual FLR on the device but the ++ * functionality may not be present or exposed. In the later case ++ * we attempt to locate the capability even though it is not ++ * chained into the capabilities list. ++ * ++ * In some cases, there is no way to perform the actual FLR so we ++ * fall back to some alternate methods (which are not as effective ++ * or useful). ++ */ ++void pciback_flr_device(struct pci_dev *dev) ++{ ++ struct pciback_dev_data *dev_data = pci_get_drvdata(dev); ++ int err = 0; ++ ++ if (dev_data->no_flr) { ++ dev_dbg(&dev->dev, "FLR disabled for device\n"); ++ return; ++ } ++ dev_dbg(&dev->dev, "FLR invoked for device\n"); ++ ++ do { ++ /* First, always try to do an FLR */ ++ if (dev_data->dev_type == PCIBACK_TYPE_PCIe_ENDPOINT && ++ dev_data->exp_flr_offset != 0) { ++ pciback_do_pcie_flr(dev, dev_data->exp_flr_offset); ++ break; ++ } ++ if (dev_data->dev_type == PCIBACK_TYPE_PCI && ++ dev_data->af_flr_offset != 0) { ++ pciback_do_pci_flr(dev, dev_data->af_flr_offset, 1); ++ break; ++ } ++ ++ /* Next for integrated devices on the host bus 0, try some other methods */ ++ if (dev->bus->number == 0) { ++ err = pciback_do_vendor_specific_reset(dev); ++ if (err && dev_data->use_d3r) ++ err = pciback_do_dstate_transition_reset(dev); ++ if (err) ++ dev_warn(&dev->dev, "FLR functionality not supported; " ++ "attempts to use vendor FLR or D-states unsuccessful\n"); ++ break; ++ } ++ ++ /* Else attempt a secondary bus reset if all conditions are met */ ++ if (dev_data->use_sbr) { ++ err = pciback_do_secondary_bus_reset(dev, dev_data->dev_type); ++ if (err) ++ dev_warn(&dev->dev, "FLR functionality not supported; " ++ "attempts to use secondary bus reset unsuccessful;\n"); ++ break; ++ } ++ ++ err = -ENODEV; ++ } while (0); ++ ++ if (err) ++ dev_warn(&dev->dev, "FLR not performed for device\n"); ++} ++ ++/* Helper used to location the FLR capabilities for a PCIe device. ++ * When the capability cannot be found in the chain but is present, ++ * special logic is used to attempt to locate functionality. ++ * ++ * returns: the offset to the capability, zero if not found. ++ */ ++static int pciback_find_pcie_flr_caps(struct pci_dev *dev) ++{ ++ int exp_pos; ++ u32 cap = 0; ++ ++ /* First look for the PCIe FLR capabilities using the capabilities list */ ++ exp_pos = pci_find_capability(dev, PCI_CAP_ID_EXP); ++ if (exp_pos) { ++ pci_read_config_dword(dev, exp_pos + PCI_EXP_DEVCAP, &cap); ++ if (cap & PCI_EXP_DEVCAP_FLR) { ++ return exp_pos; ++ } ++ } ++ ++ return 0; ++} ++ ++/* Helper used to location the AF FLR capabilities for a PCI device. ++ * When the capability cannot be found in the chain but is present, ++ * special logic is used to attempt to locate functionality. ++ * ++ * returns: the offset to the capability, zero if not found. ++ */ ++static int pciback_find_pci_flr_caps(struct pci_dev *dev) ++{ ++ struct pci_dev *gmch; ++ int af_pos; ++ u16 device_id; ++ u8 cap = 0, reg8 = 0; ++ ++ /* First look for the PCI AF capabilities for FLR using the capabilities list. This ++ * is only used on the devices on the root/host bus (integrated devices). ++ */ ++ if (dev->bus->number != 0) ++ return 0; ++ ++ af_pos = pci_find_capability(dev, PCI_CAP_ID_AF); ++ if (af_pos) { ++ pci_read_config_byte(dev, af_pos + PCI_AF_DEVCAP, &cap); ++ if (cap & PCI_AF_CAP_FLR) { ++ return af_pos; ++ } ++ } ++ ++ /* Next look for the unchained AF capabilities for FLR using specific ++ * logic. Currently only the graphics device on the Intel Q45 etc ++ * systems has special logic for locating the hidden FLR caps. ++ */ ++ do { ++ if (dev->bus->number != 0 || dev->devfn != PCI_DEVFN(2,0) || ++ dev->vendor != PCIBACK_VENDOR_INTEL || (dev->class >> 8) != PCIBACK_CLASS_ID_VGA) ++ break; ++ ++ /* Locate the GMCH (north bridge) and test for specific Intel devices */ ++ gmch = pci_get_bus_and_slot(0, PCI_DEVFN(0,0)); ++ if (!gmch) ++ break; ++ ++ device_id = gmch->device; ++ pci_dev_put(gmch); ++ ++ if (device_id != PCI_DEVICE_ID_INTEL_GMCHQ45 && ++ device_id != PCI_DEVICE_ID_INTEL_GMCHG45 && ++ device_id != PCI_DEVICE_ID_INTEL_GMCHG41) ++ break; ++ ++ /* Correct device and platform, assume AF offset */ ++ af_pos = PCIBACK_IGFX_CAP13_OFFSET; ++ pci_read_config_byte(dev, af_pos + PCI_AF_LENFLD, ®8); ++ if (reg8 == PCI_AF_LENGTH) { ++ pci_read_config_byte(dev, af_pos + PCI_AF_DEVCAP, &cap); ++ if (cap & PCI_AF_CAP_FLR) { ++ return af_pos; ++ } ++ } ++ } while (0); ++ ++ /* Else not found */ ++ return 0; ++} ++ ++/* Classify the device, specifically determine if it is PCIe/PCI ++ * and whether it is a PCIe endpoint, bridge, or other PCI device. ++ */ ++void pciback_classify_device(struct pci_dev *dev) ++{ ++ struct pciback_dev_data *dev_data; ++ int exp_pos; ++ u16 exp_caps = 0; ++ ++ dev_data = pci_get_drvdata(dev); ++ dev_data->dev_type = PCIBACK_TYPE_UNKNOWN; ++ ++ exp_pos = pci_find_capability(dev, PCI_CAP_ID_EXP); ++ ++ if ((dev->class >> 8) != DEV_CLASS_PCI_PCI_BRIDGE) { ++ if (exp_pos != 0) { ++ dev_data->dev_type = PCIBACK_TYPE_PCIe_ENDPOINT; ++ dev_data->exp_flr_offset = pciback_find_pcie_flr_caps(dev); ++ } else { ++ dev_data->dev_type = PCIBACK_TYPE_PCI; ++ dev_data->af_flr_offset = pciback_find_pci_flr_caps(dev); ++ } ++ goto classify_done; ++ } ++ ++ if (exp_pos == 0) { ++ dev_data->dev_type = PCIBACK_TYPE_PCI_BRIDGE; ++ goto classify_done; ++ } ++ ++ pci_read_config_word(dev, exp_pos + PCI_EXP_FLAGS, &exp_caps); ++ dev_data->dev_type = (((exp_caps & PCI_EXP_FLAGS_TYPE) >> 4) == PCI_EXP_TYPE_PCI_BRIDGE) ? PCIBACK_TYPE_PCI_BRIDGE : PCIBACK_TYPE_PCIe_BRIDGE; ++ ++classify_done: ++ ++ return; ++} ++ + extern wait_queue_head_t aer_wait_queue; + extern struct workqueue_struct *pciback_wq; + /* +@@ -132,3 +719,51 @@ irqreturn_t pciback_handle_event(int irq, void *dev_id) + + return IRQ_HANDLED; + } ++ ++/* Helper routine used to parse command line parameters passed to the ++ * pciback module from the boot loader. These params all have the form ++ * of a list of one or more devices, e.g.: ++ * (XXXX:XX:XX.X)(XXXX:XX:XX.X) ++ * Which is: (domain/segment:bus:dev.func) ++ */ ++int pciback_parse_device_params(const char *device_args, int type, ++ int (*add_func) (int domain, int bus, int slot, int func, int type)) ++{ ++ int pos = 0; ++ int err = 0; ++ int domain, bus, slot, func; ++ int parsed; ++ ++ if (device_args && *device_args) { ++ do { ++ parsed = 0; ++ ++ err = sscanf(device_args + pos, ++ " (%x:%x:%x.%x) %n", ++ &domain, &bus, &slot, &func, &parsed); ++ if (err != 4) { ++ domain = 0; ++ err = sscanf(device_args + pos, ++ " (%x:%x.%x) %n", ++ &bus, &slot, &func, &parsed); ++ if (err != 3) ++ goto parse_error; ++ } ++ ++ err = add_func(domain, bus, slot, func, type); ++ if (err) ++ goto out; ++ ++ /* if parsed<=0, we've reached the end of the string */ ++ pos += parsed; ++ } while (parsed > 0 && device_args[pos]); ++ } ++ ++out: ++ return err; ++ ++parse_error: ++ printk(KERN_ERR "pciback: Error parsing device parameters \"%s\" at \"%s\"\n", ++ device_args, device_args + pos); ++ return -EINVAL; ++} +diff --git a/drivers/xen/pciback/slot.c b/drivers/xen/pciback/slot.c +index 105a8b6..9c66633 100644 +--- a/drivers/xen/pciback/slot.c ++++ b/drivers/xen/pciback/slot.c +@@ -109,7 +109,7 @@ void pciback_release_pci_dev(struct pciback_device *pdev, struct pci_dev *dev) + spin_unlock_irqrestore(&slot_dev->lock, flags); + + if (found_dev) +- pcistub_put_pci_dev(found_dev); ++ pcistub_put_pci_dev(found_dev, 0); + } + + int pciback_init_devices(struct pciback_device *pdev) +@@ -149,7 +149,7 @@ void pciback_release_devices(struct pciback_device *pdev) + for (slot = 0; slot < PCI_SLOT_MAX; slot++) { + dev = slot_dev->slots[bus][slot]; + if (dev != NULL) +- pcistub_put_pci_dev(dev); ++ pcistub_put_pci_dev(dev, 0); + } + + kfree(slot_dev); +diff --git a/drivers/xen/pciback/vpci.c b/drivers/xen/pciback/vpci.c +index a5b7ece..259e71d 100644 +--- a/drivers/xen/pciback/vpci.c ++++ b/drivers/xen/pciback/vpci.c +@@ -162,7 +162,7 @@ void pciback_release_pci_dev(struct pciback_device *pdev, struct pci_dev *dev) + spin_unlock_irqrestore(&vpci_dev->lock, flags); + + if (found_dev) +- pcistub_put_pci_dev(found_dev); ++ pcistub_put_pci_dev(found_dev, 0); + } + + int pciback_init_devices(struct pciback_device *pdev) +@@ -202,7 +202,7 @@ void pciback_release_devices(struct pciback_device *pdev) + list_for_each_entry_safe(e, tmp, &vpci_dev->dev_list[slot], + list) { + list_del(&e->list); +- pcistub_put_pci_dev(e->dev); ++ pcistub_put_pci_dev(e->dev, 0); + kfree(e); + } + } +diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h +index 12080a3..4a6eba5 100644 +--- a/include/linux/pci_ids.h ++++ b/include/linux/pci_ids.h +@@ -2495,6 +2495,14 @@ + #define PCI_DEVICE_ID_INTEL_IXP2800 0x9004 + #define PCI_DEVICE_ID_INTEL_S21152BB 0xb152 + ++#define PCI_DEVICE_ID_INTEL_GMCHQ45 0x2e10 ++#define PCI_DEVICE_ID_INTEL_GMCHG45 0x2e20 ++#define PCI_DEVICE_ID_INTEL_MCHP45 0x2e20 ++#define PCI_DEVICE_ID_INTEL_GMCHG41 0x2e30 ++#define PCI_DEVICE_ID_INTEL_GMCHGM45 0x2a40 ++ ++#define PCI_DEVICE_ID_INTEL_GMCHG41 0x2e30 ++ + #define PCI_VENDOR_ID_SCALEMP 0x8686 + #define PCI_DEVICE_ID_SCALEMP_VSMP_CTL 0x1010 + +diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h +index 450684f..97812c6 100644 +--- a/include/linux/pci_regs.h ++++ b/include/linux/pci_regs.h +@@ -210,6 +210,7 @@ + #define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ + #define PCI_CAP_ID_EXP 0x10 /* PCI Express */ + #define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ ++#define PCI_CAP_ID_AF 0x13 /* Advanced Features Capability */ + #define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ + #define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ + #define PCI_CAP_SIZEOF 4 +@@ -239,6 +240,11 @@ + #define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ + #define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ + #define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ ++#define PCI_PM_CTRL_DATA_DSTATE_MASK 0x3 /* D0 - D3 */ ++#define PCI_PM_CTRL_D0 0x0 ++#define PCI_PM_CTRL_D1 0x1 ++#define PCI_PM_CTRL_D2 0x2 ++#define PCI_PM_CTRL_D3HOT 0x3 + #define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ + #define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ + #define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ +@@ -377,6 +383,7 @@ + #define PCI_EXP_DEVCAP_RBER 0x8000 /* Role-Based Error Reporting */ + #define PCI_EXP_DEVCAP_PWR_VAL 0x3fc0000 /* Slot Power Limit Value */ + #define PCI_EXP_DEVCAP_PWR_SCL 0xc000000 /* Slot Power Limit Scale */ ++#define PCI_EXP_DEVCAP_FLR 0x10000000 /* Function Level Reset */ + #define PCI_EXP_DEVCTL 8 /* Device Control */ + #define PCI_EXP_DEVCTL_CERE 0x0001 /* Correctable Error Reporting En. */ + #define PCI_EXP_DEVCTL_NFERE 0x0002 /* Non-Fatal Error Reporting Enable */ +@@ -389,6 +396,7 @@ + #define PCI_EXP_DEVCTL_AUX_PME 0x0400 /* Auxiliary Power PM Enable */ + #define PCI_EXP_DEVCTL_NOSNOOP_EN 0x0800 /* Enable No Snoop */ + #define PCI_EXP_DEVCTL_READRQ 0x7000 /* Max_Read_Request_Size */ ++#define PCI_EXP_DEVCTL_BCR_FLR 0x8000 /* Bridge Configuration Retry / FLR */ + #define PCI_EXP_DEVSTA 10 /* Device Status */ + #define PCI_EXP_DEVSTA_CED 0x01 /* Correctable Error Detected */ + #define PCI_EXP_DEVSTA_NFED 0x02 /* Non-Fatal Error Detected */ +@@ -419,6 +427,10 @@ + #define PCI_EXP_RTCTL_CRSSVE 0x10 /* CRS Software Visibility Enable */ + #define PCI_EXP_RTCAP 30 /* Root Capabilities */ + #define PCI_EXP_RTSTA 32 /* Root Status */ ++#define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ ++#define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ ++#define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ ++#define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ + + /* Extended Capabilities (PCI-X 2.0 and Express) */ + #define PCI_EXT_CAP_ID(header) (header & 0x0000ffff) +@@ -429,6 +441,7 @@ + #define PCI_EXT_CAP_ID_VC 2 + #define PCI_EXT_CAP_ID_DSN 3 + #define PCI_EXT_CAP_ID_PWR 4 ++#define PCI_EXT_CAP_ID_ARI 14 + + /* Advanced Error Reporting */ + #define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ +@@ -536,5 +549,25 @@ + #define HT_CAPTYPE_GEN3 0xD0 /* Generation 3 hypertransport configuration */ + #define HT_CAPTYPE_PM 0xE0 /* Hypertransport powermanagement configuration */ + ++/* Alternative Routing-ID Interpretation */ ++#define PCI_ARI_CAP 0x04 /* ARI Capability Register */ ++#define PCI_ARI_CAP_MFVC 0x0001 /* MFVC Function Groups Capability */ ++#define PCI_ARI_CAP_ACS 0x0002 /* ACS Function Groups Capability */ ++#define PCI_ARI_CAP_NFN(x) (((x) >> 8) & 0xff) /* Next Function Number */ ++#define PCI_ARI_CTRL 0x06 /* ARI Control Register */ ++#define PCI_ARI_CTRL_MFVC 0x0001 /* MFVC Function Groups Enable */ ++#define PCI_ARI_CTRL_ACS 0x0002 /* ACS Function Groups Enable */ ++#define PCI_ARI_CTRL_FG(x) (((x) >> 4) & 7) /* Function Group */ ++ ++/* Advanced Features Capability */ ++#define PCI_AF_LENFLD 0x02 /* Device length offset */ ++#define PCI_AF_LENGTH 0x06 ++#define PCI_AF_DEVCAP 0x03 /* Device capabilities offset */ ++#define PCI_AF_CAP_TP 0x01 ++#define PCI_AF_CAP_FLR 0x02 ++#define PCI_AF_CTRL 0x04 /* Device CTRL offset */ ++#define PCI_AF_CTRL_FLR 0x01 ++#define PCI_AF_STA 0x05 /* Device STATUS offset */ ++#define PCI_AF_STA_TP 0x01 + + #endif /* LINUX_PCI_REGS_H */ diff --git a/master/series b/master/series index a24c116..7f1cd7a 100644 --- a/master/series +++ b/master/series @@ -245,3 +245,9 @@ dom0-swap-extra-debugging oom-debugging oom-debug-me-harder sysrq-loglevel + +# +# XenClient +# +xen-acpi-wmi +pciback-flr diff --git a/master/xen-acpi-wmi b/master/xen-acpi-wmi new file mode 100644 index 0000000..77d2a20 --- /dev/null +++ b/master/xen-acpi-wmi @@ -0,0 +1,1013 @@ +diff -Nur linux-2.6-a/drivers/acpi/ec.c linux-2.6-b/drivers/acpi/ec.c +--- linux-2.6-a/drivers/acpi/ec.c 2009-04-07 16:52:00.000000000 -0400 ++++ linux-2.6-b/drivers/acpi/ec.c 2009-04-07 16:36:54.000000000 -0400 +@@ -123,6 +123,12 @@ + + int acpi_ec_intr = 1; /* Default is interrupt mode */ + ++static u32 wmi_event_data_index = 0; ++extern u8 in_query_wmi_event_data; ++extern const u8 wmi_ec_max_data_size; ++extern u32 wmi_ec_port_data_size; ++extern u8 wmi_ec_port_data[32]; ++ + /* -------------------------------------------------------------------------- + Transaction Management + -------------------------------------------------------------------------- */ +@@ -377,6 +383,22 @@ + + result = acpi_ec_transaction(ec, &t, 0); + *data = d; ++ ++ /* HACK ALERT ++ * Please refer to wmi.c for an explanation on why we added this hack. ++ */ ++ if ( in_query_wmi_event_data == TRUE ) { ++ if ( address == 0x2b ) { ++ wmi_event_data_index = 0; ++ memset(wmi_ec_port_data, 0, wmi_ec_max_data_size); ++ wmi_ec_port_data_size = *data; ++ } else if ( (address == 0x2c) && (wmi_event_data_index < wmi_ec_port_data_size) ++ && (wmi_event_data_index < wmi_ec_max_data_size) ) { ++ wmi_ec_port_data[wmi_event_data_index] = *data; ++ wmi_event_data_index++; ++ } ++ } ++ + return result; + } + +diff -Nur linux-2.6-a/drivers/acpi/wmi.c linux-2.6-b/drivers/acpi/wmi.c +--- linux-2.6-a/drivers/acpi/wmi.c 2009-04-07 15:31:56.000000000 -0400 ++++ linux-2.6-b/drivers/acpi/wmi.c 2009-04-07 17:29:43.000000000 -0400 +@@ -1,13 +1,18 @@ + /* + * ACPI-WMI mapping driver + * +- * Copyright (C) 2007-2008 Carlos Corbacho ++ * Copyright (C) 2007-2008 Carlos Corbacho strangeworlds.co.uk> + * +- * GUID parsing code from ldm.c is: +- * Copyright (C) 2001,2002 Richard Russon +- * Copyright (c) 2001-2007 Anton Altaparmakov +- * Copyright (C) 2001,2002 Jakob Kemi ++ * Modifications: ++ * Copyright (c) 2009 Kamala Narasimhan - Citrix Systems, Inc. + * ++ * Following modifications where made to fit our usecase - ++ * a) Route WMI events to acpid. ++ * b) Remove exports not required for our usecase, remove notification ++ * installation/uninstallation code (as we now route events to acpid). ++ * c) Minor device id issue. ++ * d) Remove GUID parsing code as our usecase does not require supporting ++ * 36 char guid input. + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify +@@ -32,6 +37,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -45,8 +51,6 @@ + #undef PREFIX + #define PREFIX "ACPI: WMI: " + +-static DEFINE_MUTEX(wmi_data_lock); +- + struct guid_block { + char guid[16]; + union { +@@ -64,11 +68,25 @@ + struct list_head list; + struct guid_block gblock; + acpi_handle handle; +- wmi_notify_handler handler; +- void *handler_data; + }; + + static struct wmi_block wmi_blocks; ++static DEFINE_MUTEX(wmi_mutex); ++ ++/* ++ * YIKEES! HACK ALERT ++ * Due to buffer dereferencing bugs at firmware layer, some firmware(s) ++ * (Dell in specific) return all 0s for wmi event data when ++ * queried through _WED. To work around this firmware bug and provide ++ * appropriate event data to guest firmware, we will sniff the embedded ++ * controller port access to addresses 0x2b and 0x2c and slap that data ++ * at the beginning of _WED return buffer which would otherwise be all 0s. ++ */ ++static u8 enable_wmi_event_data_hack = FALSE; ++u8 in_query_wmi_event_data = FALSE; ++const u8 wmi_ec_max_data_size = 32; ++u32 wmi_ec_port_data_size = 0; ++u8 wmi_ec_port_data[32]; + + /* + * If the GUID data block is marked as expensive, we must enable and +@@ -82,133 +100,27 @@ + static int acpi_wmi_remove(struct acpi_device *device, int type); + static int acpi_wmi_add(struct acpi_device *device); + +-static const struct acpi_device_id wmi_device_ids[] = { +- {"PNP0C14", 0}, +- {"pnp0c14", 0}, +- {"", 0}, +-}; +-MODULE_DEVICE_TABLE(acpi, wmi_device_ids); +- + static struct acpi_driver acpi_wmi_driver = { + .name = "wmi", + .class = ACPI_WMI_CLASS, +- .ids = wmi_device_ids, ++ .ids = "PNP0C14, pnp0c14", + .ops = { + .add = acpi_wmi_add, + .remove = acpi_wmi_remove, + }, + }; + +-/* +- * GUID parsing functions +- */ +- +-/** +- * wmi_parse_hexbyte - Convert a ASCII hex number to a byte +- * @src: Pointer to at least 2 characters to convert. +- * +- * Convert a two character ASCII hex string to a number. +- * +- * Return: 0-255 Success, the byte was parsed correctly +- * -1 Error, an invalid character was supplied +- */ +-static int wmi_parse_hexbyte(const u8 *src) +-{ +- unsigned int x; /* For correct wrapping */ +- int h; +- +- /* high part */ +- x = src[0]; +- if (x - '0' <= '9' - '0') { +- h = x - '0'; +- } else if (x - 'a' <= 'f' - 'a') { +- h = x - 'a' + 10; +- } else if (x - 'A' <= 'F' - 'A') { +- h = x - 'A' + 10; +- } else { +- return -1; +- } +- h <<= 4; +- +- /* low part */ +- x = src[1]; +- if (x - '0' <= '9' - '0') +- return h | (x - '0'); +- if (x - 'a' <= 'f' - 'a') +- return h | (x - 'a' + 10); +- if (x - 'A' <= 'F' - 'A') +- return h | (x - 'A' + 10); +- return -1; +-} +- +-/** +- * wmi_swap_bytes - Rearrange GUID bytes to match GUID binary +- * @src: Memory block holding binary GUID (16 bytes) +- * @dest: Memory block to hold byte swapped binary GUID (16 bytes) +- * +- * Byte swap a binary GUID to match it's real GUID value +- */ +-static void wmi_swap_bytes(u8 *src, u8 *dest) +-{ +- int i; +- +- for (i = 0; i <= 3; i++) +- memcpy(dest + i, src + (3 - i), 1); +- +- for (i = 0; i <= 1; i++) +- memcpy(dest + 4 + i, src + (5 - i), 1); +- +- for (i = 0; i <= 1; i++) +- memcpy(dest + 6 + i, src + (7 - i), 1); +- +- memcpy(dest + 8, src + 8, 8); +-} +- +-/** +- * wmi_parse_guid - Convert GUID from ASCII to binary +- * @src: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba +- * @dest: Memory block to hold binary GUID (16 bytes) +- * +- * N.B. The GUID need not be NULL terminated. +- * +- * Return: 'true' @dest contains binary GUID +- * 'false' @dest contents are undefined +- */ +-static bool wmi_parse_guid(const u8 *src, u8 *dest) +-{ +- static const int size[] = { 4, 2, 2, 2, 6 }; +- int i, j, v; +- +- if (src[8] != '-' || src[13] != '-' || +- src[18] != '-' || src[23] != '-') +- return false; +- +- for (j = 0; j < 5; j++, src++) { +- for (i = 0; i < size[j]; i++, src += 2, *dest++ = v) { +- v = wmi_parse_hexbyte(src); +- if (v < 0) +- return false; +- } +- } +- +- return true; +-} +- + static bool find_guid(const char *guid_string, struct wmi_block **out) + { +- char tmp[16], guid_input[16]; + struct wmi_block *wblock; + struct guid_block *block; + struct list_head *p; + +- wmi_parse_guid(guid_string, tmp); +- wmi_swap_bytes(tmp, guid_input); +- + list_for_each(p, &wmi_blocks.list) { + wblock = list_entry(p, struct wmi_block, list); + block = &wblock->gblock; + +- if (memcmp(block->guid, guid_input, 16) == 0) { ++ if (memcmp(block->guid, guid_string, 16) == 0) { + if (out) + *out = wblock; + return 1; +@@ -217,12 +129,46 @@ + return 0; + } + ++static acpi_status wmi_enable_event_data_blocks(int enable) ++{ ++ struct list_head *p; ++ struct guid_block *gblock; ++ struct wmi_block *wblock; ++ char method[5]; ++ struct acpi_object_list input; ++ union acpi_object params[1]; ++ acpi_status status; ++ ++ list_for_each(p, &wmi_blocks.list) { ++ wblock = list_entry(p, struct wmi_block, list); ++ gblock = &wblock->gblock; ++ ++ if (gblock->flags & ACPI_WMI_EVENT) { ++ input.count = 1; ++ input.pointer = params; ++ params[0].type = ACPI_TYPE_INTEGER; ++ params[0].integer.value = enable; ++ ++ snprintf(method, 5, "WE%02X", gblock->notify_id); ++ status = acpi_evaluate_object(wblock->handle, method, &input, NULL); ++ ++ if (status != AE_OK && status != AE_NOT_FOUND) { ++ printk(KERN_INFO PREFIX "Event block %s enable failed\n", method); ++ return status; ++ } else ++ return AE_OK; ++ } ++ } ++ ++ return AE_OK; /* if we don't have a wmi block (though odd), just return success */ ++} ++ + /* + * Exported WMI functions + */ + /** + * wmi_evaluate_method - Evaluate a WMI method +- * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba ++ * @guid_string: 16 byte guid + * @instance: Instance index + * @method_id: Method ID to call + * &in: Buffer containing input for the method call +@@ -247,7 +193,7 @@ + block = &wblock->gblock; + handle = wblock->handle; + +- if (!(block->flags & ACPI_WMI_METHOD)) ++ if (!(block->flags & ACPI_WMI_METHOD)) + return AE_BAD_DATA; + + if (block->instance_count < instance) +@@ -282,7 +228,7 @@ + + /** + * wmi_query_block - Return contents of a WMI block +- * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba ++ * @guid_string: 16 byte guid + * @instance: Instance index + * &out: Empty buffer to return the contents of the data block to + * +@@ -338,10 +284,10 @@ + * expensive, but have no corresponding WCxx method. So we + * should not fail if this happens. + */ +- wc_status = acpi_get_handle(handle, wc_method, &wc_handle); +- if (ACPI_SUCCESS(wc_status)) +- wc_status = acpi_evaluate_object(handle, wc_method, +- &wc_input, NULL); ++ wc_status = acpi_get_handle(handle, wc_method, &wc_handle); ++ if (ACPI_SUCCESS(wc_status)) ++ wc_status = acpi_evaluate_object(handle, wc_method, ++ &wc_input, NULL); + } + + strcpy(method, "WQ"); +@@ -365,7 +311,7 @@ + + /** + * wmi_set_block - Write to a WMI block +- * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba ++ * @guid_string: 16 byte guid + * @instance: Instance index + * &in: Buffer containing new values for the data block + * +@@ -417,61 +363,6 @@ + EXPORT_SYMBOL_GPL(wmi_set_block); + + /** +- * wmi_install_notify_handler - Register handler for WMI events +- * @handler: Function to handle notifications +- * @data: Data to be returned to handler when event is fired +- * +- * Register a handler for events sent to the ACPI-WMI mapper device. +- */ +-acpi_status wmi_install_notify_handler(const char *guid, +-wmi_notify_handler handler, void *data) +-{ +- struct wmi_block *block; +- +- if (!guid || !handler) +- return AE_BAD_PARAMETER; +- +- find_guid(guid, &block); +- if (!block) +- return AE_NOT_EXIST; +- +- if (block->handler) +- return AE_ALREADY_ACQUIRED; +- +- block->handler = handler; +- block->handler_data = data; +- +- return AE_OK; +-} +-EXPORT_SYMBOL_GPL(wmi_install_notify_handler); +- +-/** +- * wmi_uninstall_notify_handler - Unregister handler for WMI events +- * +- * Unregister handler for events sent to the ACPI-WMI mapper device. +- */ +-acpi_status wmi_remove_notify_handler(const char *guid) +-{ +- struct wmi_block *block; +- +- if (!guid) +- return AE_BAD_PARAMETER; +- +- find_guid(guid, &block); +- if (!block) +- return AE_NOT_EXIST; +- +- if (!block->handler) +- return AE_NULL_ENTRY; +- +- block->handler = NULL; +- block->handler_data = NULL; +- +- return AE_OK; +-} +-EXPORT_SYMBOL_GPL(wmi_remove_notify_handler); +- +-/** + * wmi_get_event_data - Get WMI data associated with an event + * + * @event - Event to find +@@ -486,6 +377,8 @@ + struct guid_block *gblock; + struct wmi_block *wblock; + struct list_head *p; ++ acpi_status status; ++ uint count; + + input.count = 1; + input.pointer = params; +@@ -497,27 +390,31 @@ + gblock = &wblock->gblock; + + if ((gblock->flags & ACPI_WMI_EVENT) && +- (gblock->notify_id == event)) +- return acpi_evaluate_object(wblock->handle, "_WED", ++ (gblock->notify_id == event)) { ++ mutex_lock(&wmi_mutex); ++ if ( enable_wmi_event_data_hack == TRUE ) { ++ wmi_ec_port_data_size = 0; ++ memset(wmi_ec_port_data, 0, 32); ++ in_query_wmi_event_data = TRUE; ++ } ++ status = acpi_evaluate_object(wblock->handle, "_WED", + &input, out); ++ if ( enable_wmi_event_data_hack == TRUE ) { ++ for ( count = 0; count < wmi_ec_port_data_size; count++) ++ ((char *)((union acpi_object *) ++ out->pointer)->buffer.pointer)[count] ++ = wmi_ec_port_data[count]; ++ in_query_wmi_event_data = FALSE; ++ } ++ mutex_unlock(&wmi_mutex); ++ return status; ++ } + } + + return AE_NOT_FOUND; + } + EXPORT_SYMBOL_GPL(wmi_get_event_data); + +-/** +- * wmi_has_guid - Check if a GUID is available +- * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba +- * +- * Check if a given GUID is defined by _WDG +- */ +-bool wmi_has_guid(const char *guid_string) +-{ +- return find_guid(guid_string, NULL); +-} +-EXPORT_SYMBOL_GPL(wmi_has_guid); +- + /* + * Parse the _WDG method for the GUID data blocks + */ +@@ -621,12 +518,7 @@ + + if ((block->flags & ACPI_WMI_EVENT) && + (block->notify_id == event)) { +- if (wblock->handler) +- wblock->handler(event, wblock->handler_data); +- +- acpi_bus_generate_netlink_event( +- device->pnp.device_class, device->dev.bus_id, +- event, 0); ++ acpi_bus_generate_proc_event(device, event, 0); + break; + } + } +@@ -640,6 +532,7 @@ + acpi_remove_address_space_handler(device->handle, + ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); + ++ wmi_enable_event_data_blocks(0); + return 0; + } + +@@ -659,26 +552,33 @@ + ACPI_ADR_SPACE_EC, + &acpi_wmi_ec_space_handler, + NULL, NULL); +- if (ACPI_FAILURE(status)) ++ if (ACPI_FAILURE(status)) { ++ printk(KERN_ERR PREFIX "Error installing EC region handler\n"); + return -ENODEV; ++ } + + status = parse_wdg(device->handle); + if (ACPI_FAILURE(status)) { +- printk(KERN_ERR PREFIX "Error installing EC region handler\n"); ++ printk(KERN_ERR PREFIX "parse_wdg failed!\n"); + return -ENODEV; + } + ++ /* No need to check and fail if wmi_enable_event_data_blocks should fail. ++ * wmi_enable_event_data_blocks will print an error message. ++ */ ++ wmi_enable_event_data_blocks(1); + return result; + } + + static int __init acpi_wmi_init(void) + { + acpi_status result; ++ char *dmi_sys_info; + + INIT_LIST_HEAD(&wmi_blocks.list); + +- if (acpi_disabled) +- return -ENODEV; ++ if (acpi_disabled) ++ return -ENODEV; + + result = acpi_bus_register_driver(&acpi_wmi_driver); + +@@ -688,6 +588,13 @@ + printk(KERN_INFO PREFIX "Mapper loaded\n"); + } + ++ dmi_sys_info = dmi_get_system_info(DMI_SYS_VENDOR); ++ if ( dmi_sys_info == NULL ) ++ return result; ++ ++ if ( strstr(dmi_sys_info, "Dell") != NULL ) ++ enable_wmi_event_data_hack = TRUE; ++ + return result; + } + +diff -Nur linux-2.6-a/drivers/xen/acpi-wmi/acpi-wmi.c linux-2.6-b/drivers/xen/acpi-wmi/acpi-wmi.c +--- linux-2.6-a/drivers/xen/acpi-wmi/acpi-wmi.c 1969-12-31 19:00:00.000000000 -0500 ++++ linux-2.6-b/drivers/xen/acpi-wmi/acpi-wmi.c 2009-04-07 16:23:07.000000000 -0400 +@@ -0,0 +1,352 @@ ++/****************************************************************************** ++ * drivers/xen/acpi-wmi/acpi-wmi.c ++ * ++ * Copyright (c) 2009 Kamala Narasimhan ++ * Copyright (c) 2009 Citrix Systems, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation; or, when distributed ++ * separately from the Linux kernel or incorporated into other ++ * software packages, subject to the following license: ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this source file (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, copy, modify, ++ * merge, publish, distribute, sublicense, and/or sell copies of the Software, ++ * and to permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++/* Xen acpi-wmi implementation provides the interface required for userspace ++ * module (qemu) to communicate with acpi wmi wrapper kernel driver. ++ * Upon receiving request from qemu to call a WMI method or query or set WMI ++ * data, it communicates the request to kernel acpi wmi layer which then ++ * interacts with the base firmware to get the necessary information/execute ++ * relevant AML method etc. The result returned by the base firmware is then ++ * communicated back to the userspace module (qemu). ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* #define XEN_WMI_DEBUG */ ++ ++static bool xen_wmi_misc_dev_registered = false; ++static int xen_wmi_ioctl(struct inode *inode, struct file *filp, ++ unsigned int cmd, unsigned long arg); ++ ++static struct file_operations xen_wmi_fops = { ++ .owner = THIS_MODULE, ++ .ioctl = xen_wmi_ioctl, ++}; ++ ++static struct miscdevice xen_wmi_misc = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = XEN_WMI_DEVICE_NAME, ++ .fops = &xen_wmi_fops, ++}; ++ ++/* ++ * xen_wmi_copy_input_buffer ++ */ ++int xen_wmi_copy_input_buffer(xen_wmi_buffer_t *user_buf, struct acpi_buffer *acpi_buf) ++{ ++ if ( user_buf->length <= 0 ) ++ return XEN_WMI_SUCCESS; ++ ++ acpi_buf->pointer = kmalloc(user_buf->length, GFP_KERNEL); ++ if ( acpi_buf->pointer == NULL ) ++ { ++ printk("XEN WMI: xen_wmi_copy_input_buffer - Buffer allocation failure\n"); ++ return XEN_WMI_NOT_ENOUGH_MEMORY; ++ } ++ ++ if ( copy_from_user(acpi_buf->pointer, ++ (char __user *)user_buf->pointer, ++ user_buf->length) ) ++ { ++ printk("XEN WMI: Unable to copy input buffer argument\n"); ++ kfree(acpi_buf->pointer); ++ return XEN_WMI_EFAULT; ++ } ++ ++ acpi_buf->length = user_buf->length; ++ return XEN_WMI_SUCCESS; ++} ++ ++/* ++ * xen_wmi_copy_output_buffer ++ */ ++int xen_wmi_copy_output_buffer(struct acpi_buffer *acpi_buf, xen_wmi_buffer_t *user_buf) ++{ ++ /* IMPORTANT NOTE: It is a little short sighted to assume that the return type ++ * will not be anything other than buffer type. A follow-up check-in will ++ * cover more types. ++ */ ++ ++ union acpi_object *acpi_obj = acpi_buf->pointer; ++ ++ if ( acpi_obj == NULL ) ++ { ++ printk("XEN WMI: Invalid acpi buffer!\n"); ++ return XEN_WMI_EFAULT; ++ } ++ ++ if ( acpi_obj->type != ACPI_TYPE_BUFFER ) ++ { ++ printk("XEN WMI: Unsupported acpi return object type - %d\n", acpi_obj->type); ++ return XEN_WMI_UNSUPPORTED_TYPE; ++ } ++ ++ if ( copy_to_user((char __user *) user_buf->copied_length, &acpi_obj->buffer.length, sizeof(size_t)) ) ++ { ++ printk("XEN WMI: Invalid copied length user buffer!\n"); ++ return XEN_WMI_INVALID_ARGUMENT; ++ } ++ ++ if ( user_buf->length < acpi_obj->buffer.length ) ++ { ++ printk("XEN WMI: Required buffer length is - %d\n", acpi_obj->buffer.length); ++ printk("XEN WMI: Passed in length is - %d\n", user_buf->length); ++ return XEN_WMI_BUFFER_TOO_SMALL; ++ } ++ ++ if ( copy_to_user((char __user *) user_buf->pointer, acpi_obj->buffer.pointer, acpi_obj->buffer.length) ) ++ { ++ printk("XEN WMI: Invalid user output buffer\n"); ++ return XEN_WMI_NOT_ENOUGH_MEMORY; ++ } ++ ++ return XEN_WMI_SUCCESS; ++} ++ ++#ifdef XEN_WMI_DEBUG ++ ++/* ++ * xen_wmi_print_buffer ++ */ ++void xen_wmi_print_buffer(struct acpi_buffer *acpi_buf) ++{ ++ int count; ++ union acpi_object *acpi_obj = acpi_buf->pointer; ++ ++ if ( acpi_obj == NULL || acpi_obj->type != ACPI_TYPE_BUFFER ) ++ { ++ printk("XEN WMI: Unsupported output buffer data!\n"); ++ return ; ++ } ++ ++ printk("XEN WMI: Output buffer length is - %d\n", acpi_obj->buffer.length); ++ printk("XEN WMI: Buffer: "); ++ for (count=0; count < acpi_obj->buffer.length; count++) ++ printk("%d ", ((byte *)(acpi_obj->buffer.pointer))[count]); ++ printk("\n"); ++} ++ ++#endif /* XEN_WMI_DEBUG */ ++ ++/* ++ * xen_wmi_invoke_method ++ */ ++int xen_wmi_invoke_method(xen_wmi_obj_invocation_data_t *obj_inv_data) ++{ ++ int result; ++ struct acpi_buffer in_buf, *in_arg = NULL, out_buf = {ACPI_ALLOCATE_BUFFER, NULL}; ++ ++ result = xen_wmi_copy_input_buffer(&obj_inv_data->xen_wmi_arg.xen_wmi_method_arg.in_buf, ++ &in_buf); ++ if ( result != XEN_WMI_SUCCESS ) ++ return result; ++ ++ if ( in_buf.length > 0 ) ++ in_arg = &in_buf; ++ ++ result = wmi_evaluate_method(obj_inv_data->guid, ++ obj_inv_data->xen_wmi_arg.xen_wmi_method_arg.instance, ++ obj_inv_data->xen_wmi_arg.xen_wmi_method_arg.method_id, ++ in_arg, &out_buf); ++ ++ if ( in_arg != NULL ) ++ kfree(in_buf.pointer); ++ ++ if ( out_buf.length > 0 && result == XEN_WMI_SUCCESS ) ++ { ++#ifdef XEN_WMI_DEBUG ++ xen_wmi_print_buffer(&out_buf); ++#endif ++ result = xen_wmi_copy_output_buffer(&out_buf, ++ &obj_inv_data->xen_wmi_arg.xen_wmi_method_arg.out_buf); ++ kfree(out_buf.pointer); ++ } ++ else if ( result != XEN_WMI_SUCCESS ) ++ printk("XEN WMI- Invoke WMI method failed with error - %d\n", result); ++ ++ return result; ++} ++ ++/* ++ * xen_wmi_query_object ++ */ ++int xen_wmi_query_object(xen_wmi_obj_invocation_data_t *obj_inv_data) ++{ ++ int result; ++ struct acpi_buffer out_buf = {ACPI_ALLOCATE_BUFFER, NULL}; ++ ++ result = wmi_query_block(obj_inv_data->guid, ++ obj_inv_data->xen_wmi_arg.xen_wmi_query_obj_arg.instance, ++ &out_buf); ++ ++ if ( out_buf.length > 0 && result == XEN_WMI_SUCCESS ) ++ { ++#ifdef XEN_WMI_DEBUG ++ xen_wmi_print_buffer(&out_buf); ++#endif ++ result = xen_wmi_copy_output_buffer(&out_buf, ++ &obj_inv_data->xen_wmi_arg.xen_wmi_query_obj_arg.out_buf); ++ kfree(out_buf.pointer); ++ } ++ else ++ printk("XEN WMI - Query WMI object failed with error - %d; output buffer length - %d\n", ++ result, out_buf.length); ++ ++ return result; ++} ++ ++/* ++ * xen_wmi_set_object ++ */ ++int xen_wmi_set_object(xen_wmi_obj_invocation_data_t *obj_inv_data) ++{ ++ int result; ++ struct acpi_buffer in_buf; ++ ++ if ( obj_inv_data->xen_wmi_arg.xen_wmi_set_obj_arg.in_buf.length <= 0 ) ++ return XEN_WMI_INVALID_ARGUMENT; ++ ++ result = xen_wmi_copy_input_buffer(&obj_inv_data->xen_wmi_arg.xen_wmi_set_obj_arg.in_buf, ++ &in_buf); ++ if ( result != XEN_WMI_SUCCESS ) ++ return result; ++ ++ result = wmi_set_block(obj_inv_data->guid, ++ obj_inv_data->xen_wmi_arg.xen_wmi_set_obj_arg.instance, ++ &in_buf); ++ if ( result != XEN_WMI_SUCCESS ) ++ printk("XEN WMI: Set object failed with error - %d\n", result); ++ ++ kfree(in_buf.pointer); ++ return result; ++} ++ ++/* ++ * xen_wmi_get_event_data ++ */ ++int xen_wmi_get_event_data(xen_wmi_obj_invocation_data_t *obj_inv_data) ++{ ++ int result; ++ struct acpi_buffer out_buf = {ACPI_ALLOCATE_BUFFER, NULL}; ++ ++ result = wmi_get_event_data(obj_inv_data->xen_wmi_arg.xen_wmi_event_data_arg.event_id, ++ &out_buf); ++ ++ if ( out_buf.length > 0 && result == XEN_WMI_SUCCESS ) ++ { ++#ifdef XEN_WMI_DEBUG ++ xen_wmi_print_buffer(&out_buf); ++#endif ++ result = xen_wmi_copy_output_buffer(&out_buf, ++ &obj_inv_data->xen_wmi_arg.xen_wmi_event_data_arg.out_buf); ++ kfree(out_buf.pointer); ++ } ++ else ++ printk("XEN WMI: Get event data failed with error - %d\n", result); ++ ++ return result; ++} ++ ++/* ++ * xen_wmi_ioctl ++ */ ++static int xen_wmi_ioctl(struct inode *inode, struct file *filp, ++ unsigned int cmd, unsigned long arg) ++{ ++ xen_wmi_obj_invocation_data_t obj_inv_data; ++ ++#ifdef XEN_WMI_DEBUG ++ printk("XEN WMI: In xen_wmi_ioctl - %d\n", cmd); ++#endif ++ ++ memset(&obj_inv_data, 0, sizeof(obj_inv_data)); ++ if ( copy_from_user(&obj_inv_data, (char __user *)arg, sizeof(obj_inv_data)) ) ++ { ++ printk("XEN WMI: Invalid object invocation parameter\n"); ++ return XEN_WMI_EFAULT; ++ } ++ ++ switch ( cmd ) ++ { ++ case XEN_WMI_IOCTL_CALL_METHOD: ++ return xen_wmi_invoke_method(&obj_inv_data); ++ case XEN_WMI_IOCTL_QUERY_OBJECT: ++ return xen_wmi_query_object(&obj_inv_data); ++ case XEN_WMI_IOCTL_SET_OBJECT: ++ return xen_wmi_set_object(&obj_inv_data); ++ case XEN_WMI_IOCTL_GET_EVENT_DATA: ++ return xen_wmi_get_event_data(&obj_inv_data); ++ } ++ ++ return XEN_WMI_ENOIOCTLCMD; ++} ++ ++/* ++ * xen_wmi_init ++ */ ++static int __init xen_wmi_init(void) ++{ ++ int ret; ++ ++ ret = misc_register(&xen_wmi_misc); ++ if ( ret < 0 ) ++ printk("XEN WMI: misc_register failed with error - %d\n", ret); ++ else ++ xen_wmi_misc_dev_registered = true; ++ ++#ifdef XEN_WMI_DEBUG ++ printk("XEN WMI: xen-acpi-wmi misc_register succeeded!\n"); ++#endif ++ return ret; ++} ++ ++/* ++ * xen_wmi_exit ++ */ ++static void xen_wmi_exit(void) ++{ ++ int ret; ++ ++ if ( xen_wmi_misc_dev_registered == false ) ++ return; ++ ++ if ( (ret = misc_deregister(&xen_wmi_misc)) < 0 ) ++ printk("XEN WMI: misc_deregister failed with error - %d\n", ret); ++} ++ ++module_init(xen_wmi_init); ++module_exit(xen_wmi_exit); ++MODULE_LICENSE("Dual BSD/GPL"); ++ +diff -Nur linux-2.6-a/drivers/xen/acpi-wmi/Makefile linux-2.6-b/drivers/xen/acpi-wmi/Makefile +--- linux-2.6-a/drivers/xen/acpi-wmi/Makefile 1969-12-31 19:00:00.000000000 -0500 ++++ linux-2.6-b/drivers/xen/acpi-wmi/Makefile 2009-04-07 16:23:07.000000000 -0400 +@@ -0,0 +1,2 @@ ++ ++obj-y := acpi-wmi.o +diff -Nur linux-2.6-a/drivers/xen/Kconfig linux-2.6-b/drivers/xen/Kconfig +--- linux-2.6-a/drivers/xen/Kconfig 2009-04-07 16:51:58.000000000 -0400 ++++ linux-2.6-b/drivers/xen/Kconfig 2009-04-07 16:25:37.000000000 -0400 +@@ -265,6 +265,13 @@ + Specify the total number of virtual devices (i.e. both frontend + and backend) that you want the kernel to be able to service. + ++config XEN_ACPI_WMI_WRAPPER ++ tristate "Xen ACPI WMI wrapper driver" ++ depends on ACPI_WMI ++ help ++ Facilitates OEM specific hotkey implementation within ++ guest space. ++ + choice + prompt "Xen version compatibility" + default XEN_COMPAT_030002_AND_LATER +diff -Nur linux-2.6-a/drivers/xen/Makefile linux-2.6-b/drivers/xen/Makefile +--- linux-2.6-a/drivers/xen/Makefile 2009-04-07 16:52:14.000000000 -0400 ++++ linux-2.6-b/drivers/xen/Makefile 2009-04-07 16:26:11.000000000 -0400 +@@ -30,3 +30,4 @@ + obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_UTIL) += sfc_netutil/ + obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_FRONTEND) += sfc_netfront/ + obj-$(CONFIG_XEN_NETDEV_ACCEL_SFC_BACKEND) += sfc_netback/ ++obj-$(CONFIG_XEN_ACPI_WMI_WRAPPER) += acpi-wmi/ +diff -Nur linux-2.6-a/include/xen/public/acpi-wmi.h linux-2.6-b/include/xen/public/acpi-wmi.h +--- linux-2.6-a/include/xen/public/acpi-wmi.h 1969-12-31 19:00:00.000000000 -0500 ++++ linux-2.6-b/include/xen/public/acpi-wmi.h 2009-04-07 16:44:29.000000000 -0400 +@@ -0,0 +1,94 @@ ++/****************************************************************************** ++ * acpi-wmi.h ++ * ++ * Interface to /proc/misc/xen-acpi-wmi ++ * ++ * Copyright (c) 2009 Kamala Narasimhan ++ * Copyright (c) 2009 Citrix Systems, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation; or, when distributed ++ * separately from the Linux kernel or incorporated into other ++ * software packages, subject to the following license: ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this source file (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, copy, modify, ++ * merge, publish, distribute, sublicense, and/or sell copies of the Software, ++ * and to permit persons to whom the Software is furnished to do so, subject to ++ * the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ++ * IN THE SOFTWARE. ++ */ ++ ++ ++#ifndef _XEN_WMI_ACPI ++#define _XEN_WMI_ACPI ++ ++/* ++ * Userspace Interface ++ */ ++ ++#define XEN_WMI_DEVICE_NAME "xen-acpi-wmi" ++#define XEN_WMI_GUID_SIZE 16 ++ ++#define XEN_WMI_SUCCESS 0 ++#define XEN_WMI_UNSUPPORTED_TYPE -1 ++#define XEN_WMI_BUFFER_TOO_SMALL -11 ++#define XEN_WMI_NOT_ENOUGH_MEMORY -12 ++#define XEN_WMI_EFAULT -14 ++#define XEN_WMI_INVALID_ARGUMENT -22 ++#define XEN_WMI_ENOIOCTLCMD -515 ++ ++#define XEN_WMI_IOCTL_CALL_METHOD 100 ++#define XEN_WMI_IOCTL_QUERY_OBJECT 101 ++#define XEN_WMI_IOCTL_SET_OBJECT 102 ++#define XEN_WMI_IOCTL_GET_EVENT_DATA 103 ++ ++typedef unsigned char byte; ++ ++typedef struct xen_wmi_buffer { ++ size_t length; ++ void *pointer; ++ size_t *copied_length; ++} xen_wmi_buffer_t; ++ ++typedef struct xen_wmi_obj_invocation_data { ++ byte guid[XEN_WMI_GUID_SIZE]; ++ union { ++ struct { ++ ushort instance; ++ uint method_id; ++ xen_wmi_buffer_t in_buf; ++ xen_wmi_buffer_t out_buf; ++ } xen_wmi_method_arg; ++ ++ struct { ++ ushort instance; ++ xen_wmi_buffer_t out_buf; ++ } xen_wmi_query_obj_arg; ++ ++ struct { ++ ushort instance; ++ xen_wmi_buffer_t in_buf; ++ } xen_wmi_set_obj_arg; ++ ++ struct { ++ ushort event_id; ++ xen_wmi_buffer_t out_buf; ++ } xen_wmi_event_data_arg; ++ } xen_wmi_arg; ++} xen_wmi_obj_invocation_data_t; ++ ++#endif /* _XEN_WMI_ACPI */ ++