xenpci.c revision 185803
1/* 2 * Copyright (c) [year] [your name] 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$"); 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 * The softc is automatically allocated by the parent bus using the 72 * size specified in the driver_t declaration below. 73 */ 74#define DEVICE2SOFTC(dev) ((struct xenpci_softc *) device_get_softc(dev)) 75 76/* Function prototypes (these should all be static). */ 77static int xenpci_deallocate_resources(device_t device); 78static int xenpci_allocate_resources(device_t device); 79static int xenpci_attach(device_t device, struct xenpci_softc *scp); 80static int xenpci_detach(device_t device, struct xenpci_softc *scp); 81 82static int xenpci_alloc_space_int(struct xenpci_softc *scp, size_t sz, 83 u_long *pa); 84 85static devclass_t xenpci_devclass; 86 87static int xenpci_pci_probe(device_t); 88static int xenpci_pci_attach(device_t); 89static int xenpci_pci_detach(device_t); 90static int xenpci_pci_resume(device_t); 91 92static device_method_t xenpci_pci_methods[] = { 93 /* Device interface */ 94 DEVMETHOD(device_probe, xenpci_pci_probe), 95 DEVMETHOD(device_attach, xenpci_pci_attach), 96 DEVMETHOD(device_detach, xenpci_pci_detach), 97 DEVMETHOD(device_resume, xenpci_pci_resume), 98 99 /* Bus interface */ 100 DEVMETHOD(bus_add_child, bus_generic_add_child), 101 102 { 0, 0 } 103}; 104 105static driver_t xenpci_pci_driver = { 106 "xenpci", 107 xenpci_pci_methods, 108 sizeof(struct xenpci_softc), 109}; 110 111DRIVER_MODULE(xenpci, pci, xenpci_pci_driver, xenpci_devclass, 0, 0); 112 113static struct _pcsid 114{ 115 u_int32_t type; 116 const char *desc; 117} pci_ids[] = { 118 { 0x00015853, "Xen Platform Device" }, 119 { 0x00000000, NULL } 120}; 121 122static int 123xenpci_pci_probe (device_t device) 124{ 125 u_int32_t type = pci_get_devid(device); 126 struct _pcsid *ep = pci_ids; 127 128 while (ep->type && ep->type != type) 129 ++ep; 130 if (ep->desc) { 131 device_set_desc(device, ep->desc); 132 return (bus_generic_probe(device)); 133 } else 134 return (ENXIO); 135} 136 137static int 138xenpci_pci_attach(device_t device) 139{ 140 int error; 141 struct xenpci_softc *scp = DEVICE2SOFTC(device); 142 143 error = xenpci_attach(device, scp); 144 if (error) 145 xenpci_pci_detach(device); 146 return (error); 147} 148 149static int 150xenpci_pci_detach (device_t device) 151{ 152 struct xenpci_softc *scp = DEVICE2SOFTC(device); 153 154 return (xenpci_detach(device, scp)); 155} 156 157static int 158xenpci_pci_resume(device_t device) 159{ 160 161 return (bus_generic_resume(device)); 162} 163 164/* 165 * Common Attachment sub-functions 166 */ 167static uint32_t 168xenpci_cpuid_base(void) 169{ 170 uint32_t base, regs[4]; 171 172 for (base = 0x40000000; base < 0x40001000; base += 0x100) { 173 do_cpuid(base, regs); 174 if (!memcmp("XenVMMXenVMM", ®s[1], 12) 175 && (regs[0] - base) >= 2) 176 return (base); 177 } 178 return (0); 179} 180 181static int 182xenpci_init_hypercall_stubs(device_t device, struct xenpci_softc * scp) 183{ 184 uint32_t base, regs[4]; 185 int i; 186 187 base = xenpci_cpuid_base(); 188 if (!base) { 189 device_printf(device, "Xen platform device but not Xen VMM\n"); 190 return (EINVAL); 191 } 192 193 if (bootverbose) { 194 do_cpuid(base + 1, regs); 195 device_printf(device, "Xen version %d.%d.\n", 196 regs[0] >> 16, regs[0] & 0xffff); 197 } 198 199 /* 200 * Find the hypercall pages. 201 */ 202 do_cpuid(base + 2, regs); 203 204 hypercall_stubs = malloc(regs[0] * PAGE_SIZE, M_TEMP, M_WAITOK); 205 206 for (i = 0; i < regs[0]; i++) { 207 wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i); 208 } 209 210 return (0); 211} 212 213static void 214xenpci_resume_hypercall_stubs(device_t device, struct xenpci_softc * scp) 215{ 216 uint32_t base, regs[4]; 217 int i; 218 219 base = xenpci_cpuid_base(); 220 221 do_cpuid(base + 2, regs); 222 for (i = 0; i < regs[0]; i++) { 223 wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i); 224 } 225} 226 227static void 228xenpci_set_callback(device_t device) 229{ 230 int irq; 231 uint64_t callback; 232 struct xen_hvm_param xhp; 233 234 irq = pci_get_irq(device); 235 if (irq < 16) { 236 callback = irq; 237 } else { 238 callback = (pci_get_intpin(device) - 1) & 3; 239 callback |= pci_get_slot(device) << 11; 240 callback |= 1ull << 56; 241 } 242 243 xhp.domid = DOMID_SELF; 244 xhp.index = HVM_PARAM_CALLBACK_IRQ; 245 xhp.value = callback; 246 if (HYPERVISOR_hvm_op(HVMOP_set_param, &xhp)) 247 panic("Can't set evtchn callback"); 248} 249 250static int 251xenpci_attach(device_t device, struct xenpci_softc * scp) 252{ 253 struct xen_add_to_physmap xatp; 254 vm_offset_t shared_va; 255 256 if (xenpci_allocate_resources(device)) 257 goto errexit; 258 259 scp->phys_next = rman_get_start(scp->res_memory); 260 261 if (xenpci_init_hypercall_stubs(device, scp)) 262 goto errexit; 263 264 setup_xen_features(); 265 266 xenpci_alloc_space_int(scp, PAGE_SIZE, &shared_info_pa); 267 268 xatp.domid = DOMID_SELF; 269 xatp.idx = 0; 270 xatp.space = XENMAPSPACE_shared_info; 271 xatp.gpfn = shared_info_pa >> PAGE_SHIFT; 272 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) 273 panic("HYPERVISOR_memory_op failed"); 274 275 shared_va = kmem_alloc_nofault(kernel_map, PAGE_SIZE); 276 pmap_kenter(shared_va, shared_info_pa); 277 HYPERVISOR_shared_info = (void *) shared_va; 278 279 /* 280 * Hook the irq up to evtchn 281 */ 282 xenpci_irq_init(device, scp); 283 xenpci_set_callback(device); 284 285 return (bus_generic_attach(device)); 286 287errexit: 288 /* 289 * Undo anything we may have done. 290 */ 291 xenpci_detach(device, scp); 292 return (ENXIO); 293} 294 295static int 296xenpci_detach(device_t device, struct xenpci_softc *scp) 297{ 298 device_t parent = device_get_parent(device); 299 300 /* 301 * Take our interrupt handler out of the list of handlers 302 * that can handle this irq. 303 */ 304 if (scp->intr_cookie != NULL) { 305 if (BUS_TEARDOWN_INTR(parent, device, 306 scp->res_irq, scp->intr_cookie) != 0) 307 printf("intr teardown failed.. continuing\n"); 308 scp->intr_cookie = NULL; 309 } 310 311 /* 312 * Deallocate any system resources we may have 313 * allocated on behalf of this driver. 314 */ 315 return xenpci_deallocate_resources(device); 316} 317 318static int 319xenpci_allocate_resources(device_t device) 320{ 321 int error; 322 struct xenpci_softc *scp = DEVICE2SOFTC(device); 323 324 scp->res_irq = bus_alloc_resource_any(device, SYS_RES_IRQ, 325 &scp->rid_irq, RF_SHAREABLE|RF_ACTIVE); 326 if (scp->res_irq == NULL) 327 goto errexit; 328 329 scp->rid_ioport = PCIR_BAR(0); 330 scp->res_ioport = bus_alloc_resource_any(device, SYS_RES_IOPORT, 331 &scp->rid_ioport, RF_ACTIVE); 332 if (scp->res_ioport == NULL) 333 goto errexit; 334 335 scp->rid_memory = PCIR_BAR(1); 336 scp->res_memory = bus_alloc_resource_any(device, SYS_RES_MEMORY, 337 &scp->rid_memory, RF_ACTIVE); 338 if (scp->res_memory == NULL) 339 goto errexit; 340 return (0); 341 342errexit: 343 error = ENXIO; 344 /* Cleanup anything we may have assigned. */ 345 xenpci_deallocate_resources(device); 346 return (ENXIO); /* For want of a better idea. */ 347} 348 349static int 350xenpci_deallocate_resources(device_t device) 351{ 352 struct xenpci_softc *scp = DEVICE2SOFTC(device); 353 354 if (scp->res_irq != 0) { 355 bus_deactivate_resource(device, SYS_RES_IRQ, 356 scp->rid_irq, scp->res_irq); 357 bus_release_resource(device, SYS_RES_IRQ, 358 scp->rid_irq, scp->res_irq); 359 scp->res_irq = 0; 360 } 361 if (scp->res_ioport != 0) { 362 bus_deactivate_resource(device, SYS_RES_IOPORT, 363 scp->rid_ioport, scp->res_ioport); 364 bus_release_resource(device, SYS_RES_IOPORT, 365 scp->rid_ioport, scp->res_ioport); 366 scp->res_ioport = 0; 367 } 368 if (scp->res_memory != 0) { 369 bus_deactivate_resource(device, SYS_RES_MEMORY, 370 scp->rid_memory, scp->res_memory); 371 bus_release_resource(device, SYS_RES_MEMORY, 372 scp->rid_memory, scp->res_memory); 373 scp->res_memory = 0; 374 } 375 376 return (0); 377} 378 379static int 380xenpci_alloc_space_int(struct xenpci_softc *scp, size_t sz, 381 vm_paddr_t *pa) 382{ 383 384 if (scp->phys_next + sz > rman_get_end(scp->res_memory)) { 385 return (ENOMEM); 386 } 387 388 *pa = scp->phys_next; 389 scp->phys_next += sz; 390 391 return (0); 392} 393 394int 395xenpci_alloc_space(size_t sz, vm_paddr_t *pa) 396{ 397 device_t device = devclass_get_device(xenpci_devclass, 0); 398 399 if (device) { 400 return (xenpci_alloc_space_int(DEVICE2SOFTC(device), 401 sz, pa)); 402 } else { 403 return (ENOMEM); 404 } 405} 406 407void 408xenpci_resume() 409{ 410 device_t device = devclass_get_device(xenpci_devclass, 0); 411 struct xenpci_softc *scp = DEVICE2SOFTC(device); 412 struct xen_add_to_physmap xatp; 413 414 xenpci_resume_hypercall_stubs(device, scp); 415 416 xatp.domid = DOMID_SELF; 417 xatp.idx = 0; 418 xatp.space = XENMAPSPACE_shared_info; 419 xatp.gpfn = shared_info_pa >> PAGE_SHIFT; 420 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) 421 panic("HYPERVISOR_memory_op failed"); 422 423 pmap_kenter((vm_offset_t) HYPERVISOR_shared_info, shared_info_pa); 424 425 xenpci_set_callback(device); 426 427 gnttab_resume(); 428 irq_resume(); 429} 430 431