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