Module Name: src
Committed By: snj
Date: Sat Oct 3 23:54:05 UTC 2009
Modified Files:
src/sys/arch/xen/conf [netbsd-5]: files.xen
src/sys/arch/xen/include [netbsd-5]: pci_machdep.h xen.h
src/sys/arch/xen/xen [netbsd-5]: hypervisor.c pci_intr_machdep.c
xen_machdep.c
Added Files:
src/sys/arch/xen/xen [netbsd-5]: pciback.c xpci_xenbus.c
Log Message:
Pull up following revision(s) (requested by bouyer in ticket #1054):
sys/arch/xen/conf/files.xen: revision 1.93
sys/arch/xen/include/pci_machdep.h: revision 1.11
sys/arch/xen/include/xen.h: revision 1.31
sys/arch/xen/xen/hypervisor.c: revision 1.44
sys/arch/xen/xen/pci_intr_machdep.c: revision 1.8
sys/arch/xen/xen/pciback.c: revision 1.1
sys/arch/xen/xen/xen_machdep.c: revision 1.5
sys/arch/xen/xen/xpci_xenbus.c: revision 1.1
Work in progress on PCI front-end/back-end support
front-end:
- add a xpci* at xenbus? which provides pci busses from the dom0
xpci provides support routines for PCI config space operations and
enumeration in xpci_xenbus.c
- hypervisor.c: do dom0-style PCI attach only ifdef DOM0OPS
- pci_intr_machdep.c: check line value only if DOM0OPS
back-end:
- add a pciback* at pci? device which takes precedences over all
other PCI devices (match return 500) and matches all devices passed
to pciback.hide option on boot command line.
It exports the PCI device informations to files in /kern/xen/pci/
- hypervisor.c: create /kern/xen earlier so pciback can create its
entries while PCI devices are probed
- xen_machdep.c: add handling for pciback.hide=
frontend is know working on Xen 3.1.x dom0 with ahc(4) and pciide(4)
devices. uhci(4) fail when trying to allocate a large contigous DMA
buffer.
backend is work in progress; support in xentools is not there yet.
To generate a diff of this commit:
cvs rdiff -u -r1.88.4.4 -r1.88.4.5 src/sys/arch/xen/conf/files.xen
cvs rdiff -u -r1.10 -r1.10.8.1 src/sys/arch/xen/include/pci_machdep.h
cvs rdiff -u -r1.30 -r1.30.4.1 src/sys/arch/xen/include/xen.h
cvs rdiff -u -r1.42.4.1 -r1.42.4.2 src/sys/arch/xen/xen/hypervisor.c
cvs rdiff -u -r1.7 -r1.7.6.1 src/sys/arch/xen/xen/pci_intr_machdep.c
cvs rdiff -u -r0 -r1.4.6.2 src/sys/arch/xen/xen/pciback.c
cvs rdiff -u -r1.4 -r1.4.8.1 src/sys/arch/xen/xen/xen_machdep.c
cvs rdiff -u -r0 -r1.2.6.2 src/sys/arch/xen/xen/xpci_xenbus.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/arch/xen/conf/files.xen
diff -u src/sys/arch/xen/conf/files.xen:1.88.4.4 src/sys/arch/xen/conf/files.xen:1.88.4.5
--- src/sys/arch/xen/conf/files.xen:1.88.4.4 Fri Jun 19 21:22:11 2009
+++ src/sys/arch/xen/conf/files.xen Sat Oct 3 23:54:04 2009
@@ -1,4 +1,4 @@
-# $NetBSD: files.xen,v 1.88.4.4 2009/06/19 21:22:11 snj Exp $
+# $NetBSD: files.xen,v 1.88.4.5 2009/10/03 23:54:04 snj Exp $
# NetBSD: files.x86,v 1.10 2003/10/08 17:30:00 bouyer Exp
# NetBSD: files.i386,v 1.254 2004/03/25 23:32:10 jmc Exp
@@ -67,7 +67,7 @@
file crypto/blowfish/arch/i386/bf_enc.S blowfish
file crypto/blowfish/arch/i386/bf_cbc.S blowfish
elifdef amd64
-file arch/amd64/amd64/busfunc.S dom0ops
+file arch/amd64/amd64/busfunc.S
file arch/amd64/amd64/cpufunc.S
file arch/amd64/amd64/cpu_in_cksum.S (inet | inet6) & cpu_in_cksum
file arch/amd64/amd64/db_disasm.c ddb
@@ -216,6 +216,11 @@
#file arch/xen/xen/xbd.c xbd | xbd_hypervisor | xbd_xenbus needs-flag
file arch/xen/xen/xbd.c xbd_hypervisor needs-flag
+# PCI frontend
+device xpci: pcibus
+attach xpci at xenbus with xpci_xenbus
+file arch/xen/xen/xpci_xenbus.c xpci_xenbus
+
# Non-Xen specific devices and options
include "dev/pckbport/files.pckbport"
@@ -228,6 +233,11 @@
include "dev/pci/files.agp"
file arch/xen/xen/pciide_machdep.c pciide_common
+device pciback {unit = -1}
+attach pciback at pci
+file arch/xen/xen/pciback.c pciback
+
+
# x86 specific PCI hardware
include "arch/x86/pci/files.pci"
@@ -381,7 +391,7 @@
file arch/xen/xen/xbdback.c dom0ops & !xen3
file arch/xen/xen/xennetback.c dom0ops & !xen3
file arch/xen/xen/pci_machdep.c hypervisor & pci & !xen3
-file arch/x86/pci/pci_machdep.c hypervisor & pci & xen3
+file arch/x86/pci/pci_machdep.c hypervisor & pci & xen3 & dom0ops
file arch/xen/xen/pci_intr_machdep.c hypervisor & pci
file arch/xen/xen/isa_machdep.c hypervisor & dom0ops
file arch/xen/xen/xenevt.c xenevt & dom0ops
Index: src/sys/arch/xen/include/pci_machdep.h
diff -u src/sys/arch/xen/include/pci_machdep.h:1.10 src/sys/arch/xen/include/pci_machdep.h:1.10.8.1
--- src/sys/arch/xen/include/pci_machdep.h:1.10 Fri May 30 16:22:51 2008
+++ src/sys/arch/xen/include/pci_machdep.h Sat Oct 3 23:54:05 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: pci_machdep.h,v 1.10 2008/05/30 16:22:51 cegger Exp $ */
+/* $NetBSD: pci_machdep.h,v 1.10.8.1 2009/10/03 23:54:05 snj Exp $ */
/*
* Copyright (c) 2006 Manuel Bouyer.
@@ -64,6 +64,8 @@
#include "opt_xen.h"
+struct pci_attach_args;
+
extern struct x86_bus_dma_tag pci_bus_dma_tag;
#ifdef _LP64
extern struct x86_bus_dma_tag pci_bus_dma64_tag;
@@ -85,6 +87,12 @@
typedef union x86_pci_tag_u pcitag_t;
+#ifndef DOM0OPS
+int xpci_enumerate_bus(struct pci_softc *, const int *,
+ int (*)(struct pci_attach_args *), struct pci_attach_args *);
+#define PCI_MACHDEP_ENUMERATE_BUS xpci_enumerate_bus
+#endif
+
#else /* XEN3 */
extern uint32_t pci_bus_attached[];
Index: src/sys/arch/xen/include/xen.h
diff -u src/sys/arch/xen/include/xen.h:1.30 src/sys/arch/xen/include/xen.h:1.30.4.1
--- src/sys/arch/xen/include/xen.h:1.30 Tue Oct 21 15:46:32 2008
+++ src/sys/arch/xen/include/xen.h Sat Oct 3 23:54:05 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: xen.h,v 1.30 2008/10/21 15:46:32 cegger Exp $ */
+/* $NetBSD: xen.h,v 1.30.4.1 2009/10/03 23:54:05 snj Exp $ */
/*
*
@@ -44,12 +44,14 @@
char xcp_bootdev[16]; /* sizeof(dv_xname) */
struct xen_netinfo xcp_netinfo;
char xcp_console[16];
+ char xcp_pcidevs[64];
};
#define XEN_PARSE_BOOTDEV 0
#define XEN_PARSE_NETINFO 1
#define XEN_PARSE_CONSOLE 2
#define XEN_PARSE_BOOTFLAGS 3
+#define XEN_PARSE_PCIBACK 4
void xen_parse_cmdline(int, union xen_cmdline_parseinfo *);
Index: src/sys/arch/xen/xen/hypervisor.c
diff -u src/sys/arch/xen/xen/hypervisor.c:1.42.4.1 src/sys/arch/xen/xen/hypervisor.c:1.42.4.2
--- src/sys/arch/xen/xen/hypervisor.c:1.42.4.1 Thu Jan 22 20:17:13 2009
+++ src/sys/arch/xen/xen/hypervisor.c Sat Oct 3 23:54:05 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: hypervisor.c,v 1.42.4.1 2009/01/22 20:17:13 snj Exp $ */
+/* $NetBSD: hypervisor.c,v 1.42.4.2 2009/10/03 23:54:05 snj Exp $ */
/*
* Copyright (c) 2005 Manuel Bouyer.
@@ -63,7 +63,7 @@
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: hypervisor.c,v 1.42.4.1 2009/01/22 20:17:13 snj Exp $");
+__KERNEL_RCSID(0, "$NetBSD: hypervisor.c,v 1.42.4.2 2009/10/03 23:54:05 snj Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -246,6 +246,11 @@
xen_version & 0x0000ffff);
xengnt_init();
+#ifdef DOM0OPS
+ if (xendomain_is_privileged()) {
+ xenkernfs_init();
+ }
+#endif
memset(&hac.hac_vcaa, 0, sizeof(hac.hac_vcaa));
hac.hac_vcaa.vcaa_name = "vcpu";
@@ -281,6 +286,7 @@
#endif
#if NPCI > 0
#ifdef XEN3
+#ifdef DOM0OPS
#if NACPI > 0
if (acpi_present) {
hac.hac_acpi.aa_iot = X86_BUS_SPACE_IO;
@@ -320,6 +326,7 @@
if (mp_verbose)
acpi_pci_link_state();
#endif
+#endif /* DOM0OPS */
#else /* !XEN3 */
physdev_op.cmd = PHYSDEVOP_PCI_PROBE_ROOT_BUSES;
if ((i = HYPERVISOR_physdev_op(&physdev_op)) < 0) {
@@ -371,7 +378,6 @@
#ifdef DOM0OPS
if (xendomain_is_privileged()) {
- xenkernfs_init();
xenprivcmd_init();
xen_shm_init();
#ifndef XEN3
Index: src/sys/arch/xen/xen/pci_intr_machdep.c
diff -u src/sys/arch/xen/xen/pci_intr_machdep.c:1.7 src/sys/arch/xen/xen/pci_intr_machdep.c:1.7.6.1
--- src/sys/arch/xen/xen/pci_intr_machdep.c:1.7 Thu Jul 3 15:44:19 2008
+++ src/sys/arch/xen/xen/pci_intr_machdep.c Sat Oct 3 23:54:05 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: pci_intr_machdep.c,v 1.7 2008/07/03 15:44:19 drochner Exp $ */
+/* $NetBSD: pci_intr_machdep.c,v 1.7.6.1 2009/10/03 23:54:05 snj Exp $ */
/*
* Copyright (c) 2005 Manuel Bouyer.
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pci_intr_machdep.c,v 1.7 2008/07/03 15:44:19 drochner Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pci_intr_machdep.c,v 1.7.6.1 2009/10/03 23:54:05 snj Exp $");
#include <sys/types.h>
#include <sys/param.h>
@@ -126,10 +126,12 @@
goto bad;
}
#ifdef XEN3
+#ifdef DOM0OPS
if (line >= NUM_LEGACY_IRQS) {
printf("pci_intr_map: bad interrupt line %d\n", line);
goto bad;
}
+#endif
if (line == 2) {
printf("pci_intr_map: changed line 2 to line 9\n");
line = 9;
Index: src/sys/arch/xen/xen/xen_machdep.c
diff -u src/sys/arch/xen/xen/xen_machdep.c:1.4 src/sys/arch/xen/xen/xen_machdep.c:1.4.8.1
--- src/sys/arch/xen/xen/xen_machdep.c:1.4 Sat May 10 16:27:57 2008
+++ src/sys/arch/xen/xen/xen_machdep.c Sat Oct 3 23:54:05 2009
@@ -1,4 +1,4 @@
-/* $NetBSD: xen_machdep.c,v 1.4 2008/05/10 16:27:57 ad Exp $ */
+/* $NetBSD: xen_machdep.c,v 1.4.8.1 2009/10/03 23:54:05 snj Exp $ */
/*
* Copyright (c) 2006 Manuel Bouyer.
@@ -63,7 +63,7 @@
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xen_machdep.c,v 1.4 2008/05/10 16:27:57 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xen_machdep.c,v 1.4.8.1 2009/10/03 23:54:05 snj Exp $");
#include "opt_xen.h"
@@ -196,6 +196,11 @@
}
}
break;
+ case XEN_PARSE_PCIBACK:
+ if (strncasecmp(opt, "pciback.hide=", 13) == 0)
+ strncpy(xcp->xcp_pcidevs, opt + 13,
+ sizeof(xcp->xcp_pcidevs));
+ break;
}
if (cmd_line)
Added files:
Index: src/sys/arch/xen/xen/pciback.c
diff -u /dev/null src/sys/arch/xen/xen/pciback.c:1.4.6.2
--- /dev/null Sat Oct 3 23:54:05 2009
+++ src/sys/arch/xen/xen/pciback.c Sat Oct 3 23:54:05 2009
@@ -0,0 +1,787 @@
+/* $NetBSD: pciback.c,v 1.4.6.2 2009/10/03 23:54:05 snj Exp $ */
+
+/*
+ * Copyright (c) 2009 Manuel Bouyer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: pciback.c,v 1.4.6.2 2009/10/03 23:54:05 snj Exp $");
+
+#include "opt_xen.h"
+#include "rnd.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/queue.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <machine/bus_private.h>
+
+#include <dev/isa/isareg.h>
+
+#include <xen/hypervisor.h>
+#include <xen/evtchn.h>
+#include <xen/granttables.h>
+#include <xen/xen3-public/io/pciif.h>
+#include <xen/xenbus.h>
+
+#include <sys/stat.h>
+#include <sys/dirent.h>
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/kernfs/kernfs.h>
+#include <xen/kernfs_machdep.h>
+
+#include "locators.h"
+
+#include <dev/pci/pcivar.h>
+#include <machine/i82093var.h>
+
+struct pciback_pci_softc;
+struct pb_xenbus_instance;
+/* list of devices we handle */
+struct pciback_pci_dev {
+ SLIST_ENTRY(pciback_pci_dev) pb_devlist_next; /* global list of pbd */
+ SLIST_ENTRY(pciback_pci_dev) pb_guest_next; /* per-guest list of pbd */
+ u_int pb_bus; /* our location */
+ u_int pb_device;
+ u_int pb_function;
+ pci_chipset_tag_t pb_pc;
+ pcitag_t pb_tag;
+ struct pciback_pci_softc *pb_pci_softc;
+ struct pb_xenbus_instance *pbx_instance;
+};
+
+/* list of devices we want to match */
+SLIST_HEAD(pciback_pci_devlist, pciback_pci_dev) pciback_pci_devlist_head =
+ SLIST_HEAD_INITIALIZER(pciback_pci_devlist_head);
+
+/* PCI-related functions and definitions */
+
+#define PCI_NBARS ((PCI_MAPREG_END - PCI_MAPREG_START) / 4)
+
+struct pciback_pci_softc {
+ device_t sc_dev;
+ void *sc_ih; /* our interrupt; */
+ struct pciback_pci_dev *sc_pb; /* our location */
+ struct pci_bar {
+ bus_space_tag_t b_t;
+ bus_space_handle_t b_h;
+ bus_addr_t b_addr;
+ bus_size_t b_size;
+ int b_type;
+ int b_valid;
+ } sc_bars[PCI_NBARS];
+ pci_intr_handle_t sc_intrhandle;
+ int sc_irq;
+ char sc_kernfsname[16];
+};
+
+int pciback_pci_match(device_t, cfdata_t, void *);
+void pciback_pci_attach(device_t, device_t, void *);
+static struct pciback_pci_dev* pciback_pci_lookup(u_int, u_int, u_int);
+static void pciback_pci_init(void);
+
+static int pciback_parse_pci(const char *, u_int *, u_int *, u_int *);
+
+/* kernfs-related functions and definitions */
+
+static kernfs_parentdir_t *pciback_kern_pkt;
+static int pciback_kernfs_read(void *);
+
+#define DIR_MODE (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+#define FILE_MODE (S_IRUSR)
+static const struct kernfs_fileop pciback_dev_fileops[] = {
+ { .kf_fileop = KERNFS_FILEOP_READ, .kf_vop = pciback_kernfs_read },
+};
+
+/* xenbus-related functions and definitions */
+
+static int pciback_xenbus_create(struct xenbus_device *);
+static int pciback_xenbus_destroy(void *);
+static void pciback_xenbus_frontend_changed(void *, XenbusState);
+static struct pb_xenbus_instance * pbxif_lookup(domid_t);
+static void pciback_xenbus_export_device(struct pb_xenbus_instance *, char *);
+static void pciback_xenbus_export_roots(struct pb_xenbus_instance *);
+
+static int pciback_xenbus_evthandler(void *);
+
+/* emulate byte and word access to PCI config space */
+static inline u_int8_t
+pciback_read_byte(pci_chipset_tag_t pc, pcitag_t pa, int reg)
+{
+
+ return (pci_conf_read(pc, pa, (reg & ~0x03)) >>
+ ((reg & 0x03) * 8) & 0xff);
+}
+
+static inline u_int16_t
+pciback_read_word(pci_chipset_tag_t pc, pcitag_t pa, int reg)
+{
+ return (pci_conf_read(pc, pa, (reg & ~0x03)) >>
+ ((reg & 0x03) * 8) & 0xffff);
+}
+
+static inline void
+pciback_write_byte(pci_chipset_tag_t pc, pcitag_t pa, int reg, uint8_t val)
+{
+ pcireg_t pcival;
+
+ pcival = pci_conf_read(pc, pa, (reg & ~0x03));
+ pcival &= ~(0xff << ((reg & 0x03) * 8));
+ pcival |= (val << ((reg & 0x03) * 8));
+ pci_conf_write(pc, pa, (reg & ~0x03), pcival);
+}
+
+static inline void
+pciback_write_word(pci_chipset_tag_t pc, pcitag_t pa, int reg, uint16_t val)
+{
+ pcireg_t pcival;
+
+ pcival = pci_conf_read(pc, pa, (reg & ~0x03));
+ pcival &= ~(0xffff << ((reg & 0x03) * 8));
+ pcival |= (val << ((reg & 0x03) * 8));
+ pci_conf_write(pc, pa, (reg & ~0x03), pcival);
+}
+
+
+
+CFATTACH_DECL_NEW(pciback, sizeof(struct pciback_pci_softc),
+ pciback_pci_match, pciback_pci_attach, NULL, NULL);
+
+static int pciback_pci_inited = 0;
+
+/* a xenbus PCI backend instance */
+struct pb_xenbus_instance {
+ SLIST_ENTRY(pb_xenbus_instance) pbx_next; /* list of backend instances*/
+ struct xenbus_device *pbx_xbusd;
+ domid_t pbx_domid;
+ struct pciback_pci_devlist pbx_pb_pci_dev; /* list of exported PCi devices */
+ /* communication with the domU */
+ unsigned int pbx_evtchn; /* our even channel */
+ struct xen_pci_sharedinfo *pbx_sh_info;
+ grant_handle_t pbx_shinfo_handle; /* to unmap shared page */
+};
+
+SLIST_HEAD(, pb_xenbus_instance) pb_xenbus_instances;
+
+static struct xenbus_backend_driver pci_backend_driver = {
+ .xbakd_create = pciback_xenbus_create,
+ .xbakd_type = "pci"
+};
+
+int
+pciback_pci_match(device_t parent, cfdata_t match, void *aux)
+{
+ struct pci_attach_args *pa = aux;
+ if (pciback_pci_inited == 0) {
+ pciback_pci_init();
+ pciback_pci_inited = 1;
+ }
+ if (pciback_pci_lookup(pa->pa_bus, pa->pa_device, pa->pa_function))
+ return 500; /* we really want to take over anything else */
+ return 0;
+}
+
+void
+pciback_pci_attach(device_t parent, device_t self, void *aux)
+{
+ struct pci_attach_args *pa = aux;
+ struct pciback_pci_softc *sc = device_private(self);
+ char devinfo[256];
+ int i;
+ const char *intrstr;
+ kernfs_entry_t *dkt;
+ kfstype kfst;
+ pcireg_t reg;
+
+ sc->sc_dev = self;
+ sc->sc_pb = pciback_pci_lookup(pa->pa_bus, pa->pa_device, pa->pa_function);
+ if (sc->sc_pb == NULL)
+ panic("pciback_pci_attach: pciback_lookup");
+ pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo, sizeof(devinfo));
+ aprint_normal(": %s (rev. 0x%02x)\n", devinfo,
+ PCI_REVISION(pa->pa_class));
+ sc->sc_pb->pb_pci_softc = sc;
+ sc->sc_pb->pb_pc = pa->pa_pc;
+ sc->sc_pb->pb_tag = pa->pa_tag;
+
+ for (i = 0; i < PCI_NBARS;) {
+ sc->sc_bars[i].b_type = pci_mapreg_type(pa->pa_pc, pa->pa_tag,
+ PCI_MAPREG_START + i * 4);
+ if (pci_mapreg_map(pa, PCI_MAPREG_START + i * 4,
+ sc->sc_bars[i].b_type, 0,
+ &sc->sc_bars[i].b_t, &sc->sc_bars[i].b_h,
+ &sc->sc_bars[i].b_addr, &sc->sc_bars[i].b_size) == 0)
+ sc->sc_bars[i].b_valid = 1;
+ if (sc->sc_bars[i].b_valid) {
+ aprint_verbose_dev(self, "%s: 0x%08jx - 0x%08jx\n",
+ (sc->sc_bars[i].b_type == PCI_MAPREG_TYPE_IO) ?
+ "I/O" : "mem",
+ (uintmax_t)sc->sc_bars[i].b_addr,
+ (uintmax_t)sc->sc_bars[i].b_size);
+ }
+
+ if (sc->sc_bars[i].b_type ==
+ (PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT))
+ i += 2;
+ else
+ i += 1;
+ }
+ /* map the irq so interrupt routing is done */
+ if (pci_intr_map(pa, &sc->sc_intrhandle) != 0) {
+ aprint_error_dev(self, "couldn't map interrupt\n");
+ } else {
+ intrstr = pci_intr_string(pa->pa_pc, sc->sc_intrhandle);
+ aprint_normal_dev(self, "interrupting at %s\n",
+ intrstr ? intrstr : "unknown interrupt");
+ }
+ if (sc->sc_intrhandle.pirq & APIC_INT_VIA_APIC) {
+ sc->sc_irq = APIC_IRQ_PIN(sc->sc_intrhandle.pirq);
+ } else {
+ sc->sc_irq = APIC_IRQ_LEGACY_IRQ(sc->sc_intrhandle.pirq);
+ }
+ /* XXX should be done elsewhere ? */
+ reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_INTERRUPT_REG);
+ reg &= ~ (PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT);
+ reg |= (sc->sc_irq << PCI_INTERRUPT_LINE_SHIFT);
+ pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_INTERRUPT_REG, reg);
+ printf("irq line %d pin %d sc %p\n",
+ PCI_INTERRUPT_LINE(pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_INTERRUPT_REG)),
+ PCI_INTERRUPT_PIN(pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_INTERRUPT_REG)), sc);
+ /*
+ * don't establish the interrupt, we're not interested in
+ * getting it here.
+ */
+ /* publish the informations about this device to /kern/xen/pci */
+ snprintf(sc->sc_kernfsname, sizeof(sc->sc_kernfsname),
+ "0000:%02x:%02x.%x", pa->pa_bus, pa->pa_device, pa->pa_function);
+ kfst = KERNFS_ALLOCTYPE(pciback_dev_fileops);
+ KERNFS_ALLOCENTRY(dkt, M_TEMP, M_WAITOK);
+ KERNFS_INITENTRY(dkt, DT_REG, sc->sc_kernfsname, sc,
+ kfst, VREG, FILE_MODE);
+ kernfs_addentry(pciback_kern_pkt, dkt);
+}
+
+static int
+pciback_kernfs_read(void *v)
+{
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap = v;
+ struct kernfs_node *kfs = VTOKERN(ap->a_vp);
+ struct uio *uio = ap->a_uio;
+ struct pciback_pci_softc *sc = kfs->kfs_kt->kt_data;
+#define PCIBACK_KERNFS_SIZE 512
+ static char buf[PCIBACK_KERNFS_SIZE];
+ off_t off;
+ off_t len;
+ int error, i;
+
+ off = uio->uio_offset;
+ len = 0;
+ for(i = 0; i < PCI_NBARS; i++) {
+ if (sc->sc_bars[i].b_valid) {
+ len += snprintf(&buf[len], PCIBACK_KERNFS_SIZE - len,
+ "%s: 0x%08jx - 0x%08jx\n",
+ (sc->sc_bars[i].b_type == PCI_MAPREG_TYPE_IO) ?
+ "I/O" : "mem",
+ (uintmax_t)sc->sc_bars[i].b_addr,
+ (uintmax_t)(sc->sc_bars[i].b_addr + sc->sc_bars[i].b_size));
+ }
+ }
+ len += snprintf(&buf[len], PCIBACK_KERNFS_SIZE - len,
+ "irq: %d\n", sc->sc_irq);
+ if (off >= len) {
+ error = uiomove(buf, 0, uio);
+ } else {
+ error = uiomove(&buf[off], len - off, uio);
+ }
+ return error;
+}
+
+static struct pciback_pci_dev*
+pciback_pci_lookup(u_int bus, u_int dev, u_int func)
+{
+ struct pciback_pci_dev *pbd;
+ SLIST_FOREACH(pbd, &pciback_pci_devlist_head, pb_devlist_next) {
+ if (pbd->pb_bus == bus &&
+ pbd->pb_device == dev &&
+ pbd->pb_function == func)
+ return pbd;
+ }
+ return NULL;
+}
+
+static void
+pciback_pci_init(void)
+{
+ union xen_cmdline_parseinfo xi;
+ char *pcidevs, *c;
+ u_int bus, dev, func;
+ struct pciback_pci_dev *pb;
+ kernfs_entry_t *dkt;
+
+ memset(&xi, 0, sizeof(xi));
+ xen_parse_cmdline(XEN_PARSE_PCIBACK, &xi);
+ if (strlen(xi.xcp_pcidevs) == 0)
+ return;
+ pcidevs = xi.xcp_pcidevs;
+ for(pcidevs = xi.xcp_pcidevs; *pcidevs != '\0';) {
+ if (*pcidevs != '(')
+ goto error;
+ pcidevs++;
+ /* parse location */
+ c = strchr(pcidevs, ')');
+ if (c == NULL)
+ goto error;
+ *c = '\0';
+ if (pciback_parse_pci(pcidevs, &bus, &dev, &func) == 0) {
+ pb = malloc(sizeof(struct pciback_pci_dev), M_DEVBUF,
+ M_NOWAIT | M_ZERO);
+ if (pb == NULL) {
+ aprint_error("pciback_pci_init: out or memory\n");
+ return;
+ }
+ pb->pb_bus = bus;
+ pb->pb_device = dev;
+ pb->pb_function = func;
+ aprint_verbose("pciback_pci_init: hide claim device "
+ "%x:%x:%x\n", bus, dev, func);
+ SLIST_INSERT_HEAD(&pciback_pci_devlist_head, pb,
+ pb_devlist_next);
+ }
+ pcidevs = c + 1;
+ }
+ xenbus_backend_register(&pci_backend_driver);
+
+ KERNFS_ALLOCENTRY(dkt, M_TEMP, M_WAITOK);
+ KERNFS_INITENTRY(dkt, DT_DIR, "pci", NULL, KFSsubdir, VDIR, DIR_MODE);
+ kernfs_addentry(kernxen_pkt, dkt);
+ pciback_kern_pkt = KERNFS_ENTOPARENTDIR(dkt);
+ return;
+error:
+ aprint_error("pciback_pci_init: syntax error at %s\n", pcidevs);
+ return;
+}
+
+static int
+pciback_parse_pci(const char *str, u_int *busp, u_int *devp, u_int *funcp)
+{
+ char *c;
+
+ /* parse location */
+ c = strchr(str, ':');
+ if (c == NULL)
+ goto error;
+ if (strncmp("0000", str, 4) == 0) {
+ /* must be domain number, get next */
+ str = c + 1;
+ }
+ *busp = strtoul(str, &c, 16);
+ if (*c != ':')
+ goto error;
+ str = c + 1;
+ *devp = strtoul(str, &c, 16);
+ if (*c != '.')
+ goto error;
+ str = c + 1;
+ *funcp = strtoul(str, &c, 16);
+ if (*c != '\0')
+ goto error;
+ return 0;
+error:
+ aprint_error("pciback_pci_init: syntax error at char %c\n", *c);
+ return EINVAL;
+}
+
+static int
+pciback_xenbus_create(struct xenbus_device *xbusd)
+{
+ struct pb_xenbus_instance *pbxi;
+ long domid;
+ char *val;
+ char path[10];
+ int i, err;
+ u_long num_devs;
+
+ if ((err = xenbus_read_ul(NULL, xbusd->xbusd_path,
+ "frontend-id", &domid, 10)) != 0) {
+ aprint_error("pciback: can' read %s/frontend-id: %d\n",
+ xbusd->xbusd_path, err);
+ return err;
+ }
+
+ if (pbxif_lookup(domid) != NULL) {
+ return EEXIST;
+ }
+ pbxi = malloc(sizeof(struct pb_xenbus_instance), M_DEVBUF,
+ M_NOWAIT | M_ZERO);
+ if (pbxi == NULL) {
+ return ENOMEM;
+ }
+ pbxi->pbx_domid = domid;
+
+ xbusd->xbusd_u.b.b_cookie = pbxi;
+ xbusd->xbusd_u.b.b_detach = pciback_xenbus_destroy;
+ pbxi->pbx_xbusd = xbusd;
+
+ SLIST_INIT(&pbxi->pbx_pb_pci_dev);
+
+ SLIST_INSERT_HEAD(&pb_xenbus_instances, pbxi, pbx_next);
+
+ xbusd->xbusd_otherend_changed = pciback_xenbus_frontend_changed;
+
+ err = xenbus_switch_state(xbusd, NULL, XenbusStateInitWait);
+ if (err) {
+ printf("failed to switch state on %s: %d\n",
+ xbusd->xbusd_path, err);
+ goto fail;
+ }
+ if ((err = xenbus_read_ul(NULL, xbusd->xbusd_path, "num_devs",
+ &num_devs, 10)) != 0) {
+ aprint_error("pciback: can' read %s/num_devs %d\n",
+ xbusd->xbusd_path, err);
+ goto fail;
+ }
+ for (i = 0; i < num_devs; i++) {
+ snprintf(path, sizeof(path), "dev-%d", i);
+ if ((err = xenbus_read(NULL, xbusd->xbusd_path, path,
+ NULL, &val)) != 0) {
+ aprint_error("pciback: can' read %s/%s: %d\n",
+ xbusd->xbusd_path, path, err);
+ goto fail;
+ }
+ pciback_xenbus_export_device(pbxi, val);
+ free(val, M_DEVBUF);
+ }
+ pciback_xenbus_export_roots(pbxi);
+ if ((err = xenbus_switch_state(xbusd, NULL, XenbusStateInitialised))) {
+ printf("failed to switch state on %s: %d\n",
+ xbusd->xbusd_path, err);
+ goto fail;
+ }
+
+ return 0;
+fail:
+ free(pbxi, M_DEVBUF);
+ return err;
+}
+
+static int
+pciback_xenbus_destroy(void *arg)
+{
+ struct pb_xenbus_instance *pbxi = arg;
+ struct pciback_pci_dev *pbd;
+ struct gnttab_unmap_grant_ref op;
+ int err;
+
+ hypervisor_mask_event(pbxi->pbx_evtchn);
+ event_remove_handler(pbxi->pbx_evtchn,
+ pciback_xenbus_evthandler, pbxi);
+
+ SLIST_REMOVE(&pb_xenbus_instances,
+ pbxi, pb_xenbus_instance, pbx_next);
+
+ if (pbxi->pbx_sh_info) {
+ op.host_addr = (vaddr_t)pbxi->pbx_sh_info;
+ op.handle = pbxi->pbx_shinfo_handle;
+ op.dev_bus_addr = 0;
+ err = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref,
+ &op, 1);
+ if (err)
+ aprint_error("pciback: unmap_grant_ref failed: %d\n",
+ err);
+ }
+ SLIST_FOREACH(pbd, &pbxi->pbx_pb_pci_dev, pb_guest_next) {
+ pbd->pbx_instance = NULL;
+ }
+ uvm_km_free(kernel_map, (vaddr_t)pbxi->pbx_sh_info,
+ PAGE_SIZE, UVM_KMF_VAONLY);
+ free(pbxi, M_DEVBUF);
+ return 0;
+}
+
+static void
+pciback_xenbus_frontend_changed(void *arg, XenbusState new_state)
+{
+ struct pb_xenbus_instance *pbxi = arg;
+ struct xenbus_device *xbusd = pbxi->pbx_xbusd;
+ int err;
+ struct gnttab_map_grant_ref op;
+ evtchn_op_t evop;
+ u_long shared_ref;
+ u_long revtchn;
+
+ /* do it only once */
+ if (xenbus_read_driver_state(xbusd->xbusd_path) !=
+ XenbusStateInitialised)
+ return;
+
+ switch(new_state) {
+ case XenbusStateInitialising:
+ case XenbusStateConnected:
+ break;
+ case XenbusStateInitialised:
+ /* read comunication informations */
+ err = xenbus_read_ul(NULL, xbusd->xbusd_otherend,
+ "pci-op-ref", &shared_ref, 10);
+ if (err) {
+ xenbus_dev_fatal(xbusd, err, "reading %s/pci-op-ref",
+ xbusd->xbusd_otherend);
+ break;
+ }
+ err = xenbus_read_ul(NULL, xbusd->xbusd_otherend,
+ "event-channel", &revtchn, 10);
+ if (err) {
+ xenbus_dev_fatal(xbusd, err, "reading %s/event-channel",
+ xbusd->xbusd_otherend);
+ break;
+ }
+ /* allocate VA space and map rings */
+ pbxi->pbx_sh_info = (void *)uvm_km_alloc(kernel_map,
+ PAGE_SIZE, 0, UVM_KMF_VAONLY);
+ if (pbxi->pbx_sh_info == 0) {
+ xenbus_dev_fatal(xbusd, ENOMEM,
+ "can't get VA for shared infos",
+ xbusd->xbusd_otherend);
+ break;
+ }
+ op.host_addr = (vaddr_t)pbxi->pbx_sh_info;
+ op.flags = GNTMAP_host_map;
+ op.ref = shared_ref;
+ op.dom = pbxi->pbx_domid;
+ err = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1);
+ if (err || op.status) {
+ aprint_error("pciback: can't map shared grant ref: "
+ "%d/%d\n", err, op.status);
+ goto err1;
+ }
+ pbxi->pbx_shinfo_handle = op.handle;
+
+ evop.cmd = EVTCHNOP_bind_interdomain;
+ evop.u.bind_interdomain.remote_dom = pbxi->pbx_domid;
+ evop.u.bind_interdomain.remote_port = revtchn;
+ err = HYPERVISOR_event_channel_op(&evop);
+ if (err) {
+ printf("pciback: can't get event channel: %d\n", err);
+ goto err1;
+ }
+ pbxi->pbx_evtchn = evop.u.bind_interdomain.local_port;
+ x86_sfence();
+ xenbus_switch_state(xbusd, NULL, XenbusStateConnected);
+ x86_sfence();
+ event_set_handler(pbxi->pbx_evtchn, pciback_xenbus_evthandler,
+ pbxi, IPL_BIO, "pciback");
+ hypervisor_enable_event(pbxi->pbx_evtchn);
+ hypervisor_notify_via_evtchn(pbxi->pbx_evtchn);
+ break;
+
+ case XenbusStateClosing:
+ xenbus_switch_state(xbusd, NULL, XenbusStateClosing);
+ break;
+
+ case XenbusStateClosed:
+ /* otherend_changed() should handle it for us */
+ panic("pciback_xenbus_frontend_changed: closed\n");
+ case XenbusStateUnknown:
+ case XenbusStateInitWait:
+ default:
+ aprint_error("pciback: invalid frontend state %d\n",
+ new_state);
+ break;
+ }
+ return;
+err1:
+ uvm_km_free(kernel_map, (vaddr_t)pbxi->pbx_sh_info,
+ PAGE_SIZE, UVM_KMF_VAONLY);
+}
+
+/* lookup a pbxi based on domain id and interface handle */
+static struct pb_xenbus_instance *
+pbxif_lookup(domid_t dom)
+{
+ struct pb_xenbus_instance *pbxi;
+
+ SLIST_FOREACH(pbxi, &pb_xenbus_instances, pbx_next) {
+ if (pbxi->pbx_domid == dom)
+ return pbxi;
+ }
+ return NULL;
+}
+
+static void
+pciback_xenbus_export_device(struct pb_xenbus_instance *pbxi, char *val)
+{
+ u_int bus, dev, func;
+ struct pciback_pci_dev *pbd;
+ if (pciback_parse_pci(val, &bus, &dev, &func)) {
+ aprint_error("pciback: can't parse %s\n", val);
+ return;
+ }
+ pbd = pciback_pci_lookup(bus, dev, func);
+ if (pbd == NULL) {
+ aprint_error("pciback: can't locate 0x%02x:0x%02x.0x%02x\n",
+ bus, dev, func);
+ return;
+ }
+ if (pbd->pb_pci_softc == NULL) {
+ aprint_error("pciback: 0x%02x:0x%02x.0x%02x not detected\n",
+ bus, dev, func);
+ return;
+ }
+ pbd->pbx_instance = pbxi;
+ SLIST_INSERT_HEAD(&pbxi->pbx_pb_pci_dev, pbd, pb_guest_next);
+ return;
+}
+
+static void
+pciback_xenbus_export_roots(struct pb_xenbus_instance *pbxi)
+{
+ char bus[256];
+ char root[10];
+ struct pciback_pci_dev *pbd;
+ int num_roots = 0;
+ int err;
+
+ memset(bus, 0, sizeof(bus));
+
+ SLIST_FOREACH(pbd, &pbxi->pbx_pb_pci_dev, pb_guest_next) {
+ if (bus[pbd->pb_bus] == 0) {
+ /* not published yet */
+ snprintf(root, sizeof(root), "root-%d", num_roots);
+ err = xenbus_printf(NULL,
+ pbxi->pbx_xbusd->xbusd_path,
+ root, "0000:%02x", pbd->pb_bus);
+ if (err) {
+ aprint_error("pciback: can't write to %s/%s: "
+ "%d\n", pbxi->pbx_xbusd->xbusd_path, root,
+ err);
+ }
+ num_roots++;
+ }
+ }
+ err = xenbus_printf(NULL, pbxi->pbx_xbusd->xbusd_path, "root_num",
+ "%d", num_roots);
+ if (err) {
+ aprint_error("pciback: can't write to %s/root_num: "
+ "%d\n", pbxi->pbx_xbusd->xbusd_path, err);
+ }
+}
+
+static int
+pciback_xenbus_evthandler(void * arg)
+{
+ struct pb_xenbus_instance *pbxi = arg;
+ struct pciback_pci_dev *pbd;
+ struct xen_pci_op *op = &pbxi->pbx_sh_info->op;
+ u_int bus, dev, func;
+
+ hypervisor_clear_event(pbxi->pbx_evtchn);
+ if (xen_atomic_test_bit(&pbxi->pbx_sh_info->flags,
+ _XEN_PCIF_active) == 0)
+ return 0;
+ if (op->domain != 0) {
+ aprint_error("pciback: domain %d != 0", op->domain);
+ op->err = XEN_PCI_ERR_dev_not_found;
+ goto end;
+ }
+ bus = op->bus;
+ dev = (op->devfn >> 3) & 0xff;
+ func = (op->devfn) & 0x7;
+ SLIST_FOREACH(pbd, &pbxi->pbx_pb_pci_dev, pb_guest_next) {
+ if (pbd->pb_bus == bus &&
+ pbd->pb_device == dev &&
+ pbd->pb_function == func)
+ break;
+ }
+ if (pbd == NULL) {
+ aprint_error("pciback: %02x:%02x.%x not found\n",
+ bus, dev, func);
+ op->err = XEN_PCI_ERR_dev_not_found;
+ goto end;
+ }
+ switch(op->cmd) {
+ case XEN_PCI_OP_conf_read:
+ op->err = XEN_PCI_ERR_success;
+ switch (op->size) {
+ case 1:
+ op->value = pciback_read_byte(pbd->pb_pc, pbd->pb_tag,
+ op->offset);
+ break;
+ case 2:
+ op->value = pciback_read_word(pbd->pb_pc, pbd->pb_tag,
+ op->offset);
+ break;
+ case 4:
+ op->value = pci_conf_read(pbd->pb_pc, pbd->pb_tag,
+ op->offset);
+ break;
+ default:
+ aprint_error("pciback: bad size %d\n", op->size);
+ break;
+ }
+ break;
+ case XEN_PCI_OP_conf_write:
+ op->err = XEN_PCI_ERR_success;
+ switch(op->size) {
+ case 1:
+ pciback_write_byte(pbd->pb_pc, pbd->pb_tag,
+ op->offset, op->value);
+ break;
+ case 2:
+ pciback_write_word(pbd->pb_pc, pbd->pb_tag,
+ op->offset, op->value);
+ break;
+ case 4:
+ pci_conf_write(pbd->pb_pc, pbd->pb_tag,
+ op->offset, op->value);
+ break;
+ default:
+ aprint_error("pciback: bad size %d\n", op->size);
+ op->err = XEN_PCI_ERR_invalid_offset;
+ break;
+ }
+ default:
+ aprint_error("pciback: unknown cmd %d\n", op->cmd);
+ op->err = XEN_PCI_ERR_not_implemented;
+ }
+end:
+ xen_atomic_clear_bit(&pbxi->pbx_sh_info->flags, _XEN_PCIF_active);
+ hypervisor_notify_via_evtchn(pbxi->pbx_evtchn);
+ return 1;
+}
Index: src/sys/arch/xen/xen/xpci_xenbus.c
diff -u /dev/null src/sys/arch/xen/xen/xpci_xenbus.c:1.2.6.2
--- /dev/null Sat Oct 3 23:54:05 2009
+++ src/sys/arch/xen/xen/xpci_xenbus.c Sat Oct 3 23:54:05 2009
@@ -0,0 +1,708 @@
+/* $NetBSD: xpci_xenbus.c,v 1.2.6.2 2009/10/03 23:54:05 snj Exp $ */
+
+/*
+ * Copyright (c) 2009 Manuel Bouyer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: xpci_xenbus.c,v 1.2.6.2 2009/10/03 23:54:05 snj Exp $");
+
+#include "opt_xen.h"
+#include "rnd.h"
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <machine/bus_private.h>
+
+#include <dev/isa/isareg.h>
+
+#include <xen/hypervisor.h>
+#include <xen/evtchn.h>
+#include <xen/granttables.h>
+#include <xen/xen3-public/io/pciif.h>
+#include <xen/xenbus.h>
+
+#include "locators.h"
+
+#include <dev/pci/pcivar.h>
+
+#undef XPCI_DEBUG
+#ifdef XPCI_DEBUG
+#define DPRINTF(x) printf x;
+#else
+#define DPRINTF(x)
+#endif
+
+struct xpci_xenbus_softc {
+ device_t sc_dev;
+ struct xenbus_device *sc_xbusd;
+ unsigned int sc_evtchn;
+ int sc_backend_status; /* our status with backend */
+#define XPCI_STATE_DISCONNECTED 0
+#define XPCI_STATE_CONNECTED 1
+#define XPCI_STATE_SUSPENDED 2
+ int sc_shutdown;
+ struct xen_pci_sharedinfo *sc_shared;
+ grant_ref_t sc_shared_gntref;
+};
+#define GRANT_INVALID_REF -1
+
+static int xpci_xenbus_match(device_t, cfdata_t, void *);
+static void xpci_xenbus_attach(device_t, device_t, void *);
+static int xpci_xenbus_detach(device_t, int);
+
+static void xpci_backend_changed(void *, XenbusState);
+static int xpci_xenbus_resume(void *);
+static void xpci_connect(struct xpci_xenbus_softc *);
+static void xpci_attach_pcibus(int, int);
+
+static struct xpci_xenbus_softc *xpci_sc = NULL;
+
+CFATTACH_DECL_NEW(xpci_xenbus, sizeof(struct xpci_xenbus_softc),
+ xpci_xenbus_match, xpci_xenbus_attach, xpci_xenbus_detach, NULL);
+
+struct x86_bus_dma_tag pci_bus_dma_tag = {
+ 0, /* tag_needs_free */
+#if defined(_LP64) || defined(PAE)
+ PCI32_DMA_BOUNCE_THRESHOLD, /* bounce_thresh */
+ 0, /* bounce_alloclo */
+ PCI32_DMA_BOUNCE_THRESHOLD, /* bounce_allochi */
+#else
+ 0,
+ 0,
+ 0,
+#endif
+ NULL, /* _may_bounce */
+ _bus_dmamap_create,
+ _bus_dmamap_destroy,
+ _bus_dmamap_load,
+ _bus_dmamap_load_mbuf,
+ _bus_dmamap_load_uio,
+ _bus_dmamap_load_raw,
+ _bus_dmamap_unload,
+ _bus_dmamap_sync,
+ _bus_dmamem_alloc,
+ _bus_dmamem_free,
+ _bus_dmamem_map,
+ _bus_dmamem_unmap,
+ _bus_dmamem_mmap,
+ _bus_dmatag_subregion,
+ _bus_dmatag_destroy,
+};
+
+#ifdef _LP64
+struct x86_bus_dma_tag pci_bus_dma64_tag = {
+ 0, /* tag_needs_free */
+ 0,
+ 0,
+ 0,
+ NULL, /* _may_bounce */
+ _bus_dmamap_create,
+ _bus_dmamap_destroy,
+ _bus_dmamap_load,
+ _bus_dmamap_load_mbuf,
+ _bus_dmamap_load_uio,
+ _bus_dmamap_load_raw,
+ _bus_dmamap_unload,
+ NULL,
+ _bus_dmamem_alloc,
+ _bus_dmamem_free,
+ _bus_dmamem_map,
+ _bus_dmamem_unmap,
+ _bus_dmamem_mmap,
+ _bus_dmatag_subregion,
+ _bus_dmatag_destroy,
+};
+#endif
+
+static int
+xpci_xenbus_match(device_t parent, cfdata_t match, void *aux)
+{
+ struct xenbusdev_attach_args *xa = aux;
+
+ if (strcmp(xa->xa_type, "pci") != 0)
+ return 0;
+
+ if (match->cf_loc[XENBUSCF_ID] != XENBUSCF_ID_DEFAULT &&
+ match->cf_loc[XENBUSCF_ID] != xa->xa_id)
+ return 0;
+
+ return 1;
+}
+
+static void
+xpci_xenbus_attach(device_t parent, device_t self, void *aux)
+{
+ struct xpci_xenbus_softc *sc = device_private(self);
+ struct xenbusdev_attach_args *xa = aux;
+#ifdef XBD_DEBUG
+ char **dir, *val;
+ int dir_n = 0;
+ char id_str[20];
+ int err;
+#endif
+
+ if (xpci_sc != NULL) {
+ aprint_error("Xen PCI frontend already attached\n");
+ return;
+ }
+ xpci_sc = sc;
+ DPRINTF(("xpci_sc %p\n", xpci_sc));
+
+ config_pending_incr();
+ aprint_normal(": Xen PCI passthrough Interface\n");
+ sc->sc_dev = self;
+
+ sc->sc_xbusd = xa->xa_xbusd;
+ sc->sc_xbusd->xbusd_otherend_changed = xpci_backend_changed;
+
+ sc->sc_backend_status = XPCI_STATE_DISCONNECTED;
+ sc->sc_shutdown = 1;
+ /* initialise shared structures and tell backend that we are ready */
+ xpci_xenbus_resume(sc);
+}
+
+static int
+xpci_xenbus_detach(device_t dev, int flags)
+{
+ return EBUSY;
+}
+
+static int
+xpci_xenbus_resume(void *p)
+{
+ struct xpci_xenbus_softc *sc = p;
+ struct xenbus_transaction *xbt;
+ int error;
+ struct xen_pci_sharedinfo *shared;
+ paddr_t ma;
+ const char *errmsg;
+
+ sc->sc_shared_gntref = GRANT_INVALID_REF;
+ /* setup device: alloc event channel and shared info structure */
+ sc->sc_shared = shared = (void *)uvm_km_alloc(kernel_map, PAGE_SIZE, 0,
+ UVM_KMF_ZERO | UVM_KMF_WIRED);
+ if (shared == NULL)
+ panic("xpci_xenbus_resume: can't alloc shared info");
+
+ (void)pmap_extract_ma(pmap_kernel(), (vaddr_t)shared, &ma);
+ error = xenbus_grant_ring(sc->sc_xbusd, ma, &sc->sc_shared_gntref);
+ if (error)
+ return error;
+ DPRINTF(("shared %p ma 0x%jx ref 0x%u\n", shared, (uintmax_t)ma,
+ sc->sc_shared_gntref));
+ error = xenbus_alloc_evtchn(sc->sc_xbusd, &sc->sc_evtchn);
+ if (error)
+ return error;
+ aprint_verbose_dev(sc->sc_dev, "using event channel %d\n",
+ sc->sc_evtchn);
+#if 0
+ event_set_handler(sc->sc_evtchn, &xpci_handler, sc,
+ IPL_BIO, device_xname(sc->sc_dev));
+#endif
+
+again:
+ xbt = xenbus_transaction_start();
+ if (xbt == NULL)
+ return ENOMEM;
+ error = xenbus_printf(xbt, sc->sc_xbusd->xbusd_path,
+ "pci-op-ref","%u", sc->sc_shared_gntref);
+ if (error) {
+ errmsg = "writing pci-op-ref";
+ goto abort_transaction;
+ }
+ error = xenbus_printf(xbt, sc->sc_xbusd->xbusd_path,
+ "event-channel", "%u", sc->sc_evtchn);
+ if (error) {
+ errmsg = "writing event channel";
+ goto abort_transaction;
+ }
+ error = xenbus_printf(xbt, sc->sc_xbusd->xbusd_path,
+ "magic", "%s", XEN_PCI_MAGIC);
+ if (error) {
+ errmsg = "writing magic";
+ goto abort_transaction;
+ }
+ error = xenbus_switch_state(sc->sc_xbusd, xbt, XenbusStateInitialised);
+ if (error) {
+ errmsg = "writing frontend XenbusStateInitialised";
+ goto abort_transaction;
+ }
+ error = xenbus_transaction_end(xbt, 0);
+ if (error == EAGAIN)
+ goto again;
+ if (error) {
+ xenbus_dev_fatal(sc->sc_xbusd, error, "completing transaction");
+ return -1;
+ }
+ return 0;
+
+abort_transaction:
+ xenbus_transaction_end(xbt, 1);
+ xenbus_dev_fatal(sc->sc_xbusd, error, "%s", errmsg);
+ return error;
+}
+
+static void
+xpci_backend_changed(void *arg, XenbusState new_state)
+{
+ struct xpci_xenbus_softc *sc = device_private((device_t)arg);
+ int s;
+ DPRINTF(("%s: new backend state %d\n", device_xname(sc->sc_dev), new_state));
+
+ switch (new_state) {
+ case XenbusStateUnknown:
+ case XenbusStateInitialising:
+ case XenbusStateInitWait:
+ case XenbusStateInitialised:
+ break;
+ case XenbusStateClosing:
+ s = splbio();
+ sc->sc_shutdown = 1;
+ /* wait for requests to complete */
+#if 0
+ while (sc->sc_backend_status == XPCI_STATE_CONNECTED &&
+ sc->sc_dksc.sc_dkdev.dk_stats->io_busy > 0)
+ tsleep(xpci_xenbus_detach, PRIBIO, "xpcidetach",
+ hz/2);
+#endif
+ splx(s);
+ xenbus_switch_state(sc->sc_xbusd, NULL, XenbusStateClosed);
+ break;
+ case XenbusStateConnected:
+ /*
+ * note that xpci_backend_changed() can only be called by
+ * the xenbus thread.
+ */
+
+ if (sc->sc_backend_status == XPCI_STATE_CONNECTED)
+ /* already connected */
+ return;
+
+ sc->sc_shutdown = 0;
+ xpci_connect(sc);
+
+ sc->sc_backend_status = XPCI_STATE_CONNECTED;
+
+ /* the devices should be working now */
+ config_pending_decr();
+ break;
+ default:
+ panic("bad backend state %d", new_state);
+ }
+}
+
+static void
+xpci_connect(struct xpci_xenbus_softc *sc)
+{
+ u_long num_roots;
+ int err;
+ char *string;
+ char *domain, *bus, *ep;
+ char node[10];
+ u_long busn;
+ int i;
+ int s, oldcold;
+
+ err = xenbus_read_ul(NULL, sc->sc_xbusd->xbusd_otherend,
+ "root_num", &num_roots, 10);
+ if (err == ENOENT) {
+ aprint_error_dev(sc->sc_dev,
+ "No PCI Roots found, trying 0000:00\n");
+ s = splhigh();
+ xpci_attach_pcibus(0, 0);
+ splx(s);
+ xenbus_switch_state(sc->sc_xbusd, NULL, XenbusStateConnected);
+ return;
+ } else if (err) {
+ aprint_error_dev(sc->sc_dev, "can't read root_num: %d\n", err);
+ return;
+ }
+ aprint_verbose_dev(sc->sc_dev, "%lu bus%s\n", num_roots,
+ (num_roots > 1) ? "ses" : "");
+ for (i = 0; i < num_roots; i++) {
+ snprintf(node, sizeof(node), "root-%d", i);
+ xenbus_read(NULL, sc->sc_xbusd->xbusd_otherend, node,
+ NULL, &string);
+ /* split dddd:bb in 2 strings, a la strtok */
+ domain = string;
+ string[4] = '\0';
+ bus = &string[5];
+ if (strcmp(domain, "0000") != 0) {
+ aprint_error_dev(sc->sc_dev,
+ "non-zero PCI domain %s not supported\n", domain);
+ } else {
+ busn = strtoul(bus, &ep, 16);
+ if (*ep != '\0')
+ aprint_error_dev(sc->sc_dev,
+ "%s is not a number\n", bus);
+ else {
+ s = splhigh();
+ oldcold = cold;
+ cold = 1;
+ xpci_attach_pcibus(0, busn);
+ cold = oldcold;
+ splx(s);
+ }
+ }
+ free(string, M_DEVBUF);
+ }
+ xenbus_switch_state(sc->sc_xbusd, NULL, XenbusStateConnected);
+}
+
+static void
+xpci_attach_pcibus(int domain, int busn)
+{
+ struct pcibus_attach_args pba;
+
+ memset(&pba, 0, sizeof(struct pcibus_attach_args));
+ pba.pba_iot = X86_BUS_SPACE_IO;
+ pba.pba_memt = X86_BUS_SPACE_MEM;
+ pba.pba_dmat = &pci_bus_dma_tag;
+#ifdef _LP64
+ pba.pba_dmat64 = &pci_bus_dma64_tag;
+#else
+ pba.pba_dmat64 = NULL;
+#endif /* _LP64 */
+ pba.pba_flags = PCI_FLAGS_MEM_ENABLED | PCI_FLAGS_IO_ENABLED |
+ PCI_FLAGS_MRL_OKAY | PCI_FLAGS_MRM_OKAY | PCI_FLAGS_MWI_OKAY;
+ pba.pba_bridgetag = NULL;
+ pba.pba_bus = busn;
+ config_found_ia(xpci_sc->sc_dev, "pcibus", &pba, pcibusprint);
+}
+
+/* functions required by the MI PCI system */
+
+void
+pci_attach_hook(device_t parent, device_t self, struct pcibus_attach_args *pba)
+{
+ /* nothing */
+}
+
+int
+pci_bus_maxdevs(pci_chipset_tag_t pc, int busno)
+{
+ return (32);
+}
+
+pcitag_t
+pci_make_tag(pci_chipset_tag_t pc, int bus, int device, int function)
+{
+ pcitag_t tag;
+ KASSERT((function & ~0x7) == 0);
+ KASSERT((device & ~0x1f) == 0);
+ KASSERT((bus & ~0xff) == 0);
+ tag.mode1 = (bus << 8) | (device << 3) | (function << 0);
+ return tag;
+}
+
+void
+pci_decompose_tag(pci_chipset_tag_t pc, pcitag_t tag,
+ int *bp, int *dp, int *fp)
+{
+ if (bp != NULL)
+ *bp = (tag.mode1 >> 8) & 0xff;
+ if (dp != NULL)
+ *dp = (tag.mode1 >> 3) & 0x1f;
+ if (fp != NULL)
+ *fp = (tag.mode1 >> 0) & 0x7;
+ return;
+}
+
+static void
+xpci_do_op(struct xen_pci_op *op)
+{
+
+ struct xen_pci_op *active_op = &xpci_sc->sc_shared->op;
+ static __cpu_simple_lock_t pci_conf_lock = __SIMPLELOCK_UNLOCKED;
+ int s;
+
+ s = splhigh();
+ __cpu_simple_lock(&pci_conf_lock);
+ memcpy(active_op, op, sizeof(struct xen_pci_op));
+ x86_sfence();
+ xen_atomic_set_bit(&xpci_sc->sc_shared->flags, _XEN_PCIF_active);
+ hypervisor_notify_via_evtchn(xpci_sc->sc_evtchn);
+ while (xen_atomic_test_bit(&xpci_sc->sc_shared->flags,
+ _XEN_PCIF_active)) {
+ hypervisor_clear_event(xpci_sc->sc_evtchn);
+ /* HYPERVISOR_yield(); */
+ }
+ memcpy(op, active_op, sizeof(struct xen_pci_op));
+ __cpu_simple_unlock(&pci_conf_lock);
+ splx(s);
+}
+
+static int
+xpci_conf_read( pci_chipset_tag_t pc, pcitag_t tag, int reg, int size,
+ pcireg_t *value)
+{
+ int bus, dev, func;
+ struct xen_pci_op op = {
+ .cmd = XEN_PCI_OP_conf_read,
+ .domain = 0, /* XXX */
+ };
+ pci_decompose_tag(pc, tag, &bus, &dev, &func);
+ DPRINTF(("pci_conf_read %d:%d:%d reg 0x%x", bus, dev, func, reg));
+ op.bus = bus;
+ op.devfn = (dev << 3) | func;
+ op.offset = reg;
+ op.size = size;
+ xpci_do_op(&op);
+ *value = op.value;
+ DPRINTF((" val 0x%x err %d\n", *value, op.err));
+ return op.err;
+}
+
+pcireg_t
+pci_conf_read( pci_chipset_tag_t pc, pcitag_t tag, int reg)
+{
+ static pcireg_t value, v;
+ /*
+ * deal with linux stupidity: linux backend doesn't allow
+ * 4-byte read on some registers :(
+ */
+ switch(reg) {
+#if 0
+ case 0x0004:
+ xpci_conf_read(pc, tag, reg, 2, &v);
+ value = v;
+ xpci_conf_read(pc, tag, reg + 2, 2, &v);
+ value |= v << 16;
+ return value;
+ case 0x003c:
+ case 0x000c:
+ value = xpci_conf_read(pc, tag, reg, 1, &v);
+ value = v;
+ xpci_conf_read(pc, tag, reg + 1, 1, &v);
+ value |= v << 8;
+ xpci_conf_read(pc, tag, reg + 2, 1, &v);
+ value |= v << 16;
+ xpci_conf_read(pc, tag, reg + 3, 1, &v);
+ value |= v << 24;
+ return value;
+#endif
+ default:
+ xpci_conf_read(pc, tag, reg, 4, &v);
+ value = v;
+ return value;
+ }
+}
+
+static int
+xpci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, int size,
+ pcireg_t data)
+{
+ int bus, dev, func;
+ struct xen_pci_op op = {
+ .cmd = XEN_PCI_OP_conf_write,
+ .domain = 0, /* XXX */
+ };
+ pci_decompose_tag(pc, tag, &bus, &dev, &func);
+ DPRINTF(("pci_conf_write %d:%d:%d reg 0x%x val 0x%x", bus, dev, func, reg, data));
+ op.bus = bus;
+ op.devfn = (dev << 3) | func;
+ op.offset = reg;
+ op.size = 4;
+ op.value = data;
+ xpci_do_op(&op);
+ DPRINTF((" err %d\n", op.err));
+ return op.err;
+}
+
+void
+pci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data)
+{
+ /* as for read we have to work around linux limitations */
+ switch (reg) {
+#if 0
+ case 0x0004:
+ xpci_conf_write(pc, tag, reg , 2, data & 0xffff);
+ xpci_conf_write(pc, tag, reg + 2, 2, (data >> 16) & 0xffff);
+ return;
+ case 0x003c:
+ case 0x000c:
+ xpci_conf_write(pc, tag, reg , 1, data & 0xff);
+ xpci_conf_write(pc, tag, reg + 1, 1, (data >> 8) & 0xff);
+ xpci_conf_write(pc, tag, reg + 2, 1, (data >> 16) & 0xff);
+ xpci_conf_write(pc, tag, reg + 3, 1, (data >> 24) & 0xff);
+ return;
+#endif
+ default:
+ xpci_conf_write(pc, tag, reg, 4, data);
+ }
+}
+
+int
+xpci_enumerate_bus(struct pci_softc *sc, const int *locators,
+ int (*match)(struct pci_attach_args *), struct pci_attach_args *pap)
+{
+#if 0
+ char *string;
+ char *domain, *bus, *dev, *func, *ep;
+ u_long busn, devn, funcn;
+ char node[10];
+ u_long num_devs;
+ int i;
+ int err;
+ pcitag_t tag;
+ pci_chipset_tag_t pc = sc->sc_pc;
+
+ err = xenbus_read_ul(NULL, xpci_sc->sc_xbusd->xbusd_otherend,
+ "num_devs", &num_devs, 10);
+ if (err) {
+ aprint_error_dev(xpci_sc->sc_dev,
+ "can't read num_devs: %d\n", err);
+ return err;
+ }
+ for (i = 0; i < num_devs; i++) {
+ snprintf(node, sizeof(node), "dev-%d", i);
+ xenbus_read(NULL, xpci_sc->sc_xbusd->xbusd_otherend,
+ node, NULL, &string);
+ /* split dddd:bb:dd:ff in 4 strings, a la strtok */
+ domain = string;
+ string[4] = '\0';
+ bus = &string[5];
+ string[7] = '\0';
+ dev = &string[8];
+ string[10] = '\0';
+ func = &string[11];
+ if (strcmp(domain, "0000") != 0) {
+ aprint_error_dev(xpci_sc->sc_dev,
+ "non-zero PCI domain %s not supported\n", domain);
+ } else {
+ busn = strtoul(bus, &ep, 16);
+ if (*ep != '\0') {
+ aprint_error_dev(xpci_sc->sc_dev,
+ "%s is not a number\n", bus);
+ goto endfor;
+ }
+ devn = strtoul(dev, &ep, 16);
+ if (*ep != '\0') {
+ aprint_error_dev(xpci_sc->sc_dev,
+ "%s is not a number\n", dev);
+ goto endfor;
+ }
+ funcn = strtoul(func, &ep, 16);
+ if (*ep != '\0') {
+ aprint_error_dev(xpci_sc->sc_dev,
+ "%s is not a number\n", func);
+ goto endfor;
+ }
+ if (busn != sc->sc_bus)
+ goto endfor;
+ tag = pci_make_tag(pc, busn, devn, funcn);
+ err = pci_probe_device(sc, tag, match, pap);
+ if (match != NULL && err != 0)
+ return (err);
+ }
+endfor:
+ free(string, M_DEVBUF);
+ }
+ return (0);
+#else
+ int devn, funcn;
+ pcitag_t tag;
+ pci_chipset_tag_t pc = sc->sc_pc;
+ int err;
+ /*
+ * Xen is lacking an important info: the domain:bus:dev:func
+ * present in dev-xx in the store are real PCI bus:dev:func, and
+ * xenback may present us fake ones, and unfortunably it's not
+ * the list of devices is not published in the store with this
+ * information. So we have to scan all dev/func combination :(
+ * the MI scan function isn't enough because it doesn't search
+ * for functions >= 1 if function 0 is not there.
+ */
+ for (devn = 0; devn < 32; devn++) {
+ for (funcn = 0; funcn < 8; funcn++) {
+ pcireg_t csr, bar;
+ int reg;
+ tag = pci_make_tag(pc, sc->sc_bus, devn, funcn);
+ /* try a READ on device ID. if it fails, no device */
+ if (xpci_conf_read(pc, tag, PCI_ID_REG, 4, &csr) != 0)
+ continue;
+ /* check CSR. linux disable the device, sigh */
+ if (xpci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG, 4,
+ &csr) != 0) {
+ aprint_error(
+ "0x%2x:0x%2x:0x%2x failed to read CSR\n",
+ sc->sc_bus, devn, funcn);
+ continue;
+ }
+ if ((csr &
+ (PCI_COMMAND_IO_ENABLE|PCI_COMMAND_MEM_ENABLE))
+ == 0) {
+ /* need to enable the device */
+ for (reg = PCI_MAPREG_START;
+ reg < PCI_MAPREG_END;
+ reg += 4) {
+ if (xpci_conf_read(pc, tag, reg, 4,
+ &bar) != 0) {
+ aprint_error(
+ "0x%2x:0x%2x:0x%2x "
+ "failed to read 0x%x\n",
+ sc->sc_bus, devn, funcn,
+ reg);
+ goto next;
+
+ }
+ if (bar & PCI_MAPREG_TYPE_IO)
+ csr |= PCI_COMMAND_IO_ENABLE;
+ else if (bar)
+ csr |= PCI_COMMAND_MEM_ENABLE;
+ }
+ DPRINTF(("write CSR 0x%x\n", csr));
+ if (xpci_conf_write(pc, tag,
+ PCI_COMMAND_STATUS_REG, 4, csr)) {
+ aprint_error(
+ "0x%2x:0x%2x:0x%2x "
+ "failed to write CSR\n",
+ sc->sc_bus, devn, funcn);
+ goto next;
+ }
+ }
+ err = pci_probe_device(sc, tag, match, pap);
+ if (match != NULL && err != 0)
+ return (err);
+next:
+ continue;
+ }
+ }
+ return 0;
+#endif
+}