1 /* 2 * Copyright (c) 2008 Citrix Systems, Inc. 3 * Copyright (c) 2012 Spectra Logic Corporation 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/bus.h> 33 #include <sys/kernel.h> 34 #include <sys/malloc.h> 35 #include <sys/proc.h> 36 37 #include <dev/pci/pcivar.h> 38 #include <machine/cpufunc.h> 39 40 #include <xen/xen-os.h> 41 #include <xen/features.h> 42 #include <xen/gnttab.h> 43 #include <xen/hypervisor.h> 44 #include <xen/hvm.h> 45 #include <xen/xen_intr.h> 46 47 #include <vm/vm.h> 48 #include <vm/pmap.h> 49 50 #include <xen/interface/hvm/params.h> 51 #include <xen/interface/vcpu.h> 52 53 static MALLOC_DEFINE(M_XENHVM, "xen_hvm", "Xen HVM PV Support"); 54 55 DPCPU_DEFINE(struct vcpu_info, vcpu_local_info); 56 DPCPU_DEFINE(struct vcpu_info *, vcpu_info); 57 58 /*-------------------------------- Global Data -------------------------------*/ 59 /** 60 * If non-zero, the hypervisor has been configured to use a direct 61 * IDT event callback for interrupt injection. 62 */ 63 int xen_vector_callback_enabled; 64 65 /*------------------ Hypervisor Access Shared Memory Regions -----------------*/ 66 /** Hypercall table accessed via HYPERVISOR_*_op() methods. */ 67 char *hypercall_stubs; 68 shared_info_t *HYPERVISOR_shared_info; 69 enum xen_domain_type xen_domain_type = XEN_NATIVE; 70 71 static uint32_t 72 xen_hvm_cpuid_base(void) 73 { 74 uint32_t base, regs[4]; 75 76 for (base = 0x40000000; base < 0x40010000; base += 0x100) { 77 do_cpuid(base, regs); 78 if (!memcmp("XenVMMXenVMM", ®s[1], 12) 79 && (regs[0] - base) >= 2) 80 return (base); 81 } 82 return (0); 83 } 84 85 /* 86 * Allocate and fill in the hypcall page. 87 */ 88 static int 89 xen_hvm_init_hypercall_stubs(void) 90 { 91 uint32_t base, regs[4]; 92 int i; 93 94 base = xen_hvm_cpuid_base(); 95 if (base == 0) 96 return (ENXIO); 97 98 if (hypercall_stubs == NULL) { 99 do_cpuid(base + 1, regs); 100 printf("XEN: Hypervisor version %d.%d detected.\n", 101 regs[0] >> 16, regs[0] & 0xffff); 102 } 103 104 /* 105 * Find the hypercall pages. 106 */ 107 do_cpuid(base + 2, regs); 108 109 if (hypercall_stubs == NULL) { 110 size_t call_region_size; 111 112 call_region_size = regs[0] * PAGE_SIZE; 113 hypercall_stubs = malloc(call_region_size, M_XENHVM, M_NOWAIT); 114 if (hypercall_stubs == NULL) 115 panic("Unable to allocate Xen hypercall region"); 116 } 117 118 for (i = 0; i < regs[0]; i++) 119 wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i); 120 121 return (0); 122 } 123 124 static void 125 xen_hvm_init_shared_info_page(void) 126 { 127 struct xen_add_to_physmap xatp; 128 129 if (HYPERVISOR_shared_info == NULL) { 130 HYPERVISOR_shared_info = malloc(PAGE_SIZE, M_XENHVM, M_NOWAIT); 131 if (HYPERVISOR_shared_info == NULL) 132 panic("Unable to allocate Xen shared info page"); 133 } 134 135 xatp.domid = DOMID_SELF; 136 xatp.idx = 0; 137 xatp.space = XENMAPSPACE_shared_info; 138 xatp.gpfn = vtophys(HYPERVISOR_shared_info) >> PAGE_SHIFT; 139 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp)) 140 panic("HYPERVISOR_memory_op failed"); 141 } 142 143 /* 144 * Tell the hypervisor how to contact us for event channel callbacks. 145 */ 146 void 147 xen_hvm_set_callback(device_t dev) 148 { 149 struct xen_hvm_param xhp; 150 int irq; 151 152 xhp.domid = DOMID_SELF; 153 xhp.index = HVM_PARAM_CALLBACK_IRQ; 154 if (xen_feature(XENFEAT_hvm_callback_vector) != 0) { 155 int error; 156 157 xhp.value = HVM_CALLBACK_VECTOR(IDT_EVTCHN); 158 error = HYPERVISOR_hvm_op(HVMOP_set_param, &xhp); 159 if (error == 0) { 160 xen_vector_callback_enabled = 1; 161 return; 162 } 163 printf("Xen HVM callback vector registration failed (%d). " 164 "Falling back to emulated device interrupt\n", error); 165 } 166 xen_vector_callback_enabled = 0; 167 if (dev == NULL) { 168 /* 169 * Called from early boot or resume. 170 * xenpci will invoke us again later. 171 */ 172 return; 173 } 174 175 irq = pci_get_irq(dev); 176 if (irq < 16) { 177 xhp.value = HVM_CALLBACK_GSI(irq); 178 } else { 179 u_int slot; 180 u_int pin; 181 182 slot = pci_get_slot(dev); 183 pin = pci_get_intpin(dev) - 1; 184 xhp.value = HVM_CALLBACK_PCI_INTX(slot, pin); 185 } 186 187 if (HYPERVISOR_hvm_op(HVMOP_set_param, &xhp) != 0) 188 panic("Can't set evtchn callback"); 189 } 190 191 #define XEN_MAGIC_IOPORT 0x10 192 enum { 193 XMI_MAGIC = 0x49d2, 194 XMI_UNPLUG_IDE_DISKS = 0x01, 195 XMI_UNPLUG_NICS = 0x02, 196 XMI_UNPLUG_IDE_EXCEPT_PRI_MASTER = 0x04 197 }; 198 199 static void 200 xen_hvm_disable_emulated_devices(void) 201 { 202 if (inw(XEN_MAGIC_IOPORT) != XMI_MAGIC) 203 return; 204 205 if (bootverbose) 206 printf("XEN: Disabling emulated block and network devices\n"); 207 outw(XEN_MAGIC_IOPORT, XMI_UNPLUG_IDE_DISKS|XMI_UNPLUG_NICS); 208 } 209 210 void 211 xen_hvm_suspend(void) 212 { 213 } 214 215 void 216 xen_hvm_resume(void) 217 { 218 219 xen_hvm_init_hypercall_stubs(); 220 xen_hvm_init_shared_info_page(); 221 } 222 223 static void 224 xen_hvm_init(void *dummy __unused) 225 { 226 227 if (xen_hvm_init_hypercall_stubs() != 0) 228 return; 229 230 xen_domain_type = XEN_HVM_DOMAIN; 231 setup_xen_features(); 232 xen_hvm_init_shared_info_page(); 233 xen_hvm_set_callback(NULL); 234 xen_hvm_disable_emulated_devices(); 235 } 236 237 void xen_hvm_init_cpu(void) 238 { 239 struct vcpu_register_vcpu_info info; 240 struct vcpu_info *vcpu_info; 241 int cpu, rc; 242 243 cpu = PCPU_GET(acpi_id); 244 vcpu_info = DPCPU_PTR(vcpu_local_info); 245 info.mfn = vtophys(vcpu_info) >> PAGE_SHIFT; 246 info.offset = vtophys(vcpu_info) - trunc_page(vtophys(vcpu_info)); 247 248 rc = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_info, cpu, &info); 249 if (rc != 0) 250 DPCPU_SET(vcpu_info, &HYPERVISOR_shared_info->vcpu_info[cpu]); 251 else 252 DPCPU_SET(vcpu_info, vcpu_info); 253 } 254 255 SYSINIT(xen_hvm_init, SI_SUB_HYPERVISOR, SI_ORDER_FIRST, xen_hvm_init, NULL); 256 SYSINIT(xen_hvm_init_cpu, SI_SUB_INTR, SI_ORDER_FIRST, xen_hvm_init_cpu, NULL); 257