1*d40ddd52SDavid Woodhouse /* 2*d40ddd52SDavid Woodhouse * QEMU Xen emulation: Shared/overlay pages support 3*d40ddd52SDavid Woodhouse * 4*d40ddd52SDavid Woodhouse * Copyright © 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. 5*d40ddd52SDavid Woodhouse * 6*d40ddd52SDavid Woodhouse * Authors: David Woodhouse <dwmw2@infradead.org> 7*d40ddd52SDavid Woodhouse * 8*d40ddd52SDavid Woodhouse * This work is licensed under the terms of the GNU GPL, version 2 or later. 9*d40ddd52SDavid Woodhouse * See the COPYING file in the top-level directory. 10*d40ddd52SDavid Woodhouse */ 11*d40ddd52SDavid Woodhouse 12*d40ddd52SDavid Woodhouse #include "qemu/osdep.h" 13*d40ddd52SDavid Woodhouse #include "qemu/host-utils.h" 14*d40ddd52SDavid Woodhouse #include "qemu/module.h" 15*d40ddd52SDavid Woodhouse #include "qemu/main-loop.h" 16*d40ddd52SDavid Woodhouse #include "qapi/error.h" 17*d40ddd52SDavid Woodhouse #include "qom/object.h" 18*d40ddd52SDavid Woodhouse #include "exec/target_page.h" 19*d40ddd52SDavid Woodhouse #include "exec/address-spaces.h" 20*d40ddd52SDavid Woodhouse #include "migration/vmstate.h" 21*d40ddd52SDavid Woodhouse 22*d40ddd52SDavid Woodhouse #include "hw/sysbus.h" 23*d40ddd52SDavid Woodhouse #include "hw/xen/xen.h" 24*d40ddd52SDavid Woodhouse #include "xen_overlay.h" 25*d40ddd52SDavid Woodhouse 26*d40ddd52SDavid Woodhouse #include "sysemu/kvm.h" 27*d40ddd52SDavid Woodhouse #include "sysemu/kvm_xen.h" 28*d40ddd52SDavid Woodhouse #include <linux/kvm.h> 29*d40ddd52SDavid Woodhouse 30*d40ddd52SDavid Woodhouse #include "hw/xen/interface/memory.h" 31*d40ddd52SDavid Woodhouse 32*d40ddd52SDavid Woodhouse 33*d40ddd52SDavid Woodhouse #define TYPE_XEN_OVERLAY "xen-overlay" 34*d40ddd52SDavid Woodhouse OBJECT_DECLARE_SIMPLE_TYPE(XenOverlayState, XEN_OVERLAY) 35*d40ddd52SDavid Woodhouse 36*d40ddd52SDavid Woodhouse #define XEN_PAGE_SHIFT 12 37*d40ddd52SDavid Woodhouse #define XEN_PAGE_SIZE (1ULL << XEN_PAGE_SHIFT) 38*d40ddd52SDavid Woodhouse 39*d40ddd52SDavid Woodhouse struct XenOverlayState { 40*d40ddd52SDavid Woodhouse /*< private >*/ 41*d40ddd52SDavid Woodhouse SysBusDevice busdev; 42*d40ddd52SDavid Woodhouse /*< public >*/ 43*d40ddd52SDavid Woodhouse 44*d40ddd52SDavid Woodhouse MemoryRegion shinfo_mem; 45*d40ddd52SDavid Woodhouse void *shinfo_ptr; 46*d40ddd52SDavid Woodhouse uint64_t shinfo_gpa; 47*d40ddd52SDavid Woodhouse }; 48*d40ddd52SDavid Woodhouse 49*d40ddd52SDavid Woodhouse struct XenOverlayState *xen_overlay_singleton; 50*d40ddd52SDavid Woodhouse 51*d40ddd52SDavid Woodhouse static void xen_overlay_do_map_page(MemoryRegion *page, uint64_t gpa) 52*d40ddd52SDavid Woodhouse { 53*d40ddd52SDavid Woodhouse /* 54*d40ddd52SDavid Woodhouse * Xen allows guests to map the same page as many times as it likes 55*d40ddd52SDavid Woodhouse * into guest physical frames. We don't, because it would be hard 56*d40ddd52SDavid Woodhouse * to track and restore them all. One mapping of each page is 57*d40ddd52SDavid Woodhouse * perfectly sufficient for all known guests... and we've tested 58*d40ddd52SDavid Woodhouse * that theory on a few now in other implementations. dwmw2. 59*d40ddd52SDavid Woodhouse */ 60*d40ddd52SDavid Woodhouse if (memory_region_is_mapped(page)) { 61*d40ddd52SDavid Woodhouse if (gpa == INVALID_GPA) { 62*d40ddd52SDavid Woodhouse memory_region_del_subregion(get_system_memory(), page); 63*d40ddd52SDavid Woodhouse } else { 64*d40ddd52SDavid Woodhouse /* Just move it */ 65*d40ddd52SDavid Woodhouse memory_region_set_address(page, gpa); 66*d40ddd52SDavid Woodhouse } 67*d40ddd52SDavid Woodhouse } else if (gpa != INVALID_GPA) { 68*d40ddd52SDavid Woodhouse memory_region_add_subregion_overlap(get_system_memory(), gpa, page, 0); 69*d40ddd52SDavid Woodhouse } 70*d40ddd52SDavid Woodhouse } 71*d40ddd52SDavid Woodhouse 72*d40ddd52SDavid Woodhouse /* KVM is the only existing back end for now. Let's not overengineer it yet. */ 73*d40ddd52SDavid Woodhouse static int xen_overlay_set_be_shinfo(uint64_t gfn) 74*d40ddd52SDavid Woodhouse { 75*d40ddd52SDavid Woodhouse struct kvm_xen_hvm_attr xa = { 76*d40ddd52SDavid Woodhouse .type = KVM_XEN_ATTR_TYPE_SHARED_INFO, 77*d40ddd52SDavid Woodhouse .u.shared_info.gfn = gfn, 78*d40ddd52SDavid Woodhouse }; 79*d40ddd52SDavid Woodhouse 80*d40ddd52SDavid Woodhouse return kvm_vm_ioctl(kvm_state, KVM_XEN_HVM_SET_ATTR, &xa); 81*d40ddd52SDavid Woodhouse } 82*d40ddd52SDavid Woodhouse 83*d40ddd52SDavid Woodhouse 84*d40ddd52SDavid Woodhouse static void xen_overlay_realize(DeviceState *dev, Error **errp) 85*d40ddd52SDavid Woodhouse { 86*d40ddd52SDavid Woodhouse XenOverlayState *s = XEN_OVERLAY(dev); 87*d40ddd52SDavid Woodhouse 88*d40ddd52SDavid Woodhouse if (xen_mode != XEN_EMULATE) { 89*d40ddd52SDavid Woodhouse error_setg(errp, "Xen overlay page support is for Xen emulation"); 90*d40ddd52SDavid Woodhouse return; 91*d40ddd52SDavid Woodhouse } 92*d40ddd52SDavid Woodhouse 93*d40ddd52SDavid Woodhouse memory_region_init_ram(&s->shinfo_mem, OBJECT(dev), "xen:shared_info", 94*d40ddd52SDavid Woodhouse XEN_PAGE_SIZE, &error_abort); 95*d40ddd52SDavid Woodhouse memory_region_set_enabled(&s->shinfo_mem, true); 96*d40ddd52SDavid Woodhouse 97*d40ddd52SDavid Woodhouse s->shinfo_ptr = memory_region_get_ram_ptr(&s->shinfo_mem); 98*d40ddd52SDavid Woodhouse s->shinfo_gpa = INVALID_GPA; 99*d40ddd52SDavid Woodhouse memset(s->shinfo_ptr, 0, XEN_PAGE_SIZE); 100*d40ddd52SDavid Woodhouse } 101*d40ddd52SDavid Woodhouse 102*d40ddd52SDavid Woodhouse static int xen_overlay_post_load(void *opaque, int version_id) 103*d40ddd52SDavid Woodhouse { 104*d40ddd52SDavid Woodhouse XenOverlayState *s = opaque; 105*d40ddd52SDavid Woodhouse 106*d40ddd52SDavid Woodhouse if (s->shinfo_gpa != INVALID_GPA) { 107*d40ddd52SDavid Woodhouse xen_overlay_do_map_page(&s->shinfo_mem, s->shinfo_gpa); 108*d40ddd52SDavid Woodhouse xen_overlay_set_be_shinfo(s->shinfo_gpa >> XEN_PAGE_SHIFT); 109*d40ddd52SDavid Woodhouse } 110*d40ddd52SDavid Woodhouse 111*d40ddd52SDavid Woodhouse return 0; 112*d40ddd52SDavid Woodhouse } 113*d40ddd52SDavid Woodhouse 114*d40ddd52SDavid Woodhouse static bool xen_overlay_is_needed(void *opaque) 115*d40ddd52SDavid Woodhouse { 116*d40ddd52SDavid Woodhouse return xen_mode == XEN_EMULATE; 117*d40ddd52SDavid Woodhouse } 118*d40ddd52SDavid Woodhouse 119*d40ddd52SDavid Woodhouse static const VMStateDescription xen_overlay_vmstate = { 120*d40ddd52SDavid Woodhouse .name = "xen_overlay", 121*d40ddd52SDavid Woodhouse .version_id = 1, 122*d40ddd52SDavid Woodhouse .minimum_version_id = 1, 123*d40ddd52SDavid Woodhouse .needed = xen_overlay_is_needed, 124*d40ddd52SDavid Woodhouse .post_load = xen_overlay_post_load, 125*d40ddd52SDavid Woodhouse .fields = (VMStateField[]) { 126*d40ddd52SDavid Woodhouse VMSTATE_UINT64(shinfo_gpa, XenOverlayState), 127*d40ddd52SDavid Woodhouse VMSTATE_END_OF_LIST() 128*d40ddd52SDavid Woodhouse } 129*d40ddd52SDavid Woodhouse }; 130*d40ddd52SDavid Woodhouse 131*d40ddd52SDavid Woodhouse static void xen_overlay_reset(DeviceState *dev) 132*d40ddd52SDavid Woodhouse { 133*d40ddd52SDavid Woodhouse kvm_xen_soft_reset(); 134*d40ddd52SDavid Woodhouse } 135*d40ddd52SDavid Woodhouse 136*d40ddd52SDavid Woodhouse static void xen_overlay_class_init(ObjectClass *klass, void *data) 137*d40ddd52SDavid Woodhouse { 138*d40ddd52SDavid Woodhouse DeviceClass *dc = DEVICE_CLASS(klass); 139*d40ddd52SDavid Woodhouse 140*d40ddd52SDavid Woodhouse dc->reset = xen_overlay_reset; 141*d40ddd52SDavid Woodhouse dc->realize = xen_overlay_realize; 142*d40ddd52SDavid Woodhouse dc->vmsd = &xen_overlay_vmstate; 143*d40ddd52SDavid Woodhouse } 144*d40ddd52SDavid Woodhouse 145*d40ddd52SDavid Woodhouse static const TypeInfo xen_overlay_info = { 146*d40ddd52SDavid Woodhouse .name = TYPE_XEN_OVERLAY, 147*d40ddd52SDavid Woodhouse .parent = TYPE_SYS_BUS_DEVICE, 148*d40ddd52SDavid Woodhouse .instance_size = sizeof(XenOverlayState), 149*d40ddd52SDavid Woodhouse .class_init = xen_overlay_class_init, 150*d40ddd52SDavid Woodhouse }; 151*d40ddd52SDavid Woodhouse 152*d40ddd52SDavid Woodhouse void xen_overlay_create(void) 153*d40ddd52SDavid Woodhouse { 154*d40ddd52SDavid Woodhouse xen_overlay_singleton = XEN_OVERLAY(sysbus_create_simple(TYPE_XEN_OVERLAY, 155*d40ddd52SDavid Woodhouse -1, NULL)); 156*d40ddd52SDavid Woodhouse 157*d40ddd52SDavid Woodhouse /* If xen_domid wasn't explicitly set, at least make sure it isn't zero. */ 158*d40ddd52SDavid Woodhouse if (xen_domid == DOMID_QEMU) { 159*d40ddd52SDavid Woodhouse xen_domid = 1; 160*d40ddd52SDavid Woodhouse }; 161*d40ddd52SDavid Woodhouse } 162*d40ddd52SDavid Woodhouse 163*d40ddd52SDavid Woodhouse static void xen_overlay_register_types(void) 164*d40ddd52SDavid Woodhouse { 165*d40ddd52SDavid Woodhouse type_register_static(&xen_overlay_info); 166*d40ddd52SDavid Woodhouse } 167*d40ddd52SDavid Woodhouse 168*d40ddd52SDavid Woodhouse type_init(xen_overlay_register_types) 169*d40ddd52SDavid Woodhouse 170*d40ddd52SDavid Woodhouse int xen_overlay_map_shinfo_page(uint64_t gpa) 171*d40ddd52SDavid Woodhouse { 172*d40ddd52SDavid Woodhouse XenOverlayState *s = xen_overlay_singleton; 173*d40ddd52SDavid Woodhouse int ret; 174*d40ddd52SDavid Woodhouse 175*d40ddd52SDavid Woodhouse if (!s) { 176*d40ddd52SDavid Woodhouse return -ENOENT; 177*d40ddd52SDavid Woodhouse } 178*d40ddd52SDavid Woodhouse 179*d40ddd52SDavid Woodhouse assert(qemu_mutex_iothread_locked()); 180*d40ddd52SDavid Woodhouse 181*d40ddd52SDavid Woodhouse if (s->shinfo_gpa) { 182*d40ddd52SDavid Woodhouse /* If removing shinfo page, turn the kernel magic off first */ 183*d40ddd52SDavid Woodhouse ret = xen_overlay_set_be_shinfo(INVALID_GFN); 184*d40ddd52SDavid Woodhouse if (ret) { 185*d40ddd52SDavid Woodhouse return ret; 186*d40ddd52SDavid Woodhouse } 187*d40ddd52SDavid Woodhouse } 188*d40ddd52SDavid Woodhouse 189*d40ddd52SDavid Woodhouse xen_overlay_do_map_page(&s->shinfo_mem, gpa); 190*d40ddd52SDavid Woodhouse if (gpa != INVALID_GPA) { 191*d40ddd52SDavid Woodhouse ret = xen_overlay_set_be_shinfo(gpa >> XEN_PAGE_SHIFT); 192*d40ddd52SDavid Woodhouse if (ret) { 193*d40ddd52SDavid Woodhouse return ret; 194*d40ddd52SDavid Woodhouse } 195*d40ddd52SDavid Woodhouse } 196*d40ddd52SDavid Woodhouse s->shinfo_gpa = gpa; 197*d40ddd52SDavid Woodhouse 198*d40ddd52SDavid Woodhouse return 0; 199*d40ddd52SDavid Woodhouse } 200*d40ddd52SDavid Woodhouse 201*d40ddd52SDavid Woodhouse void *xen_overlay_get_shinfo_ptr(void) 202*d40ddd52SDavid Woodhouse { 203*d40ddd52SDavid Woodhouse XenOverlayState *s = xen_overlay_singleton; 204*d40ddd52SDavid Woodhouse 205*d40ddd52SDavid Woodhouse if (!s) { 206*d40ddd52SDavid Woodhouse return NULL; 207*d40ddd52SDavid Woodhouse } 208*d40ddd52SDavid Woodhouse 209*d40ddd52SDavid Woodhouse return s->shinfo_ptr; 210*d40ddd52SDavid Woodhouse } 211