1a72ccc7fSDavid Woodhouse /*
2a72ccc7fSDavid Woodhouse * QEMU Xen emulation: Primary console support
3a72ccc7fSDavid Woodhouse *
4a72ccc7fSDavid Woodhouse * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5a72ccc7fSDavid Woodhouse *
6a72ccc7fSDavid Woodhouse * Authors: David Woodhouse <dwmw2@infradead.org>
7a72ccc7fSDavid Woodhouse *
8a72ccc7fSDavid Woodhouse * This work is licensed under the terms of the GNU GPL, version 2 or later.
9a72ccc7fSDavid Woodhouse * See the COPYING file in the top-level directory.
10a72ccc7fSDavid Woodhouse */
11a72ccc7fSDavid Woodhouse
12a72ccc7fSDavid Woodhouse #include "qemu/osdep.h"
13a72ccc7fSDavid Woodhouse
14a72ccc7fSDavid Woodhouse #include "qapi/error.h"
15a72ccc7fSDavid Woodhouse
16a72ccc7fSDavid Woodhouse #include "hw/sysbus.h"
17a72ccc7fSDavid Woodhouse #include "hw/xen/xen.h"
18a72ccc7fSDavid Woodhouse #include "hw/xen/xen_backend_ops.h"
19a72ccc7fSDavid Woodhouse #include "xen_evtchn.h"
20a72ccc7fSDavid Woodhouse #include "xen_overlay.h"
21a72ccc7fSDavid Woodhouse #include "xen_primary_console.h"
22a72ccc7fSDavid Woodhouse
2332cad1ffSPhilippe Mathieu-Daudé #include "system/kvm.h"
2432cad1ffSPhilippe Mathieu-Daudé #include "system/kvm_xen.h"
25a72ccc7fSDavid Woodhouse
26a72ccc7fSDavid Woodhouse #include "trace.h"
27a72ccc7fSDavid Woodhouse
28a72ccc7fSDavid Woodhouse #include "hw/xen/interface/event_channel.h"
29a72ccc7fSDavid Woodhouse #include "hw/xen/interface/grant_table.h"
30a72ccc7fSDavid Woodhouse
31a72ccc7fSDavid Woodhouse #define TYPE_XEN_PRIMARY_CONSOLE "xen-primary-console"
32a72ccc7fSDavid Woodhouse OBJECT_DECLARE_SIMPLE_TYPE(XenPrimaryConsoleState, XEN_PRIMARY_CONSOLE)
33a72ccc7fSDavid Woodhouse
34a72ccc7fSDavid Woodhouse struct XenPrimaryConsoleState {
35a72ccc7fSDavid Woodhouse /*< private >*/
36a72ccc7fSDavid Woodhouse SysBusDevice busdev;
37a72ccc7fSDavid Woodhouse /*< public >*/
38a72ccc7fSDavid Woodhouse
39a72ccc7fSDavid Woodhouse MemoryRegion console_page;
40a72ccc7fSDavid Woodhouse void *cp;
41a72ccc7fSDavid Woodhouse
42a72ccc7fSDavid Woodhouse evtchn_port_t guest_port;
43a72ccc7fSDavid Woodhouse evtchn_port_t be_port;
44a72ccc7fSDavid Woodhouse
45a72ccc7fSDavid Woodhouse struct xengntdev_handle *gt;
46a72ccc7fSDavid Woodhouse void *granted_xs;
47a72ccc7fSDavid Woodhouse };
48a72ccc7fSDavid Woodhouse
49a72ccc7fSDavid Woodhouse struct XenPrimaryConsoleState *xen_primary_console_singleton;
50a72ccc7fSDavid Woodhouse
xen_primary_console_realize(DeviceState * dev,Error ** errp)51a72ccc7fSDavid Woodhouse static void xen_primary_console_realize(DeviceState *dev, Error **errp)
52a72ccc7fSDavid Woodhouse {
53a72ccc7fSDavid Woodhouse XenPrimaryConsoleState *s = XEN_PRIMARY_CONSOLE(dev);
54a72ccc7fSDavid Woodhouse
55a72ccc7fSDavid Woodhouse if (xen_mode != XEN_EMULATE) {
56a72ccc7fSDavid Woodhouse error_setg(errp, "Xen primary console support is for Xen emulation");
57a72ccc7fSDavid Woodhouse return;
58a72ccc7fSDavid Woodhouse }
59a72ccc7fSDavid Woodhouse
60a72ccc7fSDavid Woodhouse memory_region_init_ram(&s->console_page, OBJECT(dev), "xen:console_page",
61a72ccc7fSDavid Woodhouse XEN_PAGE_SIZE, &error_abort);
62a72ccc7fSDavid Woodhouse memory_region_set_enabled(&s->console_page, true);
63a72ccc7fSDavid Woodhouse s->cp = memory_region_get_ram_ptr(&s->console_page);
64a72ccc7fSDavid Woodhouse memset(s->cp, 0, XEN_PAGE_SIZE);
65a72ccc7fSDavid Woodhouse
66a72ccc7fSDavid Woodhouse /* We can't map it this early as KVM isn't ready */
67a72ccc7fSDavid Woodhouse xen_primary_console_singleton = s;
68a72ccc7fSDavid Woodhouse }
69a72ccc7fSDavid Woodhouse
xen_primary_console_class_init(ObjectClass * klass,const void * data)70*12d1a768SPhilippe Mathieu-Daudé static void xen_primary_console_class_init(ObjectClass *klass, const void *data)
71a72ccc7fSDavid Woodhouse {
72a72ccc7fSDavid Woodhouse DeviceClass *dc = DEVICE_CLASS(klass);
73a72ccc7fSDavid Woodhouse
74a72ccc7fSDavid Woodhouse dc->realize = xen_primary_console_realize;
75a72ccc7fSDavid Woodhouse }
76a72ccc7fSDavid Woodhouse
77a72ccc7fSDavid Woodhouse static const TypeInfo xen_primary_console_info = {
78a72ccc7fSDavid Woodhouse .name = TYPE_XEN_PRIMARY_CONSOLE,
79a72ccc7fSDavid Woodhouse .parent = TYPE_SYS_BUS_DEVICE,
80a72ccc7fSDavid Woodhouse .instance_size = sizeof(XenPrimaryConsoleState),
81a72ccc7fSDavid Woodhouse .class_init = xen_primary_console_class_init,
82a72ccc7fSDavid Woodhouse };
83a72ccc7fSDavid Woodhouse
84a72ccc7fSDavid Woodhouse
xen_primary_console_create(void)85a72ccc7fSDavid Woodhouse void xen_primary_console_create(void)
86a72ccc7fSDavid Woodhouse {
87a72ccc7fSDavid Woodhouse DeviceState *dev = sysbus_create_simple(TYPE_XEN_PRIMARY_CONSOLE, -1, NULL);
88a72ccc7fSDavid Woodhouse
89a72ccc7fSDavid Woodhouse trace_xen_primary_console_create();
90a72ccc7fSDavid Woodhouse
91a72ccc7fSDavid Woodhouse xen_primary_console_singleton = XEN_PRIMARY_CONSOLE(dev);
92a72ccc7fSDavid Woodhouse
93a72ccc7fSDavid Woodhouse /*
94a72ccc7fSDavid Woodhouse * Defer the init (xen_primary_console_reset()) until KVM is set up and the
95a72ccc7fSDavid Woodhouse * overlay page can be mapped.
96a72ccc7fSDavid Woodhouse */
97a72ccc7fSDavid Woodhouse }
98a72ccc7fSDavid Woodhouse
xen_primary_console_register_types(void)99a72ccc7fSDavid Woodhouse static void xen_primary_console_register_types(void)
100a72ccc7fSDavid Woodhouse {
101a72ccc7fSDavid Woodhouse type_register_static(&xen_primary_console_info);
102a72ccc7fSDavid Woodhouse }
103a72ccc7fSDavid Woodhouse
type_init(xen_primary_console_register_types)104a72ccc7fSDavid Woodhouse type_init(xen_primary_console_register_types)
105a72ccc7fSDavid Woodhouse
106a72ccc7fSDavid Woodhouse uint16_t xen_primary_console_get_port(void)
107a72ccc7fSDavid Woodhouse {
108a72ccc7fSDavid Woodhouse XenPrimaryConsoleState *s = xen_primary_console_singleton;
109a72ccc7fSDavid Woodhouse if (!s) {
110a72ccc7fSDavid Woodhouse return 0;
111a72ccc7fSDavid Woodhouse }
112a72ccc7fSDavid Woodhouse return s->guest_port;
113a72ccc7fSDavid Woodhouse }
114a72ccc7fSDavid Woodhouse
xen_primary_console_set_be_port(uint16_t port)115a72ccc7fSDavid Woodhouse void xen_primary_console_set_be_port(uint16_t port)
116a72ccc7fSDavid Woodhouse {
117a72ccc7fSDavid Woodhouse XenPrimaryConsoleState *s = xen_primary_console_singleton;
118a72ccc7fSDavid Woodhouse if (s) {
119a72ccc7fSDavid Woodhouse s->be_port = port;
120a72ccc7fSDavid Woodhouse }
121a72ccc7fSDavid Woodhouse }
122a72ccc7fSDavid Woodhouse
xen_primary_console_get_pfn(void)123a72ccc7fSDavid Woodhouse uint64_t xen_primary_console_get_pfn(void)
124a72ccc7fSDavid Woodhouse {
125a72ccc7fSDavid Woodhouse XenPrimaryConsoleState *s = xen_primary_console_singleton;
126a72ccc7fSDavid Woodhouse if (!s) {
127a72ccc7fSDavid Woodhouse return 0;
128a72ccc7fSDavid Woodhouse }
129a72ccc7fSDavid Woodhouse return XEN_SPECIAL_PFN(CONSOLE);
130a72ccc7fSDavid Woodhouse }
131a72ccc7fSDavid Woodhouse
xen_primary_console_get_map(void)132a72ccc7fSDavid Woodhouse void *xen_primary_console_get_map(void)
133a72ccc7fSDavid Woodhouse {
134a72ccc7fSDavid Woodhouse XenPrimaryConsoleState *s = xen_primary_console_singleton;
135a72ccc7fSDavid Woodhouse if (!s) {
136a72ccc7fSDavid Woodhouse return 0;
137a72ccc7fSDavid Woodhouse }
138a72ccc7fSDavid Woodhouse return s->cp;
139a72ccc7fSDavid Woodhouse }
140a72ccc7fSDavid Woodhouse
alloc_guest_port(XenPrimaryConsoleState * s)141a72ccc7fSDavid Woodhouse static void alloc_guest_port(XenPrimaryConsoleState *s)
142a72ccc7fSDavid Woodhouse {
143a72ccc7fSDavid Woodhouse struct evtchn_alloc_unbound alloc = {
144a72ccc7fSDavid Woodhouse .dom = DOMID_SELF,
145a72ccc7fSDavid Woodhouse .remote_dom = DOMID_QEMU,
146a72ccc7fSDavid Woodhouse };
147a72ccc7fSDavid Woodhouse
148a72ccc7fSDavid Woodhouse if (!xen_evtchn_alloc_unbound_op(&alloc)) {
149a72ccc7fSDavid Woodhouse s->guest_port = alloc.port;
150a72ccc7fSDavid Woodhouse }
151a72ccc7fSDavid Woodhouse }
152a72ccc7fSDavid Woodhouse
rebind_guest_port(XenPrimaryConsoleState * s)153a72ccc7fSDavid Woodhouse static void rebind_guest_port(XenPrimaryConsoleState *s)
154a72ccc7fSDavid Woodhouse {
155a72ccc7fSDavid Woodhouse struct evtchn_bind_interdomain inter = {
156a72ccc7fSDavid Woodhouse .remote_dom = DOMID_QEMU,
157a72ccc7fSDavid Woodhouse .remote_port = s->be_port,
158a72ccc7fSDavid Woodhouse };
159a72ccc7fSDavid Woodhouse
160a72ccc7fSDavid Woodhouse if (!xen_evtchn_bind_interdomain_op(&inter)) {
161a72ccc7fSDavid Woodhouse s->guest_port = inter.local_port;
162a72ccc7fSDavid Woodhouse }
163a72ccc7fSDavid Woodhouse
164a72ccc7fSDavid Woodhouse s->be_port = 0;
165a72ccc7fSDavid Woodhouse }
166a72ccc7fSDavid Woodhouse
xen_primary_console_reset(void)167a72ccc7fSDavid Woodhouse int xen_primary_console_reset(void)
168a72ccc7fSDavid Woodhouse {
169a72ccc7fSDavid Woodhouse XenPrimaryConsoleState *s = xen_primary_console_singleton;
170a72ccc7fSDavid Woodhouse if (!s) {
171a72ccc7fSDavid Woodhouse return 0;
172a72ccc7fSDavid Woodhouse }
173a72ccc7fSDavid Woodhouse
174a72ccc7fSDavid Woodhouse if (!memory_region_is_mapped(&s->console_page)) {
175a72ccc7fSDavid Woodhouse uint64_t gpa = XEN_SPECIAL_PFN(CONSOLE) << TARGET_PAGE_BITS;
176a72ccc7fSDavid Woodhouse xen_overlay_do_map_page(&s->console_page, gpa);
177a72ccc7fSDavid Woodhouse }
178a72ccc7fSDavid Woodhouse
179a72ccc7fSDavid Woodhouse if (s->be_port) {
180a72ccc7fSDavid Woodhouse rebind_guest_port(s);
181a72ccc7fSDavid Woodhouse } else {
182a72ccc7fSDavid Woodhouse alloc_guest_port(s);
183a72ccc7fSDavid Woodhouse }
184a72ccc7fSDavid Woodhouse
185a72ccc7fSDavid Woodhouse trace_xen_primary_console_reset(s->guest_port);
186a72ccc7fSDavid Woodhouse
187a72ccc7fSDavid Woodhouse s->gt = qemu_xen_gnttab_open();
188a72ccc7fSDavid Woodhouse uint32_t xs_gntref = GNTTAB_RESERVED_CONSOLE;
189a72ccc7fSDavid Woodhouse s->granted_xs = qemu_xen_gnttab_map_refs(s->gt, 1, xen_domid, &xs_gntref,
190a72ccc7fSDavid Woodhouse PROT_READ | PROT_WRITE);
191a72ccc7fSDavid Woodhouse
192a72ccc7fSDavid Woodhouse return 0;
193a72ccc7fSDavid Woodhouse }
194