1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2008 Citrix Systems, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/kernel.h> 35 #include <sys/malloc.h> 36 #include <sys/module.h> 37 38 #include <machine/bus.h> 39 #include <machine/resource.h> 40 #include <sys/rman.h> 41 42 #include <machine/stdarg.h> 43 44 #include <xen/xen-os.h> 45 #include <xen/features.h> 46 #include <xen/hypervisor.h> 47 #include <xen/hvm.h> 48 #include <xen/xen_intr.h> 49 50 #include <dev/pci/pcireg.h> 51 #include <dev/pci/pcivar.h> 52 53 #include <dev/xen/xenpci/xenpcivar.h> 54 55 static int 56 xenpci_intr_filter(void *trap_frame) 57 { 58 xen_intr_handle_upcall(trap_frame); 59 return (FILTER_HANDLED); 60 } 61 62 static int 63 xenpci_irq_init(device_t device, struct xenpci_softc *scp) 64 { 65 int error; 66 67 error = BUS_SETUP_INTR(device_get_parent(device), device, 68 scp->res_irq, INTR_MPSAFE|INTR_TYPE_MISC, 69 xenpci_intr_filter, NULL, /*trap_frame*/NULL, 70 &scp->intr_cookie); 71 if (error) 72 return error; 73 74 #ifdef SMP 75 /* 76 * When using the PCI event delivery callback we cannot assign 77 * events to specific vCPUs, so all events are delivered to vCPU#0 by 78 * Xen. Since the PCI interrupt can fire on any CPU by default, we 79 * need to bind it to vCPU#0 in order to ensure that 80 * xen_intr_handle_upcall always gets called on vCPU#0. 81 */ 82 error = BUS_BIND_INTR(device_get_parent(device), device, 83 scp->res_irq, 0); 84 if (error) 85 return error; 86 #endif 87 88 xen_hvm_set_callback(device); 89 return (0); 90 } 91 92 /* 93 * Deallocate anything allocated by xenpci_allocate_resources. 94 */ 95 static int 96 xenpci_deallocate_resources(device_t dev) 97 { 98 struct xenpci_softc *scp = device_get_softc(dev); 99 100 if (scp->res_irq != 0) { 101 bus_deactivate_resource(dev, SYS_RES_IRQ, 102 scp->rid_irq, scp->res_irq); 103 bus_release_resource(dev, SYS_RES_IRQ, 104 scp->rid_irq, scp->res_irq); 105 scp->res_irq = 0; 106 } 107 108 return (0); 109 } 110 111 /* 112 * Allocate irq and memory resources. 113 */ 114 static int 115 xenpci_allocate_resources(device_t dev) 116 { 117 struct xenpci_softc *scp = device_get_softc(dev); 118 119 scp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 120 &scp->rid_irq, RF_SHAREABLE|RF_ACTIVE); 121 if (scp->res_irq == NULL) { 122 printf("xenpci Could not allocate irq.\n"); 123 goto errexit; 124 } 125 126 return (0); 127 128 errexit: 129 /* Cleanup anything we may have assigned. */ 130 xenpci_deallocate_resources(dev); 131 return (ENXIO); /* For want of a better idea. */ 132 } 133 134 /* 135 * Probe - just check device ID. 136 */ 137 static int 138 xenpci_probe(device_t dev) 139 { 140 141 if (pci_get_devid(dev) != 0x00015853) 142 return (ENXIO); 143 144 device_set_desc(dev, "Xen Platform Device"); 145 return (BUS_PROBE_DEFAULT); 146 } 147 148 /* 149 * Attach - find resources and talk to Xen. 150 */ 151 static int 152 xenpci_attach(device_t dev) 153 { 154 struct xenpci_softc *scp = device_get_softc(dev); 155 int error; 156 157 error = xenpci_allocate_resources(dev); 158 if (error) { 159 device_printf(dev, "xenpci_allocate_resources failed(%d).\n", 160 error); 161 goto errexit; 162 } 163 164 /* 165 * Hook the irq up to evtchn 166 */ 167 error = xenpci_irq_init(dev, scp); 168 if (error) { 169 device_printf(dev, "xenpci_irq_init failed(%d).\n", 170 error); 171 goto errexit; 172 } 173 174 return (0); 175 176 errexit: 177 /* 178 * Undo anything we may have done. 179 */ 180 xenpci_deallocate_resources(dev); 181 return (error); 182 } 183 184 /* 185 * Detach - reverse anything done by attach. 186 */ 187 static int 188 xenpci_detach(device_t dev) 189 { 190 struct xenpci_softc *scp = device_get_softc(dev); 191 device_t parent = device_get_parent(dev); 192 193 /* 194 * Take our interrupt handler out of the list of handlers 195 * that can handle this irq. 196 */ 197 if (scp->intr_cookie != NULL) { 198 if (BUS_TEARDOWN_INTR(parent, dev, 199 scp->res_irq, scp->intr_cookie) != 0) 200 device_printf(dev, 201 "intr teardown failed.. continuing\n"); 202 scp->intr_cookie = NULL; 203 } 204 205 /* 206 * Deallocate any system resources we may have 207 * allocated on behalf of this driver. 208 */ 209 return (xenpci_deallocate_resources(dev)); 210 } 211 212 static int 213 xenpci_resume(device_t dev) 214 { 215 xen_hvm_set_callback(dev); 216 return (0); 217 } 218 219 static device_method_t xenpci_methods[] = { 220 /* Device interface */ 221 DEVMETHOD(device_probe, xenpci_probe), 222 DEVMETHOD(device_attach, xenpci_attach), 223 DEVMETHOD(device_detach, xenpci_detach), 224 DEVMETHOD(device_resume, xenpci_resume), 225 { 0, 0 } 226 }; 227 228 static driver_t xenpci_driver = { 229 "xenpci", 230 xenpci_methods, 231 sizeof(struct xenpci_softc), 232 }; 233 234 DRIVER_MODULE(xenpci, pci, xenpci_driver, 0, 0); 235