1ebf5747bSPedro F. Giffuni /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3ebf5747bSPedro F. Giffuni * 4e44af46eSJustin T. Gibbs * Copyright (c) 2008, 2013 Citrix Systems, Inc. 576acc41fSJustin T. Gibbs * Copyright (c) 2012 Spectra Logic Corporation 676acc41fSJustin T. Gibbs * All rights reserved. 776acc41fSJustin T. Gibbs * 876acc41fSJustin T. Gibbs * Redistribution and use in source and binary forms, with or without 976acc41fSJustin T. Gibbs * modification, are permitted provided that the following conditions 1076acc41fSJustin T. Gibbs * are met: 1176acc41fSJustin T. Gibbs * 1. Redistributions of source code must retain the above copyright 1276acc41fSJustin T. Gibbs * notice, this list of conditions and the following disclaimer. 1376acc41fSJustin T. Gibbs * 2. Redistributions in binary form must reproduce the above copyright 1476acc41fSJustin T. Gibbs * notice, this list of conditions and the following disclaimer in the 1576acc41fSJustin T. Gibbs * documentation and/or other materials provided with the distribution. 1676acc41fSJustin T. Gibbs * 1776acc41fSJustin T. Gibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND 1876acc41fSJustin T. Gibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1976acc41fSJustin T. Gibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2076acc41fSJustin T. Gibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2176acc41fSJustin T. Gibbs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2276acc41fSJustin T. Gibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2376acc41fSJustin T. Gibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2476acc41fSJustin T. Gibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2576acc41fSJustin T. Gibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2676acc41fSJustin T. Gibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2776acc41fSJustin T. Gibbs * SUCH DAMAGE. 2876acc41fSJustin T. Gibbs */ 2976acc41fSJustin T. Gibbs 3076acc41fSJustin T. Gibbs #include <sys/param.h> 3176acc41fSJustin T. Gibbs #include <sys/bus.h> 3276acc41fSJustin T. Gibbs #include <sys/kernel.h> 33027b66d6SRoger Pau Monné #include <sys/linker.h> 3476acc41fSJustin T. Gibbs #include <sys/malloc.h> 3576acc41fSJustin T. Gibbs #include <sys/proc.h> 36e44af46eSJustin T. Gibbs #include <sys/smp.h> 37e44af46eSJustin T. Gibbs #include <sys/systm.h> 38e44af46eSJustin T. Gibbs 39e44af46eSJustin T. Gibbs #include <vm/vm.h> 40e44af46eSJustin T. Gibbs #include <vm/pmap.h> 4107c2711fSRoger Pau Monné #include <vm/vm_param.h> 4276acc41fSJustin T. Gibbs 4376acc41fSJustin T. Gibbs #include <dev/pci/pcivar.h> 44e44af46eSJustin T. Gibbs 459a687d1fSRoger Pau Monné #include <machine/_inttypes.h> 4676acc41fSJustin T. Gibbs #include <machine/cpufunc.h> 47e44af46eSJustin T. Gibbs #include <machine/cpu.h> 489a687d1fSRoger Pau Monné #include <machine/md_var.h> 49027b66d6SRoger Pau Monné #include <machine/metadata.h> 5069c47485SRoger Pau Monné #include <machine/pc/bios.h> 51e44af46eSJustin T. Gibbs #include <machine/smp.h> 52e44af46eSJustin T. Gibbs 53e44af46eSJustin T. Gibbs #include <x86/apicreg.h> 5476acc41fSJustin T. Gibbs 5576acc41fSJustin T. Gibbs #include <xen/xen-os.h> 5627c36a12SRoger Pau Monné #include <xen/error.h> 5776acc41fSJustin T. Gibbs #include <xen/features.h> 5876acc41fSJustin T. Gibbs #include <xen/gnttab.h> 5976acc41fSJustin T. Gibbs #include <xen/hypervisor.h> 6076acc41fSJustin T. Gibbs #include <xen/hvm.h> 6176acc41fSJustin T. Gibbs #include <xen/xen_intr.h> 6276acc41fSJustin T. Gibbs 63ad7dd514SElliott Mitchell #include <contrib/xen/arch-x86/cpuid.h> 64ad7dd514SElliott Mitchell #include <contrib/xen/hvm/params.h> 65ad7dd514SElliott Mitchell #include <contrib/xen/vcpu.h> 6676acc41fSJustin T. Gibbs 67e44af46eSJustin T. Gibbs /*--------------------------- Forward Declarations ---------------------------*/ 68566a5f50SJustin T. Gibbs static void xen_hvm_cpu_init(void); 6976acc41fSJustin T. Gibbs 7076acc41fSJustin T. Gibbs /*-------------------------------- Global Data -------------------------------*/ 716b779733SJohn Baldwin #ifdef SMP 72566a5f50SJustin T. Gibbs struct cpu_ops xen_hvm_cpu_ops = { 73566a5f50SJustin T. Gibbs .cpu_init = xen_hvm_cpu_init, 7415226522SRoger Pau Monné .cpu_resume = xen_hvm_cpu_init 75566a5f50SJustin T. Gibbs }; 766b779733SJohn Baldwin #endif 77566a5f50SJustin T. Gibbs 78e44af46eSJustin T. Gibbs static MALLOC_DEFINE(M_XENHVM, "xen_hvm", "Xen HVM PV Support"); 79e44af46eSJustin T. Gibbs 8076acc41fSJustin T. Gibbs /** 8176acc41fSJustin T. Gibbs * If non-zero, the hypervisor has been configured to use a direct 8276acc41fSJustin T. Gibbs * IDT event callback for interrupt injection. 8376acc41fSJustin T. Gibbs */ 8476acc41fSJustin T. Gibbs int xen_vector_callback_enabled; 8576acc41fSJustin T. Gibbs 86b0663c33SRoger Pau Monné /** 8727c36a12SRoger Pau Monné * Signal whether the vector injected for the event channel upcall requires to 8827c36a12SRoger Pau Monné * be EOI'ed on the local APIC. 8927c36a12SRoger Pau Monné */ 9027c36a12SRoger Pau Monné bool xen_evtchn_needs_ack; 9127c36a12SRoger Pau Monné 92e44af46eSJustin T. Gibbs /*------------------------------- Per-CPU Data -------------------------------*/ 9320fc5bf7SElliott Mitchell DPCPU_DECLARE(struct vcpu_info *, vcpu_info); 94e44af46eSJustin T. Gibbs 95f8f1bb83SRoger Pau Monné /*------------------------------ Sysctl tunables -----------------------------*/ 96f8f1bb83SRoger Pau Monné int xen_disable_pv_disks = 0; 97f8f1bb83SRoger Pau Monné int xen_disable_pv_nics = 0; 98f8f1bb83SRoger Pau Monné TUNABLE_INT("hw.xen.disable_pv_disks", &xen_disable_pv_disks); 99f8f1bb83SRoger Pau Monné TUNABLE_INT("hw.xen.disable_pv_nics", &xen_disable_pv_nics); 100f8f1bb83SRoger Pau Monné 101e44af46eSJustin T. Gibbs /*---------------------- XEN Hypervisor Probe and Setup ----------------------*/ 10285189975SRoger Pau Monné 103399386f1SRoger Pau Monné void xen_emergency_print(const char *str, size_t size) 104399386f1SRoger Pau Monné { 105399386f1SRoger Pau Monné outsb(XEN_HVM_DEBUGCONS_IOPORT, str, size); 106399386f1SRoger Pau Monné } 107399386f1SRoger Pau Monné 10807c2711fSRoger Pau Monné static void 10907c2711fSRoger Pau Monné hypervisor_quirks(unsigned int major, unsigned int minor) 11076acc41fSJustin T. Gibbs { 111a47632d4SJohn Baldwin #ifdef SMP 11230224470SRoger Pau Monné if (((major < 4) || (major == 4 && minor <= 5)) && 11330224470SRoger Pau Monné msix_disable_migration == -1) { 11430224470SRoger Pau Monné /* 11530224470SRoger Pau Monné * Xen hypervisors prior to 4.6.0 do not properly 11630224470SRoger Pau Monné * handle updates to enabled MSI-X table entries, 11730224470SRoger Pau Monné * so disable MSI-X interrupt migration in that 11830224470SRoger Pau Monné * case. 11930224470SRoger Pau Monné */ 12030224470SRoger Pau Monné if (bootverbose) 12130224470SRoger Pau Monné printf( 12230224470SRoger Pau Monné "Disabling MSI-X interrupt migration due to Xen hypervisor bug.\n" 12330224470SRoger Pau Monné "Set machdep.msix_disable_migration=0 to forcefully enable it.\n"); 12430224470SRoger Pau Monné msix_disable_migration = 1; 12530224470SRoger Pau Monné } 126a47632d4SJohn Baldwin #endif 12776acc41fSJustin T. Gibbs } 12876acc41fSJustin T. Gibbs 12907c2711fSRoger Pau Monné static void 13007c2711fSRoger Pau Monné hypervisor_version(void) 13107c2711fSRoger Pau Monné { 13207c2711fSRoger Pau Monné uint32_t regs[4]; 13307c2711fSRoger Pau Monné int major, minor; 13407c2711fSRoger Pau Monné 135f0cf86c0SRoger Pau Monné do_cpuid(hv_base + 1, regs); 13607c2711fSRoger Pau Monné 13707c2711fSRoger Pau Monné major = regs[0] >> 16; 13807c2711fSRoger Pau Monné minor = regs[0] & 0xffff; 13907c2711fSRoger Pau Monné printf("XEN: Hypervisor version %d.%d detected.\n", major, minor); 14007c2711fSRoger Pau Monné 14107c2711fSRoger Pau Monné hypervisor_quirks(major, minor); 14207c2711fSRoger Pau Monné } 14307c2711fSRoger Pau Monné 14407c2711fSRoger Pau Monné /* 1459a687d1fSRoger Pau Monné * Translate linear to physical address when still running on the bootloader 1469a687d1fSRoger Pau Monné * created page-tables. 1479a687d1fSRoger Pau Monné */ 1489a687d1fSRoger Pau Monné static vm_paddr_t 1499a687d1fSRoger Pau Monné early_init_vtop(void *addr) 1509a687d1fSRoger Pau Monné { 1519a687d1fSRoger Pau Monné 1529a687d1fSRoger Pau Monné /* 1539a687d1fSRoger Pau Monné * Using a KASSERT won't print anything, as this is before console 1549a687d1fSRoger Pau Monné * initialization. 1559a687d1fSRoger Pau Monné */ 1569a687d1fSRoger Pau Monné if (__predict_false((uintptr_t)addr < KERNBASE)) { 157f62d90e5SRoger Pau Monné xc_printf("invalid linear address: %p\n", addr); 1589a687d1fSRoger Pau Monné halt(); 1599a687d1fSRoger Pau Monné } 1609a687d1fSRoger Pau Monné 1619a687d1fSRoger Pau Monné return ((uintptr_t)addr - KERNBASE 1629a687d1fSRoger Pau Monné #ifdef __amd64__ 1639a687d1fSRoger Pau Monné + kernphys - KERNLOAD 1649a687d1fSRoger Pau Monné #endif 1659a687d1fSRoger Pau Monné ); 1669a687d1fSRoger Pau Monné } 1679a687d1fSRoger Pau Monné 1685d62aba7SRoger Pau Monné static int 1695d62aba7SRoger Pau Monné map_shared_info(void) 1705d62aba7SRoger Pau Monné { 1715d62aba7SRoger Pau Monné /* 1725d62aba7SRoger Pau Monné * TODO shared info page should be mapped in an unpopulated (IOW: 1735d62aba7SRoger Pau Monné * non-RAM) address. But finding one at this point in boot is 1745d62aba7SRoger Pau Monné * complicated, hence re-use a RAM address for the time being. This 1755d62aba7SRoger Pau Monné * sadly causes super-page shattering in the second stage translation 1765d62aba7SRoger Pau Monné * page tables. 1775d62aba7SRoger Pau Monné */ 1785d62aba7SRoger Pau Monné static union { 1795d62aba7SRoger Pau Monné shared_info_t shared_info; 1805d62aba7SRoger Pau Monné uint8_t raw[PAGE_SIZE]; 1815d62aba7SRoger Pau Monné } shared_page __attribute__((aligned(PAGE_SIZE))); 1825d62aba7SRoger Pau Monné static struct xen_add_to_physmap xatp = { 1835d62aba7SRoger Pau Monné .domid = DOMID_SELF, 1845d62aba7SRoger Pau Monné .space = XENMAPSPACE_shared_info, 1855d62aba7SRoger Pau Monné }; 1865d62aba7SRoger Pau Monné int rc; 1875d62aba7SRoger Pau Monné 1885d62aba7SRoger Pau Monné _Static_assert(sizeof(shared_page) == PAGE_SIZE, 1895d62aba7SRoger Pau Monné "invalid Xen shared_info struct size"); 1905d62aba7SRoger Pau Monné 1915d62aba7SRoger Pau Monné if (xatp.gpfn == 0) 1925d62aba7SRoger Pau Monné xatp.gpfn = atop(early_init_vtop(&shared_page.shared_info)); 1935d62aba7SRoger Pau Monné 1945d62aba7SRoger Pau Monné rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp); 1955d62aba7SRoger Pau Monné if (rc != 0) { 1965d62aba7SRoger Pau Monné xc_printf("cannot map shared info page: %d\n", rc); 1975d62aba7SRoger Pau Monné HYPERVISOR_shared_info = NULL; 1985d62aba7SRoger Pau Monné } else if (HYPERVISOR_shared_info == NULL) 1995d62aba7SRoger Pau Monné HYPERVISOR_shared_info = &shared_page.shared_info; 2005d62aba7SRoger Pau Monné 2015d62aba7SRoger Pau Monné return (rc); 2025d62aba7SRoger Pau Monné } 2035d62aba7SRoger Pau Monné 204027b66d6SRoger Pau Monné static void 205027b66d6SRoger Pau Monné fixup_console(void) 206027b66d6SRoger Pau Monné { 207027b66d6SRoger Pau Monné struct xen_platform_op op = { 208027b66d6SRoger Pau Monné .cmd = XENPF_get_dom0_console, 209027b66d6SRoger Pau Monné }; 210027b66d6SRoger Pau Monné xenpf_dom0_console_t *console = &op.u.dom0_console; 211027b66d6SRoger Pau Monné union { 212027b66d6SRoger Pau Monné struct efi_fb efi; 213027b66d6SRoger Pau Monné struct vbe_fb vbe; 214027b66d6SRoger Pau Monné } *fb = NULL; 215027b66d6SRoger Pau Monné int size; 216027b66d6SRoger Pau Monné 217027b66d6SRoger Pau Monné size = HYPERVISOR_platform_op(&op); 218027b66d6SRoger Pau Monné if (size < 0) { 219027b66d6SRoger Pau Monné xc_printf("Failed to get video console info: %d\n", size); 220027b66d6SRoger Pau Monné return; 221027b66d6SRoger Pau Monné } 222027b66d6SRoger Pau Monné 223027b66d6SRoger Pau Monné switch (console->video_type) { 224027b66d6SRoger Pau Monné case XEN_VGATYPE_VESA_LFB: 225*b72ae900SAhmad Khalifa fb = (__typeof__ (fb))preload_search_info(preload_kmdp, 226027b66d6SRoger Pau Monné MODINFO_METADATA | MODINFOMD_VBE_FB); 227027b66d6SRoger Pau Monné 228027b66d6SRoger Pau Monné if (fb == NULL) { 229027b66d6SRoger Pau Monné xc_printf("No VBE FB in kernel metadata\n"); 230027b66d6SRoger Pau Monné return; 231027b66d6SRoger Pau Monné } 232027b66d6SRoger Pau Monné 233027b66d6SRoger Pau Monné _Static_assert(offsetof(struct vbe_fb, fb_bpp) == 234027b66d6SRoger Pau Monné offsetof(struct efi_fb, fb_mask_reserved) + 235027b66d6SRoger Pau Monné sizeof(fb->efi.fb_mask_reserved), 236027b66d6SRoger Pau Monné "Bad structure overlay\n"); 237027b66d6SRoger Pau Monné fb->vbe.fb_bpp = console->u.vesa_lfb.bits_per_pixel; 238027b66d6SRoger Pau Monné /* FALLTHROUGH */ 239027b66d6SRoger Pau Monné case XEN_VGATYPE_EFI_LFB: 240027b66d6SRoger Pau Monné if (fb == NULL) { 241*b72ae900SAhmad Khalifa fb = (__typeof__ (fb))preload_search_info(preload_kmdp, 242027b66d6SRoger Pau Monné MODINFO_METADATA | MODINFOMD_EFI_FB); 243027b66d6SRoger Pau Monné if (fb == NULL) { 244027b66d6SRoger Pau Monné xc_printf("No EFI FB in kernel metadata\n"); 245027b66d6SRoger Pau Monné return; 246027b66d6SRoger Pau Monné } 247027b66d6SRoger Pau Monné } 248027b66d6SRoger Pau Monné 249027b66d6SRoger Pau Monné fb->efi.fb_addr = console->u.vesa_lfb.lfb_base; 250027b66d6SRoger Pau Monné if (size > 251027b66d6SRoger Pau Monné offsetof(xenpf_dom0_console_t, u.vesa_lfb.ext_lfb_base)) 252027b66d6SRoger Pau Monné fb->efi.fb_addr |= 253027b66d6SRoger Pau Monné (uint64_t)console->u.vesa_lfb.ext_lfb_base << 32; 254027b66d6SRoger Pau Monné fb->efi.fb_size = console->u.vesa_lfb.lfb_size << 16; 255027b66d6SRoger Pau Monné fb->efi.fb_height = console->u.vesa_lfb.height; 256027b66d6SRoger Pau Monné fb->efi.fb_width = console->u.vesa_lfb.width; 257027b66d6SRoger Pau Monné fb->efi.fb_stride = (console->u.vesa_lfb.bytes_per_line << 3) / 258027b66d6SRoger Pau Monné console->u.vesa_lfb.bits_per_pixel; 259027b66d6SRoger Pau Monné #define FBMASK(c) \ 260027b66d6SRoger Pau Monné ((~0u << console->u.vesa_lfb.c ## _pos) & \ 261027b66d6SRoger Pau Monné (~0u >> (32 - console->u.vesa_lfb.c ## _pos - \ 262027b66d6SRoger Pau Monné console->u.vesa_lfb.c ## _size))) 263027b66d6SRoger Pau Monné fb->efi.fb_mask_red = FBMASK(red); 264027b66d6SRoger Pau Monné fb->efi.fb_mask_green = FBMASK(green); 265027b66d6SRoger Pau Monné fb->efi.fb_mask_blue = FBMASK(blue); 266027b66d6SRoger Pau Monné fb->efi.fb_mask_reserved = FBMASK(rsvd); 267027b66d6SRoger Pau Monné #undef FBMASK 268027b66d6SRoger Pau Monné break; 269027b66d6SRoger Pau Monné 270027b66d6SRoger Pau Monné default: 271027b66d6SRoger Pau Monné xc_printf("Video console type unsupported\n"); 272027b66d6SRoger Pau Monné return; 273027b66d6SRoger Pau Monné } 274027b66d6SRoger Pau Monné } 275027b66d6SRoger Pau Monné 2769a687d1fSRoger Pau Monné /* Early initialization when running as a Xen guest. */ 2779a687d1fSRoger Pau Monné void 2789a687d1fSRoger Pau Monné xen_early_init(void) 2799a687d1fSRoger Pau Monné { 2809a687d1fSRoger Pau Monné uint32_t regs[4]; 2815d62aba7SRoger Pau Monné int rc; 2829a687d1fSRoger Pau Monné 2838f5406c7SRoger Pau Monné if (hv_high < hv_base + 2) { 2848f5406c7SRoger Pau Monné xc_printf("Invalid maximum leaves for hv_base\n"); 2858f5406c7SRoger Pau Monné vm_guest = VM_GUEST_VM; 2869a687d1fSRoger Pau Monné return; 2878f5406c7SRoger Pau Monné } 2889a687d1fSRoger Pau Monné 2899a687d1fSRoger Pau Monné /* Find the hypercall pages. */ 2908f5406c7SRoger Pau Monné do_cpuid(hv_base + 2, regs); 2919a687d1fSRoger Pau Monné if (regs[0] != 1) { 2929a687d1fSRoger Pau Monné xc_printf("Invalid number of hypercall pages %u\n", 2939a687d1fSRoger Pau Monné regs[0]); 2949a687d1fSRoger Pau Monné vm_guest = VM_GUEST_VM; 2959a687d1fSRoger Pau Monné return; 2969a687d1fSRoger Pau Monné } 2979a687d1fSRoger Pau Monné 2989a687d1fSRoger Pau Monné wrmsr(regs[1], early_init_vtop(&hypercall_page)); 2995d62aba7SRoger Pau Monné 3005d62aba7SRoger Pau Monné rc = map_shared_info(); 3015d62aba7SRoger Pau Monné if (rc != 0) { 3025d62aba7SRoger Pau Monné vm_guest = VM_GUEST_VM; 3035d62aba7SRoger Pau Monné return; 3045d62aba7SRoger Pau Monné } 305027b66d6SRoger Pau Monné 306027b66d6SRoger Pau Monné if (xen_initial_domain()) 307027b66d6SRoger Pau Monné /* Fixup video console information in case Xen changed the mode. */ 308027b66d6SRoger Pau Monné fixup_console(); 3099a687d1fSRoger Pau Monné } 3109a687d1fSRoger Pau Monné 31127c36a12SRoger Pau Monné static int 31227c36a12SRoger Pau Monné set_percpu_callback(unsigned int vcpu) 31327c36a12SRoger Pau Monné { 31427c36a12SRoger Pau Monné struct xen_hvm_evtchn_upcall_vector vec; 31527c36a12SRoger Pau Monné int error; 31627c36a12SRoger Pau Monné 31727c36a12SRoger Pau Monné vec.vcpu = vcpu; 31827c36a12SRoger Pau Monné vec.vector = IDT_EVTCHN; 31927c36a12SRoger Pau Monné error = HYPERVISOR_hvm_op(HVMOP_set_evtchn_upcall_vector, &vec); 32027c36a12SRoger Pau Monné 32127c36a12SRoger Pau Monné return (error != 0 ? xen_translate_error(error) : 0); 32227c36a12SRoger Pau Monné } 32327c36a12SRoger Pau Monné 32476acc41fSJustin T. Gibbs /* 32576acc41fSJustin T. Gibbs * Tell the hypervisor how to contact us for event channel callbacks. 32676acc41fSJustin T. Gibbs */ 32776acc41fSJustin T. Gibbs void 32876acc41fSJustin T. Gibbs xen_hvm_set_callback(device_t dev) 32976acc41fSJustin T. Gibbs { 33076acc41fSJustin T. Gibbs struct xen_hvm_param xhp; 33176acc41fSJustin T. Gibbs int irq; 33276acc41fSJustin T. Gibbs 333428b7ca2SJustin T. Gibbs if (xen_vector_callback_enabled) 334428b7ca2SJustin T. Gibbs return; 335428b7ca2SJustin T. Gibbs 33676acc41fSJustin T. Gibbs xhp.domid = DOMID_SELF; 33776acc41fSJustin T. Gibbs xhp.index = HVM_PARAM_CALLBACK_IRQ; 338f5f4f7f2SJustin T. Gibbs if (xen_feature(XENFEAT_hvm_callback_vector) != 0) { 33976acc41fSJustin T. Gibbs int error; 34076acc41fSJustin T. Gibbs 34127c36a12SRoger Pau Monné error = set_percpu_callback(0); 34227c36a12SRoger Pau Monné if (error == 0) { 34327c36a12SRoger Pau Monné xen_evtchn_needs_ack = true; 34427c36a12SRoger Pau Monné /* Trick toolstack to think we are enlightened */ 34527c36a12SRoger Pau Monné xhp.value = 1; 34627c36a12SRoger Pau Monné } else 34776acc41fSJustin T. Gibbs xhp.value = HVM_CALLBACK_VECTOR(IDT_EVTCHN); 34876acc41fSJustin T. Gibbs error = HYPERVISOR_hvm_op(HVMOP_set_param, &xhp); 34976acc41fSJustin T. Gibbs if (error == 0) { 35076acc41fSJustin T. Gibbs xen_vector_callback_enabled = 1; 35176acc41fSJustin T. Gibbs return; 35227c36a12SRoger Pau Monné } else if (xen_evtchn_needs_ack) 35327c36a12SRoger Pau Monné panic("Unable to setup fake HVM param: %d", error); 35427c36a12SRoger Pau Monné 35576acc41fSJustin T. Gibbs printf("Xen HVM callback vector registration failed (%d). " 356f5f4f7f2SJustin T. Gibbs "Falling back to emulated device interrupt\n", error); 35776acc41fSJustin T. Gibbs } 35876acc41fSJustin T. Gibbs xen_vector_callback_enabled = 0; 35976acc41fSJustin T. Gibbs if (dev == NULL) { 36076acc41fSJustin T. Gibbs /* 36176acc41fSJustin T. Gibbs * Called from early boot or resume. 36276acc41fSJustin T. Gibbs * xenpci will invoke us again later. 36376acc41fSJustin T. Gibbs */ 36476acc41fSJustin T. Gibbs return; 36576acc41fSJustin T. Gibbs } 36676acc41fSJustin T. Gibbs 36776acc41fSJustin T. Gibbs irq = pci_get_irq(dev); 36876acc41fSJustin T. Gibbs if (irq < 16) { 36976acc41fSJustin T. Gibbs xhp.value = HVM_CALLBACK_GSI(irq); 37076acc41fSJustin T. Gibbs } else { 37176acc41fSJustin T. Gibbs u_int slot; 37276acc41fSJustin T. Gibbs u_int pin; 37376acc41fSJustin T. Gibbs 37476acc41fSJustin T. Gibbs slot = pci_get_slot(dev); 37576acc41fSJustin T. Gibbs pin = pci_get_intpin(dev) - 1; 37676acc41fSJustin T. Gibbs xhp.value = HVM_CALLBACK_PCI_INTX(slot, pin); 37776acc41fSJustin T. Gibbs } 37876acc41fSJustin T. Gibbs 379f5f4f7f2SJustin T. Gibbs if (HYPERVISOR_hvm_op(HVMOP_set_param, &xhp) != 0) 38076acc41fSJustin T. Gibbs panic("Can't set evtchn callback"); 38176acc41fSJustin T. Gibbs } 38276acc41fSJustin T. Gibbs 38376acc41fSJustin T. Gibbs #define XEN_MAGIC_IOPORT 0x10 38476acc41fSJustin T. Gibbs enum { 38576acc41fSJustin T. Gibbs XMI_MAGIC = 0x49d2, 38676acc41fSJustin T. Gibbs XMI_UNPLUG_IDE_DISKS = 0x01, 38776acc41fSJustin T. Gibbs XMI_UNPLUG_NICS = 0x02, 38876acc41fSJustin T. Gibbs XMI_UNPLUG_IDE_EXCEPT_PRI_MASTER = 0x04 38976acc41fSJustin T. Gibbs }; 39076acc41fSJustin T. Gibbs 39176acc41fSJustin T. Gibbs static void 39276acc41fSJustin T. Gibbs xen_hvm_disable_emulated_devices(void) 39376acc41fSJustin T. Gibbs { 394f8f1bb83SRoger Pau Monné u_short disable_devs = 0; 3952afbc44bSRoger Pau Monné 3962afbc44bSRoger Pau Monné if (xen_pv_domain()) { 3972afbc44bSRoger Pau Monné /* 3982afbc44bSRoger Pau Monné * No emulated devices in the PV case, so no need to unplug 3992afbc44bSRoger Pau Monné * anything. 4002afbc44bSRoger Pau Monné */ 401f8f1bb83SRoger Pau Monné if (xen_disable_pv_disks != 0 || xen_disable_pv_nics != 0) 402f8f1bb83SRoger Pau Monné printf("PV devices cannot be disabled in PV guests\n"); 4032afbc44bSRoger Pau Monné return; 4042afbc44bSRoger Pau Monné } 4052afbc44bSRoger Pau Monné 40676acc41fSJustin T. Gibbs if (inw(XEN_MAGIC_IOPORT) != XMI_MAGIC) 40776acc41fSJustin T. Gibbs return; 40876acc41fSJustin T. Gibbs 409f8f1bb83SRoger Pau Monné if (xen_disable_pv_disks == 0) { 41076acc41fSJustin T. Gibbs if (bootverbose) 411f8f1bb83SRoger Pau Monné printf("XEN: disabling emulated disks\n"); 412f8f1bb83SRoger Pau Monné disable_devs |= XMI_UNPLUG_IDE_DISKS; 413f8f1bb83SRoger Pau Monné } 414f8f1bb83SRoger Pau Monné if (xen_disable_pv_nics == 0) { 415f8f1bb83SRoger Pau Monné if (bootverbose) 416f8f1bb83SRoger Pau Monné printf("XEN: disabling emulated nics\n"); 417f8f1bb83SRoger Pau Monné disable_devs |= XMI_UNPLUG_NICS; 418f8f1bb83SRoger Pau Monné } 419f8f1bb83SRoger Pau Monné 420f8f1bb83SRoger Pau Monné if (disable_devs != 0) 421f8f1bb83SRoger Pau Monné outw(XEN_MAGIC_IOPORT, disable_devs); 42276acc41fSJustin T. Gibbs } 42376acc41fSJustin T. Gibbs 424428b7ca2SJustin T. Gibbs static void 425428b7ca2SJustin T. Gibbs xen_hvm_init(enum xen_hvm_init_type init_type) 426428b7ca2SJustin T. Gibbs { 4278f5406c7SRoger Pau Monné unsigned int i; 428428b7ca2SJustin T. Gibbs 429b0165dc4SRoger Pau Monné if (!xen_domain() || 430b0165dc4SRoger Pau Monné init_type == XEN_HVM_INIT_CANCELLED_SUSPEND) 431428b7ca2SJustin T. Gibbs return; 432428b7ca2SJustin T. Gibbs 4338f5406c7SRoger Pau Monné hypervisor_version(); 434428b7ca2SJustin T. Gibbs 435428b7ca2SJustin T. Gibbs switch (init_type) { 43607c2711fSRoger Pau Monné case XEN_HVM_INIT_LATE: 437428b7ca2SJustin T. Gibbs setup_xen_features(); 4386b779733SJohn Baldwin #ifdef SMP 43994083754SJustin T. Gibbs cpu_ops = xen_hvm_cpu_ops; 4406b779733SJohn Baldwin #endif 441428b7ca2SJustin T. Gibbs break; 442428b7ca2SJustin T. Gibbs case XEN_HVM_INIT_RESUME: 443566a5f50SJustin T. Gibbs /* Clear stale vcpu_info. */ 444566a5f50SJustin T. Gibbs CPU_FOREACH(i) 445566a5f50SJustin T. Gibbs DPCPU_ID_SET(i, vcpu_info, NULL); 4468f5406c7SRoger Pau Monné 4478f5406c7SRoger Pau Monné if (map_shared_info() != 0) 4488f5406c7SRoger Pau Monné panic("cannot map Xen shared info page"); 4498f5406c7SRoger Pau Monné 450428b7ca2SJustin T. Gibbs break; 451428b7ca2SJustin T. Gibbs default: 452428b7ca2SJustin T. Gibbs panic("Unsupported HVM initialization type"); 453428b7ca2SJustin T. Gibbs } 454428b7ca2SJustin T. Gibbs 455428b7ca2SJustin T. Gibbs xen_vector_callback_enabled = 0; 45627c36a12SRoger Pau Monné xen_evtchn_needs_ack = false; 457428b7ca2SJustin T. Gibbs xen_hvm_set_callback(NULL); 4582afbc44bSRoger Pau Monné 459428b7ca2SJustin T. Gibbs xen_hvm_disable_emulated_devices(); 460428b7ca2SJustin T. Gibbs } 461428b7ca2SJustin T. Gibbs 46276acc41fSJustin T. Gibbs void 46376acc41fSJustin T. Gibbs xen_hvm_suspend(void) 46476acc41fSJustin T. Gibbs { 46576acc41fSJustin T. Gibbs } 46676acc41fSJustin T. Gibbs 46776acc41fSJustin T. Gibbs void 468428b7ca2SJustin T. Gibbs xen_hvm_resume(bool suspend_cancelled) 46976acc41fSJustin T. Gibbs { 470f5f4f7f2SJustin T. Gibbs 471428b7ca2SJustin T. Gibbs xen_hvm_init(suspend_cancelled ? 472428b7ca2SJustin T. Gibbs XEN_HVM_INIT_CANCELLED_SUSPEND : XEN_HVM_INIT_RESUME); 473428b7ca2SJustin T. Gibbs 474428b7ca2SJustin T. Gibbs /* Register vcpu_info area for CPU#0. */ 475566a5f50SJustin T. Gibbs xen_hvm_cpu_init(); 47676acc41fSJustin T. Gibbs } 47776acc41fSJustin T. Gibbs 47876acc41fSJustin T. Gibbs static void 479428b7ca2SJustin T. Gibbs xen_hvm_sysinit(void *arg __unused) 48076acc41fSJustin T. Gibbs { 48107c2711fSRoger Pau Monné xen_hvm_init(XEN_HVM_INIT_LATE); 48276acc41fSJustin T. Gibbs } 48385189975SRoger Pau Monné SYSINIT(xen_hvm_init, SI_SUB_HYPERVISOR, SI_ORDER_FIRST, xen_hvm_sysinit, NULL); 4845fdd34eeSJustin T. Gibbs 4855fdd34eeSJustin T. Gibbs static void 486566a5f50SJustin T. Gibbs xen_hvm_cpu_init(void) 48776acc41fSJustin T. Gibbs { 48885189975SRoger Pau Monné uint32_t regs[4]; 48920fc5bf7SElliott Mitchell int rc; 49076acc41fSJustin T. Gibbs 491566a5f50SJustin T. Gibbs if (!xen_domain()) 492566a5f50SJustin T. Gibbs return; 493566a5f50SJustin T. Gibbs 494428b7ca2SJustin T. Gibbs if (DPCPU_GET(vcpu_info) != NULL) { 495428b7ca2SJustin T. Gibbs /* 496428b7ca2SJustin T. Gibbs * vcpu_info is already set. We're resuming 497428b7ca2SJustin T. Gibbs * from a failed migration and our pre-suspend 498428b7ca2SJustin T. Gibbs * configuration is still valid. 499428b7ca2SJustin T. Gibbs */ 500428b7ca2SJustin T. Gibbs return; 501428b7ca2SJustin T. Gibbs } 502428b7ca2SJustin T. Gibbs 50385189975SRoger Pau Monné /* 50485189975SRoger Pau Monné * Set vCPU ID. If available fetch the ID from CPUID, if not just use 50585189975SRoger Pau Monné * the ACPI ID. 50685189975SRoger Pau Monné */ 507f0cf86c0SRoger Pau Monné KASSERT(hv_base != 0, ("Invalid base Xen CPUID leaf")); 508f0cf86c0SRoger Pau Monné cpuid_count(hv_base + 4, 0, regs); 509c9a591b0SRoger Pau Monné KASSERT((regs[0] & XEN_HVM_CPUID_VCPU_ID_PRESENT) || 510c9a591b0SRoger Pau Monné !xen_pv_domain(), 511c9a591b0SRoger Pau Monné ("Xen PV domain without vcpu_id in cpuid")); 51285189975SRoger Pau Monné PCPU_SET(vcpu_id, (regs[0] & XEN_HVM_CPUID_VCPU_ID_PRESENT) ? 51385189975SRoger Pau Monné regs[1] : PCPU_GET(acpi_id)); 51485189975SRoger Pau Monné 51527c36a12SRoger Pau Monné if (xen_evtchn_needs_ack && !IS_BSP()) { 51627c36a12SRoger Pau Monné /* 51727c36a12SRoger Pau Monné * Setup the per-vpcu event channel upcall vector. This is only 51827c36a12SRoger Pau Monné * required when using the new HVMOP_set_evtchn_upcall_vector 51927c36a12SRoger Pau Monné * hypercall, which allows using a different vector for each 52027c36a12SRoger Pau Monné * vCPU. Note that FreeBSD uses the same vector for all vCPUs 52127c36a12SRoger Pau Monné * because it's not dynamically allocated. 52227c36a12SRoger Pau Monné */ 52327c36a12SRoger Pau Monné rc = set_percpu_callback(PCPU_GET(vcpu_id)); 52427c36a12SRoger Pau Monné if (rc != 0) 52527c36a12SRoger Pau Monné panic("Event channel upcall vector setup failed: %d", 52627c36a12SRoger Pau Monné rc); 52727c36a12SRoger Pau Monné } 52827c36a12SRoger Pau Monné 52920fc5bf7SElliott Mitchell xen_setup_vcpu_info(); 53076acc41fSJustin T. Gibbs } 531566a5f50SJustin T. Gibbs SYSINIT(xen_hvm_cpu_init, SI_SUB_INTR, SI_ORDER_FIRST, xen_hvm_cpu_init, NULL); 532091febc0SRoger Pau Monné 533091febc0SRoger Pau Monné bool 534091febc0SRoger Pau Monné xen_has_iommu_maps(void) 535091febc0SRoger Pau Monné { 536091febc0SRoger Pau Monné uint32_t regs[4]; 537091febc0SRoger Pau Monné 538f0cf86c0SRoger Pau Monné KASSERT(hv_base != 0, ("Invalid base Xen CPUID leaf")); 539f0cf86c0SRoger Pau Monné cpuid_count(hv_base + 4, 0, regs); 540091febc0SRoger Pau Monné 541091febc0SRoger Pau Monné return (regs[0] & XEN_HVM_CPUID_IOMMU_MAPPINGS); 542091febc0SRoger Pau Monné } 54369c47485SRoger Pau Monné 54469c47485SRoger Pau Monné int 54569c47485SRoger Pau Monné xen_arch_init_physmem(device_t dev, struct rman *mem) 54669c47485SRoger Pau Monné { 54769c47485SRoger Pau Monné static struct bios_smap smap[128]; 54869c47485SRoger Pau Monné struct xen_memory_map memmap = { 54969c47485SRoger Pau Monné .nr_entries = nitems(smap), 55069c47485SRoger Pau Monné }; 55169c47485SRoger Pau Monné unsigned int i; 55269c47485SRoger Pau Monné int error; 55369c47485SRoger Pau Monné 55469c47485SRoger Pau Monné set_xen_guest_handle(memmap.buffer, smap); 55569c47485SRoger Pau Monné error = HYPERVISOR_memory_op(XENMEM_memory_map, &memmap); 55669c47485SRoger Pau Monné if (error != 0) 557c1287a3bSRoger Pau Monné return (0); 55869c47485SRoger Pau Monné 55969c47485SRoger Pau Monné /* 56069c47485SRoger Pau Monné * Fill with UNUSABLE regions, as it's always fine to use those for 56169c47485SRoger Pau Monné * foreign mappings, they will never be populated. 56269c47485SRoger Pau Monné */ 56369c47485SRoger Pau Monné for (i = 0; i < memmap.nr_entries; i++) { 56469c47485SRoger Pau Monné const vm_paddr_t max_phys = cpu_getmaxphyaddr(); 56569c47485SRoger Pau Monné vm_paddr_t start, end; 56669c47485SRoger Pau Monné 56769c47485SRoger Pau Monné if (smap[i].type != SMAP_TYPE_ACPI_ERROR) 56869c47485SRoger Pau Monné continue; 56969c47485SRoger Pau Monné 57069c47485SRoger Pau Monné start = round_page(smap[i].base); 57169c47485SRoger Pau Monné /* In 32bit mode we possibly need to truncate the addresses. */ 57269c47485SRoger Pau Monné end = MIN(trunc_page(smap[i].base + smap[i].length) - 1, 57369c47485SRoger Pau Monné max_phys); 57469c47485SRoger Pau Monné 57569c47485SRoger Pau Monné if (start >= end) 57669c47485SRoger Pau Monné continue; 57769c47485SRoger Pau Monné 57869c47485SRoger Pau Monné if (bootverbose != 0) 57969c47485SRoger Pau Monné device_printf(dev, 58069c47485SRoger Pau Monné "scratch mapping region @ [%016jx, %016jx]\n", 58169c47485SRoger Pau Monné start, end); 58269c47485SRoger Pau Monné 58369c47485SRoger Pau Monné error = rman_manage_region(mem, start, end); 58469c47485SRoger Pau Monné if (error != 0) 58569c47485SRoger Pau Monné device_printf(dev, 58669c47485SRoger Pau Monné "unable to add scratch region [%016jx, %016jx]: %d\n", 58769c47485SRoger Pau Monné start, end, error); 58869c47485SRoger Pau Monné } 58969c47485SRoger Pau Monné 59069c47485SRoger Pau Monné return (0); 59169c47485SRoger Pau Monné } 592