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