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$"); 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 */ 66 char *hypercall_stubs; 67 shared_info_t *HYPERVISOR_shared_info; 68 static vm_paddr_t shared_info_pa; 69 70 /* 71 * This is used to find our platform device instance. 72 */ 73 static devclass_t xenpci_devclass; 74 75 /* 76 * Return the CPUID base address for Xen functions. 77 */ 78 static uint32_t 79 xenpci_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 */ 95 static int 96 xenpci_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 */ 130 static void 131 xenpci_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 */ 147 static void 148 xenpci_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 */ 174 static int 175 xenpci_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 */ 200 static int 201 xenpci_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 217 errexit: 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 */ 226 static int 227 xenpci_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 */ 244 int 245 xenpci_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 */ 262 void 263 xenpci_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 */ 289 static int 290 xenpci_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 */ 303 static int 304 xenpci_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 344 errexit: 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 */ 355 static int 356 xenpci_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 379 static 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 393 static driver_t xenpci_driver = { 394 "xenpci", 395 xenpci_methods, 396 sizeof(struct xenpci_softc), 397 }; 398 399 DRIVER_MODULE(xenpci, pci, xenpci_driver, xenpci_devclass, 0, 0); 400