xenpci.c revision 255040
11638Srgrimes/* 21638Srgrimes * Copyright (c) 2008 Citrix Systems, Inc. 31638Srgrimes * All rights reserved. 41638Srgrimes * 51638Srgrimes * Redistribution and use in source and binary forms, with or without 61638Srgrimes * modification, are permitted provided that the following conditions 71638Srgrimes * are met: 81638Srgrimes * 1. Redistributions of source code must retain the above copyright 91638Srgrimes * notice, this list of conditions and the following disclaimer. 101638Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111638Srgrimes * notice, this list of conditions and the following disclaimer in the 121638Srgrimes * documentation and/or other materials provided with the distribution. 131638Srgrimes * 141638Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND 151638Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161638Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 171638Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 181638Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191638Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 201638Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211638Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221638Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231638Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241638Srgrimes * SUCH DAMAGE. 251638Srgrimes */ 261638Srgrimes 271638Srgrimes#include <sys/cdefs.h> 281638Srgrimes__FBSDID("$FreeBSD: head/sys/dev/xen/xenpci/xenpci.c 255040 2013-08-29 19:52:18Z gibbs $"); 291638Srgrimes 301638Srgrimes#include <sys/param.h> 311638Srgrimes#include <sys/bus.h> 321638Srgrimes#include <sys/kernel.h> 331638Srgrimes#include <sys/malloc.h> 341638Srgrimes#include <sys/module.h> 351638Srgrimes 361638Srgrimes#include <machine/bus.h> 371638Srgrimes#include <machine/resource.h> 381638Srgrimes#include <sys/rman.h> 391638Srgrimes 401638Srgrimes#include <machine/stdarg.h> 411638Srgrimes 421638Srgrimes#include <xen/xen-os.h> 431638Srgrimes#include <xen/features.h> 441638Srgrimes#include <xen/hypervisor.h> 451638Srgrimes#include <xen/hvm.h> 461638Srgrimes 471638Srgrimes#include <dev/pci/pcireg.h> 481638Srgrimes#include <dev/pci/pcivar.h> 491638Srgrimes 501638Srgrimes#include <dev/xen/xenpci/xenpcivar.h> 511638Srgrimes 521638Srgrimesextern void xen_intr_handle_upcall(struct trapframe *trap_frame); 531638Srgrimes 541638Srgrimesstatic device_t nexus; 551638Srgrimes 561638Srgrimes/* 571638Srgrimes * This is used to find our platform device instance. 581638Srgrimes */ 591638Srgrimesstatic devclass_t xenpci_devclass; 601638Srgrimes 611638Srgrimesstatic int 621638Srgrimesxenpci_intr_filter(void *trap_frame) 631638Srgrimes{ 641638Srgrimes xen_intr_handle_upcall(trap_frame); 651638Srgrimes return (FILTER_HANDLED); 661638Srgrimes} 671638Srgrimes 681638Srgrimesstatic int 691638Srgrimesxenpci_irq_init(device_t device, struct xenpci_softc *scp) 701638Srgrimes{ 711638Srgrimes int error; 721638Srgrimes 731638Srgrimes error = BUS_SETUP_INTR(device_get_parent(device), device, 74101089Smarkm scp->res_irq, INTR_MPSAFE|INTR_TYPE_MISC, 751638Srgrimes xenpci_intr_filter, NULL, /*trap_frame*/NULL, 761638Srgrimes &scp->intr_cookie); 771638Srgrimes if (error) 781638Srgrimes return error; 791638Srgrimes 801638Srgrimes /* 811638Srgrimes * When using the PCI event delivery callback we cannot assign 821638Srgrimes * events to specific vCPUs, so all events are delivered to vCPU#0 by 831638Srgrimes * Xen. Since the PCI interrupt can fire on any CPU by default, we 841638Srgrimes * need to bind it to vCPU#0 in order to ensure that 851638Srgrimes * xen_intr_handle_upcall always gets called on vCPU#0. 861638Srgrimes */ 871638Srgrimes error = BUS_BIND_INTR(device_get_parent(device), device, 881638Srgrimes scp->res_irq, 0); 89101089Smarkm if (error) 901638Srgrimes return error; 911638Srgrimes 921638Srgrimes xen_hvm_set_callback(device); 931638Srgrimes return (0); 941638Srgrimes} 951638Srgrimes 961638Srgrimes/* 971638Srgrimes * Deallocate anything allocated by xenpci_allocate_resources. 981638Srgrimes */ 991638Srgrimesstatic int 1001638Srgrimesxenpci_deallocate_resources(device_t dev) 1011638Srgrimes{ 1021638Srgrimes struct xenpci_softc *scp = device_get_softc(dev); 1031638Srgrimes 1041638Srgrimes if (scp->res_irq != 0) { 1051638Srgrimes bus_deactivate_resource(dev, SYS_RES_IRQ, 1061638Srgrimes scp->rid_irq, scp->res_irq); 1071638Srgrimes bus_release_resource(dev, SYS_RES_IRQ, 1081638Srgrimes scp->rid_irq, scp->res_irq); 1091638Srgrimes scp->res_irq = 0; 1101638Srgrimes } 1111638Srgrimes if (scp->res_memory != 0) { 1121638Srgrimes bus_deactivate_resource(dev, SYS_RES_MEMORY, 1131638Srgrimes scp->rid_memory, scp->res_memory); 1141638Srgrimes bus_release_resource(dev, SYS_RES_MEMORY, 1151638Srgrimes scp->rid_memory, scp->res_memory); 1161638Srgrimes scp->res_memory = 0; 1171638Srgrimes } 1181638Srgrimes 1191638Srgrimes return (0); 1201638Srgrimes} 1211638Srgrimes 1221638Srgrimes/* 1231638Srgrimes * Allocate irq and memory resources. 1241638Srgrimes */ 1251638Srgrimesstatic int 1261638Srgrimesxenpci_allocate_resources(device_t dev) 1271638Srgrimes{ 1281638Srgrimes struct xenpci_softc *scp = device_get_softc(dev); 1291638Srgrimes 1301638Srgrimes scp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 1311638Srgrimes &scp->rid_irq, RF_SHAREABLE|RF_ACTIVE); 1321638Srgrimes if (scp->res_irq == NULL) { 1331638Srgrimes printf("xenpci Could not allocate irq.\n"); 1341638Srgrimes goto errexit; 1351638Srgrimes } 1361638Srgrimes 1371638Srgrimes scp->rid_memory = PCIR_BAR(1); 1381638Srgrimes scp->res_memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 1391638Srgrimes &scp->rid_memory, RF_ACTIVE); 1401638Srgrimes if (scp->res_memory == NULL) { 1411638Srgrimes printf("xenpci Could not allocate memory bar.\n"); 1421638Srgrimes goto errexit; 1431638Srgrimes } 1441638Srgrimes 145101089Smarkm scp->phys_next = rman_get_start(scp->res_memory); 1461638Srgrimes 1471638Srgrimes return (0); 1481638Srgrimes 1491638Srgrimeserrexit: 1501638Srgrimes /* Cleanup anything we may have assigned. */ 1511638Srgrimes xenpci_deallocate_resources(dev); 1521638Srgrimes return (ENXIO); /* For want of a better idea. */ 1531638Srgrimes} 1541638Srgrimes 1551638Srgrimes/* 1561638Srgrimes * Allocate a physical address range from our mmio region. 1571638Srgrimes */ 1581638Srgrimesstatic int 1591638Srgrimesxenpci_alloc_space_int(struct xenpci_softc *scp, size_t sz, 1601638Srgrimes vm_paddr_t *pa) 1611638Srgrimes{ 1621638Srgrimes 1631638Srgrimes if (scp->phys_next + sz > rman_get_end(scp->res_memory)) { 1641638Srgrimes return (ENOMEM); 1651638Srgrimes } 166101089Smarkm 1671638Srgrimes *pa = scp->phys_next; 1681638Srgrimes scp->phys_next += sz; 1691638Srgrimes 1701638Srgrimes return (0); 1711638Srgrimes} 1721638Srgrimes 1731638Srgrimes/* 1741638Srgrimes * Allocate a physical address range from our mmio region. 1751638Srgrimes */ 1761638Srgrimesint 177101089Smarkmxenpci_alloc_space(size_t sz, vm_paddr_t *pa) 1781638Srgrimes{ 1791638Srgrimes device_t dev = devclass_get_device(xenpci_devclass, 0); 1801638Srgrimes 1811638Srgrimes if (dev) { 1821638Srgrimes return (xenpci_alloc_space_int(device_get_softc(dev), 1831638Srgrimes sz, pa)); 1841638Srgrimes } else { 1851638Srgrimes return (ENOMEM); 1861638Srgrimes } 1871638Srgrimes} 1881638Srgrimes 1891638Srgrimesstatic struct resource * 1901638Srgrimesxenpci_alloc_resource(device_t dev, device_t child, int type, int *rid, 1911638Srgrimes u_long start, u_long end, u_long count, u_int flags) 192101089Smarkm{ 1931638Srgrimes return (BUS_ALLOC_RESOURCE(nexus, child, type, rid, start, 1941638Srgrimes end, count, flags)); 1951638Srgrimes} 1961638Srgrimes 1971638Srgrimes 1981638Srgrimesstatic int 1991638Srgrimesxenpci_release_resource(device_t dev, device_t child, int type, int rid, 2001638Srgrimes struct resource *r) 2011638Srgrimes{ 2021638Srgrimes return (BUS_RELEASE_RESOURCE(nexus, child, type, rid, r)); 2031638Srgrimes} 2041638Srgrimes 2051638Srgrimesstatic int 2061638Srgrimesxenpci_activate_resource(device_t dev, device_t child, int type, int rid, 2071638Srgrimes struct resource *r) 2081638Srgrimes{ 209101089Smarkm return (BUS_ACTIVATE_RESOURCE(nexus, child, type, rid, r)); 2101638Srgrimes} 2111638Srgrimes 2121638Srgrimesstatic int 2131638Srgrimesxenpci_deactivate_resource(device_t dev, device_t child, int type, 2141638Srgrimes int rid, struct resource *r) 2151638Srgrimes{ 2161638Srgrimes return (BUS_DEACTIVATE_RESOURCE(nexus, child, type, rid, r)); 2171638Srgrimes} 2181638Srgrimes 2191638Srgrimes/* 2201638Srgrimes * Probe - just check device ID. 2211638Srgrimes */ 2221638Srgrimesstatic int 2231638Srgrimesxenpci_probe(device_t dev) 2241638Srgrimes{ 2251638Srgrimes 2261638Srgrimes if (pci_get_devid(dev) != 0x00015853) 2271638Srgrimes return (ENXIO); 2281638Srgrimes 2291638Srgrimes device_set_desc(dev, "Xen Platform Device"); 2301638Srgrimes return (bus_generic_probe(dev)); 2311638Srgrimes} 2321638Srgrimes 2331638Srgrimes/* 2341638Srgrimes * Attach - find resources and talk to Xen. 2351638Srgrimes */ 2361638Srgrimesstatic int 2371638Srgrimesxenpci_attach(device_t dev) 2381638Srgrimes{ 239101089Smarkm struct xenpci_softc *scp = device_get_softc(dev); 2401638Srgrimes devclass_t dc; 2411638Srgrimes int error; 2421638Srgrimes 2431638Srgrimes /* 2441638Srgrimes * Find and record nexus0. Since we are not really on the 2451638Srgrimes * PCI bus, all resource operations are directed to nexus 2461638Srgrimes * instead of through our parent. 2471638Srgrimes */ 2481638Srgrimes if ((dc = devclass_find("nexus")) == 0 249101089Smarkm || (nexus = devclass_get_device(dc, 0)) == 0) { 2501638Srgrimes device_printf(dev, "unable to find nexus."); 2511638Srgrimes return (ENOENT); 2521638Srgrimes } 2531638Srgrimes 2541638Srgrimes error = xenpci_allocate_resources(dev); 2551638Srgrimes if (error) { 256101089Smarkm device_printf(dev, "xenpci_allocate_resources failed(%d).\n", 2571638Srgrimes error); 2581638Srgrimes goto errexit; 2591638Srgrimes } 2601638Srgrimes 2611638Srgrimes /* 2621638Srgrimes * Hook the irq up to evtchn 2631638Srgrimes */ 2641638Srgrimes error = xenpci_irq_init(dev, scp); 2651638Srgrimes if (error) { 2661638Srgrimes device_printf(dev, "xenpci_irq_init failed(%d).\n", 2671638Srgrimes error); 2681638Srgrimes goto errexit; 2691638Srgrimes } 2701638Srgrimes 2711638Srgrimes return (bus_generic_attach(dev)); 272101089Smarkm 2731638Srgrimeserrexit: 2741638Srgrimes /* 2751638Srgrimes * Undo anything we may have done. 2761638Srgrimes */ 2771638Srgrimes xenpci_deallocate_resources(dev); 2781638Srgrimes return (error); 2791638Srgrimes} 2801638Srgrimes 2811638Srgrimes/* 2821638Srgrimes * Detach - reverse anything done by attach. 2831638Srgrimes */ 2841638Srgrimesstatic int 2851638Srgrimesxenpci_detach(device_t dev) 2861638Srgrimes{ 2871638Srgrimes struct xenpci_softc *scp = device_get_softc(dev); 2881638Srgrimes device_t parent = device_get_parent(dev); 2891638Srgrimes 2901638Srgrimes /* 2911638Srgrimes * Take our interrupt handler out of the list of handlers 2921638Srgrimes * that can handle this irq. 2931638Srgrimes */ 2941638Srgrimes if (scp->intr_cookie != NULL) { 295101089Smarkm if (BUS_TEARDOWN_INTR(parent, dev, 2961638Srgrimes scp->res_irq, scp->intr_cookie) != 0) 2971638Srgrimes device_printf(dev, 2981638Srgrimes "intr teardown failed.. continuing\n"); 2991638Srgrimes scp->intr_cookie = NULL; 3001638Srgrimes } 301101089Smarkm 3021638Srgrimes /* 3031638Srgrimes * Deallocate any system resources we may have 3041638Srgrimes * allocated on behalf of this driver. 3051638Srgrimes */ 3061638Srgrimes return (xenpci_deallocate_resources(dev)); 3071638Srgrimes} 3081638Srgrimes 309101089Smarkmstatic int 3101638Srgrimesxenpci_suspend(device_t dev) 3111638Srgrimes{ 3121638Srgrimes struct xenpci_softc *scp = device_get_softc(dev); 3131638Srgrimes device_t parent = device_get_parent(dev); 3141638Srgrimes 3151638Srgrimes if (scp->intr_cookie != NULL) { 3161638Srgrimes if (BUS_TEARDOWN_INTR(parent, dev, scp->res_irq, 3171638Srgrimes scp->intr_cookie) != 0) 3181638Srgrimes printf("intr teardown failed.. continuing\n"); 3191638Srgrimes scp->intr_cookie = NULL; 3201638Srgrimes } 3211638Srgrimes 3221638Srgrimes return (bus_generic_suspend(dev)); 3231638Srgrimes} 3241638Srgrimes 3251638Srgrimesstatic int 3261638Srgrimesxenpci_resume(device_t dev) 3271638Srgrimes{ 3281638Srgrimes struct xenpci_softc *scp = device_get_softc(dev); 3291638Srgrimes device_t parent = device_get_parent(dev); 3301638Srgrimes 3311638Srgrimes BUS_SETUP_INTR(parent, dev, scp->res_irq, 3321638Srgrimes INTR_MPSAFE|INTR_TYPE_MISC, xenpci_intr_filter, NULL, 3331638Srgrimes /*trap_frame*/NULL, &scp->intr_cookie); 3341638Srgrimes xen_hvm_set_callback(dev); 3351638Srgrimes return (bus_generic_resume(dev)); 3361638Srgrimes} 3371638Srgrimes 3381638Srgrimesstatic device_method_t xenpci_methods[] = { 3391638Srgrimes /* Device interface */ 3401638Srgrimes DEVMETHOD(device_probe, xenpci_probe), 3411638Srgrimes DEVMETHOD(device_attach, xenpci_attach), 3421638Srgrimes DEVMETHOD(device_detach, xenpci_detach), 3431638Srgrimes DEVMETHOD(device_suspend, xenpci_suspend), 344101089Smarkm DEVMETHOD(device_resume, xenpci_resume), 3451638Srgrimes 3461638Srgrimes /* Bus interface */ 3471638Srgrimes DEVMETHOD(bus_add_child, bus_generic_add_child), 3481638Srgrimes DEVMETHOD(bus_alloc_resource, xenpci_alloc_resource), 3491638Srgrimes DEVMETHOD(bus_release_resource, xenpci_release_resource), 3501638Srgrimes DEVMETHOD(bus_activate_resource, xenpci_activate_resource), 3511638Srgrimes DEVMETHOD(bus_deactivate_resource, xenpci_deactivate_resource), 3521638Srgrimes 3531638Srgrimes { 0, 0 } 3541638Srgrimes}; 3551638Srgrimes 3561638Srgrimesstatic driver_t xenpci_driver = { 3571638Srgrimes "xenpci", 3581638Srgrimes xenpci_methods, 3591638Srgrimes sizeof(struct xenpci_softc), 3601638Srgrimes}; 3611638Srgrimes 3621638SrgrimesDRIVER_MODULE(xenpci, pci, xenpci_driver, xenpci_devclass, 0, 0); 3631638Srgrimes