Ryan
2006-Jan-30 13:24 UTC
[Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
This patch contains the PCI backend and frontend drivers for Linux 2.6.12. There are a couple of compile-time options in the backend and frontend although the defaults should be sufficient to get you up and running. In the PCI backend, there are two modes of operation: pass-through and virtual PCI bus. The virtual PCI bus mode renumbers the slot addresses and places all of the exported devices on bus 0. For example, a device at 06:01.0 will appear to the PCI frontend to be at 00:00.0 (a 2nd exported device will show up at 00:01.0 and so on...). In pass-through mode, no renumbering occurs. A device at 06:01.0 will appear at 06:01.0 to the PCI frontend (this is more similar to how things worked in Xen 2.0.x). In both modes, PCI bridges are not currently exported. While virtual PCI bus mode can somewhat mask the real slot addresses to the frontend (they''re still visible in Xenstore at present), it may break certain specialized devices and drivers which need to know the location of other PCI devices (pass-through mode may be better suited for this scenario). There are two methods for the PCI frontend as well. The default method integrates with the architecture-specific PCI code (in this case, i386). This allows some PCI things that are architecture-specific to keep working (like pci_mmap_page_range) that can''t be handled by the architecture-specific code in the backend. The other method is an attempt at an architecture-independent driver that replaces the architecture-specific PCI code in Linux with one that exclusively uses Xen. It requires less patching to the architecture-specific code (I just prevent it from compiling at all). While not tested on architectures other than x86, it *should* enable easy porting of the PCI frontend to ia64 and other architectures that Xen will support. I believe this method also demonstrates that by leveraging the PCI backend, a driver domain does not have to be concerned with as many architecture-specific details regarding the PCI bus and devices. It''s not yet clear to me which method is best for the PCI frontend and I''d like to pick one or the other so that there aren''t two mutually-exclusive code paths to maintain. Please let me know if you have problems with either method and if you have any suggestions/comments on the merits of each approach. If you have problems, there is a compile-time debug option that enables some extra logging that may be useful in tracking down the problem. There is also a run-time module parameter ("verbose_request") in the frontend and backend that will output each configuration space request that is sent/received across the shared page. Signed-off-by: Ryan Wilson <hap9@epoch.ncsc.mil> _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2006-Jan-30 15:06 UTC
Re: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
Hi Ryan, It would be nice if you could run your patches through lindent and resubmit. You do a couple things that don''t follow kernel style guidelines (lack of spaces around operators and else clauses on newlines). Strictly adhering to the CodingStyle now will make upstream merge significantly easier in the future. Thanks, Anthony Liguori Ryan wrote:>This patch contains the PCI backend and frontend drivers for Linux >2.6.12. There are a couple of compile-time options in the backend and >frontend although the defaults should be sufficient to get you up and >running. > >In the PCI backend, there are two modes of operation: pass-through and >virtual PCI bus. The virtual PCI bus mode renumbers the slot addresses >and places all of the exported devices on bus 0. For example, a device >at 06:01.0 will appear to the PCI frontend to be at 00:00.0 (a 2nd >exported device will show up at 00:01.0 and so on...). In pass-through >mode, no renumbering occurs. A device at 06:01.0 will appear at 06:01.0 >to the PCI frontend (this is more similar to how things worked in Xen >2.0.x). In both modes, PCI bridges are not currently exported. While >virtual PCI bus mode can somewhat mask the real slot addresses to the >frontend (they''re still visible in Xenstore at present), it may break >certain specialized devices and drivers which need to know the location >of other PCI devices (pass-through mode may be better suited for this >scenario). > >There are two methods for the PCI frontend as well. The default method >integrates with the architecture-specific PCI code (in this case, i386). >This allows some PCI things that are architecture-specific to keep >working (like pci_mmap_page_range) that can''t be handled by the >architecture-specific code in the backend. The other method is an >attempt at an architecture-independent driver that replaces the >architecture-specific PCI code in Linux with one that exclusively uses >Xen. It requires less patching to the architecture-specific code (I just >prevent it from compiling at all). While not tested on architectures >other than x86, it *should* enable easy porting of the PCI frontend to >ia64 and other architectures that Xen will support. I believe this >method also demonstrates that by leveraging the PCI backend, a driver >domain does not have to be concerned with as many architecture-specific >details regarding the PCI bus and devices. It''s not yet clear to me >which method is best for the PCI frontend and I''d like to pick one or >the other so that there aren''t two mutually-exclusive code paths to >maintain. Please let me know if you have problems with either method and >if you have any suggestions/comments on the merits of each approach. > >If you have problems, there is a compile-time debug option that enables >some extra logging that may be useful in tracking down the problem. >There is also a run-time module parameter ("verbose_request") in the >frontend and backend that will output each configuration space request >that is sent/received across the shared page. > >Signed-off-by: Ryan Wilson <hap9@epoch.ncsc.mil> > > >------------------------------------------------------------------------ > >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 <asm-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 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 <linux/kernel.h> >+#include <linux/pci.h> >+#include <linux/list.h> >+ >+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 <hap9@epoch.ncsc.mil> >+ */ >+#include <linux/module.h> >+#include <linux/init.h> >+#include <linux/pci.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; >+ >+ 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 <hap9@epoch.ncsc.mil> >+ */ >+ >+#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 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 <hap9@epoch.ncsc.mil> >+ */ >+ >+#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 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 <hap9@epoch.ncsc.mil> >+ */ >+ >+#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; >+ 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 <hap9@epoch.ncsc.mil> >+ */ >+ >+#include <linux/list.h> >+#include <linux/pci.h> >+#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 <hap9@epoch.ncsc.mil> >+ */ >+#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: 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 <hap9@epoch.ncsc.mil> >+ */ >+#ifndef __XEN_PCIBACK_H__ >+#define __XEN_PCIBACK_H__ >+ >+#include <linux/pci.h> >+#include <linux/interrupt.h> >+#include <asm-xen/xenbus.h> >+#include <linux/list.h> >+#include <linux/spinlock.h> >+#include <asm-xen/xen-public/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 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 <hap9@epoch.ncsc.mil> >+ */ >+#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))) { >+ 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 <hap9@epoch.ncsc.mil> >+ */ >+ >+#include <linux/list.h> >+#include <linux/slab.h> >+#include <linux/pci.h> >+#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)<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; >+} >+ >+/* 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; i<PCI_SLOT_MAX; i++) { >+ if (!list_empty(&vpci_dev->dev_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; i<PCI_SLOT_MAX; i++) { >+ if (list_empty(&vpci_dev->dev_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; i<PCI_SLOT_MAX; i++) { >+ INIT_LIST_HEAD(&vpci_dev->dev_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; i<PCI_SLOT_MAX; i++) { >+ struct pci_dev_entry *e,*tmp; >+ list_for_each_entry_safe(e, tmp, &vpci_dev->dev_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 <hap9@epoch.ncsc.mil> >+ */ >+#include <linux/module.h> >+#include <linux/init.h> >+#include <linux/list.h> >+#include <asm-xen/xenbus.h> >+#include <asm-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: 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<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 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 <hap9@epoch.ncsc.mil> >+ */ >+#include <linux/module.h> >+#include <linux/init.h> >+#include <linux/pci.h> >+ >+/* 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; i<PCI_NUM_RESOURCES; i++) { >+ struct resource *r = &dev->resource[i]; >+ >+ if (!(mask&(1<<i))) >+ continue; >+ >+ if (r->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 <hap9@epoch.ncsc.mil> >+ */ >+#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 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 <hap9@epoch.ncsc.mil> >+ */ >+#include <linux/module.h> >+#include <linux/init.h> >+#include <linux/pci.h> >+#include <linux/spinlock.h> >+#include <asm-xen/evtchn.h> >+#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 <hap9@epoch.ncsc.mil> >+ */ >+#ifndef __XEN_PCIFRONT_H__ >+#define __XEN_PCIFRONT_H__ >+ >+#include <linux/spinlock.h> >+#include <linux/pci.h> >+#include <asm-xen/xenbus.h> >+#include <asm-xen/xen-public/io/pciif.h> >+#include <asm-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 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 <hap9@epoch.ncsc.mil> >+ */ >+#include <linux/module.h> >+#include <linux/init.h> >+#include <linux/mm.h> >+#include <asm-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 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 <hap9@epoch.ncsc.mil> >+ */ >+#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 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 <hap9@epoch.ncsc.mil> >+ */ >+#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-devel mailing list >Xen-devel@lists.xensource.com >http://lists.xensource.com/xen-devel > >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Ryan
2006-Jan-30 18:43 UTC
Re: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
On Mon, 2006-01-30 at 09:06 -0600, Anthony Liguori wrote:> Hi Ryan, > > It would be nice if you could run your patches through lindent and > resubmit. You do a couple things that don''t follow kernel style > guidelines (lack of spaces around operators and else clauses on > newlines). Strictly adhering to the CodingStyle now will make upstream > merge significantly easier in the future. > > Thanks, > > Anthony Liguori ><snip> Is the attached file better formatted? I used lindent. I also added a couple of macros to simplify readability and reduce code size (lindent changed some formatting that brought some code duplication to my attention). _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Anthony Liguori
2006-Jan-31 00:37 UTC
Re: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
Very much so, thanks! Regards, Anthony Liguori Ryan wrote:>On Mon, 2006-01-30 at 09:06 -0600, Anthony Liguori wrote: > > >>Hi Ryan, >> >>It would be nice if you could run your patches through lindent and >>resubmit. You do a couple things that don''t follow kernel style >>guidelines (lack of spaces around operators and else clauses on >>newlines). Strictly adhering to the CodingStyle now will make upstream >>merge significantly easier in the future. >> >>Thanks, >> >>Anthony Liguori >> >> >> ><snip> > >Is the attached file better formatted? I used lindent. I also added a >couple of macros to simplify readability and reduce code size (lindent >changed some formatting that brought some code duplication to my >attention). > > > >------------------------------------------------------------------------ > >diff -r 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 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 4868d0f773ce 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 Mon Jan 30 18:32:09 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 4868d0f773ce 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 Mon Jan 30 18:32:09 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 4868d0f773ce 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 Mon Jan 30 18:32:09 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 4868d0f773ce 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 Mon Jan 30 18:32:09 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 4868d0f773ce 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 Mon Jan 30 18:32:09 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 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -124,6 +124,10 @@ > > #endif /* __KERNEL__ */ > >+#ifdef CONFIG_XEN_PCIDEV_FRONTEND >+#include <asm-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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 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 <linux/kernel.h> >+#include <linux/pci.h> >+#include <linux/list.h> >+ >+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 4868d0f773ce 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 Mon Jan 30 18:32:09 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 <hap9@epoch.ncsc.mil> >+ */ >+#include <linux/module.h> >+#include <linux/init.h> >+#include <linux/pci.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; >+ >+ return 0; >+} >+ >+arch_initcall(pcifront_x86_stub_init); >diff -r 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 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@epoch.ncsc.mil> >+ */ >+ >+#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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,97 @@ >+/* >+ * PCI Backend - Common data structures for overriding the configuration space >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+ >+#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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,270 @@ >+/* >+ * PCI Backend - Handles the virtual fields in the configuration space headers. >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+ >+#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; >+ 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, >+ }, >+}; >+ >+#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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,117 @@ >+/* >+ * PCI Backend - Provides restricted access to the real PCI bus topology >+ * to the frontend >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+ >+#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; >+ >+ domain = (unsigned int)pci_domain_nr(dev_entry->dev->bus); >+ bus = (unsigned int)dev_entry->dev->bus->number; >+ >+ 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, 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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,377 @@ >+/* >+ * PCI Stub Driver - Grabs devices in backend to be exported later >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+#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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,73 @@ >+/* >+ * PCI Backend Common Data Structures & Function Declarations >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+#ifndef __XEN_PCIBACK_H__ >+#define __XEN_PCIBACK_H__ >+ >+#include <linux/pci.h> >+#include <linux/interrupt.h> >+#include <asm-xen/xenbus.h> >+#include <linux/list.h> >+#include <linux/spinlock.h> >+#include <asm-xen/xen-public/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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,84 @@ >+/* >+ * PCI Backend Operations - respond to PCI requests from Frontend >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+#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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,163 @@ >+/* >+ * PCI Backend - Provides a Virtual PCI bus (with real devices) >+ * to the frontend >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+ >+#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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,435 @@ >+/* >+ * PCI Backend Xenbus Setup - handles setup with frontend and xend >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+#include <linux/module.h> >+#include <linux/init.h> >+#include <linux/list.h> >+#include <asm-xen/xenbus.h> >+#include <asm-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: 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 < 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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 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) >+EXTRA_CFLAGS += -DDEBUG >+endif >diff -r 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,98 @@ >+/* >+ * 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 <hap9@epoch.ncsc.mil> >+ */ >+#include <linux/module.h> >+#include <linux/init.h> >+#include <linux/pci.h> >+ >+/* 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; i < PCI_NUM_RESOURCES; i++) { >+ struct resource *r = &dev->resource[i]; >+ >+ if (!(mask & (1 << i))) >+ continue; >+ >+ if (r->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; >+} >+ >+#define CMD_DISABLE_BITS \ >+ (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER) >+void pcibios_disable_device(struct pci_dev *dev) >+{ >+ u16 cmd; >+ >+ pci_read_config_word(dev, PCI_COMMAND, &cmd); >+ >+ if (cmd & CMD_DISABLE_BITS) { >+ cmd &= ~CMD_DISABLE_BITS; >+ >+ 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 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,44 @@ >+/* >+ * PCI Frontend Operations - ensure only one PCI frontend runs at a time >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+#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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,224 @@ >+/* >+ * PCI Frontend Operations - Communicates with frontend >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+#include <linux/module.h> >+#include <linux/init.h> >+#include <linux/pci.h> >+#include <linux/spinlock.h> >+#include <asm-xen/evtchn.h> >+#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 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); >+ >+ /* 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 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,40 @@ >+/* >+ * PCI Frontend - Common data structures & function declarations >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+#ifndef __XEN_PCIFRONT_H__ >+#define __XEN_PCIFRONT_H__ >+ >+#include <linux/spinlock.h> >+#include <linux/pci.h> >+#include <asm-xen/xenbus.h> >+#include <asm-xen/xen-public/io/pciif.h> >+#include <asm-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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,295 @@ >+/* >+ * PCI Frontend Xenbus Setup - handles setup with backend (imports page/evtchn) >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+#include <linux/module.h> >+#include <linux/init.h> >+#include <linux/mm.h> >+#include <asm-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 2add7a262530 -r 4868d0f773ce 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 Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,39 @@ >+/* >+ * PCI Frontend - arch-dependendent declarations >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+#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 2add7a262530 -r 4868d0f773ce xen/include/public/io/pciif.h >--- /dev/null Fri Jan 27 15:17:38 2006 >+++ b/xen/include/public/io/pciif.h Mon Jan 30 18:32:09 2006 >@@ -0,0 +1,55 @@ >+/* >+ * PCI Backend/Frontend Common Data Structures & Macros >+ * >+ * Author: Ryan Wilson <hap9@epoch.ncsc.mil> >+ */ >+#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-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Dave Thompson \(davetho\)
2006-Feb-06 18:45 UTC
RE: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
Ryan, I found a bug in this patch. The function pciback_publish_pci_roots() added in the file linux-2.6-xen-sparse/drivers/xen/pciback/passthrough.c sets the local variables domain and bus using the dev_entry pointer which is uninitialized at this point. The two lines which set these variables should be moved down so that they are within the list_for_each_entry loop. FYI, Dave Thompson> -----Original Message----- > From: xen-devel-bounces@lists.xensource.com > [mailto:xen-devel-bounces@lists.xensource.com] On Behalf Of Ryan > Sent: Monday, January 30, 2006 7:24 AM > To: xen-devel > Subject: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI > Backend/Frontend > > This patch contains the PCI backend and frontend drivers for Linux > 2.6.12. There are a couple of compile-time options in the backend and > frontend although the defaults should be sufficient to get you up and > running. >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Ryan
2006-Feb-06 20:08 UTC
RE: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
Dave, Thanks for spotting that. I believe those were incorrectly moved when I was correcting some coding style issues. I''m preparing a new patch against xen-unstable (to include the recent Linux 2.6.16-rc2 import into the tree) which I hope to post in the next few days. I''ll include this fix in that patch. Ryan On Mon, 2006-02-06 at 13:45 -0500, Dave Thompson (davetho) wrote:> Ryan, > > I found a bug in this patch. The function pciback_publish_pci_roots() > added in the file linux-2.6-xen-sparse/drivers/xen/pciback/passthrough.c > sets the local variables domain and bus using the dev_entry pointer > which is uninitialized at this point. The two lines which set these > variables should be moved down so that they are within the > list_for_each_entry loop. > > FYI, > Dave Thompson > > > -----Original Message----- > > From: xen-devel-bounces@lists.xensource.com > > [mailto:xen-devel-bounces@lists.xensource.com] On Behalf Of Ryan > > Sent: Monday, January 30, 2006 7:24 AM > > To: xen-devel > > Subject: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI > > Backend/Frontend > > > > This patch contains the PCI backend and frontend drivers for Linux > > 2.6.12. There are a couple of compile-time options in the backend and > > frontend although the defaults should be sufficient to get you up and > > running. > > >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Ryan
2006-Feb-07 14:37 UTC
[Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
Resubmitting the driver domain patches against the latest xen-unstable. These should apply cleanly to 8772:55268b90a519. Thanks to Dave Thompson for pointing out a bug in the PCI backend. His fix is now included. Other than changing the location of some files to reflect the new sub-arch directory layout, the two significant changes for PCI driver domains and 2.6.16-rc2 are: 1) Need to call pci_bus_add_devices after creating the bus (previously, this used to be done for you as part of pci_scan_bus_parented). 2) arch/i386/pci/i386-xen.c needed to return. Calling pci_assign_unassigned_resources in a driver domain with the PCI frontend fails. The PCI backend restricts writing to the configuration space (it would be dangerous if we let the driver domain change the resource allocations in the BARs, see drivers/xen/pciback/conf_space_header.c for how this is done). When pci_assign_unassigned_resources tries to update the BARs, it will fail (and then the struct pci_dev''s resources will not reflect the real resources of the devices and things just won''t work). To fix this problem, I added a #ifdef around the call to pci_assign_unassigned_resources in i386-xen.c so that it only works if the PCI frontend is not compiled in. However, I''m not certain that this is the best solution so please let me know if you can think of a better way. Signed-off-by: Ryan Wilson <hap9@epoch.ncsc.mil> _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Keir Fraser
2006-Feb-07 15:19 UTC
Re: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
On 7 Feb 2006, at 14:37, Ryan wrote:> 2) arch/i386/pci/i386-xen.c needed to return. Calling > pci_assign_unassigned_resources in a driver domain with the PCI > frontend > fails. The PCI backend restricts writing to the configuration space (it > would be dangerous if we let the driver domain change the resource > allocations in the BARs, see drivers/xen/pciback/conf_space_header.c > for > how this is done). When pci_assign_unassigned_resources tries to update > the BARs, it will fail (and then the struct pci_dev''s resources will > not > reflect the real resources of the devices and things just won''t work). > To fix this problem, I added a #ifdef around the call to > pci_assign_unassigned_resources in i386-xen.c so that it only works if > the PCI frontend is not compiled in. However, I''m not certain that this > is the best solution so please let me know if you can think of a better > way.Isn''t absence of PCI_ASSIGN_ROMS supposed to mean that the OS tries to use the existing resource allocations? So, unless someone says ''pci=rom'' as a boot parameter I would have hoped that things would just work and the OS would not try shuffling resources around. If it is shuffling things, that''s a bit worrying. In any case, it is important that we continue to be able to build a single kernel image that works as both dom0 and domU. This means that you need to support both direct PCI access and virtualised PCI access *in the same kernel build*. Compile-time ifdef''s are right out: worst case you''ll have to test at runtime whether or not you are dom0. Perhaps this restriction will make it easier to pick between the raw-op and non-raw-op virtualised pci access methods? :-) -- Keir _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Ryan
2006-Feb-07 15:50 UTC
Re: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
On Tue, 2006-02-07 at 15:19 +0000, Keir Fraser wrote:> Isn''t absence of PCI_ASSIGN_ROMS supposed to mean that the OS tries to > use the existing resource allocations? So, unless someone says > ''pci=rom'' as a boot parameter I would have hoped that things would just > work and the OS would not try shuffling resources around. If it is > shuffling things, that''s a bit worrying.There was a change somewhere between 2.6.12 and 2.6.15 (I can''t remember exactly where now) where Linux now tries to reassign resources that it detects as unassigned. I think the behavior that you reference used to be true, but is different now. See pcibios_assign_resources in arch/i386/pci/i386.c. It is strange that it tries to shuffle things... I can''t seem to track down in the source the exact conditions under which Linux tries to reassign the BARs and doesn''t just use what the BIOS/Backend provided. pci_assign_unassigned_resources was never called in 2.6.12 (at least not on x86) so it was not an issue before. If someone else is more familiar with that code, please let me know, perhaps there''s a better workaround.> > In any case, it is important that we continue to be able to build a > single kernel image that works as both dom0 and domU. This means that > you need to support both direct PCI access and virtualised PCI access > *in the same kernel build*. Compile-time ifdef''s are right out: worst > case you''ll have to test at runtime whether or not you are dom0.I thought about testing at run-time for whether or not the PCI frontend was active, but I decided against it because I suspected that would involve creating some kind of global variable or globally-visible function. I don''t want to create a test for dom0 because that rules out putting the PCI backend in it''s own driver domain. Perhaps a global variable is a better workaround for now than a compile-time ifdef.> > Perhaps this restriction will make it easier to pick between the raw-op > and non-raw-op virtualised pci access methods? :-) >Perhaps if I understood why the -domU was trying to reallocate the PCI resources, maybe it would help make the decision for us. But I''m not sure that the choice between the two methods of operation for the PCI frontend is related to this problem with pci_assign_unassigned_resources. Ryan _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Ryan
2006-Feb-07 15:59 UTC
Re: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
> > > > Perhaps this restriction will make it easier to pick between the raw-op > > and non-raw-op virtualised pci access methods? :-) > > >Sorry, I misunderstood you the first time I read your statement above. Yes, I think the restriction of having one domain compile for both -dom0 and -domU will make the decision for us as the non-raw-op version (the arch-independent version) actually replaces the native PCI code. Ryan _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Keir Fraser
2006-Feb-07 17:57 UTC
Re: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
On 7 Feb 2006, at 15:59, Ryan wrote:>>> >>> Perhaps this restriction will make it easier to pick between the >>> raw-op >>> and non-raw-op virtualised pci access methods? :-) >>> >> > > Sorry, I misunderstood you the first time I read your statement above. > Yes, I think the restriction of having one domain compile for both > -dom0 > and -domU will make the decision for us as the non-raw-op version (the > arch-independent version) actually replaces the native PCI code.If we have to get ''grubby'' and do the raw-op version and modify arch-dep pci code, perhaps it makes sense to gate pci_assign_unassigned_resources on a global am-i-virtual flag. I expect we would add the virtual access ops as a fall-back pci access method (preferring direct hardware access if possible CONF1/CONF2/MMCONF etc). If we do fall back then we set the global flag and gate certain things. It certainly would be better to not need to gate things at all -- but clearly calling pci_assign_unassigned_resources can never help things in a virtualized-pci guest. :-) Will you be working support for dual-mode pci access support into your patches? I''d probably check them in now except for this current limitation. -- Keir _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Ryan
2006-Feb-07 19:58 UTC
Re: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
On Tue, 2006-02-07 at 17:57 +0000, Keir Fraser wrote:> > If we have to get ''grubby'' and do the raw-op version and modify > arch-dep pci code, perhaps it makes sense to gate > pci_assign_unassigned_resources on a global am-i-virtual flag. > > I expect we would add the virtual access ops as a fall-back pci access > method (preferring direct hardware access if possible > CONF1/CONF2/MMCONF etc). If we do fall back then we set the global flag > and gate certain things. > > It certainly would be better to not need to gate things at all -- but > clearly calling pci_assign_unassigned_resources can never help things > in a virtualized-pci guest. :-)I believe that I''ve found the correct solution to the resource issue with pci_assign_unassigned_resources and it doesn''t involve any kind of global variables. It turns out that pci_assign_unassigned_resources (and the functions it calls) iterate over all of the resources in a struct pci_dev and if they don''t have a parent resource, it considers them "unassigned". By forcibly claiming resources (using pci_claim_resource), the PCI frontend can mark all of those resources as assigned so the kernel doesn''t try to change them later in pci_assign_unassigned_resources.> > Will you be working support for dual-mode pci access support into your > patches? I''d probably check them in now except for this current > limitation.When you say "dual-mode", to you mean having the PCI frontend and backend in the same kernel? If so, yes. I tested it out with this patch and my dom0 kernel works fine in domU and dom0 with both the PCI frontend and the PCI backend compiled in. It prefers direct access to the PCI bus and falls back on the Xen pcifront stub if that fails. I''ve attached a new patch for the PCI frontend/backend that replaces patch 2 of 4 that I sent earlier today. The other patches sent today remain unchanged. One issue with this patch (at least the frontend part of it) is that it will probably only work correctly with x86 (I don''t have access to other architectures to test on) and the kconfig menu entries will appear regardless of the architecture (they don''t list x86 as a dependency). Should they list x86 as a dependency? Right now, they''re under the "Xen" menu with the other backends and frontends. Should I move the PCI frontend configuration option into the "Bus options/PCI Access" menu next to the mmconfig, direct, and bios options? I think it would be better placed under the "Bus options/PCI Access" menu but I wasn''t sure if all the backends and frontends needed to be listed together. Signed-off-by: Ryan Wilson <hap9@epoch.ncsc.mil> _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Keir Fraser
2006-Feb-07 23:17 UTC
Re: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
On 7 Feb 2006, at 19:58, Ryan wrote:> One issue with this patch (at least the frontend part of it) is that it > will probably only work correctly with x86 (I don''t have access to > other > architectures to test on) and the kconfig menu entries will appear > regardless of the architecture (they don''t list x86 as a dependency). > Should they list x86 as a dependency? Right now, they''re under the > "Xen" > menu with the other backends and frontends. Should I move the PCI > frontend configuration option into the "Bus options/PCI Access" menu > next to the mmconfig, direct, and bios options? I think it would be > better placed under the "Bus options/PCI Access" menu but I wasn''t sure > if all the backends and frontends needed to be listed together.By x86 you mean i386 and x86_64? I guess it makes sense to list X86 as a dependency for now. And yes, I think ''Bus Options/PCI Access'' is the correct place to place the pci frontend option. Maybe call the option ''Xen PCI channel'' or something like that? -- Keir _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Ryan
2006-Feb-08 17:36 UTC
Re: [Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
On Tue, 2006-02-07 at 23:17 +0000, Keir Fraser wrote:> On 7 Feb 2006, at 19:58, Ryan wrote: > > > One issue with this patch (at least the frontend part of it) is that it > > will probably only work correctly with x86 (I don''t have access to > > other > > architectures to test on) and the kconfig menu entries will appear > > regardless of the architecture (they don''t list x86 as a dependency). > > Should they list x86 as a dependency? Right now, they''re under the > > "Xen" > > menu with the other backends and frontends. Should I move the PCI > > frontend configuration option into the "Bus options/PCI Access" menu > > next to the mmconfig, direct, and bios options? I think it would be > > better placed under the "Bus options/PCI Access" menu but I wasn''t sure > > if all the backends and frontends needed to be listed together. > > By x86 you mean i386 and x86_64? I guess it makes sense to list X86 as > a dependency for now. And yes, I think ''Bus Options/PCI Access'' is the > correct place to place the pci frontend option. Maybe call the option > ''Xen PCI channel'' or something like that?I can only say i386 because that''s all I''ve tested, but I would think it should work for x86_64 unmodified (although I haven''t placed the PCI frontend options in the x86_64 kconfig file since I can''t test it). Here''s the updated patch with the PCI frontend configuration option in its new place. Signed-off-by: Ryan Wilson <hap9@epoch.ncsc.mil> _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Ryan
2006-Feb-16 21:56 UTC
[Xen-devel] [PATCH][2/4] PCI Driver Domains: PCI Backend/Frontend
Resubmitting the driver domain patches against the latest xen-unstable. These should apply cleanly to 8857:40d7eef7d3f5. This patch contains the PCI backend and frontend drivers for Linux 2.6.16-rc3. These patches have now been tested on x86_64 and this patch includes the configuration options for that architecture. Signed-off-by: Ryan Wilson <hap9@epoch.ncsc.mil> _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel