xref: /qemu/hw/i386/kvm/xen_overlay.c (revision d40ddd529004709350202114fc1c2fc99127647d)
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