xenpci.c revision 214077
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 214077 2010-10-19 20:53:30Z 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#include <sys/proc.h> 36#include <sys/systm.h> 37#include <sys/time.h> 38 39#include <machine/bus.h> 40#include <machine/resource.h> 41#include <sys/rman.h> 42 43#include <machine/stdarg.h> 44#include <machine/xen/xen-os.h> 45#include <xen/features.h> 46#include <xen/hypervisor.h> 47#include <xen/gnttab.h> 48#include <xen/xen_intr.h> 49#include <xen/interface/memory.h> 50#include <xen/interface/hvm/params.h> 51 52#include <dev/pci/pcireg.h> 53#include <dev/pci/pcivar.h> 54 55#include <vm/vm.h> 56#include <vm/vm_extern.h> 57#include <vm/vm_kern.h> 58#include <vm/pmap.h> 59 60#include <dev/xen/xenpci/xenpcivar.h> 61 62/* 63 * These variables are used by the rest of the kernel to access the 64 * hypervisor. 65 */ 66char *hypercall_stubs; 67shared_info_t *HYPERVISOR_shared_info; 68static vm_paddr_t shared_info_pa; 69static device_t nexus; 70 71/* 72 * This is used to find our platform device instance. 73 */ 74static devclass_t xenpci_devclass; 75 76/* 77 * Return the CPUID base address for Xen functions. 78 */ 79static uint32_t 80xenpci_cpuid_base(void) 81{ 82 uint32_t base, regs[4]; 83 84 for (base = 0x40000000; base < 0x40010000; base += 0x100) { 85 do_cpuid(base, regs); 86 if (!memcmp("XenVMMXenVMM", ®s[1], 12) 87 && (regs[0] - base) >= 2) 88 return (base); 89 } 90 return (0); 91} 92 93/* 94 * Allocate and fill in the hypcall page. 95 */ 96static int 97xenpci_init_hypercall_stubs(device_t dev, struct xenpci_softc * scp) 98{ 99 uint32_t base, regs[4]; 100 int i; 101 102 base = xenpci_cpuid_base(); 103 if (!base) { 104 device_printf(dev, "Xen platform device but not Xen VMM\n"); 105 return (EINVAL); 106 } 107 108 if (bootverbose) { 109 do_cpuid(base + 1, regs); 110 device_printf(dev, "Xen version %d.%d.\n", 111 regs[0] >> 16, regs[0] & 0xffff); 112 } 113 114 /* 115 * Find the hypercall pages. 116 */ 117 do_cpuid(base + 2, regs); 118 119 hypercall_stubs = malloc(regs[0] * PAGE_SIZE, M_TEMP, M_WAITOK); 120 121 for (i = 0; i < regs[0]; i++) { 122 wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i); 123 } 124 125 return (0); 126} 127 128/* 129 * After a resume, re-initialise the hypercall page. 130 */ 131static void 132xenpci_resume_hypercall_stubs(device_t dev, struct xenpci_softc * scp) 133{ 134 uint32_t base, regs[4]; 135 int i; 136 137 base = xenpci_cpuid_base(); 138 139 do_cpuid(base + 2, regs); 140 for (i = 0; i < regs[0]; i++) { 141 wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i); 142 } 143} 144 145/* 146 * Tell the hypervisor how to contact us for event channel callbacks. 147 */ 148static void 149xenpci_set_callback(device_t dev) 150{ 151 int irq; 152 uint64_t callback; 153 struct xen_hvm_param xhp; 154 155 irq = pci_get_irq(dev); 156 if (irq < 16) { 157 callback = irq; 158 } else { 159 callback = (pci_get_intpin(dev) - 1) & 3; 160 callback |= pci_get_slot(dev) << 11; 161 callback |= 1ull << 56; 162 } 163 164 xhp.domid = DOMID_SELF; 165 xhp.index = HVM_PARAM_CALLBACK_IRQ; 166 xhp.value = callback; 167 if (HYPERVISOR_hvm_op(HVMOP_set_param, &xhp)) 168 panic("Can't set evtchn callback"); 169} 170 171 172/* 173 * Deallocate anything allocated by xenpci_allocate_resources. 174 */ 175static int 176xenpci_deallocate_resources(device_t dev) 177{ 178 struct xenpci_softc *scp = device_get_softc(dev); 179 180 if (scp->res_irq != 0) { 181 bus_deactivate_resource(dev, SYS_RES_IRQ, 182 scp->rid_irq, scp->res_irq); 183 bus_release_resource(dev, SYS_RES_IRQ, 184 scp->rid_irq, scp->res_irq); 185 scp->res_irq = 0; 186 } 187 if (scp->res_memory != 0) { 188 bus_deactivate_resource(dev, SYS_RES_MEMORY, 189 scp->rid_memory, scp->res_memory); 190 bus_release_resource(dev, SYS_RES_MEMORY, 191 scp->rid_memory, scp->res_memory); 192 scp->res_memory = 0; 193 } 194 195 return (0); 196} 197 198/* 199 * Allocate irq and memory resources. 200 */ 201static int 202xenpci_allocate_resources(device_t dev) 203{ 204 struct xenpci_softc *scp = device_get_softc(dev); 205 206 scp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 207 &scp->rid_irq, RF_SHAREABLE|RF_ACTIVE); 208 if (scp->res_irq == NULL) { 209 printf("xenpci Could not allocate irq.\n"); 210 goto errexit; 211 } 212 213 scp->rid_memory = PCIR_BAR(1); 214 scp->res_memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 215 &scp->rid_memory, RF_ACTIVE); 216 if (scp->res_memory == NULL) { 217 printf("xenpci Could not allocate memory bar.\n"); 218 goto errexit; 219 } 220 221 scp->phys_next = rman_get_start(scp->res_memory); 222 223 return (0); 224 225errexit: 226 /* Cleanup anything we may have assigned. */ 227 xenpci_deallocate_resources(dev); 228 return (ENXIO); /* For want of a better idea. */ 229} 230 231/* 232 * Allocate a physical address range from our mmio region. 233 */ 234static int 235xenpci_alloc_space_int(struct xenpci_softc *scp, size_t sz, 236 vm_paddr_t *pa) 237{ 238 239 if (scp->phys_next + sz > rman_get_end(scp->res_memory)) { 240 return (ENOMEM); 241 } 242 243 *pa = scp->phys_next; 244 scp->phys_next += sz; 245 246 return (0); 247} 248 249/* 250 * Allocate a physical address range from our mmio region. 251 */ 252int 253xenpci_alloc_space(size_t sz, vm_paddr_t *pa) 254{ 255 device_t dev = devclass_get_device(xenpci_devclass, 0); 256 257 if (dev) { 258 return (xenpci_alloc_space_int(device_get_softc(dev), 259 sz, pa)); 260 } else { 261 return (ENOMEM); 262 } 263} 264 265static struct resource * 266xenpci_alloc_resource(device_t dev, device_t child, int type, int *rid, 267 u_long start, u_long end, u_long count, u_int flags) 268{ 269 return (BUS_ALLOC_RESOURCE(nexus, child, type, rid, start, 270 end, count, flags)); 271} 272 273 274static int 275xenpci_release_resource(device_t dev, device_t child, int type, int rid, 276 struct resource *r) 277{ 278 return (BUS_RELEASE_RESOURCE(nexus, child, type, rid, r)); 279} 280 281static int 282xenpci_activate_resource(device_t dev, device_t child, int type, int rid, 283 struct resource *r) 284{ 285 return (BUS_ACTIVATE_RESOURCE(nexus, child, type, rid, r)); 286} 287 288static int 289xenpci_deactivate_resource(device_t dev, device_t child, int type, 290 int rid, struct resource *r) 291{ 292 return (BUS_DEACTIVATE_RESOURCE(nexus, child, type, rid, r)); 293} 294 295/* 296 * Called very early in the resume sequence - reinitialise the various 297 * bits of Xen machinery including the hypercall page and the shared 298 * info page. 299 */ 300void 301xenpci_resume() 302{ 303 device_t dev = devclass_get_device(xenpci_devclass, 0); 304 struct xenpci_softc *scp = device_get_softc(dev); 305 struct xen_add_to_physmap xatp; 306 307 xenpci_resume_hypercall_stubs(dev, scp); 308 309 xatp.domid = DOMID_SELF; 310 xatp.idx = 0; 311 xatp.space = XENMAPSPACE_shared_info; 312 xatp.gpfn = shared_info_pa >> PAGE_SHIFT; 313 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) 314 panic("HYPERVISOR_memory_op failed"); 315 316 pmap_kenter((vm_offset_t) HYPERVISOR_shared_info, shared_info_pa); 317 318 xenpci_set_callback(dev); 319 320 gnttab_resume(); 321 irq_resume(); 322} 323 324/* 325 * Probe - just check device ID. 326 */ 327static int 328xenpci_probe(device_t dev) 329{ 330 331 if (pci_get_devid(dev) != 0x00015853) 332 return (ENXIO); 333 334 device_set_desc(dev, "Xen Platform Device"); 335 return (bus_generic_probe(dev)); 336} 337 338/* 339 * Attach - find resources and talk to Xen. 340 */ 341static int 342xenpci_attach(device_t dev) 343{ 344 int error; 345 struct xenpci_softc *scp = device_get_softc(dev); 346 struct xen_add_to_physmap xatp; 347 vm_offset_t shared_va; 348 devclass_t dc; 349 350 /* 351 * Find and record nexus0. Since we are not really on the 352 * PCI bus, all resource operations are directed to nexus 353 * instead of through our parent. 354 */ 355 if ((dc = devclass_find("nexus")) == 0 356 || (nexus = devclass_get_device(dc, 0)) == 0) { 357 device_printf(dev, "unable to find nexus."); 358 return (ENOENT); 359 } 360 361 error = xenpci_allocate_resources(dev); 362 if (error) { 363 device_printf(dev, "xenpci_allocate_resources failed(%d).\n", 364 error); 365 goto errexit; 366 } 367 368 error = xenpci_init_hypercall_stubs(dev, scp); 369 if (error) { 370 device_printf(dev, "xenpci_init_hypercall_stubs failed(%d).\n", 371 error); 372 goto errexit; 373 } 374 375 setup_xen_features(); 376 377 xenpci_alloc_space_int(scp, PAGE_SIZE, &shared_info_pa); 378 379 xatp.domid = DOMID_SELF; 380 xatp.idx = 0; 381 xatp.space = XENMAPSPACE_shared_info; 382 xatp.gpfn = shared_info_pa >> PAGE_SHIFT; 383 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) 384 panic("HYPERVISOR_memory_op failed"); 385 386 shared_va = kmem_alloc_nofault(kernel_map, PAGE_SIZE); 387 pmap_kenter(shared_va, shared_info_pa); 388 HYPERVISOR_shared_info = (void *) shared_va; 389 390 /* 391 * Hook the irq up to evtchn 392 */ 393 xenpci_irq_init(dev, scp); 394 xenpci_set_callback(dev); 395 396 return (bus_generic_attach(dev)); 397 398errexit: 399 /* 400 * Undo anything we may have done. 401 */ 402 xenpci_deallocate_resources(dev); 403 return (error); 404} 405 406/* 407 * Detach - reverse anything done by attach. 408 */ 409static int 410xenpci_detach(device_t dev) 411{ 412 struct xenpci_softc *scp = device_get_softc(dev); 413 device_t parent = device_get_parent(dev); 414 415 /* 416 * Take our interrupt handler out of the list of handlers 417 * that can handle this irq. 418 */ 419 if (scp->intr_cookie != NULL) { 420 if (BUS_TEARDOWN_INTR(parent, dev, 421 scp->res_irq, scp->intr_cookie) != 0) 422 device_printf(dev, 423 "intr teardown failed.. continuing\n"); 424 scp->intr_cookie = NULL; 425 } 426 427 /* 428 * Deallocate any system resources we may have 429 * allocated on behalf of this driver. 430 */ 431 return (xenpci_deallocate_resources(dev)); 432} 433 434static device_method_t xenpci_methods[] = { 435 /* Device interface */ 436 DEVMETHOD(device_probe, xenpci_probe), 437 DEVMETHOD(device_attach, xenpci_attach), 438 DEVMETHOD(device_detach, xenpci_detach), 439 DEVMETHOD(device_suspend, bus_generic_suspend), 440 DEVMETHOD(device_resume, bus_generic_resume), 441 442 /* Bus interface */ 443 DEVMETHOD(bus_add_child, bus_generic_add_child), 444 DEVMETHOD(bus_alloc_resource, xenpci_alloc_resource), 445 DEVMETHOD(bus_release_resource, xenpci_release_resource), 446 DEVMETHOD(bus_activate_resource, xenpci_activate_resource), 447 DEVMETHOD(bus_deactivate_resource, xenpci_deactivate_resource), 448 449 { 0, 0 } 450}; 451 452static driver_t xenpci_driver = { 453 "xenpci", 454 xenpci_methods, 455 sizeof(struct xenpci_softc), 456}; 457 458DRIVER_MODULE(xenpci, pci, xenpci_driver, xenpci_devclass, 0, 0); 459