xref: /qemu/hw/i386/kvm/xen_gnttab.c (revision e995d5cc)
1 /*
2  * QEMU Xen emulation: Grant table 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/lockable.h"
16 #include "qemu/main-loop.h"
17 #include "qapi/error.h"
18 #include "qom/object.h"
19 #include "exec/target_page.h"
20 #include "exec/address-spaces.h"
21 #include "migration/vmstate.h"
22 
23 #include "hw/sysbus.h"
24 #include "hw/xen/xen.h"
25 #include "xen_overlay.h"
26 #include "xen_gnttab.h"
27 
28 #include "sysemu/kvm.h"
29 #include "sysemu/kvm_xen.h"
30 
31 #include "hw/xen/interface/memory.h"
32 #include "hw/xen/interface/grant_table.h"
33 
34 #define TYPE_XEN_GNTTAB "xen-gnttab"
35 OBJECT_DECLARE_SIMPLE_TYPE(XenGnttabState, XEN_GNTTAB)
36 
37 #define XEN_PAGE_SHIFT 12
38 #define XEN_PAGE_SIZE (1ULL << XEN_PAGE_SHIFT)
39 
40 #define ENTRIES_PER_FRAME_V1 (XEN_PAGE_SIZE / sizeof(grant_entry_v1_t))
41 
42 struct XenGnttabState {
43     /*< private >*/
44     SysBusDevice busdev;
45     /*< public >*/
46 
47     QemuMutex gnt_lock;
48 
49     uint32_t nr_frames;
50     uint32_t max_frames;
51 
52     union {
53         grant_entry_v1_t *v1;
54         /* Theoretically, v2 support could be added here. */
55     } entries;
56 
57     MemoryRegion gnt_frames;
58     MemoryRegion *gnt_aliases;
59     uint64_t *gnt_frame_gpas;
60 };
61 
62 struct XenGnttabState *xen_gnttab_singleton;
63 
64 static void xen_gnttab_realize(DeviceState *dev, Error **errp)
65 {
66     XenGnttabState *s = XEN_GNTTAB(dev);
67     int i;
68 
69     if (xen_mode != XEN_EMULATE) {
70         error_setg(errp, "Xen grant table support is for Xen emulation");
71         return;
72     }
73     s->nr_frames = 0;
74     s->max_frames = kvm_xen_get_gnttab_max_frames();
75     memory_region_init_ram(&s->gnt_frames, OBJECT(dev), "xen:grant_table",
76                            XEN_PAGE_SIZE * s->max_frames, &error_abort);
77     memory_region_set_enabled(&s->gnt_frames, true);
78     s->entries.v1 = memory_region_get_ram_ptr(&s->gnt_frames);
79     memset(s->entries.v1, 0, XEN_PAGE_SIZE * s->max_frames);
80 
81     /* Create individual page-sizes aliases for overlays */
82     s->gnt_aliases = (void *)g_new0(MemoryRegion, s->max_frames);
83     s->gnt_frame_gpas = (void *)g_new(uint64_t, s->max_frames);
84     for (i = 0; i < s->max_frames; i++) {
85         memory_region_init_alias(&s->gnt_aliases[i], OBJECT(dev),
86                                  NULL, &s->gnt_frames,
87                                  i * XEN_PAGE_SIZE, XEN_PAGE_SIZE);
88         s->gnt_frame_gpas[i] = INVALID_GPA;
89     }
90 
91     qemu_mutex_init(&s->gnt_lock);
92 
93     xen_gnttab_singleton = s;
94 }
95 
96 static int xen_gnttab_post_load(void *opaque, int version_id)
97 {
98     XenGnttabState *s = XEN_GNTTAB(opaque);
99     uint32_t i;
100 
101     for (i = 0; i < s->nr_frames; i++) {
102         if (s->gnt_frame_gpas[i] != INVALID_GPA) {
103             xen_overlay_do_map_page(&s->gnt_aliases[i], s->gnt_frame_gpas[i]);
104         }
105     }
106     return 0;
107 }
108 
109 static bool xen_gnttab_is_needed(void *opaque)
110 {
111     return xen_mode == XEN_EMULATE;
112 }
113 
114 static const VMStateDescription xen_gnttab_vmstate = {
115     .name = "xen_gnttab",
116     .version_id = 1,
117     .minimum_version_id = 1,
118     .needed = xen_gnttab_is_needed,
119     .post_load = xen_gnttab_post_load,
120     .fields = (VMStateField[]) {
121         VMSTATE_UINT32(nr_frames, XenGnttabState),
122         VMSTATE_VARRAY_UINT32(gnt_frame_gpas, XenGnttabState, nr_frames, 0,
123                               vmstate_info_uint64, uint64_t),
124         VMSTATE_END_OF_LIST()
125     }
126 };
127 
128 static void xen_gnttab_class_init(ObjectClass *klass, void *data)
129 {
130     DeviceClass *dc = DEVICE_CLASS(klass);
131 
132     dc->realize = xen_gnttab_realize;
133     dc->vmsd = &xen_gnttab_vmstate;
134 }
135 
136 static const TypeInfo xen_gnttab_info = {
137     .name          = TYPE_XEN_GNTTAB,
138     .parent        = TYPE_SYS_BUS_DEVICE,
139     .instance_size = sizeof(XenGnttabState),
140     .class_init    = xen_gnttab_class_init,
141 };
142 
143 void xen_gnttab_create(void)
144 {
145     xen_gnttab_singleton = XEN_GNTTAB(sysbus_create_simple(TYPE_XEN_GNTTAB,
146                                                            -1, NULL));
147 }
148 
149 static void xen_gnttab_register_types(void)
150 {
151     type_register_static(&xen_gnttab_info);
152 }
153 
154 type_init(xen_gnttab_register_types)
155 
156 int xen_gnttab_map_page(uint64_t idx, uint64_t gfn)
157 {
158     XenGnttabState *s = xen_gnttab_singleton;
159     uint64_t gpa = gfn << XEN_PAGE_SHIFT;
160 
161     if (!s) {
162         return -ENOTSUP;
163     }
164 
165     if (idx >= s->max_frames) {
166         return -EINVAL;
167     }
168 
169     QEMU_IOTHREAD_LOCK_GUARD();
170     QEMU_LOCK_GUARD(&s->gnt_lock);
171 
172     xen_overlay_do_map_page(&s->gnt_aliases[idx], gpa);
173 
174     s->gnt_frame_gpas[idx] = gpa;
175 
176     if (s->nr_frames <= idx) {
177         s->nr_frames = idx + 1;
178     }
179 
180     return 0;
181 }
182 
183 int xen_gnttab_set_version_op(struct gnttab_set_version *set)
184 {
185     int ret;
186 
187     switch (set->version) {
188     case 1:
189         ret = 0;
190         break;
191 
192     case 2:
193         /* Behave as before set_version was introduced. */
194         ret = -ENOSYS;
195         break;
196 
197     default:
198         ret = -EINVAL;
199     }
200 
201     set->version = 1;
202     return ret;
203 }
204 
205 int xen_gnttab_get_version_op(struct gnttab_get_version *get)
206 {
207     if (get->dom != DOMID_SELF && get->dom != xen_domid) {
208         return -ESRCH;
209     }
210 
211     get->version = 1;
212     return 0;
213 }
214 
215 int xen_gnttab_query_size_op(struct gnttab_query_size *size)
216 {
217     XenGnttabState *s = xen_gnttab_singleton;
218 
219     if (!s) {
220         return -ENOTSUP;
221     }
222 
223     if (size->dom != DOMID_SELF && size->dom != xen_domid) {
224         size->status = GNTST_bad_domain;
225         return 0;
226     }
227 
228     size->status = GNTST_okay;
229     size->nr_frames = s->nr_frames;
230     size->max_nr_frames = s->max_frames;
231     return 0;
232 }
233