xenpci.c revision 188685
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: user/dfr/xenhvm/7/sys/dev/xen/xenpci/xenpci.c 186767 2009-01-05 10:43:48Z dfr $"); 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; 69 70/* 71 * This is used to find our platform device instance. 72 */ 73static devclass_t xenpci_devclass; 74 75/* 76 * Return the CPUID base address for Xen functions. 77 */ 78static uint32_t 79xenpci_cpuid_base(void) 80{ 81 uint32_t base, regs[4]; 82 83 for (base = 0x40000000; base < 0x40001000; base += 0x100) { 84 do_cpuid(base, regs); 85 if (!memcmp("XenVMMXenVMM", ®s[1], 12) 86 && (regs[0] - base) >= 2) 87 return (base); 88 } 89 return (0); 90} 91 92/* 93 * Allocate and fill in the hypcall page. 94 */ 95static int 96xenpci_init_hypercall_stubs(device_t dev, struct xenpci_softc * scp) 97{ 98 uint32_t base, regs[4]; 99 int i; 100 101 base = xenpci_cpuid_base(); 102 if (!base) { 103 device_printf(dev, "Xen platform device but not Xen VMM\n"); 104 return (EINVAL); 105 } 106 107 if (bootverbose) { 108 do_cpuid(base + 1, regs); 109 device_printf(dev, "Xen version %d.%d.\n", 110 regs[0] >> 16, regs[0] & 0xffff); 111 } 112 113 /* 114 * Find the hypercall pages. 115 */ 116 do_cpuid(base + 2, regs); 117 118 hypercall_stubs = malloc(regs[0] * PAGE_SIZE, M_TEMP, M_WAITOK); 119 120 for (i = 0; i < regs[0]; i++) { 121 wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i); 122 } 123 124 return (0); 125} 126 127/* 128 * After a resume, re-initialise the hypercall page. 129 */ 130static void 131xenpci_resume_hypercall_stubs(device_t dev, struct xenpci_softc * scp) 132{ 133 uint32_t base, regs[4]; 134 int i; 135 136 base = xenpci_cpuid_base(); 137 138 do_cpuid(base + 2, regs); 139 for (i = 0; i < regs[0]; i++) { 140 wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i); 141 } 142} 143 144/* 145 * Tell the hypervisor how to contact us for event channel callbacks. 146 */ 147static void 148xenpci_set_callback(device_t dev) 149{ 150 int irq; 151 uint64_t callback; 152 struct xen_hvm_param xhp; 153 154 irq = pci_get_irq(dev); 155 if (irq < 16) { 156 callback = irq; 157 } else { 158 callback = (pci_get_intpin(dev) - 1) & 3; 159 callback |= pci_get_slot(dev) << 11; 160 callback |= 1ull << 56; 161 } 162 163 xhp.domid = DOMID_SELF; 164 xhp.index = HVM_PARAM_CALLBACK_IRQ; 165 xhp.value = callback; 166 if (HYPERVISOR_hvm_op(HVMOP_set_param, &xhp)) 167 panic("Can't set evtchn callback"); 168} 169 170 171/* 172 * Deallocate anything allocated by xenpci_allocate_resources. 173 */ 174static int 175xenpci_deallocate_resources(device_t dev) 176{ 177 struct xenpci_softc *scp = device_get_softc(dev); 178 179 if (scp->res_irq != 0) { 180 bus_deactivate_resource(dev, SYS_RES_IRQ, 181 scp->rid_irq, scp->res_irq); 182 bus_release_resource(dev, SYS_RES_IRQ, 183 scp->rid_irq, scp->res_irq); 184 scp->res_irq = 0; 185 } 186 if (scp->res_memory != 0) { 187 bus_deactivate_resource(dev, SYS_RES_MEMORY, 188 scp->rid_memory, scp->res_memory); 189 bus_release_resource(dev, SYS_RES_MEMORY, 190 scp->rid_memory, scp->res_memory); 191 scp->res_memory = 0; 192 } 193 194 return (0); 195} 196 197/* 198 * Allocate irq and memory resources. 199 */ 200static int 201xenpci_allocate_resources(device_t dev) 202{ 203 struct xenpci_softc *scp = device_get_softc(dev); 204 205 scp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, 206 &scp->rid_irq, RF_SHAREABLE|RF_ACTIVE); 207 if (scp->res_irq == NULL) 208 goto errexit; 209 210 scp->rid_memory = PCIR_BAR(1); 211 scp->res_memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 212 &scp->rid_memory, RF_ACTIVE); 213 if (scp->res_memory == NULL) 214 goto errexit; 215 return (0); 216 217errexit: 218 /* Cleanup anything we may have assigned. */ 219 xenpci_deallocate_resources(dev); 220 return (ENXIO); /* For want of a better idea. */ 221} 222 223/* 224 * Allocate a physical address range from our mmio region. 225 */ 226static int 227xenpci_alloc_space_int(struct xenpci_softc *scp, size_t sz, 228 vm_paddr_t *pa) 229{ 230 231 if (scp->phys_next + sz > rman_get_end(scp->res_memory)) { 232 return (ENOMEM); 233 } 234 235 *pa = scp->phys_next; 236 scp->phys_next += sz; 237 238 return (0); 239} 240 241/* 242 * Allocate a physical address range from our mmio region. 243 */ 244int 245xenpci_alloc_space(size_t sz, vm_paddr_t *pa) 246{ 247 device_t dev = devclass_get_device(xenpci_devclass, 0); 248 249 if (dev) { 250 return (xenpci_alloc_space_int(device_get_softc(dev), 251 sz, pa)); 252 } else { 253 return (ENOMEM); 254 } 255} 256 257/* 258 * Called very early in the resume sequence - reinitialise the various 259 * bits of Xen machinery including the hypercall page and the shared 260 * info page. 261 */ 262void 263xenpci_resume() 264{ 265 device_t dev = devclass_get_device(xenpci_devclass, 0); 266 struct xenpci_softc *scp = device_get_softc(dev); 267 struct xen_add_to_physmap xatp; 268 269 xenpci_resume_hypercall_stubs(dev, scp); 270 271 xatp.domid = DOMID_SELF; 272 xatp.idx = 0; 273 xatp.space = XENMAPSPACE_shared_info; 274 xatp.gpfn = shared_info_pa >> PAGE_SHIFT; 275 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) 276 panic("HYPERVISOR_memory_op failed"); 277 278 pmap_kenter((vm_offset_t) HYPERVISOR_shared_info, shared_info_pa); 279 280 xenpci_set_callback(dev); 281 282 gnttab_resume(); 283 irq_resume(); 284} 285 286/* 287 * Probe - just check device ID. 288 */ 289static int 290xenpci_probe(device_t dev) 291{ 292 293 if (pci_get_devid(dev) != 0x00015853) 294 return (ENXIO); 295 296 device_set_desc(dev, "Xen Platform Device"); 297 return (bus_generic_probe(dev)); 298} 299 300/* 301 * Attach - find resources and talk to Xen. 302 */ 303static int 304xenpci_attach(device_t dev) 305{ 306 int error; 307 struct xenpci_softc *scp = device_get_softc(dev); 308 struct xen_add_to_physmap xatp; 309 vm_offset_t shared_va; 310 311 error = xenpci_allocate_resources(dev); 312 if (error) 313 goto errexit; 314 315 scp->phys_next = rman_get_start(scp->res_memory); 316 317 error = xenpci_init_hypercall_stubs(dev, scp); 318 if (error) 319 goto errexit; 320 321 setup_xen_features(); 322 323 xenpci_alloc_space_int(scp, PAGE_SIZE, &shared_info_pa); 324 325 xatp.domid = DOMID_SELF; 326 xatp.idx = 0; 327 xatp.space = XENMAPSPACE_shared_info; 328 xatp.gpfn = shared_info_pa >> PAGE_SHIFT; 329 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) 330 panic("HYPERVISOR_memory_op failed"); 331 332 shared_va = kmem_alloc_nofault(kernel_map, PAGE_SIZE); 333 pmap_kenter(shared_va, shared_info_pa); 334 HYPERVISOR_shared_info = (void *) shared_va; 335 336 /* 337 * Hook the irq up to evtchn 338 */ 339 xenpci_irq_init(dev, scp); 340 xenpci_set_callback(dev); 341 342 return (bus_generic_attach(dev)); 343 344errexit: 345 /* 346 * Undo anything we may have done. 347 */ 348 xenpci_deallocate_resources(dev); 349 return (error); 350} 351 352/* 353 * Detach - reverse anything done by attach. 354 */ 355static int 356xenpci_detach(device_t dev) 357{ 358 struct xenpci_softc *scp = device_get_softc(dev); 359 device_t parent = device_get_parent(dev); 360 361 /* 362 * Take our interrupt handler out of the list of handlers 363 * that can handle this irq. 364 */ 365 if (scp->intr_cookie != NULL) { 366 if (BUS_TEARDOWN_INTR(parent, dev, 367 scp->res_irq, scp->intr_cookie) != 0) 368 printf("intr teardown failed.. continuing\n"); 369 scp->intr_cookie = NULL; 370 } 371 372 /* 373 * Deallocate any system resources we may have 374 * allocated on behalf of this driver. 375 */ 376 return (xenpci_deallocate_resources(dev)); 377} 378 379static device_method_t xenpci_methods[] = { 380 /* Device interface */ 381 DEVMETHOD(device_probe, xenpci_probe), 382 DEVMETHOD(device_attach, xenpci_attach), 383 DEVMETHOD(device_detach, xenpci_detach), 384 DEVMETHOD(device_suspend, bus_generic_suspend), 385 DEVMETHOD(device_resume, bus_generic_resume), 386 387 /* Bus interface */ 388 DEVMETHOD(bus_add_child, bus_generic_add_child), 389 390 { 0, 0 } 391}; 392 393static driver_t xenpci_driver = { 394 "xenpci", 395 xenpci_methods, 396 sizeof(struct xenpci_softc), 397}; 398 399DRIVER_MODULE(xenpci, pci, xenpci_driver, xenpci_devclass, 0, 0); 400