]>
Commit | Line | Data |
---|---|---|
b6cacfea DW |
1 | /* |
2 | * QEMU Xen backend support: Operations for true Xen | |
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" | |
ba2a92db | 13 | #include "qemu/uuid.h" |
b6cacfea DW |
14 | #include "qapi/error.h" |
15 | ||
e2abfe5e | 16 | #include "hw/xen/xen_native.h" |
b6cacfea | 17 | #include "hw/xen/xen_backend_ops.h" |
b6cacfea DW |
18 | |
19 | /* | |
20 | * If we have new enough libxenctrl then we do not want/need these compat | |
21 | * interfaces, despite what the user supplied cflags might say. They | |
22 | * must be undefined before including xenctrl.h | |
23 | */ | |
24 | #undef XC_WANT_COMPAT_EVTCHN_API | |
c412ba47 | 25 | #undef XC_WANT_COMPAT_GNTTAB_API |
15e283c5 | 26 | #undef XC_WANT_COMPAT_MAP_FOREIGN_API |
b6cacfea DW |
27 | |
28 | #include <xenctrl.h> | |
29 | ||
30 | /* | |
2f20b173 | 31 | * We don't support Xen prior to 4.7.1. |
b6cacfea DW |
32 | */ |
33 | ||
b6cacfea | 34 | #include <xenevtchn.h> |
c412ba47 | 35 | #include <xengnttab.h> |
15e283c5 | 36 | #include <xenforeignmemory.h> |
b6cacfea | 37 | |
c412ba47 DW |
38 | /* Xen before 4.8 */ |
39 | ||
40 | static int libxengnttab_fallback_grant_copy(xengnttab_handle *xgt, | |
41 | bool to_domain, uint32_t domid, | |
42 | XenGrantCopySegment segs[], | |
43 | unsigned int nr_segs, Error **errp) | |
44 | { | |
45 | uint32_t *refs = g_new(uint32_t, nr_segs); | |
46 | int prot = to_domain ? PROT_WRITE : PROT_READ; | |
47 | void *map; | |
48 | unsigned int i; | |
49 | int rc = 0; | |
50 | ||
51 | for (i = 0; i < nr_segs; i++) { | |
52 | XenGrantCopySegment *seg = &segs[i]; | |
53 | ||
54 | refs[i] = to_domain ? seg->dest.foreign.ref : | |
55 | seg->source.foreign.ref; | |
56 | } | |
57 | map = xengnttab_map_domain_grant_refs(xgt, nr_segs, domid, refs, prot); | |
58 | if (!map) { | |
59 | if (errp) { | |
60 | error_setg_errno(errp, errno, | |
61 | "xengnttab_map_domain_grant_refs failed"); | |
62 | } | |
63 | rc = -errno; | |
64 | goto done; | |
65 | } | |
66 | ||
67 | for (i = 0; i < nr_segs; i++) { | |
68 | XenGrantCopySegment *seg = &segs[i]; | |
69 | void *page = map + (i * XEN_PAGE_SIZE); | |
70 | ||
71 | if (to_domain) { | |
72 | memcpy(page + seg->dest.foreign.offset, seg->source.virt, | |
73 | seg->len); | |
74 | } else { | |
75 | memcpy(seg->dest.virt, page + seg->source.foreign.offset, | |
76 | seg->len); | |
77 | } | |
78 | } | |
79 | ||
80 | if (xengnttab_unmap(xgt, map, nr_segs)) { | |
81 | if (errp) { | |
82 | error_setg_errno(errp, errno, "xengnttab_unmap failed"); | |
83 | } | |
84 | rc = -errno; | |
85 | } | |
86 | ||
87 | done: | |
88 | g_free(refs); | |
89 | return rc; | |
90 | } | |
91 | ||
92 | #if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800 | |
93 | ||
94 | static int libxengnttab_backend_grant_copy(xengnttab_handle *xgt, | |
95 | bool to_domain, uint32_t domid, | |
96 | XenGrantCopySegment *segs, | |
97 | uint32_t nr_segs, Error **errp) | |
98 | { | |
99 | xengnttab_grant_copy_segment_t *xengnttab_segs; | |
100 | unsigned int i; | |
101 | int rc; | |
102 | ||
103 | xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); | |
104 | ||
105 | for (i = 0; i < nr_segs; i++) { | |
106 | XenGrantCopySegment *seg = &segs[i]; | |
107 | xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; | |
108 | ||
109 | if (to_domain) { | |
110 | xengnttab_seg->flags = GNTCOPY_dest_gref; | |
111 | xengnttab_seg->dest.foreign.domid = domid; | |
112 | xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref; | |
113 | xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset; | |
114 | xengnttab_seg->source.virt = seg->source.virt; | |
115 | } else { | |
116 | xengnttab_seg->flags = GNTCOPY_source_gref; | |
117 | xengnttab_seg->source.foreign.domid = domid; | |
118 | xengnttab_seg->source.foreign.ref = seg->source.foreign.ref; | |
119 | xengnttab_seg->source.foreign.offset = | |
120 | seg->source.foreign.offset; | |
121 | xengnttab_seg->dest.virt = seg->dest.virt; | |
122 | } | |
123 | ||
124 | xengnttab_seg->len = seg->len; | |
125 | } | |
126 | ||
127 | if (xengnttab_grant_copy(xgt, nr_segs, xengnttab_segs)) { | |
128 | if (errp) { | |
129 | error_setg_errno(errp, errno, "xengnttab_grant_copy failed"); | |
130 | } | |
131 | rc = -errno; | |
132 | goto done; | |
133 | } | |
134 | ||
135 | rc = 0; | |
136 | for (i = 0; i < nr_segs; i++) { | |
137 | xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; | |
138 | ||
139 | if (xengnttab_seg->status != GNTST_okay) { | |
140 | if (errp) { | |
141 | error_setg(errp, "xengnttab_grant_copy seg[%u] failed", i); | |
142 | } | |
143 | rc = -EIO; | |
144 | break; | |
145 | } | |
146 | } | |
147 | ||
148 | done: | |
149 | g_free(xengnttab_segs); | |
150 | return rc; | |
151 | } | |
152 | #endif | |
153 | ||
b6cacfea DW |
154 | static xenevtchn_handle *libxenevtchn_backend_open(void) |
155 | { | |
156 | return xenevtchn_open(NULL, 0); | |
157 | } | |
158 | ||
159 | struct evtchn_backend_ops libxenevtchn_backend_ops = { | |
160 | .open = libxenevtchn_backend_open, | |
161 | .close = xenevtchn_close, | |
162 | .bind_interdomain = xenevtchn_bind_interdomain, | |
163 | .unbind = xenevtchn_unbind, | |
164 | .get_fd = xenevtchn_fd, | |
165 | .notify = xenevtchn_notify, | |
166 | .unmask = xenevtchn_unmask, | |
167 | .pending = xenevtchn_pending, | |
168 | }; | |
169 | ||
c412ba47 DW |
170 | static xengnttab_handle *libxengnttab_backend_open(void) |
171 | { | |
172 | return xengnttab_open(NULL, 0); | |
173 | } | |
174 | ||
f80fad16 DW |
175 | static int libxengnttab_backend_unmap(xengnttab_handle *xgt, |
176 | void *start_address, uint32_t *refs, | |
177 | uint32_t count) | |
178 | { | |
179 | return xengnttab_unmap(xgt, start_address, count); | |
180 | } | |
181 | ||
c412ba47 DW |
182 | |
183 | static struct gnttab_backend_ops libxengnttab_backend_ops = { | |
184 | .features = XEN_GNTTAB_OP_FEATURE_MAP_MULTIPLE, | |
185 | .open = libxengnttab_backend_open, | |
186 | .close = xengnttab_close, | |
187 | .grant_copy = libxengnttab_fallback_grant_copy, | |
188 | .set_max_grants = xengnttab_set_max_grants, | |
189 | .map_refs = xengnttab_map_domain_grant_refs, | |
f80fad16 | 190 | .unmap = libxengnttab_backend_unmap, |
c412ba47 DW |
191 | }; |
192 | ||
15e283c5 DW |
193 | static void *libxenforeignmem_backend_map(uint32_t dom, void *addr, int prot, |
194 | size_t pages, xen_pfn_t *pfns, | |
195 | int *errs) | |
196 | { | |
197 | return xenforeignmemory_map2(xen_fmem, dom, addr, prot, 0, pages, pfns, | |
198 | errs); | |
199 | } | |
200 | ||
201 | static int libxenforeignmem_backend_unmap(void *addr, size_t pages) | |
202 | { | |
203 | return xenforeignmemory_unmap(xen_fmem, addr, pages); | |
204 | } | |
205 | ||
15e283c5 DW |
206 | struct foreignmem_backend_ops libxenforeignmem_backend_ops = { |
207 | .map = libxenforeignmem_backend_map, | |
208 | .unmap = libxenforeignmem_backend_unmap, | |
209 | }; | |
210 | ||
ba2a92db PD |
211 | struct qemu_xs_handle { |
212 | struct xs_handle *xsh; | |
213 | NotifierList notifiers; | |
214 | }; | |
215 | ||
216 | static void watch_event(void *opaque) | |
217 | { | |
218 | struct qemu_xs_handle *h = opaque; | |
219 | ||
220 | for (;;) { | |
221 | char **v = xs_check_watch(h->xsh); | |
222 | ||
223 | if (!v) { | |
224 | break; | |
225 | } | |
226 | ||
227 | notifier_list_notify(&h->notifiers, v); | |
228 | free(v); | |
229 | } | |
230 | } | |
231 | ||
232 | static struct qemu_xs_handle *libxenstore_open(void) | |
233 | { | |
234 | struct xs_handle *xsh = xs_open(0); | |
8442232e | 235 | struct qemu_xs_handle *h; |
ba2a92db PD |
236 | |
237 | if (!xsh) { | |
238 | return NULL; | |
239 | } | |
240 | ||
241 | h = g_new0(struct qemu_xs_handle, 1); | |
242 | h->xsh = xsh; | |
243 | ||
244 | notifier_list_init(&h->notifiers); | |
245 | qemu_set_fd_handler(xs_fileno(h->xsh), watch_event, NULL, h); | |
246 | ||
247 | return h; | |
248 | } | |
249 | ||
250 | static void libxenstore_close(struct qemu_xs_handle *h) | |
251 | { | |
252 | g_assert(notifier_list_empty(&h->notifiers)); | |
253 | qemu_set_fd_handler(xs_fileno(h->xsh), NULL, NULL, NULL); | |
254 | xs_close(h->xsh); | |
255 | g_free(h); | |
256 | } | |
257 | ||
258 | static char *libxenstore_get_domain_path(struct qemu_xs_handle *h, | |
259 | unsigned int domid) | |
260 | { | |
261 | return xs_get_domain_path(h->xsh, domid); | |
262 | } | |
263 | ||
264 | static char **libxenstore_directory(struct qemu_xs_handle *h, | |
265 | xs_transaction_t t, const char *path, | |
266 | unsigned int *num) | |
267 | { | |
268 | return xs_directory(h->xsh, t, path, num); | |
269 | } | |
270 | ||
271 | static void *libxenstore_read(struct qemu_xs_handle *h, xs_transaction_t t, | |
272 | const char *path, unsigned int *len) | |
273 | { | |
274 | return xs_read(h->xsh, t, path, len); | |
275 | } | |
276 | ||
277 | static bool libxenstore_write(struct qemu_xs_handle *h, xs_transaction_t t, | |
278 | const char *path, const void *data, | |
279 | unsigned int len) | |
280 | { | |
281 | return xs_write(h->xsh, t, path, data, len); | |
282 | } | |
283 | ||
284 | static bool libxenstore_create(struct qemu_xs_handle *h, xs_transaction_t t, | |
285 | unsigned int owner, unsigned int domid, | |
286 | unsigned int perms, const char *path) | |
287 | { | |
288 | struct xs_permissions perms_list[] = { | |
289 | { | |
290 | .id = owner, | |
291 | .perms = XS_PERM_NONE, | |
292 | }, | |
293 | { | |
294 | .id = domid, | |
295 | .perms = perms, | |
296 | }, | |
297 | }; | |
298 | ||
299 | if (!xs_mkdir(h->xsh, t, path)) { | |
300 | return false; | |
301 | } | |
302 | ||
303 | return xs_set_permissions(h->xsh, t, path, perms_list, | |
304 | ARRAY_SIZE(perms_list)); | |
305 | } | |
306 | ||
307 | static bool libxenstore_destroy(struct qemu_xs_handle *h, xs_transaction_t t, | |
308 | const char *path) | |
309 | { | |
310 | return xs_rm(h->xsh, t, path); | |
311 | } | |
312 | ||
313 | struct qemu_xs_watch { | |
314 | char *path; | |
315 | char *token; | |
316 | xs_watch_fn fn; | |
317 | void *opaque; | |
318 | Notifier notifier; | |
319 | }; | |
320 | ||
321 | static void watch_notify(Notifier *n, void *data) | |
322 | { | |
323 | struct qemu_xs_watch *w = container_of(n, struct qemu_xs_watch, notifier); | |
324 | const char **v = data; | |
325 | ||
326 | if (!strcmp(w->token, v[XS_WATCH_TOKEN])) { | |
327 | w->fn(w->opaque, v[XS_WATCH_PATH]); | |
328 | } | |
329 | } | |
330 | ||
331 | static struct qemu_xs_watch *new_watch(const char *path, xs_watch_fn fn, | |
332 | void *opaque) | |
333 | { | |
334 | struct qemu_xs_watch *w = g_new0(struct qemu_xs_watch, 1); | |
335 | QemuUUID uuid; | |
336 | ||
337 | qemu_uuid_generate(&uuid); | |
338 | ||
339 | w->token = qemu_uuid_unparse_strdup(&uuid); | |
340 | w->path = g_strdup(path); | |
341 | w->fn = fn; | |
342 | w->opaque = opaque; | |
343 | w->notifier.notify = watch_notify; | |
344 | ||
345 | return w; | |
346 | } | |
347 | ||
348 | static void free_watch(struct qemu_xs_watch *w) | |
349 | { | |
350 | g_free(w->token); | |
351 | g_free(w->path); | |
352 | ||
353 | g_free(w); | |
354 | } | |
355 | ||
356 | static struct qemu_xs_watch *libxenstore_watch(struct qemu_xs_handle *h, | |
357 | const char *path, xs_watch_fn fn, | |
358 | void *opaque) | |
359 | { | |
360 | struct qemu_xs_watch *w = new_watch(path, fn, opaque); | |
361 | ||
362 | notifier_list_add(&h->notifiers, &w->notifier); | |
363 | ||
364 | if (!xs_watch(h->xsh, path, w->token)) { | |
365 | notifier_remove(&w->notifier); | |
366 | free_watch(w); | |
367 | return NULL; | |
368 | } | |
369 | ||
370 | return w; | |
371 | } | |
372 | ||
373 | static void libxenstore_unwatch(struct qemu_xs_handle *h, | |
374 | struct qemu_xs_watch *w) | |
375 | { | |
376 | xs_unwatch(h->xsh, w->path, w->token); | |
377 | notifier_remove(&w->notifier); | |
378 | free_watch(w); | |
379 | } | |
380 | ||
381 | static xs_transaction_t libxenstore_transaction_start(struct qemu_xs_handle *h) | |
382 | { | |
383 | return xs_transaction_start(h->xsh); | |
384 | } | |
385 | ||
386 | static bool libxenstore_transaction_end(struct qemu_xs_handle *h, | |
387 | xs_transaction_t t, bool abort) | |
388 | { | |
389 | return xs_transaction_end(h->xsh, t, abort); | |
390 | } | |
391 | ||
392 | struct xenstore_backend_ops libxenstore_backend_ops = { | |
393 | .open = libxenstore_open, | |
394 | .close = libxenstore_close, | |
395 | .get_domain_path = libxenstore_get_domain_path, | |
396 | .directory = libxenstore_directory, | |
397 | .read = libxenstore_read, | |
398 | .write = libxenstore_write, | |
399 | .create = libxenstore_create, | |
400 | .destroy = libxenstore_destroy, | |
401 | .watch = libxenstore_watch, | |
402 | .unwatch = libxenstore_unwatch, | |
403 | .transaction_start = libxenstore_transaction_start, | |
404 | .transaction_end = libxenstore_transaction_end, | |
405 | }; | |
406 | ||
b6cacfea DW |
407 | void setup_xen_backend_ops(void) |
408 | { | |
c412ba47 DW |
409 | #if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 40800 |
410 | xengnttab_handle *xgt = xengnttab_open(NULL, 0); | |
411 | ||
412 | if (xgt) { | |
413 | if (xengnttab_grant_copy(xgt, 0, NULL) == 0) { | |
414 | libxengnttab_backend_ops.grant_copy = libxengnttab_backend_grant_copy; | |
415 | } | |
416 | xengnttab_close(xgt); | |
417 | } | |
418 | #endif | |
b6cacfea | 419 | xen_evtchn_ops = &libxenevtchn_backend_ops; |
c412ba47 | 420 | xen_gnttab_ops = &libxengnttab_backend_ops; |
15e283c5 | 421 | xen_foreignmem_ops = &libxenforeignmem_backend_ops; |
ba2a92db | 422 | xen_xenstore_ops = &libxenstore_backend_ops; |
b6cacfea | 423 | } |