diff -r 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/arch/xen/Kconfig --- a/linux-2.6-xen-sparse/arch/xen/Kconfig Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/arch/xen/Kconfig Fri Jan 27 18:57:35 2006 @@ -38,6 +38,85 @@ (e.g., hard drives, network cards). This allows you to configure such devices and also includes some low-level support that is otherwise not compiled into the kernel. + +config XEN_PCIDEV_BACKEND + bool "PCI device backend driver" + depends on XEN_PHYSDEV_ACCESS + 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 + +# This won't work in 2.6.12 due to what appears to be a kernel bug (related +# to reading the resources of transparent bridges in pci_read_bridge_bases). +# This bug appears to be fixed in 2.6.13: +# http://www.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=90b54929b626c80056262d9d99b3f48522e404d0 +#config XEN_PCIDEV_BACKEND_ALLOC +# bool "Re-allocate PCI Resources (DANGEROUS)" +# depends on XEN_PCIDEV_BACKEND +# default n +# ---help--- +# This forces the PCI Backend to try and re-allocate all of the resources +# on the PCI bus in an attempt to ensure that they fall entirely within +# boundaries that Xen can control. + +config XEN_PCIDEV_FRONTEND + bool "PCI device frontend driver" + depends on XEN_PHYSDEV_ACCESS + select PCI + default y if !XEN_PRIVILEGED_GUEST + help + The PCI device frontend driver allows the kernel to import arbitrary + PCI devices from a PCI backend. + +config XEN_PCIDEV_FE_DEBUG + bool "PCI Frontend Debugging" + depends on XEN_PCIDEV_FRONTEND + default n + +config XEN_PCI_FE_ARCH_REPLACE + bool "PCI frontend driver - arch-independent (EXPERIMENTAL)" + depends on XEN_PCIDEV_FRONTEND && EXPERIMENTAL + default n + help + This selects an implementation of the PCI frontend driver that is + architecture independent (i.e. it replaces the real architecture's + PCI code). The default is to use an implementation that works with + the architecture dependent PCI code (and you probably want the + default). + + If unsure, say N. config XEN_BLKDEV_BACKEND bool "Block-device backend driver" diff -r 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/arch/xen/i386/Kconfig --- a/linux-2.6-xen-sparse/arch/xen/i386/Kconfig Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/arch/xen/i386/Kconfig Fri Jan 27 18:57:35 2006 @@ -788,9 +788,17 @@ information about which PCI hardware does work under Linux and which doesn't. +# need to enable this on other platforms to support XEN_PCI_FE_ARCH_REPLACE +config PCI_NATIVE + bool + depends on PCI + default y if !XEN_PCI_FE_ARCH_REPLACE + ---help--- + Compile in Native PCI access (as opposed to Xen's PCI Frontend). + choice prompt "PCI access mode" - depends on PCI && !X86_VISWS + depends on PCI_NATIVE && !X86_VISWS default PCI_GOANY ---help--- On PCI systems, the BIOS can be used to detect the PCI devices and @@ -810,6 +818,15 @@ #config PCI_GOBIOS # bool "BIOS" +config PCI_GOXEN_PCI_FE_STUB + bool "Xen PCI Frontend Stub" + depends on XEN_PCIDEV_FRONTEND + ---help--- + The Xen PCI Frontend stub replaces the architecture's means of talking + directly to the PCI host bridge with a "dummy" handler which will act + as somewhat of a placeholder until the real Xen PCI Frontend is + initialized. + config PCI_GOMMCONFIG bool "MMConfig" @@ -835,6 +852,11 @@ bool depends on PCI && ACPI && (PCI_GOMMCONFIG || PCI_GOANY) select ACPI_BOOT + default y + +config XEN_PCI_FE_STUB + bool + depends on PCI && XEN_PCIDEV_FRONTEND && !XEN_PCI_FE_ARCH_REPLACE && (PCI_GOXEN_PCI_FE_STUB || PCI_GOANY) default y source "drivers/pci/pcie/Kconfig" diff -r 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/arch/xen/i386/Makefile --- a/linux-2.6-xen-sparse/arch/xen/i386/Makefile Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/arch/xen/i386/Makefile Fri Jan 27 18:57:35 2006 @@ -82,7 +82,7 @@ # \ # arch/xen/$(mcore-y)/ drivers-$(CONFIG_MATH_EMULATION) += arch/i386/math-emu/ -drivers-$(CONFIG_PCI) += arch/xen/i386/pci/ +drivers-$(CONFIG_PCI_NATIVE) += arch/xen/i386/pci/ # must be linked after kernel/ drivers-$(CONFIG_OPROFILE) += arch/i386/oprofile/ drivers-$(CONFIG_PM) += arch/i386/power/ diff -r 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/arch/xen/i386/pci/Makefile --- a/linux-2.6-xen-sparse/arch/xen/i386/pci/Makefile Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/arch/xen/i386/pci/Makefile Fri Jan 27 18:57:35 2006 @@ -7,6 +7,7 @@ #c-obj-$(CONFIG_PCI_BIOS) += pcbios.o c-obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o c-obj-$(CONFIG_PCI_DIRECT) += direct.o +c-obj-$(CONFIG_XEN_PCI_FE_STUB) += pcifront.o c-pci-y := fixup.o c-pci-$(CONFIG_ACPI_PCI) += acpi.o @@ -30,4 +31,6 @@ # Make sure irq.o gets linked in before common.o obj-y += $(patsubst common.o,$(l-pci-y) common.o,$(c-obj-y)) +obj-$(CONFIG_XEN_PCIDEV_BACKEND_ALLOC) += alloc.o + clean-files += $(patsubst %.o,%.c,$(c-obj-y) $(c-obj-) $(c-link)) diff -r 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/arch/xen/i386/pci/i386.c --- a/linux-2.6-xen-sparse/arch/xen/i386/pci/i386.c Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/arch/xen/i386/pci/i386.c Fri Jan 27 18:57:35 2006 @@ -58,6 +58,13 @@ res->start = start; } } +#ifdef CONFIG_XEN_PCIDEV_BACKEND_ALLOC + /* Ensure i/o memory is allocated on page boundaries */ + else if (res->flags & IORESOURCE_MEM) { + unsigned long alignto = max(align,PAGE_SIZE); + res->start = ALIGN(res->start,alignto); + } +#endif } diff -r 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/Makefile --- a/linux-2.6-xen-sparse/drivers/xen/Makefile Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/Makefile Fri Jan 27 18:57:35 2006 @@ -16,4 +16,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 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/include/asm-xen/asm-i386/pci.h --- a/linux-2.6-xen-sparse/include/asm-xen/asm-i386/pci.h Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/include/asm-xen/asm-i386/pci.h Fri Jan 27 18:57:35 2006 @@ -124,6 +124,10 @@ #endif /* __KERNEL__ */ +#ifdef CONFIG_XEN_PCIDEV_FRONTEND +#include +#endif /* CONFIG_XEN_PCIDEV_FRONTEND */ + /* implement the pci_ DMA API in terms of the generic device dma_ one */ #include diff -r 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/arch/xen/i386/pci/alloc.c --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/arch/xen/i386/pci/alloc.c Fri Jan 27 18:57:35 2006 @@ -0,0 +1,16 @@ +/* + * PCI Backend - Try to force a re-assignment of resources so that all + * memory resources are allocated on page boundaries + */ +#include +#include +#include + +static __init int pciback_alloc(void) +{ + printk(KERN_INFO "pciback: Attempt to re-allocate PCI resources\n"); + pci_assign_unassigned_resources(); + return 0; +} + +subsys_initcall(pciback_alloc); diff -r 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/arch/xen/i386/pci/pcifront.c --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/arch/xen/i386/pci/pcifront.c Fri Jan 27 18:57:35 2006 @@ -0,0 +1,48 @@ +/* + * 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 + */ +#include +#include +#include +#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; + + return 0; +} + +arch_initcall(pcifront_x86_stub_init); diff -r 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pciback/Makefile --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/Makefile Fri Jan 27 18:57:35 2006 @@ -0,0 +1,11 @@ +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) +CFLAGS_pci_stub.o += -DDEBUG +CFLAGS_xenbus.o += -DDEBUG +endif diff -r 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.c --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.c Fri Jan 27 18:57:35 2006 @@ -0,0 +1,315 @@ +/* + * 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 + */ + +#include +#include +#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_startfield_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_startfield_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 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.h --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space.h Fri Jan 27 18:57:35 2006 @@ -0,0 +1,97 @@ +/* + * PCI Backend - Common data structures for overriding the configuration space + * + * Author: Ryan Wilson + */ + +#ifndef __XEN_PCIBACK_CONF_SPACE_H__ +#define __XEN_PCIBACK_CONF_SPACE_H__ + +#include + +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 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_header.c --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/conf_space_header.c Fri Jan 27 18:57:35 2006 @@ -0,0 +1,324 @@ +/* + * PCI Backend - Handles the virtual fields in the configuration space headers. + * + * Author: Ryan Wilson + */ + +#include +#include +#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<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; + int i; + + if (unlikely(!bar)) { + printk(KERN_WARNING "pciback: driver data not found for %s\n", + pci_name(dev)); + return XEN_PCI_ERR_op_failed; + } + + bar->which = 0; + /* Because writes *could* occur in bytes, if any byte is all 1s, switch */ + for (i=0; i<4; i++) + if (((value>>(i*8))&0xff)==0xff) + bar->which = 1; + + /* 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) +{ + int i; + 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; + } + + bar->which = 0; + /* Because writes *could* occur in bytes, if any byte is all 1s, switch */ + for (i=0; i<4; i++) + if (((value>>(i*8))&0xff)==0xff) + bar->which = 1; + + 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, + }, +}; + +struct config_field header_0[] = { + { + .offset = PCI_BASE_ADDRESS_0, + .size = 4, + .init = bar_init, + .reset = bar_reset, + .release = bar_release, + .u.dw.read = bar_read, + .u.dw.write = bar_write, + }, + { + .offset = PCI_BASE_ADDRESS_1, + .size = 4, + .init = bar_init, + .reset = bar_reset, + .release = bar_release, + .u.dw.read = bar_read, + .u.dw.write = bar_write, + }, + { + .offset = PCI_BASE_ADDRESS_2, + .size = 4, + .init = bar_init, + .reset = bar_reset, + .release = bar_release, + .u.dw.read = bar_read, + .u.dw.write = bar_write, + }, + { + .offset = PCI_BASE_ADDRESS_3, + .size = 4, + .init = bar_init, + .reset = bar_reset, + .release = bar_release, + .u.dw.read = bar_read, + .u.dw.write = bar_write, + }, + { + .offset = PCI_BASE_ADDRESS_4, + .size = 4, + .init = bar_init, + .reset = bar_reset, + .release = bar_release, + .u.dw.read = bar_read, + .u.dw.write = bar_write, + }, + { + .offset = PCI_BASE_ADDRESS_5, + .size = 4, + .init = bar_init, + .reset = bar_reset, + .release = bar_release, + .u.dw.read = bar_read, + .u.dw.write = bar_write, + }, + { + .offset = PCI_ROM_ADDRESS, + .size = 4, + .init = rom_init, + .reset = bar_reset, + .release = bar_release, + .u.dw.read = bar_read, + .u.dw.write = rom_write, + }, + { + .size = 0, + }, +}; + +struct config_field header_1[] = { + { + .offset = PCI_BASE_ADDRESS_0, + .size = 4, + .init = bar_init, + .reset = bar_reset, + .release = bar_release, + .u.dw.read = bar_read, + .u.dw.write = bar_write, + }, + { + .offset = PCI_BASE_ADDRESS_1, + .size = 4, + .init = bar_init, + .reset = bar_reset, + .release = bar_release, + .u.dw.read = bar_read, + .u.dw.write = bar_write, + }, + { + .offset = PCI_ROM_ADDRESS1, + .size = 4, + .init = rom_init, + .reset = bar_reset, + .release = bar_release, + .u.dw.read = bar_read, + .u.dw.write = rom_write, + }, + { + .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 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pciback/passthrough.c --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/passthrough.c Fri Jan 27 18:57:35 2006 @@ -0,0 +1,112 @@ +/* + * PCI Backend - Provides restricted access to the real PCI bus topology + * to the frontend + * + * Author: Ryan Wilson + */ + +#include +#include +#include "pciback.h" + +typedef struct { + struct list_head dev_list; +} passthrough_dev_data_t; + +struct pci_dev * pciback_get_pci_dev(struct pciback_device *pdev, + unsigned int domain, unsigned int bus, unsigned int devfn) +{ + passthrough_dev_data_t *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) +{ + passthrough_dev_data_t *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) +{ + passthrough_dev_data_t *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; + passthrough_dev_data_t *dev_data = pdev->pci_dev_data; + struct pci_dev_entry *dev_entry, *e; + struct pci_dev *dev; + int found; + + 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; + } + } + } + + if (!found) { + err = publish_root_cb(pdev, + (unsigned int)pci_domain_nr(dev_entry->dev->bus), + (unsigned int)dev_entry->dev->bus->number); + if (err) + break; + } + } + + return err; +} + +/* Must hold pciback_device->dev_lock when calling this */ +void pciback_release_devices(struct pciback_device *pdev) +{ + passthrough_dev_data_t *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 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pciback/pci_stub.c --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/pci_stub.c Fri Jan 27 18:57:35 2006 @@ -0,0 +1,368 @@ +/* + * PCI Stub Driver - Grabs devices in backend to be exported later + * + * Author: Ryan Wilson + */ +#include +#include +#include +#include +#include +#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: error %d initializing device %s\n", + err, pci_name(psdev->dev)); + 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: can't export pci devices that don't " + "have a normal (0) or bridge (1) header type! dev=%s\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 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pciback/pciback.h --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/pciback.h Fri Jan 27 18:57:35 2006 @@ -0,0 +1,72 @@ +/* + * PCI Backend Common Data Structures & Function Declarations + * + * Author: Ryan Wilson + */ +#ifndef __XEN_PCIBACK_H__ +#define __XEN_PCIBACK_H__ + +#include +#include +#include +#include +#include +#include + +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 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pciback/pciback_ops.c --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/pciback_ops.c Fri Jan 27 18:57:35 2006 @@ -0,0 +1,84 @@ +/* + * PCI Backend Operations - respond to PCI requests from Frontend + * + * Author: Ryan Wilson + */ +#include +#include +#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))) { + printk(KERN_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 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pciback/vpci.c --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/vpci.c Fri Jan 27 18:57:35 2006 @@ -0,0 +1,147 @@ +/* + * PCI Backend - Provides a Virtual PCI bus (with real devices) + * to the frontend + * + * Author: Ryan Wilson + */ + +#include +#include +#include +#include "pciback.h" + +#define PCI_SLOT_MAX 32 + +typedef struct { + struct list_head dev_list[PCI_SLOT_MAX]; +} vpci_dev_data_t; + +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; + vpci_dev_data_t *vpci_dev = pdev->pci_dev_data; + + if (domain!=0 || bus!=0) + return NULL; + + if (PCI_SLOT(devfn)dev_list[PCI_SLOT(devfn)], + list) { + if (PCI_FUNC(dev_entry->dev->devfn)==PCI_FUNC(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) +{ + int err=0, i; + struct pci_dev_entry *t, *dev_entry; + vpci_dev_data_t *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 (i=0; idev_list[i])) { + t = list_entry(list_first(&vpci_dev->dev_list[i]), + struct pci_dev_entry, list); + + if (pci_domain_nr(t->dev->bus)==pci_domain_nr(dev->bus) + && t->dev->bus->number==dev->bus->number + && PCI_SLOT(t->dev->devfn)==PCI_SLOT(dev->devfn)) { + printk(KERN_INFO + "pciback: vpci: assign %s to virtual slot %d func %d\n", + pci_name(dev), i, PCI_FUNC(dev->devfn)); + list_add_tail(&dev_entry->list, &vpci_dev->dev_list[i]); + goto out; + } + } + } + + /* Assign to a new slot on the virtual PCI bus */ + for (i=0; idev_list[i])) { + printk(KERN_INFO "pciback: vpci: assign %s to virtual slot %d\n", + pci_name(dev),i); + list_add_tail(&dev_entry->list, &vpci_dev->dev_list[i]); + 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 i; + vpci_dev_data_t *vpci_dev = kmalloc(sizeof(*vpci_dev), GFP_KERNEL); + + if (!vpci_dev) + return -ENOMEM; + + for (i=0; idev_list[i]); + } + + 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 i; + vpci_dev_data_t *vpci_dev = pdev->pci_dev_data; + + for (i=0; idev_list[i], list) { + list_del(&e->list); + pcistub_put_pci_dev(e->dev); + kfree(e); + } + } + + kfree(vpci_dev); + pdev->pci_dev_data = NULL; +} diff -r 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pciback/xenbus.c --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pciback/xenbus.c Fri Jan 27 18:57:35 2006 @@ -0,0 +1,422 @@ +/* + * PCI Backend Xenbus Setup - handles setup with frontend and xend + * + * Author: Ryan Wilson + */ +#include +#include +#include +#include +#include +#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=(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: If this is a bridge, export all the children (this won't work for + * children dynamically added to the bus later!) - This is trivial in + * kernels >= 2.6.14 with pci_walk_bus(dev->subordinate) + */ +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=(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 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pcifront/Makefile --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pcifront/Makefile Fri Jan 27 18:57:35 2006 @@ -0,0 +1,8 @@ +obj-y += pcifront.o + +pcifront-y := pci_op.o xenbus.o pci.o +pcifront-$(CONFIG_XEN_PCI_FE_ARCH_REPLACE) += arch.o + +ifeq ($(CONFIG_XEN_PCIDEV_FE_DEBUG),y) +CFLAGS_xenbus.o += -DDEBUG +endif diff -r 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pcifront/arch.c --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pcifront/arch.c Fri Jan 27 18:57:35 2006 @@ -0,0 +1,95 @@ +/* + * PCI Frontend Arch Replacement Code - replaces an architecture's PCI access + * code with a generic, Xen version (Experimental) + * - This file *should* be arch-independent (except for + * pci_mmap_page_range) and should replace an arch's PCI implementation + * - You must prevent an architecture's PCI code from compiling into the kernel + * when you use the code contained here. + * + * Author: Ryan Wilson + */ +#include +#include +#include + +/* More or less copied from other architectures */ +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ + u16 cmd, oldcmd; + u8 irq; + int i; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + oldcmd = cmd; + + for (i=0; iresource[i]; + + if (!(mask&(1<flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + + if (cmd!=oldcmd) { + printk(KERN_DEBUG "PCI: Enabling device: (%s), cmd %x\n", + pci_name(dev), cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); + dev->irq = irq; + + return 0; +} + +void pcibios_disable_device(struct pci_dev *dev) +{ + u16 cmd; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + + if (cmd&(PCI_COMMAND_IO|PCI_COMMAND_MEMORY|PCI_COMMAND_MASTER)) { + cmd &= ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY|PCI_COMMAND_MASTER); + + printk(KERN_DEBUG "PCI: Disabling device: (%s), cmd %x\n", + pci_name(dev), cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } +} + +char *pcibios_setup(char *str) +{ + /** Return NULL if we accept an option, str if we don't */ + return str; +} + +void pcibios_set_master(struct pci_dev *dev) +{ + /* Handled by backend when we turn the master + * bit on in the PCI_COMMAND register */ +} + +void pcibios_align_resource(void *data, struct resource *res, + unsigned long size, unsigned long align) +{ +} + +void pcibios_fixup_bus(struct pci_bus *bus) +{ +} + +unsigned int pcibios_assign_all_busses(void) +{ + return 0; +} + +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + printk(KERN_ERR "%s: mmap_page_range not implemented!\n", pci_name(dev)); + return -EINVAL; +} diff -r 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pcifront/pci.c --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pcifront/pci.c Fri Jan 27 18:57:35 2006 @@ -0,0 +1,43 @@ +/* + * PCI Frontend Operations - ensure only one PCI frontend runs at a time + * + * Author: Ryan Wilson + */ +#include +#include +#include +#include +#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 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pcifront/pci_op.c --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pcifront/pci_op.c Fri Jan 27 18:57:35 2006 @@ -0,0 +1,220 @@ +/* + * PCI Frontend Operations - Communicates with frontend + * + * Author: Ryan Wilson + */ +#include +#include +#include +#include +#include +#include "pcifront.h" + +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, +}; + +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 (%d) PCI Domain!\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); + + /* In kernels >= 2.6.13, we need to: + 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 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pcifront/pcifront.h --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pcifront/pcifront.h Fri Jan 27 18:57:35 2006 @@ -0,0 +1,40 @@ +/* + * PCI Frontend - Common data structures & function declarations + * + * Author: Ryan Wilson + */ +#ifndef __XEN_PCIFRONT_H__ +#define __XEN_PCIFRONT_H__ + +#include +#include +#include +#include +#include + +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 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/drivers/xen/pcifront/xenbus.c --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pcifront/xenbus.c Fri Jan 27 18:57:35 2006 @@ -0,0 +1,286 @@ +/* + * PCI Frontend Xenbus Setup - handles setup with backend (imports page/evtchn) + * + * Author: Ryan Wilson + */ +#include +#include +#include +#include +#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=(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_statexdev, 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 2add7a262530 -r 5278641a6ea0 linux-2.6-xen-sparse/include/asm-xen/pcifront.h --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/linux-2.6-xen-sparse/include/asm-xen/pcifront.h Fri Jan 27 18:57:35 2006 @@ -0,0 +1,39 @@ +/* + * PCI Frontend - arch-dependendent declarations + * + * Author: Ryan Wilson + */ +#ifndef __XEN_ASM_PCIFRONT_H__ +#define __XEN_ASM_PCIFRONT_H__ + +#include +#include + +#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 2add7a262530 -r 5278641a6ea0 xen/include/public/io/pciif.h --- /dev/null Fri Jan 27 15:17:38 2006 +++ b/xen/include/public/io/pciif.h Fri Jan 27 18:57:35 2006 @@ -0,0 +1,55 @@ +/* + * PCI Backend/Frontend Common Data Structures & Macros + * + * Author: Ryan Wilson + */ +#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__ */