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