xenpci.c revision 255040
1/* 2 * Copyright (c) 2008 Citrix Systems, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/dev/xen/xenpci/xenpci.c 255040 2013-08-29 19:52:18Z gibbs $"); 29 30#include <sys/param.h> 31#include <sys/bus.h> 32#include <sys/kernel.h> 33#include <sys/malloc.h> 34#include <sys/module.h> 35 36#include <machine/bus.h> 37#include <machine/resource.h> 38#include <sys/rman.h> 39 40#include <machine/stdarg.h> 41 42#include <xen/xen-os.h> 43#include <xen/features.h> 44#include <xen/hypervisor.h> 45#include <xen/hvm.h> 46 47#include <dev/pci/pcireg.h> 48#include <dev/pci/pcivar.h> 49 50#include <dev/xen/xenpci/xenpcivar.h> 51 52extern void xen_intr_handle_upcall(struct trapframe *trap_frame); 53 54static device_t nexus; 55 56/* 57 * This is used to find our platform device instance. 58 */ 59static devclass_t xenpci_devclass; 60 61static int 62xenpci_intr_filter(void *trap_frame) 63{ 64 xen_intr_handle_upcall(trap_frame); 65 return (FILTER_HANDLED); 66} 67 68static int 69xenpci_irq_init(device_t device, struct xenpci_softc *scp) 70{ 71 int error; 72 73 error = BUS_SETUP_INTR(device_get_parent(device), device, 74 scp->res_irq, INTR_MPSAFE|INTR_TYPE_MISC, 75 xenpci_intr_filter, NULL, /*trap_frame*/NULL, 76 &scp->intr_cookie); 77 if (error) 78 return error; 79 80 /* 81 * When using the PCI event delivery callback we cannot assign 82 * events to specific vCPUs, so all events are delivered to vCPU#0 by 83 * Xen. Since the PCI interrupt can fire on any CPU by default, we 84 * need to bind it to vCPU#0 in order to ensure that 85 * xen_intr_handle_upcall always gets called on vCPU#0. 86 */ 87 error = BUS_BIND_INTR(device_get_parent(device), device, 88 scp->res_irq, 0); 89 if (error) 90 return error; 91 92 xen_hvm_set_callback(device); 93 return (0); 94} 95 96/* 97 * Deallocate anything allocated by xenpci_allocate_resources. 98 */ 99static int 100xenpci_deallocate_resources(device_t dev) 101{ 102 struct xenpci_softc *scp = device_get_softc(dev); 103 104 if (scp->res_irq != 0) { 105 bus_deactivate_resource(dev, SYS_RES_IRQ, 106 scp->rid_irq, scp->res_irq); 107 bus_release_resource(dev, SYS_RES_IRQ, 108 scp->rid_irq, scp->res_irq); 109 scp->res_irq = 0; 110 } 111 if (scp->res_memory != 0) { 112 bus_deactivate_resource(dev, SYS_RES_MEMORY, 113 scp->rid_memory, scp->res_memory); 114 bus_release_resource(dev, SYS_RES_MEMORY, 115 scp->rid_memory, scp->res_memory); 116 scp->res_memory = 0; 117 } 118 119 return (0); 120} 121 122/* 123 * Allocate irq and memory resources. 124 */ 125static int 126xenpci_allocate_resources(device_t dev) 127{ 128 struct xenpci_softc *scp = device_get_softc(dev); 129 130 scp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 131 &scp->rid_irq, RF_SHAREABLE|RF_ACTIVE); 132 if (scp->res_irq == NULL) { 133 printf("xenpci Could not allocate irq.\n"); 134 goto errexit; 135 } 136 137 scp->rid_memory = PCIR_BAR(1); 138 scp->res_memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 139 &scp->rid_memory, RF_ACTIVE); 140 if (scp->res_memory == NULL) { 141 printf("xenpci Could not allocate memory bar.\n"); 142 goto errexit; 143 } 144 145 scp->phys_next = rman_get_start(scp->res_memory); 146 147 return (0); 148 149errexit: 150 /* Cleanup anything we may have assigned. */ 151 xenpci_deallocate_resources(dev); 152 return (ENXIO); /* For want of a better idea. */ 153} 154 155/* 156 * Allocate a physical address range from our mmio region. 157 */ 158static int 159xenpci_alloc_space_int(struct xenpci_softc *scp, size_t sz, 160 vm_paddr_t *pa) 161{ 162 163 if (scp->phys_next + sz > rman_get_end(scp->res_memory)) { 164 return (ENOMEM); 165 } 166 167 *pa = scp->phys_next; 168 scp->phys_next += sz; 169 170 return (0); 171} 172 173/* 174 * Allocate a physical address range from our mmio region. 175 */ 176int 177xenpci_alloc_space(size_t sz, vm_paddr_t *pa) 178{ 179 device_t dev = devclass_get_device(xenpci_devclass, 0); 180 181 if (dev) { 182 return (xenpci_alloc_space_int(device_get_softc(dev), 183 sz, pa)); 184 } else { 185 return (ENOMEM); 186 } 187} 188 189static struct resource * 190xenpci_alloc_resource(device_t dev, device_t child, int type, int *rid, 191 u_long start, u_long end, u_long count, u_int flags) 192{ 193 return (BUS_ALLOC_RESOURCE(nexus, child, type, rid, start, 194 end, count, flags)); 195} 196 197 198static int 199xenpci_release_resource(device_t dev, device_t child, int type, int rid, 200 struct resource *r) 201{ 202 return (BUS_RELEASE_RESOURCE(nexus, child, type, rid, r)); 203} 204 205static int 206xenpci_activate_resource(device_t dev, device_t child, int type, int rid, 207 struct resource *r) 208{ 209 return (BUS_ACTIVATE_RESOURCE(nexus, child, type, rid, r)); 210} 211 212static int 213xenpci_deactivate_resource(device_t dev, device_t child, int type, 214 int rid, struct resource *r) 215{ 216 return (BUS_DEACTIVATE_RESOURCE(nexus, child, type, rid, r)); 217} 218 219/* 220 * Probe - just check device ID. 221 */ 222static int 223xenpci_probe(device_t dev) 224{ 225 226 if (pci_get_devid(dev) != 0x00015853) 227 return (ENXIO); 228 229 device_set_desc(dev, "Xen Platform Device"); 230 return (bus_generic_probe(dev)); 231} 232 233/* 234 * Attach - find resources and talk to Xen. 235 */ 236static int 237xenpci_attach(device_t dev) 238{ 239 struct xenpci_softc *scp = device_get_softc(dev); 240 devclass_t dc; 241 int error; 242 243 /* 244 * Find and record nexus0. Since we are not really on the 245 * PCI bus, all resource operations are directed to nexus 246 * instead of through our parent. 247 */ 248 if ((dc = devclass_find("nexus")) == 0 249 || (nexus = devclass_get_device(dc, 0)) == 0) { 250 device_printf(dev, "unable to find nexus."); 251 return (ENOENT); 252 } 253 254 error = xenpci_allocate_resources(dev); 255 if (error) { 256 device_printf(dev, "xenpci_allocate_resources failed(%d).\n", 257 error); 258 goto errexit; 259 } 260 261 /* 262 * Hook the irq up to evtchn 263 */ 264 error = xenpci_irq_init(dev, scp); 265 if (error) { 266 device_printf(dev, "xenpci_irq_init failed(%d).\n", 267 error); 268 goto errexit; 269 } 270 271 return (bus_generic_attach(dev)); 272 273errexit: 274 /* 275 * Undo anything we may have done. 276 */ 277 xenpci_deallocate_resources(dev); 278 return (error); 279} 280 281/* 282 * Detach - reverse anything done by attach. 283 */ 284static int 285xenpci_detach(device_t dev) 286{ 287 struct xenpci_softc *scp = device_get_softc(dev); 288 device_t parent = device_get_parent(dev); 289 290 /* 291 * Take our interrupt handler out of the list of handlers 292 * that can handle this irq. 293 */ 294 if (scp->intr_cookie != NULL) { 295 if (BUS_TEARDOWN_INTR(parent, dev, 296 scp->res_irq, scp->intr_cookie) != 0) 297 device_printf(dev, 298 "intr teardown failed.. continuing\n"); 299 scp->intr_cookie = NULL; 300 } 301 302 /* 303 * Deallocate any system resources we may have 304 * allocated on behalf of this driver. 305 */ 306 return (xenpci_deallocate_resources(dev)); 307} 308 309static int 310xenpci_suspend(device_t dev) 311{ 312 struct xenpci_softc *scp = device_get_softc(dev); 313 device_t parent = device_get_parent(dev); 314 315 if (scp->intr_cookie != NULL) { 316 if (BUS_TEARDOWN_INTR(parent, dev, scp->res_irq, 317 scp->intr_cookie) != 0) 318 printf("intr teardown failed.. continuing\n"); 319 scp->intr_cookie = NULL; 320 } 321 322 return (bus_generic_suspend(dev)); 323} 324 325static int 326xenpci_resume(device_t dev) 327{ 328 struct xenpci_softc *scp = device_get_softc(dev); 329 device_t parent = device_get_parent(dev); 330 331 BUS_SETUP_INTR(parent, dev, scp->res_irq, 332 INTR_MPSAFE|INTR_TYPE_MISC, xenpci_intr_filter, NULL, 333 /*trap_frame*/NULL, &scp->intr_cookie); 334 xen_hvm_set_callback(dev); 335 return (bus_generic_resume(dev)); 336} 337 338static device_method_t xenpci_methods[] = { 339 /* Device interface */ 340 DEVMETHOD(device_probe, xenpci_probe), 341 DEVMETHOD(device_attach, xenpci_attach), 342 DEVMETHOD(device_detach, xenpci_detach), 343 DEVMETHOD(device_suspend, xenpci_suspend), 344 DEVMETHOD(device_resume, xenpci_resume), 345 346 /* Bus interface */ 347 DEVMETHOD(bus_add_child, bus_generic_add_child), 348 DEVMETHOD(bus_alloc_resource, xenpci_alloc_resource), 349 DEVMETHOD(bus_release_resource, xenpci_release_resource), 350 DEVMETHOD(bus_activate_resource, xenpci_activate_resource), 351 DEVMETHOD(bus_deactivate_resource, xenpci_deactivate_resource), 352 353 { 0, 0 } 354}; 355 356static driver_t xenpci_driver = { 357 "xenpci", 358 xenpci_methods, 359 sizeof(struct xenpci_softc), 360}; 361 362DRIVER_MODULE(xenpci, pci, xenpci_driver, xenpci_devclass, 0, 0); 363