[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] PCI backend and frontend drivers for i386 and x86_64.
# HG changeset patch # User kaf24@xxxxxxxxxxxxxxxxxxxx # Node ID 5b433b4fca34e8a9a3c2eb932ffa0e2ae8594e94 # Parent 294b3a447dce176db3ca11afed1d7317d5ba36db PCI backend and frontend drivers for i386 and x86_64. Signed-off-by: Ryan Wilson <hap9@xxxxxxxxxxxxxx> diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/arch/i386/Kconfig --- a/linux-2.6-xen-sparse/arch/i386/Kconfig Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/arch/i386/Kconfig Thu Feb 16 22:44:41 2006 @@ -997,6 +997,13 @@ config PCI_GODIRECT bool "Direct" +config PCI_GOXEN_FE + bool "Xen PCI Frontend" + depends on X86_XEN + help + The PCI device frontend driver allows the kernel to import arbitrary + PCI devices from a PCI backend to support PCI driver domains. + config PCI_GOANY bool "Any" @@ -1016,6 +1023,18 @@ bool depends on PCI && ACPI && (PCI_GOMMCONFIG || PCI_GOANY) default y + +config XEN_PCIDEV_FRONTEND + bool + depends on PCI && X86_XEN && (PCI_GOXEN_FE || PCI_GOANY) + default y + +config XEN_PCIDEV_FE_DEBUG + bool "Xen PCI Frontend Debugging" + depends on XEN_PCIDEV_FRONTEND + default n + help + Enables some debug statements within the PCI Frontend. source "drivers/pci/pcie/Kconfig" diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/arch/i386/pci/Makefile --- a/linux-2.6-xen-sparse/arch/i386/pci/Makefile Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/arch/i386/pci/Makefile Thu Feb 16 22:44:41 2006 @@ -3,6 +3,10 @@ obj-$(CONFIG_PCI_BIOS) += pcbios.o obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o direct.o obj-$(CONFIG_PCI_DIRECT) += direct.o + +# pcifront should be after pcbios.o, mmconfig.o, and direct.o as it should only +# take over if direct access to the PCI bus is unavailable +obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += pcifront.o pci-y := fixup.o pci-$(CONFIG_ACPI) += acpi.o diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/arch/x86_64/Kconfig --- a/linux-2.6-xen-sparse/arch/x86_64/Kconfig Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/arch/x86_64/Kconfig Thu Feb 16 22:44:41 2006 @@ -550,6 +550,21 @@ bool "Support mmconfig PCI config space access" depends on PCI && ACPI +config XEN_PCIDEV_FRONTEND + bool "Xen PCI Frontend" + depends on PCI && X86_64_XEN + default y + help + The PCI device frontend driver allows the kernel to import arbitrary + PCI devices from a PCI backend to support PCI driver domains. + +config XEN_PCIDEV_FE_DEBUG + bool "Xen PCI Frontend Debugging" + depends on XEN_PCIDEV_FRONTEND + default n + help + Enables some debug statements within the PCI Frontend. + config UNORDERED_IO bool "Unordered IO mapping access" depends on EXPERIMENTAL diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/arch/x86_64/pci/Makefile --- a/linux-2.6-xen-sparse/arch/x86_64/pci/Makefile Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/arch/x86_64/pci/Makefile Thu Feb 16 22:44:41 2006 @@ -15,8 +15,13 @@ obj-$(CONFIG_NUMA) += k8-bus.o +# pcifront should be after mmconfig.o and direct.o as it should only +# take over if direct access to the PCI bus is unavailable +obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += pcifront.o + direct-y += ../../i386/pci/direct.o acpi-y += ../../i386/pci/acpi.o +pcifront-y += ../../i386/pci/pcifront.o legacy-y += ../../i386/pci/legacy.o irq-y += ../../i386/pci/irq.o common-y += ../../i386/pci/common.o @@ -25,7 +30,6 @@ ifdef CONFIG_XEN irq-y := ../../i386/pci/irq-xen.o -i386-y := ../../i386/pci/i386.o include $(srctree)/scripts/Makefile.xen obj-y := $(call cherrypickxen, $(obj-y)) diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/Kconfig --- a/linux-2.6-xen-sparse/drivers/xen/Kconfig Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/Kconfig Thu Feb 16 22:44:41 2006 @@ -28,6 +28,44 @@ config XEN_UNPRIVILEGED_GUEST bool default !XEN_PRIVILEGED_GUEST + +config XEN_PCIDEV_BACKEND + bool "PCI device backend driver" + select PCI + default y if XEN_PRIVILEGED_GUEST + help + The PCI device backend driver allows the kernel to export arbitrary + PCI devices to other guests. + +choice + prompt "PCI Backend Mode" + depends on XEN_PCIDEV_BACKEND + default XEN_PCIDEV_BACKEND_VPCI + +config XEN_PCIDEV_BACKEND_VPCI + bool "Virtual PCI" + ---help--- + This PCI Backend hides the true PCI topology and makes the frontend + think there is a single PCI bus with only the exported devices on it. + For example, a device at 03:05.0 will be re-assigned to 00:00.0. A + second device at 02:1a.0 will be re-assigned to 00:01.0. + +config XEN_PCIDEV_BACKEND_PASS + bool "Passthrough" + ---help--- + This PCI Backend provides a real view of the PCI topology to the + frontend (for example, a device at 06:01.b will still appear at + 06:01.b to the frontend). This is similar to how Xen 2.0.x exposed + PCI devices to its driver domains. This may be required for drivers + which depend on finding their hardward in certain bus/slot + locations. + +endchoice + +config XEN_PCIDEV_BE_DEBUG + bool "PCI Backend Debugging" + depends on XEN_PCIDEV_BACKEND + default n config XEN_BLKDEV_BACKEND bool "Block-device backend driver" diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/Makefile --- a/linux-2.6-xen-sparse/drivers/xen/Makefile Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/Makefile Thu Feb 16 22:44:41 2006 @@ -17,4 +17,6 @@ obj-$(CONFIG_XEN_NETDEV_FRONTEND) += netfront/ obj-$(CONFIG_XEN_BLKDEV_TAP) += blktap/ obj-$(CONFIG_XEN_TPMDEV_FRONTEND) += tpmfront/ +obj-$(CONFIG_XEN_PCIDEV_BACKEND) += pciback/ +obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += pcifront/ diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/pci.h --- a/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/pci.h Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/include/asm-i386/mach-xen/asm/pci.h Thu Feb 16 22:44:41 2006 @@ -134,6 +134,10 @@ #endif /* __KERNEL__ */ +#ifdef CONFIG_XEN_PCIDEV_FRONTEND +#include <xen/pcifront.h> +#endif /* CONFIG_XEN_PCIDEV_FRONTEND */ + /* implement the pci_ DMA API in terms of the generic device dma_ one */ #include <asm-generic/pci-dma-compat.h> diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/arch/i386/pci/pcifront.c --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/arch/i386/pci/pcifront.c Thu Feb 16 22:44:41 2006 @@ -0,0 +1,55 @@ +/* + * PCI Frontend Stub - puts some "dummy" functions in to the Linux x86 PCI core + * to support the Xen PCI Frontend's operation + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <asm/acpi.h> +#include "pci.h" + +static int pcifront_enable_irq(struct pci_dev *dev) +{ + u8 irq; + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); + dev->irq = irq; + + return 0; +} + +extern u8 pci_cache_line_size; + +static int __init pcifront_x86_stub_init(void) +{ + struct cpuinfo_x86 *c = &boot_cpu_data; + + /* Only install our method if we haven't found real hardware already */ + if (raw_pci_ops) + return 0; + + printk(KERN_INFO "PCI: setting up Xen PCI frontend stub\n"); + + /* Copied from arch/i386/pci/common.c */ + pci_cache_line_size = 32 >> 2; + if (c->x86 >= 6 && c->x86_vendor == X86_VENDOR_AMD) + pci_cache_line_size = 64 >> 2; /* K7 & K8 */ + else if (c->x86 > 6 && c->x86_vendor == X86_VENDOR_INTEL) + pci_cache_line_size = 128 >> 2; /* P4 */ + + /* On x86, we need to disable the normal IRQ routing table and + * just ask the backend + */ + pcibios_enable_irq = pcifront_enable_irq; + pcibios_disable_irq = NULL; + +#ifdef CONFIG_ACPI + /* Keep ACPI out of the picture */ + acpi_noirq = 1; +#endif + + return 0; +} + +arch_initcall(pcifront_x86_stub_init); diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pciback/Makefile --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/Makefile Thu Feb 16 22:44:41 2006 @@ -0,0 +1,10 @@ +obj-y += pciback.o + +pciback-y := pci_stub.o pciback_ops.o xenbus.o +pciback-y += conf_space.o conf_space_header.o +pciback-${CONFIG_XEN_PCIDEV_BACKEND_VPCI} += vpci.o +pciback-${CONFIG_XEN_PCIDEV_BACKEND_PASS} += passthrough.o + +ifeq ($(CONFIG_XEN_PCIDEV_BE_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.c --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.c Thu Feb 16 22:44:41 2006 @@ -0,0 +1,324 @@ +/* + * PCI Backend - Functions for creating a virtual configuration space for + * exported PCI Devices. + * It's dangerous to allow PCI Driver Domains to change their + * device's resources (memory, i/o ports, interrupts). We need to + * restrict changes to certain PCI Configuration registers: + * BARs, INTERRUPT_PIN, most registers in the header... + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include "pciback.h" +#include "conf_space.h" + +#define DEFINE_PCI_CONFIG(op,size,type) \ +int pciback_##op##_config_##size \ +(struct pci_dev *dev, int offset, type value, void *data) \ +{ \ + return pci_##op##_config_##size (dev, offset, value); \ +} + +DEFINE_PCI_CONFIG(read, byte, u8 *) +DEFINE_PCI_CONFIG(read, word, u16 *) +DEFINE_PCI_CONFIG(read, dword, u32 *) + +DEFINE_PCI_CONFIG(write, byte, u8) +DEFINE_PCI_CONFIG(write, word, u16) +DEFINE_PCI_CONFIG(write, dword, u32) + +static int conf_space_read(struct pci_dev *dev, + struct config_field_entry *entry, int offset, + u32 * value) +{ + int ret = 0; + struct config_field *field = entry->field; + + *value = 0; + + switch (field->size) { + case 1: + if (field->u.b.read) + ret = field->u.b.read(dev, offset, (u8 *) value, + entry->data); + break; + case 2: + if (field->u.w.read) + ret = field->u.w.read(dev, offset, (u16 *) value, + entry->data); + break; + case 4: + if (field->u.dw.read) + ret = field->u.dw.read(dev, offset, value, entry->data); + break; + } + return ret; +} + +static int conf_space_write(struct pci_dev *dev, + struct config_field_entry *entry, int offset, + u32 value) +{ + int ret = 0; + struct config_field *field = entry->field; + + switch (field->size) { + case 1: + if (field->u.b.write) + ret = field->u.b.write(dev, offset, (u8) value, + entry->data); + break; + case 2: + if (field->u.w.write) + ret = field->u.w.write(dev, offset, (u16) value, + entry->data); + break; + case 4: + if (field->u.dw.write) + ret = field->u.dw.write(dev, offset, value, + entry->data); + break; + } + return ret; +} + +static inline u32 get_mask(int size) +{ + if (size == 1) + return 0xff; + else if (size == 2) + return 0xffff; + else + return 0xffffffff; +} + +static inline int valid_request(int offset, int size) +{ + /* Validate request (no un-aligned requests) */ + if ((size == 1 || size == 2 || size == 4) && (offset % size) == 0) + return 1; + return 0; +} + +static inline u32 merge_value(u32 val, u32 new_val, u32 new_val_mask, + u32 offset) +{ + if (offset >= 0) { + new_val_mask <<= (offset * 8); + new_val <<= (offset * 8); + } else { + new_val_mask >>= (offset * -8); + new_val >>= (offset * -8); + } + val = (val & ~new_val_mask) | (new_val & new_val_mask); + + return val; +} + +static int pcibios_err_to_errno(int err) +{ + switch (err) { + case PCIBIOS_SUCCESSFUL: + return XEN_PCI_ERR_success; + case PCIBIOS_DEVICE_NOT_FOUND: + return XEN_PCI_ERR_dev_not_found; + case PCIBIOS_BAD_REGISTER_NUMBER: + return XEN_PCI_ERR_invalid_offset; + case PCIBIOS_FUNC_NOT_SUPPORTED: + return XEN_PCI_ERR_not_implemented; + case PCIBIOS_SET_FAILED: + return XEN_PCI_ERR_access_denied; + } + return err; +} + +int pciback_config_read(struct pci_dev *dev, int offset, int size, + u32 * ret_val) +{ + int err = 0; + struct pciback_dev_data *dev_data = pci_get_drvdata(dev); + struct config_field_entry *cfg_entry; + struct config_field *field; + int req_start, req_end, field_start, field_end; + /* if read fails for any reason, return 0 (as if device didn't respond) */ + u32 value = 0, tmp_val; + + if (unlikely(verbose_request)) + printk(KERN_DEBUG "pciback: %s: read %d bytes at 0x%x\n", + pci_name(dev), size, offset); + + if (!valid_request(offset, size)) { + err = XEN_PCI_ERR_invalid_offset; + goto out; + } + + /* Get the real value first, then modify as appropriate */ + switch (size) { + case 1: + err = pci_read_config_byte(dev, offset, (u8 *) & value); + break; + case 2: + err = pci_read_config_word(dev, offset, (u16 *) & value); + break; + case 4: + err = pci_read_config_dword(dev, offset, &value); + break; + } + + list_for_each_entry(cfg_entry, &dev_data->config_fields, list) { + field = cfg_entry->field; + + req_start = offset; + req_end = offset + size; + field_start = field->offset; + field_end = field->offset + field->size; + + if ((req_start >= field_start && req_start < field_end) + || (req_end > field_start && req_end <= field_end)) { + err = conf_space_read(dev, cfg_entry, offset, &tmp_val); + if (err) + goto out; + + value = merge_value(value, tmp_val, + get_mask(field->size), + field_start - req_start); + } + } + + out: + if (unlikely(verbose_request)) + printk(KERN_DEBUG "pciback: %s: read %d bytes at 0x%x = %x\n", + pci_name(dev), size, offset, value); + + *ret_val = value; + return pcibios_err_to_errno(err); +} + +int pciback_config_write(struct pci_dev *dev, int offset, int size, u32 value) +{ + int err = 0; + struct pciback_dev_data *dev_data = pci_get_drvdata(dev); + struct config_field_entry *cfg_entry; + struct config_field *field; + u32 tmp_val; + int req_start, req_end, field_start, field_end; + + if (unlikely(verbose_request)) + printk(KERN_DEBUG + "pciback: %s: write request %d bytes at 0x%x = %x\n", + pci_name(dev), size, offset, value); + + if (!valid_request(offset, size)) + return XEN_PCI_ERR_invalid_offset; + + list_for_each_entry(cfg_entry, &dev_data->config_fields, list) { + field = cfg_entry->field; + + req_start = offset; + req_end = offset + size; + field_start = field->offset; + field_end = field->offset + field->size; + + if ((req_start >= field_start && req_start < field_end) + || (req_end > field_start && req_end <= field_end)) { + tmp_val = 0; + + err = pciback_config_read(dev, offset, size, &tmp_val); + if (err) + break; + + tmp_val = merge_value(tmp_val, value, get_mask(size), + field_start - req_start); + + err = conf_space_write(dev, cfg_entry, offset, tmp_val); + } + } + + return pcibios_err_to_errno(err); +} + +void pciback_config_reset(struct pci_dev *dev) +{ + struct pciback_dev_data *dev_data = pci_get_drvdata(dev); + struct config_field_entry *cfg_entry; + struct config_field *field; + + list_for_each_entry(cfg_entry, &dev_data->config_fields, list) { + field = cfg_entry->field; + + if (field->reset) + field->reset(dev, field->offset, cfg_entry->data); + } +} + +void pciback_config_free(struct pci_dev *dev) +{ + struct pciback_dev_data *dev_data = pci_get_drvdata(dev); + struct config_field_entry *cfg_entry, *t; + struct config_field *field; + + list_for_each_entry_safe(cfg_entry, t, &dev_data->config_fields, list) { + list_del(&cfg_entry->list); + + field = cfg_entry->field; + + if (field->release) + field->release(dev, field->offset, cfg_entry->data); + + kfree(cfg_entry); + } +} + +int pciback_config_add_field(struct pci_dev *dev, struct config_field *field) +{ + int err = 0; + struct pciback_dev_data *dev_data = pci_get_drvdata(dev); + struct config_field_entry *cfg_entry; + void *tmp; + + cfg_entry = kmalloc(sizeof(*cfg_entry), GFP_KERNEL); + if (!cfg_entry) { + err = -ENOMEM; + goto out; + } + + cfg_entry->data = NULL; + cfg_entry->field = field; + + if (field->init) { + tmp = field->init(dev, field->offset); + + if (IS_ERR(tmp)) { + err = PTR_ERR(tmp); + goto out; + } + + cfg_entry->data = tmp; + } + + list_add_tail(&cfg_entry->list, &dev_data->config_fields); + + out: + if (err) + kfree(cfg_entry); + + return err; +} + +/* This sets up the device's virtual configuration space to keep track of + * certain registers (like the base address registers (BARs) so that we can + * keep the client from manipulating them directly. + */ +int pciback_config_init(struct pci_dev *dev) +{ + int err = 0; + struct pciback_dev_data *dev_data = pci_get_drvdata(dev); + + INIT_LIST_HEAD(&dev_data->config_fields); + + err = pciback_config_header_add_fields(dev); + + return err; +} diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.h --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.h Thu Feb 16 22:44:41 2006 @@ -0,0 +1,97 @@ +/* + * PCI Backend - Common data structures for overriding the configuration space + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ + +#ifndef __XEN_PCIBACK_CONF_SPACE_H__ +#define __XEN_PCIBACK_CONF_SPACE_H__ + +#include <linux/list.h> + +typedef void *(*conf_field_init) (struct pci_dev * dev, int offset); +typedef void (*conf_field_reset) (struct pci_dev * dev, int offset, void *data); +typedef void (*conf_field_free) (struct pci_dev * dev, int offset, void *data); + +typedef int (*conf_dword_write) (struct pci_dev * dev, int offset, u32 value, + void *data); +typedef int (*conf_word_write) (struct pci_dev * dev, int offset, u16 value, + void *data); +typedef int (*conf_byte_write) (struct pci_dev * dev, int offset, u8 value, + void *data); +typedef int (*conf_dword_read) (struct pci_dev * dev, int offset, u32 * value, + void *data); +typedef int (*conf_word_read) (struct pci_dev * dev, int offset, u16 * value, + void *data); +typedef int (*conf_byte_read) (struct pci_dev * dev, int offset, u8 * value, + void *data); + +/* These are the fields within the configuration space which we + * are interested in intercepting reads/writes to and changing their + * values. + */ +struct config_field { + unsigned int offset; + unsigned int size; + conf_field_init init; + conf_field_reset reset; + conf_field_free release; + union { + struct { + conf_dword_write write; + conf_dword_read read; + } dw; + struct { + conf_word_write write; + conf_word_read read; + } w; + struct { + conf_byte_write write; + conf_byte_read read; + } b; + } u; +}; + +struct config_field_entry { + struct list_head list; + struct config_field *field; + void *data; +}; + +/* Add fields to a device - the add_fields macro expects to get a pointer to + * the first entry in an array (of which the ending is marked by size==0) + */ +int pciback_config_add_field(struct pci_dev *dev, struct config_field *field); +static inline int pciback_config_add_fields(struct pci_dev *dev, + struct config_field *field) +{ + int i, err = 0; + for (i = 0; field[i].size != 0; i++) { + err = pciback_config_add_field(dev, &field[i]); + if (err) + break; + } + return err; +} + +/* Initializers which add fields to the virtual configuration space + * ** We could add initializers to allow a guest domain to touch + * the capability lists (for power management, the AGP bridge, etc.) + */ +int pciback_config_header_add_fields(struct pci_dev *dev); + +/* Read/Write the real configuration space */ +int pciback_read_config_byte(struct pci_dev *dev, int offset, u8 * value, + void *data); +int pciback_read_config_word(struct pci_dev *dev, int offset, u16 * value, + void *data); +int pciback_read_config_dword(struct pci_dev *dev, int offset, u32 * value, + void *data); +int pciback_write_config_byte(struct pci_dev *dev, int offset, u8 value, + void *data); +int pciback_write_config_word(struct pci_dev *dev, int offset, u16 value, + void *data); +int pciback_write_config_dword(struct pci_dev *dev, int offset, u32 value, + void *data); + +#endif /* __XEN_PCIBACK_CONF_SPACE_H__ */ diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_header.c --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_header.c Thu Feb 16 22:44:41 2006 @@ -0,0 +1,269 @@ +/* + * PCI Backend - Handles the virtual fields in the configuration space headers. + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include "pciback.h" +#include "conf_space.h" + +struct pci_bar_info { + u32 val; + u32 len_val; + int which; +}; + +#define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO)) +#define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER) + +static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) +{ + if (!dev->is_enabled && is_enable_cmd(value)) { + if (unlikely(verbose_request)) + printk(KERN_DEBUG "pciback: %s: enable\n", + pci_name(dev)); + dev->is_enabled = 1; + pcibios_enable_device(dev, (1 << PCI_NUM_RESOURCES) - 1); + } else if (dev->is_enabled && !is_enable_cmd(value)) { + if (unlikely(verbose_request)) + printk(KERN_DEBUG "pciback: %s: disable\n", + pci_name(dev)); + pciback_disable_device(dev); + } + + if (!dev->is_busmaster && is_master_cmd(value)) { + if (unlikely(verbose_request)) + printk(KERN_DEBUG "pciback: %s: set bus master\n", + pci_name(dev)); + dev->is_busmaster = 1; + pcibios_set_master(dev); + } + + if (value & PCI_COMMAND_INVALIDATE) { + if (unlikely(verbose_request)) + printk(KERN_DEBUG + "pciback: %s: enable memory-write-invalidate\n", + pci_name(dev)); + pci_set_mwi(dev); + } + + return pci_write_config_word(dev, offset, value); +} + +static int rom_write(struct pci_dev *dev, int offset, u32 value, void *data) +{ + struct pci_bar_info *bar = data; + + if (unlikely(!bar)) { + printk(KERN_WARNING "pciback: driver data not found for %s\n", + pci_name(dev)); + return XEN_PCI_ERR_op_failed; + } + + /* A write to obtain the length must happen as a 32-bit write. + * This does not (yet) support writing individual bytes + */ + if (value == ~PCI_ROM_ADDRESS_ENABLE) + bar->which = 1; + else + bar->which = 0; + + /* Do we need to support enabling/disabling the rom address here? */ + + return 0; +} + +/* For the BARs, only allow writes which write ~0 or + * the correct resource information + * (Needed for when the driver probes the resource usage) + */ +static int bar_write(struct pci_dev *dev, int offset, u32 value, void *data) +{ + struct pci_bar_info *bar = data; + + if (unlikely(!bar)) { + printk(KERN_WARNING "pciback: driver data not found for %s\n", + pci_name(dev)); + return XEN_PCI_ERR_op_failed; + } + + /* A write to obtain the length must happen as a 32-bit write. + * This does not (yet) support writing individual bytes + */ + if (value == ~0) + bar->which = 1; + else + bar->which = 0; + + return 0; +} + +static int bar_read(struct pci_dev *dev, int offset, u32 * value, void *data) +{ + struct pci_bar_info *bar = data; + + if (unlikely(!bar)) { + printk(KERN_WARNING "pciback: driver data not found for %s\n", + pci_name(dev)); + return XEN_PCI_ERR_op_failed; + } + + *value = bar->which ? bar->len_val : bar->val; + + return 0; +} + +static inline void read_dev_bar(struct pci_dev *dev, + struct pci_bar_info *bar_info, int offset, + u32 len_mask) +{ + pci_read_config_dword(dev, offset, &bar_info->val); + pci_write_config_dword(dev, offset, len_mask); + pci_read_config_dword(dev, offset, &bar_info->len_val); + pci_write_config_dword(dev, offset, bar_info->val); +} + +static void *bar_init(struct pci_dev *dev, int offset) +{ + struct pci_bar_info *bar = kmalloc(sizeof(*bar), GFP_KERNEL); + + if (!bar) + return ERR_PTR(-ENOMEM); + + read_dev_bar(dev, bar, offset, ~0); + bar->which = 0; + + return bar; +} + +static void *rom_init(struct pci_dev *dev, int offset) +{ + struct pci_bar_info *bar = kmalloc(sizeof(*bar), GFP_KERNEL); + + if (!bar) + return ERR_PTR(-ENOMEM); + + read_dev_bar(dev, bar, offset, ~PCI_ROM_ADDRESS_ENABLE); + bar->which = 0; + + return bar; +} + +static void bar_reset(struct pci_dev *dev, int offset, void *data) +{ + struct pci_bar_info *bar = data; + + bar->which = 0; +} + +static void bar_release(struct pci_dev *dev, int offset, void *data) +{ + kfree(data); +} + +static int interrupt_read(struct pci_dev *dev, int offset, u8 * value, + void *data) +{ + *value = (u8) dev->irq; + + return 0; +} + +struct config_field header_common[] = { + { + .offset = PCI_COMMAND, + .size = 2, + .u.w.read = pciback_read_config_word, + .u.w.write = command_write, + }, + { + .offset = PCI_INTERRUPT_LINE, + .size = 1, + .u.b.read = interrupt_read, + .u.b.write = NULL, + }, + { + /* Any side effects of letting driver domain control cache line? */ + .offset = PCI_CACHE_LINE_SIZE, + .size = 1, + .u.b.read = pciback_read_config_byte, + .u.b.write = pciback_write_config_byte, + }, + { + .size = 0, + }, +}; + +#define CFG_FIELD_BAR(reg_offset) \ + { \ + .offset = reg_offset, \ + .size = 4, \ + .init = bar_init, \ + .reset = bar_reset, \ + .release = bar_release, \ + .u.dw.read = bar_read, \ + .u.dw.write = bar_write, \ + } + +#define CFG_FIELD_ROM(reg_offset) \ + { \ + .offset = reg_offset, \ + .size = 4, \ + .init = rom_init, \ + .reset = bar_reset, \ + .release = bar_release, \ + .u.dw.read = bar_read, \ + .u.dw.write = rom_write, \ + } + +struct config_field header_0[] = { + CFG_FIELD_BAR(PCI_BASE_ADDRESS_0), + CFG_FIELD_BAR(PCI_BASE_ADDRESS_1), + CFG_FIELD_BAR(PCI_BASE_ADDRESS_2), + CFG_FIELD_BAR(PCI_BASE_ADDRESS_3), + CFG_FIELD_BAR(PCI_BASE_ADDRESS_4), + CFG_FIELD_BAR(PCI_BASE_ADDRESS_5), + CFG_FIELD_ROM(PCI_ROM_ADDRESS), + { + .size = 0, + }, +}; + +struct config_field header_1[] = { + CFG_FIELD_BAR(PCI_BASE_ADDRESS_0), + CFG_FIELD_BAR(PCI_BASE_ADDRESS_1), + CFG_FIELD_ROM(PCI_ROM_ADDRESS1), + { + .size = 0, + }, +}; + +int pciback_config_header_add_fields(struct pci_dev *dev) +{ + int err; + + err = pciback_config_add_fields(dev, header_common); + if (err) + goto out; + + switch (dev->hdr_type) { + case PCI_HEADER_TYPE_NORMAL: + err = pciback_config_add_fields(dev, header_0); + break; + + case PCI_HEADER_TYPE_BRIDGE: + err = pciback_config_add_fields(dev, header_1); + break; + + default: + err = -EINVAL; + printk(KERN_ERR "pciback: %s: Unsupported header type %d!\n", + pci_name(dev), dev->hdr_type); + break; + } + + out: + return err; +} diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pciback/passthrough.c --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/passthrough.c Thu Feb 16 22:44:41 2006 @@ -0,0 +1,116 @@ +/* + * PCI Backend - Provides restricted access to the real PCI bus topology + * to the frontend + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ + +#include <linux/list.h> +#include <linux/pci.h> +#include "pciback.h" + +struct passthrough_dev_data { + struct list_head dev_list; +}; + +struct pci_dev *pciback_get_pci_dev(struct pciback_device *pdev, + unsigned int domain, unsigned int bus, + unsigned int devfn) +{ + struct passthrough_dev_data *dev_data = pdev->pci_dev_data; + struct pci_dev_entry *dev_entry; + + list_for_each_entry(dev_entry, &dev_data->dev_list, list) { + if (domain == (unsigned int)pci_domain_nr(dev_entry->dev->bus) + && bus == (unsigned int)dev_entry->dev->bus->number + && devfn == dev_entry->dev->devfn) + return dev_entry->dev; + } + + return NULL; +} + +/* Must hold pciback_device->dev_lock when calling this */ +int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev) +{ + struct passthrough_dev_data *dev_data = pdev->pci_dev_data; + struct pci_dev_entry *dev_entry; + + dev_entry = kmalloc(sizeof(*dev_entry), GFP_KERNEL); + if (!dev_entry) + return -ENOMEM; + dev_entry->dev = dev; + + list_add_tail(&dev_entry->list, &dev_data->dev_list); + + return 0; +} + +int pciback_init_devices(struct pciback_device *pdev) +{ + struct passthrough_dev_data *dev_data; + + dev_data = kmalloc(sizeof(*dev_data), GFP_KERNEL); + if (!dev_data) + return -ENOMEM; + + INIT_LIST_HEAD(&dev_data->dev_list); + + pdev->pci_dev_data = dev_data; + + return 0; +} + +int pciback_publish_pci_roots(struct pciback_device *pdev, + publish_pci_root_cb publish_root_cb) +{ + int err = 0; + struct passthrough_dev_data *dev_data = pdev->pci_dev_data; + struct pci_dev_entry *dev_entry, *e; + struct pci_dev *dev; + int found; + unsigned int domain, bus; + + list_for_each_entry(dev_entry, &dev_data->dev_list, list) { + /* Only publish this device as a root if none of its + * parent bridges are exported + */ + found = 0; + dev = dev_entry->dev->bus->self; + for (; !found && dev != NULL; dev = dev->bus->self) { + list_for_each_entry(e, &dev_data->dev_list, list) { + if (dev == e->dev) { + found = 1; + break; + } + } + } + + domain = (unsigned int)pci_domain_nr(dev_entry->dev->bus); + bus = (unsigned int)dev_entry->dev->bus->number; + + if (!found) { + err = publish_root_cb(pdev, domain, bus); + if (err) + break; + } + } + + return err; +} + +/* Must hold pciback_device->dev_lock when calling this */ +void pciback_release_devices(struct pciback_device *pdev) +{ + struct passthrough_dev_data *dev_data = pdev->pci_dev_data; + struct pci_dev_entry *dev_entry, *t; + + 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); + kfree(dev_entry); + } + + kfree(dev_data); + pdev->pci_dev_data = NULL; +} diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pciback/pci_stub.c --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/pci_stub.c Thu Feb 16 22:44:41 2006 @@ -0,0 +1,377 @@ +/* + * PCI Stub Driver - Grabs devices in backend to be exported later + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <asm/atomic.h> +#include "pciback.h" + +static char *pci_devs_to_hide = NULL; +module_param_named(hide, pci_devs_to_hide, charp, 0444); + +struct pci_stub_device_id { + struct list_head slot_list; + int domain; + unsigned char bus; + unsigned int devfn; +}; +LIST_HEAD(pci_stub_device_ids); + +struct pci_stub_device { + struct list_head dev_list; + struct pci_dev *dev; + atomic_t in_use; +}; +/* Access to pci_stub_devices & seized_devices lists and the initialize_devices + * flag must be locked with pci_stub_devices_lock + */ +DEFINE_SPINLOCK(pci_stub_devices_lock); +LIST_HEAD(pci_stub_devices); + +/* wait for device_initcall before initializing our devices + * (see pcistub_init_devices_late) + */ +static int initialize_devices = 0; +LIST_HEAD(seized_devices); + +static inline struct pci_dev *get_pci_dev(struct pci_stub_device *psdev) +{ + if (atomic_dec_and_test(&psdev->in_use)) + return psdev->dev; + else { + atomic_inc(&psdev->in_use); + return NULL; + } +} + +struct pci_dev *pcistub_get_pci_dev_by_slot(int domain, int bus, + int slot, int func) +{ + struct pci_stub_device *psdev; + struct pci_dev *found_dev = NULL; + + spin_lock(&pci_stub_devices_lock); + + list_for_each_entry(psdev, &pci_stub_devices, dev_list) { + if (psdev->dev != NULL + && domain == pci_domain_nr(psdev->dev->bus) + && bus == psdev->dev->bus->number + && PCI_DEVFN(slot, func) == psdev->dev->devfn) { + found_dev = get_pci_dev(psdev); + break; + } + } + + spin_unlock(&pci_stub_devices_lock); + return found_dev; +} + +struct pci_dev *pcistub_get_pci_dev(struct pci_dev *dev) +{ + struct pci_stub_device *psdev; + struct pci_dev *found_dev = NULL; + + spin_lock(&pci_stub_devices_lock); + + list_for_each_entry(psdev, &pci_stub_devices, dev_list) { + if (psdev->dev == dev) { + found_dev = get_pci_dev(psdev); + break; + } + } + + spin_unlock(&pci_stub_devices_lock); + return found_dev; +} + +void pcistub_put_pci_dev(struct pci_dev *dev) +{ + struct pci_stub_device *psdev; + + spin_lock(&pci_stub_devices_lock); + + list_for_each_entry(psdev, &pci_stub_devices, dev_list) { + if (psdev->dev == dev) { + /* Cleanup our device + * (so it's ready for the next domain) + */ + pciback_reset_device(psdev->dev); + + atomic_inc(&psdev->in_use); + break; + } + } + + spin_unlock(&pci_stub_devices_lock); +} + +static int __devinit pcistub_match(struct pci_dev *dev, + struct pci_stub_device_id *pdev_id) +{ + /* Match the specified device by domain, bus, slot, func and also if + * any of the device's parent bridges match. + */ + for (; dev != NULL; dev = dev->bus->self) { + if (pci_domain_nr(dev->bus) == pdev_id->domain + && dev->bus->number == pdev_id->bus + && dev->devfn == pdev_id->devfn) + return 1; + } + + return 0; +} + +static int __devinit pcistub_init_device(struct pci_dev *dev) +{ + struct pciback_dev_data *dev_data; + int err = 0; + + /* The PCI backend is not intended to be a module (or to work with + * removable PCI devices (yet). If it were, pciback_config_free() + * would need to be called somewhere to free the memory allocated + * here and then to call kfree(pci_get_drvdata(psdev->dev)). + */ + dev_data = kmalloc(sizeof(*dev_data), GFP_KERNEL); + if (!dev_data) { + err = -ENOMEM; + goto out; + } + pci_set_drvdata(dev, dev_data); + + err = pciback_config_init(dev); + if (err) + goto out; + + /* HACK: Force device (& ACPI) to determine what IRQ it's on - we + * must do this here because pcibios_enable_device may specify + * the pci device's true irq (and possibly its other resources) + * if they differ from what's in the configuration space. + * This makes the assumption that the device's resources won't + * change after this point (otherwise this code may break!) + */ + err = pci_enable_device(dev); + if (err) + goto config_release; + + /* Now disable the device (this also ensures some private device + * data is setup before we export) + * This calls pciback_config_reset(dev) + */ + pciback_reset_device(dev); + + return 0; + + config_release: + pciback_config_free(dev); + + out: + pci_set_drvdata(dev, NULL); + kfree(dev_data); + return err; +} + +/* + * Because some initialization still happens on + * devices during fs_initcall, we need to defer + * full initialization of our devices until + * device_initcall. + */ +static int __init pcistub_init_devices_late(void) +{ + struct pci_stub_device *psdev, *t; + int err = 0; + + spin_lock(&pci_stub_devices_lock); + + list_for_each_entry_safe(psdev, t, &seized_devices, dev_list) { + list_del(&psdev->dev_list); + err = pcistub_init_device(psdev->dev); + if (err) { + printk(KERN_ERR + "pciback: %s error %d initializing device\n", + pci_name(psdev->dev), err); + kfree(psdev); + continue; + } + + list_add_tail(&psdev->dev_list, &pci_stub_devices); + } + + initialize_devices = 1; + + spin_unlock(&pci_stub_devices_lock); + + return 0; +} + +device_initcall(pcistub_init_devices_late); + +static int __devinit pcistub_seize(struct pci_dev *dev) +{ + struct pci_stub_device *psdev; + int err = 0; + + psdev = kmalloc(sizeof(*psdev), GFP_KERNEL); + if (!psdev) + return -ENOMEM; + + psdev->dev = dev; + atomic_set(&psdev->in_use, 1); + + spin_lock(&pci_stub_devices_lock); + + if (initialize_devices) { + err = pcistub_init_device(psdev->dev); + if (err) + goto out; + + list_add(&psdev->dev_list, &pci_stub_devices); + } else + list_add(&psdev->dev_list, &seized_devices); + + out: + spin_unlock(&pci_stub_devices_lock); + + if (err) + kfree(psdev); + + return err; +} + +static int __devinit pcistub_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct pci_stub_device_id *pdev_id; + struct pci_dev *seized_dev; + int err = 0; + + list_for_each_entry(pdev_id, &pci_stub_device_ids, slot_list) { + + if (!pcistub_match(dev, pdev_id)) + continue; + + if (dev->hdr_type != PCI_HEADER_TYPE_NORMAL + && dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { + printk(KERN_ERR + "pciback: %s: can't export pci devices that " + "don't have a normal (0) or bridge (1) " + "header type!\n", pci_name(dev)); + break; + } + + pr_info("pciback: seizing PCI device %s\n", pci_name(dev)); + seized_dev = pci_dev_get(dev); + + if (seized_dev) { + err = pcistub_seize(seized_dev); + if (err) { + pci_dev_put(dev); + goto out; + } + + /* Success! */ + goto out; + } + } + + /* Didn't find the device */ + err = -ENODEV; + + out: + return err; +} + +struct pci_device_id pcistub_ids[] = { + { + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + {0,}, +}; + +/* + * Note: There is no MODULE_DEVICE_TABLE entry here because this isn't + * for a normal device. I don't want it to be loaded automatically. + */ + +struct pci_driver pciback_pci_driver = { + .name = "pciback", + .id_table = pcistub_ids, + .probe = pcistub_probe, +}; + +static int __init pcistub_init(void) +{ + int pos = 0; + struct pci_stub_device_id *pci_dev_id; + 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; + } + + pci_dev_id = kmalloc(sizeof(*pci_dev_id), GFP_KERNEL); + if (!pci_dev_id) { + err = -ENOMEM; + goto out; + } + + 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); + + list_add_tail(&pci_dev_id->slot_list, + &pci_stub_device_ids); + + /* if parsed<=0, we've reached the end of the string */ + pos += parsed; + } while (parsed > 0 && pci_devs_to_hide[pos]); + + /* If we're the first PCI Device Driver to register, we're the + * first one to get offered PCI devices as they become + * available (and thus we can be the first to grab them) + */ + pci_register_driver(&pciback_pci_driver); + } + + 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; +} + +/* + * fs_initcall happens before device_initcall + * so pciback *should* get called first (b/c we + * want to suck up any device before other drivers + * get a chance by being the first pci device + * driver to register) + */ +fs_initcall(pcistub_init); diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pciback/pciback.h --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/pciback.h Thu Feb 16 22:44:41 2006 @@ -0,0 +1,73 @@ +/* + * PCI Backend Common Data Structures & Function Declarations + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ +#ifndef __XEN_PCIBACK_H__ +#define __XEN_PCIBACK_H__ + +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <xen/xenbus.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <xen/interface/io/pciif.h> + +struct pci_dev_entry { + struct list_head list; + struct pci_dev *dev; +}; + +struct pciback_device { + void *pci_dev_data; + spinlock_t dev_lock; + + struct xenbus_device *xdev; + + struct xenbus_watch be_watch; + u8 be_watching; + + int evtchn_irq; + + struct xen_pci_sharedinfo *sh_info; +}; + +struct pciback_dev_data { + struct list_head config_fields; +}; + +/* Get/Put PCI Devices that are hidden from the PCI Backend Domain */ +struct pci_dev *pcistub_get_pci_dev_by_slot(int domain, int bus, + int slot, int func); +struct pci_dev *pcistub_get_pci_dev(struct pci_dev *dev); +void pcistub_put_pci_dev(struct pci_dev *dev); + +/* Ensure a device is turned off or reset */ +void pciback_disable_device(struct pci_dev *dev); +void pciback_reset_device(struct pci_dev *pdev); + +/* Access a virtual configuration space for a PCI device */ +int pciback_config_init(struct pci_dev *dev); +void pciback_config_reset(struct pci_dev *dev); +void pciback_config_free(struct pci_dev *dev); +int pciback_config_read(struct pci_dev *dev, int offset, int size, + u32 * ret_val); +int pciback_config_write(struct pci_dev *dev, int offset, int size, u32 value); + +/* Handle requests for specific devices from the frontend */ +typedef int (*publish_pci_root_cb) (struct pciback_device * pdev, + unsigned int domain, unsigned int bus); +int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev); +struct pci_dev *pciback_get_pci_dev(struct pciback_device *pdev, + unsigned int domain, unsigned int bus, + unsigned int devfn); +int pciback_init_devices(struct pciback_device *pdev); +int pciback_publish_pci_roots(struct pciback_device *pdev, + publish_pci_root_cb cb); +void pciback_release_devices(struct pciback_device *pdev); + +/* Handles events from front-end */ +irqreturn_t pciback_handle_event(int irq, void *dev_id, struct pt_regs *regs); + +extern int verbose_request; +#endif diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pciback/pciback_ops.c --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/pciback_ops.c Thu Feb 16 22:44:41 2006 @@ -0,0 +1,84 @@ +/* + * PCI Backend Operations - respond to PCI requests from Frontend + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ +#include <linux/module.h> +#include <asm/bitops.h> +#include "pciback.h" + +int verbose_request = 0; +module_param(verbose_request, int, 0644); + +/* For those architectures without a pcibios_disable_device */ +void __attribute__ ((weak)) pcibios_disable_device(struct pci_dev *dev) { } + +void pciback_disable_device(struct pci_dev *dev) +{ + if (dev->is_enabled) { + dev->is_enabled = 0; + pcibios_disable_device(dev); + } +} + +/* Ensure a device is "turned off" and ready to be exported. + * This also sets up the device's private data to keep track of what should + * be in the base address registers (BARs) so that we can keep the + * client from manipulating them directly. + */ +void pciback_reset_device(struct pci_dev *dev) +{ + u16 cmd; + + /* Disable devices (but not bridges) */ + if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) { + pciback_disable_device(dev); + + pci_write_config_word(dev, PCI_COMMAND, 0); + + dev->is_enabled = 0; + dev->is_busmaster = 0; + } else { + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (cmd & (PCI_COMMAND_INVALIDATE)) { + cmd &= ~(PCI_COMMAND_INVALIDATE); + pci_write_config_word(dev, PCI_COMMAND, cmd); + + dev->is_busmaster = 0; + } + } + + pciback_config_reset(dev); +} + +irqreturn_t pciback_handle_event(int irq, void *dev_id, struct pt_regs *regs) +{ + struct pciback_device *pdev = dev_id; + struct pci_dev *dev; + struct xen_pci_op *op = &pdev->sh_info->op; + + if (unlikely(!test_bit(_XEN_PCIF_active, + (unsigned long *)&pdev->sh_info->flags))) { + pr_debug("pciback: interrupt, but no active operation\n"); + goto out; + } + + dev = pciback_get_pci_dev(pdev, op->domain, op->bus, op->devfn); + + if (dev == NULL) + op->err = XEN_PCI_ERR_dev_not_found; + else if (op->cmd == XEN_PCI_OP_conf_read) + op->err = pciback_config_read(dev, op->offset, op->size, + &op->value); + else if (op->cmd == XEN_PCI_OP_conf_write) + op->err = pciback_config_write(dev, op->offset, op->size, + op->value); + else + op->err = XEN_PCI_ERR_not_implemented; + + wmb(); + clear_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags); + + out: + return IRQ_HANDLED; +} diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pciback/vpci.c --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/vpci.c Thu Feb 16 22:44:41 2006 @@ -0,0 +1,163 @@ +/* + * PCI Backend - Provides a Virtual PCI bus (with real devices) + * to the frontend + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ + +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include "pciback.h" + +#define PCI_SLOT_MAX 32 + +struct vpci_dev_data { + struct list_head dev_list[PCI_SLOT_MAX]; +}; + +static inline struct list_head *list_first(struct list_head *head) +{ + return head->next; +} + +struct pci_dev *pciback_get_pci_dev(struct pciback_device *pdev, + unsigned int domain, unsigned int bus, + unsigned int devfn) +{ + struct pci_dev_entry *dev_entry; + struct vpci_dev_data *vpci_dev = pdev->pci_dev_data; + + if (domain != 0 || bus != 0) + return NULL; + + if (PCI_SLOT(devfn) < PCI_SLOT_MAX) { + /* we don't need to lock the list here because once the backend + * is in operation, it won't have any more devices addeded + * (or removed). + */ + list_for_each_entry(dev_entry, + &vpci_dev->dev_list[PCI_SLOT(devfn)], + list) { + if (PCI_FUNC(dev_entry->dev->devfn) == PCI_FUNC(devfn)) + return dev_entry->dev; + } + } + return NULL; +} + +static inline int match_slot(struct pci_dev *l, struct pci_dev *r) +{ + if (pci_domain_nr(l->bus) == pci_domain_nr(r->bus) + && l->bus == r->bus && PCI_SLOT(l->devfn) == PCI_SLOT(r->devfn)) + return 1; + + return 0; +} + +/* Must hold pciback_device->dev_lock when calling this */ +int pciback_add_pci_dev(struct pciback_device *pdev, struct pci_dev *dev) +{ + int err = 0, slot; + struct pci_dev_entry *t, *dev_entry; + struct vpci_dev_data *vpci_dev = pdev->pci_dev_data; + + if ((dev->class >> 24) == PCI_BASE_CLASS_BRIDGE) { + err = -EFAULT; + xenbus_dev_fatal(pdev->xdev, err, + "Can't export bridges on the virtual PCI bus"); + goto out; + } + + dev_entry = kmalloc(sizeof(*dev_entry), GFP_KERNEL); + if (!dev_entry) { + err = -ENOMEM; + xenbus_dev_fatal(pdev->xdev, err, + "Error adding entry to virtual PCI bus"); + goto out; + } + + dev_entry->dev = dev; + + /* Keep multi-function devices together on the virtual PCI bus */ + for (slot = 0; slot < PCI_SLOT_MAX; slot++) { + if (!list_empty(&vpci_dev->dev_list[slot])) { + t = list_entry(list_first(&vpci_dev->dev_list[slot]), + struct pci_dev_entry, list); + + if (match_slot(dev, t->dev)) { + pr_info("pciback: vpci: %s: " + "assign to virtual slot %d func %d\n", + pci_name(dev), slot, + PCI_FUNC(dev->devfn)); + list_add_tail(&dev_entry->list, + &vpci_dev->dev_list[slot]); + goto out; + } + } + } + + /* Assign to a new slot on the virtual PCI bus */ + for (slot = 0; slot < PCI_SLOT_MAX; slot++) { + if (list_empty(&vpci_dev->dev_list[slot])) { + printk(KERN_INFO + "pciback: vpci: %s: assign to virtual slot %d\n", + pci_name(dev), slot); + list_add_tail(&dev_entry->list, + &vpci_dev->dev_list[slot]); + goto out; + } + } + + err = -ENOMEM; + xenbus_dev_fatal(pdev->xdev, err, + "No more space on root virtual PCI bus"); + + out: + return err; +} + +int pciback_init_devices(struct pciback_device *pdev) +{ + int slot; + struct vpci_dev_data *vpci_dev; + + vpci_dev = kmalloc(sizeof(*vpci_dev), GFP_KERNEL); + if (!vpci_dev) + return -ENOMEM; + + for (slot = 0; slot < PCI_SLOT_MAX; slot++) { + INIT_LIST_HEAD(&vpci_dev->dev_list[slot]); + } + + pdev->pci_dev_data = vpci_dev; + + return 0; +} + +int pciback_publish_pci_roots(struct pciback_device *pdev, + publish_pci_root_cb publish_cb) +{ + /* The Virtual PCI bus has only one root */ + return publish_cb(pdev, 0, 0); +} + +/* Must hold pciback_device->dev_lock when calling this */ +void pciback_release_devices(struct pciback_device *pdev) +{ + int slot; + struct vpci_dev_data *vpci_dev = pdev->pci_dev_data; + + for (slot = 0; slot < PCI_SLOT_MAX; slot++) { + struct pci_dev_entry *e, *tmp; + list_for_each_entry_safe(e, tmp, &vpci_dev->dev_list[slot], + list) { + list_del(&e->list); + pcistub_put_pci_dev(e->dev); + kfree(e); + } + } + + kfree(vpci_dev); + pdev->pci_dev_data = NULL; +} diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pciback/xenbus.c --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/xenbus.c Thu Feb 16 22:44:41 2006 @@ -0,0 +1,439 @@ +/* + * PCI Backend Xenbus Setup - handles setup with frontend and xend + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/list.h> +#include <xen/xenbus.h> +#include <xen/evtchn.h> +#include "pciback.h" + +#define INVALID_EVTCHN_IRQ (-1) + +struct pciback_device *alloc_pdev(struct xenbus_device *xdev) +{ + struct pciback_device *pdev; + + pdev = kmalloc(sizeof(struct pciback_device), GFP_KERNEL); + if (pdev == NULL) + goto out; + dev_dbg(&xdev->dev, "allocated pdev @ 0x%p\n", pdev); + + pdev->xdev = xdev; + xdev->data = pdev; + + spin_lock_init(&pdev->dev_lock); + + pdev->sh_info = NULL; + pdev->evtchn_irq = INVALID_EVTCHN_IRQ; + pdev->be_watching = 0; + + if (pciback_init_devices(pdev)) { + kfree(pdev); + pdev = NULL; + } + out: + return pdev; +} + +void free_pdev(struct pciback_device *pdev) +{ + if (pdev->be_watching) + unregister_xenbus_watch(&pdev->be_watch); + + /* Ensure the guest can't trigger our handler before removing devices */ + if (pdev->evtchn_irq != INVALID_EVTCHN_IRQ) + unbind_from_irqhandler(pdev->evtchn_irq, pdev); + + if (pdev->sh_info) + xenbus_unmap_ring_vfree(pdev->xdev, pdev->sh_info); + + pciback_release_devices(pdev); + + pdev->xdev->data = NULL; + pdev->xdev = NULL; + + kfree(pdev); +} + +static int pciback_do_attach(struct pciback_device *pdev, int gnt_ref, + int remote_evtchn) +{ + int err = 0; + int evtchn; + dev_dbg(&pdev->xdev->dev, + "Attaching to frontend resources - gnt_ref=%d evtchn=%d\n", + gnt_ref, remote_evtchn); + + err = + xenbus_map_ring_valloc(pdev->xdev, gnt_ref, + (void **)&pdev->sh_info); + if (err) + goto out; + + err = xenbus_bind_evtchn(pdev->xdev, remote_evtchn, &evtchn); + if (err) + goto out; + + err = bind_evtchn_to_irqhandler(evtchn, pciback_handle_event, + SA_SAMPLE_RANDOM, "pciback", pdev); + if (err < 0) { + xenbus_dev_fatal(pdev->xdev, err, + "Error binding event channel to IRQ"); + goto out; + } + pdev->evtchn_irq = err; + err = 0; + + dev_dbg(&pdev->xdev->dev, "Attached!\n"); + out: + return err; +} + +static int pciback_attach(struct pciback_device *pdev) +{ + int err = 0; + int gnt_ref, remote_evtchn; + char *magic = NULL; + + spin_lock(&pdev->dev_lock); + + /* Make sure we only do this setup once */ + if (xenbus_read_driver_state(pdev->xdev->nodename) != + XenbusStateInitialised) + goto out; + + /* Wait for frontend to state that it has published the configuration */ + if (xenbus_read_driver_state(pdev->xdev->otherend) != + XenbusStateInitialised) + goto out; + + dev_dbg(&pdev->xdev->dev, "Reading frontend config\n"); + + err = xenbus_gather(XBT_NULL, pdev->xdev->otherend, + "pci-op-ref", "%u", &gnt_ref, + "event-channel", "%u", &remote_evtchn, + "magic", NULL, &magic, NULL); + if (err) { + /* If configuration didn't get read correctly, wait longer */ + xenbus_dev_fatal(pdev->xdev, err, + "Error reading configuration from frontend"); + goto out; + } + + if (magic == NULL || strcmp(magic, XEN_PCI_MAGIC) != 0) { + xenbus_dev_fatal(pdev->xdev, -EFAULT, + "version mismatch (%s/%s) with pcifront - " + "halting pciback", + magic, XEN_PCI_MAGIC); + goto out; + } + + err = pciback_do_attach(pdev, gnt_ref, remote_evtchn); + if (err) + goto out; + + dev_dbg(&pdev->xdev->dev, "Connecting...\n"); + + err = xenbus_switch_state(pdev->xdev, XBT_NULL, XenbusStateConnected); + if (err) + xenbus_dev_fatal(pdev->xdev, err, + "Error switching to connected state!"); + + dev_dbg(&pdev->xdev->dev, "Connected? %d\n", err); + out: + spin_unlock(&pdev->dev_lock); + + if (magic) + kfree(magic); + + return err; +} + +static void pciback_frontend_changed(struct xenbus_device *xdev, + XenbusState fe_state) +{ + struct pciback_device *pdev = xdev->data; + + dev_dbg(&xdev->dev, "fe state changed %d\n", fe_state); + + switch (fe_state) { + case XenbusStateInitialised: + pciback_attach(pdev); + break; + + case XenbusStateClosing: + xenbus_switch_state(xdev, XBT_NULL, XenbusStateClosing); + break; + + case XenbusStateClosed: + dev_dbg(&xdev->dev, "frontend is gone! unregister device\n"); + device_unregister(&xdev->dev); + break; + + default: + break; + } +} + +static int pciback_publish_pci_root(struct pciback_device *pdev, + unsigned int domain, unsigned int bus) +{ + unsigned int d, b; + int i, root_num, len, err; + char str[64]; + + dev_dbg(&pdev->xdev->dev, "Publishing pci roots\n"); + + err = xenbus_scanf(XBT_NULL, pdev->xdev->nodename, + "root_num", "%d", &root_num); + if (err == 0 || err == -ENOENT) + root_num = 0; + else if (err < 0) + goto out; + + /* Verify that we haven't already published this pci root */ + for (i = 0; i < root_num; i++) { + len = snprintf(str, sizeof(str), "root-%d", i); + if (unlikely(len >= (sizeof(str) - 1))) { + err = -ENOMEM; + goto out; + } + + err = xenbus_scanf(XBT_NULL, pdev->xdev->nodename, + str, "%x:%x", &d, &b); + if (err < 0) + goto out; + if (err != 2) { + err = -EINVAL; + goto out; + } + + if (d == domain && b == bus) { + err = 0; + goto out; + } + } + + len = snprintf(str, sizeof(str), "root-%d", root_num); + if (unlikely(len >= (sizeof(str) - 1))) { + err = -ENOMEM; + goto out; + } + + dev_dbg(&pdev->xdev->dev, "writing root %d at %04x:%02x\n", + root_num, domain, bus); + + err = xenbus_printf(XBT_NULL, pdev->xdev->nodename, str, + "%04x:%02x", domain, bus); + if (err) + goto out; + + err = xenbus_printf(XBT_NULL, pdev->xdev->nodename, + "root_num", "%d", (root_num + 1)); + + out: + return err; +} + +static int pciback_export_device(struct pciback_device *pdev, + int domain, int bus, int slot, int func) +{ + struct pci_dev *dev; + int err = 0; + + dev_dbg(&pdev->xdev->dev, "exporting dom %x bus %x slot %x func %x\n", + domain, bus, slot, func); + + dev = pcistub_get_pci_dev_by_slot(domain, bus, slot, func); + if (!dev) { + err = -EINVAL; + xenbus_dev_fatal(pdev->xdev, err, + "Couldn't locate PCI device " + "(%04x:%02x:%02x.%01x)! " + "perhaps already in-use?", + domain, bus, slot, func); + goto out; + } + + err = pciback_add_pci_dev(pdev, dev); + if (err) + goto out; + + /* TODO: It'd be nice to export a bridge and have all of its children + * get exported with it. This may be best done in xend (which will + * have to calculate resource usage anyway) but we probably want to + * put something in here to ensure that if a bridge gets given to a + * driver domain, that all devices under that bridge are not given + * to other driver domains (as he who controls the bridge can disable + * it and stop the other devices from working). + */ + out: + return err; +} + +static int pciback_setup_backend(struct pciback_device *pdev) +{ + /* Get configuration from xend (if available now) */ + int domain, bus, slot, func; + int err = 0; + int i, num_devs; + char dev_str[64]; + + spin_lock(&pdev->dev_lock); + + /* It's possible we could get the call to setup twice, so make sure + * we're not already connected. + */ + if (xenbus_read_driver_state(pdev->xdev->nodename) != + XenbusStateInitWait) + goto out; + + dev_dbg(&pdev->xdev->dev, "getting be setup\n"); + + err = xenbus_scanf(XBT_NULL, pdev->xdev->nodename, "num_devs", "%d", + &num_devs); + if (err != 1) { + if (err >= 0) + err = -EINVAL; + xenbus_dev_fatal(pdev->xdev, err, + "Error reading number of devices"); + goto out; + } + + for (i = 0; i < num_devs; i++) { + int l = snprintf(dev_str, sizeof(dev_str), "dev-%d", i); + if (unlikely(l >= (sizeof(dev_str) - 1))) { + err = -ENOMEM; + xenbus_dev_fatal(pdev->xdev, err, + "String overflow while reading " + "configuration"); + goto out; + } + + err = xenbus_scanf(XBT_NULL, pdev->xdev->nodename, dev_str, + "%x:%x:%x.%x", &domain, &bus, &slot, &func); + if (err < 0) { + xenbus_dev_fatal(pdev->xdev, err, + "Error reading device configuration"); + goto out; + } + if (err != 4) { + err = -EINVAL; + xenbus_dev_fatal(pdev->xdev, err, + "Error parsing pci device " + "configuration"); + goto out; + } + + err = pciback_export_device(pdev, domain, bus, slot, func); + if (err) + goto out; + } + + err = pciback_publish_pci_roots(pdev, pciback_publish_pci_root); + if (err) { + xenbus_dev_fatal(pdev->xdev, err, + "Error while publish PCI root buses " + "for frontend"); + goto out; + } + + err = xenbus_switch_state(pdev->xdev, XBT_NULL, XenbusStateInitialised); + if (err) + xenbus_dev_fatal(pdev->xdev, err, + "Error switching to initialised state!"); + + out: + spin_unlock(&pdev->dev_lock); + + if (!err) + /* see if pcifront is already configured (if not, we'll wait) */ + pciback_attach(pdev); + + return err; +} + +static void pciback_be_watch(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + struct pciback_device *pdev = + container_of(watch, struct pciback_device, be_watch); + + switch (xenbus_read_driver_state(pdev->xdev->nodename)) { + case XenbusStateInitWait: + pciback_setup_backend(pdev); + break; + + default: + break; + } +} + +static int pciback_xenbus_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + int err = 0; + struct pciback_device *pdev = alloc_pdev(dev); + + if (pdev == NULL) { + err = -ENOMEM; + xenbus_dev_fatal(dev, err, + "Error allocating pciback_device struct"); + goto out; + } + + /* wait for xend to configure us */ + err = xenbus_switch_state(dev, XBT_NULL, XenbusStateInitWait); + if (err) + goto out; + + /* watch the backend node for backend configuration information */ + err = xenbus_watch_path(dev, dev->nodename, &pdev->be_watch, + pciback_be_watch); + if (err) + goto out; + pdev->be_watching = 1; + + /* We need to force a call to our callback here in case + * xend already configured us! + */ + pciback_be_watch(&pdev->be_watch, NULL, 0); + + out: + return err; +} + +static int pciback_xenbus_remove(struct xenbus_device *dev) +{ + struct pciback_device *pdev = dev->data; + + if (pdev != NULL) + free_pdev(pdev); + + return 0; +} + +static struct xenbus_device_id xenpci_ids[] = { + {"pci"}, + {{0}}, +}; + +static struct xenbus_driver xenbus_pciback_driver = { + .name = "pciback", + .owner = THIS_MODULE, + .ids = xenpci_ids, + .probe = pciback_xenbus_probe, + .remove = pciback_xenbus_remove, + .otherend_changed = pciback_frontend_changed, +}; + +static __init int pciback_xenbus_register(void) +{ + return xenbus_register_backend(&xenbus_pciback_driver); +} + +/* Must only initialize our xenbus driver after the pcistub driver */ +device_initcall(pciback_xenbus_register); diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pcifront/Makefile --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pcifront/Makefile Thu Feb 16 22:44:41 2006 @@ -0,0 +1,7 @@ +obj-y += pcifront.o + +pcifront-y := pci_op.o xenbus.o pci.o + +ifeq ($(CONFIG_XEN_PCIDEV_FE_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pcifront/pci.c --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pcifront/pci.c Thu Feb 16 22:44:41 2006 @@ -0,0 +1,44 @@ +/* + * PCI Frontend Operations - ensure only one PCI frontend runs at a time + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include "pcifront.h" + +DEFINE_SPINLOCK(pcifront_dev_lock); +static struct pcifront_device *pcifront_dev = NULL; + +int pcifront_connect(struct pcifront_device *pdev) +{ + int err = 0; + + spin_lock(&pcifront_dev_lock); + + if (!pcifront_dev) + dev_info(&pdev->xdev->dev, "Installing PCI frontend\n"); + else { + dev_err(&pdev->xdev->dev, "PCI frontend already installed!\n"); + err = -EEXIST; + } + + spin_unlock(&pcifront_dev_lock); + + return err; +} + +void pcifront_disconnect(struct pcifront_device *pdev) +{ + spin_lock(&pcifront_dev_lock); + + if (pdev == pcifront_dev) { + dev_info(&pdev->xdev->dev, + "Disconnecting PCI Frontend Buses\n"); + pcifront_dev = NULL; + } + + spin_unlock(&pcifront_dev_lock); +} diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pcifront/pci_op.c --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pcifront/pci_op.c Thu Feb 16 22:44:41 2006 @@ -0,0 +1,245 @@ +/* + * PCI Frontend Operations - Communicates with frontend + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ +#include <linux/module.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <xen/evtchn.h> +#include "pcifront.h" + +static int verbose_request = 0; +module_param(verbose_request, int, 0644); + +static int errno_to_pcibios_err(int errno) +{ + switch (errno) { + case XEN_PCI_ERR_success: + return PCIBIOS_SUCCESSFUL; + + case XEN_PCI_ERR_dev_not_found: + return PCIBIOS_DEVICE_NOT_FOUND; + + case XEN_PCI_ERR_invalid_offset: + case XEN_PCI_ERR_op_failed: + return PCIBIOS_BAD_REGISTER_NUMBER; + + case XEN_PCI_ERR_not_implemented: + return PCIBIOS_FUNC_NOT_SUPPORTED; + + case XEN_PCI_ERR_access_denied: + return PCIBIOS_SET_FAILED; + } + return errno; +} + +static int do_pci_op(struct pcifront_device *pdev, struct xen_pci_op *op) +{ + int err = 0; + struct xen_pci_op *active_op = &pdev->sh_info->op; + unsigned long irq_flags; + + unsigned int volatile ttl = (1U << 29); + + spin_lock_irqsave(&pdev->sh_info_lock, irq_flags); + + memcpy(active_op, op, sizeof(struct xen_pci_op)); + + /* Go */ + wmb(); + set_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags); + notify_remote_via_evtchn(pdev->evtchn); + + /* IRQs are disabled for the pci config. space reads/writes, + * which means no event channel to notify us that the backend + * is done so spin while waiting for the answer */ + while (test_bit + (_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags)) { + if (!ttl) { + dev_err(&pdev->xdev->dev, + "pciback not responding!!!\n"); + clear_bit(_XEN_PCIF_active, + (unsigned long *)&pdev->sh_info->flags); + err = XEN_PCI_ERR_dev_not_found; + goto out; + } + ttl--; + } + + memcpy(op, active_op, sizeof(struct xen_pci_op)); + + err = op->err; + out: + spin_unlock_irqrestore(&pdev->sh_info_lock, irq_flags); + return err; +} + +/* Access to this function is spinlocked in drivers/pci/access.c */ +static int pcifront_bus_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + int err = 0; + struct xen_pci_op op = { + .cmd = XEN_PCI_OP_conf_read, + .domain = pci_domain_nr(bus), + .bus = bus->number, + .devfn = devfn, + .offset = where, + .size = size, + }; + struct pcifront_sd *sd = bus->sysdata; + struct pcifront_device *pdev = sd->pdev; + + if (verbose_request) + dev_info(&pdev->xdev->dev, + "read dev=%04x:%02x:%02x.%01x - offset %x size %d\n", + pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn), where, size); + + err = do_pci_op(pdev, &op); + + if (likely(!err)) { + if (verbose_request) + dev_info(&pdev->xdev->dev, "read got back value %x\n", + op.value); + + *val = op.value; + } else if (err == -ENODEV) { + /* No device here, pretend that it just returned 0 */ + err = 0; + *val = 0; + } + + return errno_to_pcibios_err(err); +} + +/* Access to this function is spinlocked in drivers/pci/access.c */ +static int pcifront_bus_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct xen_pci_op op = { + .cmd = XEN_PCI_OP_conf_write, + .domain = pci_domain_nr(bus), + .bus = bus->number, + .devfn = devfn, + .offset = where, + .size = size, + .value = val, + }; + struct pcifront_sd *sd = bus->sysdata; + struct pcifront_device *pdev = sd->pdev; + + if (verbose_request) + dev_info(&pdev->xdev->dev, + "write dev=%04x:%02x:%02x.%01x - " + "offset %x size %d val %x\n", + pci_domain_nr(bus), bus->number, + PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val); + + return errno_to_pcibios_err(do_pci_op(pdev, &op)); +} + +struct pci_ops pcifront_bus_ops = { + .read = pcifront_bus_read, + .write = pcifront_bus_write, +}; + +/* Claim resources for the PCI frontend as-is, backend won't allow changes */ +static void pcifront_claim_resource(struct pci_dev *dev, void *data) +{ + struct pcifront_device *pdev = data; + int i; + struct resource *r; + + for (i = 0; i < PCI_NUM_RESOURCES; i++) { + r = &dev->resource[i]; + + if (!r->parent && r->start && r->flags) { + dev_dbg(&pdev->xdev->dev, "claiming resource %s/%d\n", + pci_name(dev), i); + pci_claim_resource(dev, i); + } + } +} + +int pcifront_scan_root(struct pcifront_device *pdev, + unsigned int domain, unsigned int bus) +{ + struct pci_bus *b; + struct pcifront_sd *sd = NULL; + struct pci_bus_entry *bus_entry = NULL; + int err = 0; + +#ifndef CONFIG_PCI_DOMAINS + if (domain != 0) { + dev_err(&pdev->xdev->dev, + "PCI Root in non-zero PCI Domain! domain=%d\n", domain); + dev_err(&pdev->xdev->dev, + "Please compile with CONFIG_PCI_DOMAINS\n"); + err = -EINVAL; + goto err_out; + } +#endif + + dev_info(&pdev->xdev->dev, "Creating PCI Frontend Bus %04x:%02x\n", + domain, bus); + + bus_entry = kmalloc(sizeof(*bus_entry), GFP_KERNEL); + sd = kmalloc(sizeof(*sd), GFP_KERNEL); + if (!bus_entry || !sd) { + err = -ENOMEM; + goto err_out; + } + sd->domain = domain; + sd->pdev = pdev; + + b = pci_scan_bus_parented(&pdev->xdev->dev, bus, &pcifront_bus_ops, sd); + if (!b) { + dev_err(&pdev->xdev->dev, "Error creating PCI Frontend Bus!\n"); + err = -ENOMEM; + goto err_out; + } + bus_entry->bus = b; + + list_add(&bus_entry->list, &pdev->root_buses); + + /* Claim resources before going "live" with our devices */ + pci_walk_bus(b, pcifront_claim_resource, pdev); + + pci_bus_add_devices(b); + + return 0; + + err_out: + kfree(bus_entry); + kfree(sd); + + return err; +} + +void pcifront_free_roots(struct pcifront_device *pdev) +{ + struct pci_bus_entry *bus_entry, *t; + + list_for_each_entry_safe(bus_entry, t, &pdev->root_buses, list) { + /* TODO: Removing a PCI Bus is untested (as it normally + * just goes away on domain shutdown) + */ + list_del(&bus_entry->list); + + spin_lock(&pci_bus_lock); + list_del(&bus_entry->bus->node); + spin_unlock(&pci_bus_lock); + + kfree(bus_entry->bus->sysdata); + + device_unregister(bus_entry->bus->bridge); + + /* Do we need to free() the bus itself? */ + + kfree(bus_entry); + } +} diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pcifront/pcifront.h --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pcifront/pcifront.h Thu Feb 16 22:44:41 2006 @@ -0,0 +1,40 @@ +/* + * PCI Frontend - Common data structures & function declarations + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ +#ifndef __XEN_PCIFRONT_H__ +#define __XEN_PCIFRONT_H__ + +#include <linux/spinlock.h> +#include <linux/pci.h> +#include <xen/xenbus.h> +#include <xen/interface/io/pciif.h> +#include <xen/pcifront.h> + +struct pci_bus_entry { + struct list_head list; + struct pci_bus *bus; +}; + +struct pcifront_device { + struct xenbus_device *xdev; + struct list_head root_buses; + spinlock_t dev_lock; + + int evtchn; + int gnt_ref; + + /* Lock this when doing any operations in sh_info */ + spinlock_t sh_info_lock; + struct xen_pci_sharedinfo *sh_info; +}; + +int pcifront_connect(struct pcifront_device *pdev); +void pcifront_disconnect(struct pcifront_device *pdev); + +int pcifront_scan_root(struct pcifront_device *pdev, + unsigned int domain, unsigned int bus); +void pcifront_free_roots(struct pcifront_device *pdev); + +#endif /* __XEN_PCIFRONT_H__ */ diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/drivers/xen/pcifront/xenbus.c --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pcifront/xenbus.c Thu Feb 16 22:44:41 2006 @@ -0,0 +1,295 @@ +/* + * PCI Frontend Xenbus Setup - handles setup with backend (imports page/evtchn) + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <xen/xenbus.h> +#include "pcifront.h" + +#define INVALID_GRANT_REF (0) +#define INVALID_EVTCHN (-1) + +static struct pcifront_device *alloc_pdev(struct xenbus_device *xdev) +{ + struct pcifront_device *pdev; + + pdev = kmalloc(sizeof(struct pcifront_device), GFP_KERNEL); + if (pdev == NULL) + goto out; + + pdev->sh_info = + (struct xen_pci_sharedinfo *)__get_free_page(GFP_KERNEL); + if (pdev->sh_info == NULL) { + kfree(pdev); + pdev = NULL; + goto out; + } + pdev->sh_info->flags = 0; + + xdev->data = pdev; + pdev->xdev = xdev; + + INIT_LIST_HEAD(&pdev->root_buses); + + spin_lock_init(&pdev->dev_lock); + spin_lock_init(&pdev->sh_info_lock); + + pdev->evtchn = INVALID_EVTCHN; + pdev->gnt_ref = INVALID_GRANT_REF; + + dev_dbg(&xdev->dev, "Allocated pdev @ 0x%p pdev->sh_info @ 0x%p\n", + pdev, pdev->sh_info); + out: + return pdev; +} + +static void free_pdev(struct pcifront_device *pdev) +{ + dev_dbg(&pdev->xdev->dev, "freeing pdev @ 0x%p\n", pdev); + + if (pdev->evtchn != INVALID_EVTCHN) + xenbus_free_evtchn(pdev->xdev, pdev->evtchn); + + if (pdev->gnt_ref != INVALID_GRANT_REF) + gnttab_end_foreign_access(pdev->gnt_ref, 0, + (unsigned long)pdev->sh_info); + + pdev->xdev->data = NULL; + + kfree(pdev); +} + +static int pcifront_publish_info(struct pcifront_device *pdev) +{ + int err = 0; + xenbus_transaction_t trans; + + err = xenbus_grant_ring(pdev->xdev, virt_to_mfn(pdev->sh_info)); + if (err < 0) + goto out; + + pdev->gnt_ref = err; + + err = xenbus_alloc_evtchn(pdev->xdev, &pdev->evtchn); + if (err) + goto out; + + do_publish: + err = xenbus_transaction_start(&trans); + if (err) { + xenbus_dev_fatal(pdev->xdev, err, + "Error writing configuration for backend " + "(start transaction)"); + goto out; + } + + err = xenbus_printf(trans, pdev->xdev->nodename, + "pci-op-ref", "%u", pdev->gnt_ref); + if (!err) + err = xenbus_printf(trans, pdev->xdev->nodename, + "event-channel", "%u", pdev->evtchn); + if (!err) + err = xenbus_printf(trans, pdev->xdev->nodename, + "magic", XEN_PCI_MAGIC); + if (!err) + err = + xenbus_switch_state(pdev->xdev, trans, + XenbusStateInitialised); + + if (err) { + xenbus_transaction_end(trans, 1); + xenbus_dev_fatal(pdev->xdev, err, + "Error writing configuration for backend"); + goto out; + } else { + err = xenbus_transaction_end(trans, 0); + if (err == -EAGAIN) + goto do_publish; + else if (err) { + xenbus_dev_fatal(pdev->xdev, err, + "Error completing transaction " + "for backend"); + goto out; + } + } + + dev_dbg(&pdev->xdev->dev, "publishing successful!\n"); + + out: + return err; +} + +static int pcifront_try_connect(struct pcifront_device *pdev) +{ + int err = -EFAULT; + int i, num_roots, len; + char str[64]; + unsigned int domain, bus; + + spin_lock(&pdev->dev_lock); + + /* Only connect once */ + if (xenbus_read_driver_state(pdev->xdev->nodename) != + XenbusStateInitialised) + goto out; + + err = pcifront_connect(pdev); + if (err) { + xenbus_dev_fatal(pdev->xdev, err, + "Error connecting PCI Frontend"); + goto out; + } + + err = xenbus_scanf(XBT_NULL, pdev->xdev->otherend, + "root_num", "%d", &num_roots); + if (err == -ENOENT) { + xenbus_dev_error(pdev->xdev, err, + "No PCI Roots found, trying 0000:00"); + err = pcifront_scan_root(pdev, 0, 0); + num_roots = 0; + } else if (err != 1) { + if (err == 0) + err = -EINVAL; + xenbus_dev_fatal(pdev->xdev, err, + "Error reading number of PCI roots"); + goto out; + } + + for (i = 0; i < num_roots; i++) { + len = snprintf(str, sizeof(str), "root-%d", i); + if (unlikely(len >= (sizeof(str) - 1))) { + err = -ENOMEM; + goto out; + } + + err = xenbus_scanf(XBT_NULL, pdev->xdev->otherend, str, + "%x:%x", &domain, &bus); + if (err != 2) { + if (err >= 0) + err = -EINVAL; + xenbus_dev_fatal(pdev->xdev, err, + "Error reading PCI root %d", i); + goto out; + } + + err = pcifront_scan_root(pdev, domain, bus); + if (err) { + xenbus_dev_fatal(pdev->xdev, err, + "Error scanning PCI root %04x:%02x", + domain, bus); + goto out; + } + } + + err = xenbus_switch_state(pdev->xdev, XBT_NULL, XenbusStateConnected); + if (err) + goto out; + + out: + spin_unlock(&pdev->dev_lock); + return err; +} + +static int pcifront_try_disconnect(struct pcifront_device *pdev) +{ + int err = 0; + XenbusState prev_state; + + spin_lock(&pdev->dev_lock); + + prev_state = xenbus_read_driver_state(pdev->xdev->nodename); + + if (prev_state < XenbusStateClosing) + err = xenbus_switch_state(pdev->xdev, XBT_NULL, + XenbusStateClosing); + + if (!err && prev_state == XenbusStateConnected) + pcifront_disconnect(pdev); + + spin_unlock(&pdev->dev_lock); + + return err; +} + +static void pcifront_backend_changed(struct xenbus_device *xdev, + XenbusState be_state) +{ + struct pcifront_device *pdev = xdev->data; + + switch (be_state) { + case XenbusStateClosing: + dev_warn(&xdev->dev, "backend going away!\n"); + pcifront_try_disconnect(pdev); + break; + + case XenbusStateClosed: + dev_warn(&xdev->dev, "backend went away!\n"); + pcifront_try_disconnect(pdev); + + device_unregister(&pdev->xdev->dev); + break; + + case XenbusStateConnected: + pcifront_try_connect(pdev); + break; + + default: + break; + } +} + +static int pcifront_xenbus_probe(struct xenbus_device *xdev, + const struct xenbus_device_id *id) +{ + int err = 0; + struct pcifront_device *pdev = alloc_pdev(xdev); + + if (pdev == NULL) { + err = -ENOMEM; + xenbus_dev_fatal(xdev, err, + "Error allocating pcifront_device struct"); + goto out; + } + + err = pcifront_publish_info(pdev); + + out: + return err; +} + +static int pcifront_xenbus_remove(struct xenbus_device *xdev) +{ + if (xdev->data) + free_pdev(xdev->data); + + return 0; +} + +static struct xenbus_device_id xenpci_ids[] = { + {"pci"}, + {{0}}, +}; + +static struct xenbus_driver xenbus_pcifront_driver = { + .name = "pcifront", + .owner = THIS_MODULE, + .ids = xenpci_ids, + .probe = pcifront_xenbus_probe, + .remove = pcifront_xenbus_remove, + .otherend_changed = pcifront_backend_changed, +}; + +static int __init pcifront_init(void) +{ + int err = 0; + + err = xenbus_register_frontend(&xenbus_pcifront_driver); + + return err; +} + +/* Initialize after the Xen PCI Frontend Stub is initialized */ +subsys_initcall(pcifront_init); diff -r 294b3a447dce -r 5b433b4fca34 linux-2.6-xen-sparse/include/xen/pcifront.h --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/linux-2.6-xen-sparse/include/xen/pcifront.h Thu Feb 16 22:44:41 2006 @@ -0,0 +1,39 @@ +/* + * PCI Frontend - arch-dependendent declarations + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ +#ifndef __XEN_ASM_PCIFRONT_H__ +#define __XEN_ASM_PCIFRONT_H__ + +#include <linux/config.h> +#include <linux/spinlock.h> + +#ifdef __KERNEL__ + +struct pcifront_device; + +struct pcifront_sd { + int domain; + struct pcifront_device *pdev; +}; + +struct pci_bus; + +#ifdef CONFIG_PCI_DOMAINS +static inline int pci_domain_nr(struct pci_bus *bus) +{ + struct pcifront_sd *sd = bus->sysdata; + return sd->domain; +} +static inline int pci_proc_domain(struct pci_bus *bus) +{ + return pci_domain_nr(bus); +} +#endif /* CONFIG_PCI_DOMAINS */ + +extern spinlock_t pci_bus_lock; + +#endif /* __KERNEL__ */ + +#endif /* __XEN_ASM_PCIFRONT_H__ */ diff -r 294b3a447dce -r 5b433b4fca34 xen/include/public/io/pciif.h --- /dev/null Thu Feb 16 22:37:40 2006 +++ b/xen/include/public/io/pciif.h Thu Feb 16 22:44:41 2006 @@ -0,0 +1,55 @@ +/* + * PCI Backend/Frontend Common Data Structures & Macros + * + * Author: Ryan Wilson <hap9@xxxxxxxxxxxxxx> + */ +#ifndef __XEN_PCI_COMMON_H__ +#define __XEN_PCI_COMMON_H__ + +/* Be sure to bump this number if you change this file */ +#define XEN_PCI_MAGIC "7" + +/* xen_pci_sharedinfo flags */ +#define _XEN_PCIF_active (0) +#define XEN_PCIF_active (1<<_XEN_PCI_active) + +/* xen_pci_op commands */ +#define XEN_PCI_OP_conf_read (0) +#define XEN_PCI_OP_conf_write (1) + +/* xen_pci_op error numbers */ +#define XEN_PCI_ERR_success (0) +#define XEN_PCI_ERR_dev_not_found (-1) +#define XEN_PCI_ERR_invalid_offset (-2) +#define XEN_PCI_ERR_access_denied (-3) +#define XEN_PCI_ERR_not_implemented (-4) +/* XEN_PCI_ERR_op_failed - backend failed to complete the operation */ +#define XEN_PCI_ERR_op_failed (-5) + +struct xen_pci_op { + /* IN: what action to perform: XEN_PCI_OP_* */ + uint32_t cmd; + + /* OUT: will contain an error number (if any) from errno.h */ + int32_t err; + + /* IN: which device to touch */ + uint32_t domain; /* PCI Domain/Segment */ + uint32_t bus; + uint32_t devfn; + + /* IN: which configuration registers to touch */ + int32_t offset; + int32_t size; + + /* IN/OUT: Contains the result after a READ or the value to WRITE */ + uint32_t value; +}; + +struct xen_pci_sharedinfo { + /* flags - XEN_PCIF_* */ + uint32_t flags; + struct xen_pci_op op; +}; + +#endif /* __XEN_PCI_COMMON_H__ */ _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |