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