1 /* 2 * Hyper-V guest/hypervisor interaction 3 * 4 * Copyright (c) 2015-2018 Virtuozzo International GmbH. 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 * See the COPYING file in the top-level directory. 8 */ 9 10 #include "qemu/osdep.h" 11 #include "qemu/main-loop.h" 12 #include "qapi/error.h" 13 #include "sysemu/kvm.h" 14 #include "hw/hyperv/hyperv.h" 15 16 typedef struct SynICState { 17 DeviceState parent_obj; 18 19 CPUState *cs; 20 21 bool enabled; 22 hwaddr msg_page_addr; 23 hwaddr event_page_addr; 24 } SynICState; 25 26 #define TYPE_SYNIC "hyperv-synic" 27 #define SYNIC(obj) OBJECT_CHECK(SynICState, (obj), TYPE_SYNIC) 28 29 static SynICState *get_synic(CPUState *cs) 30 { 31 return SYNIC(object_resolve_path_component(OBJECT(cs), "synic")); 32 } 33 34 static void synic_update(SynICState *synic, bool enable, 35 hwaddr msg_page_addr, hwaddr event_page_addr) 36 { 37 38 synic->enabled = enable; 39 synic->msg_page_addr = msg_page_addr; 40 synic->event_page_addr = event_page_addr; 41 } 42 43 void hyperv_synic_update(CPUState *cs, bool enable, 44 hwaddr msg_page_addr, hwaddr event_page_addr) 45 { 46 SynICState *synic = get_synic(cs); 47 48 if (!synic) { 49 return; 50 } 51 52 synic_update(synic, enable, msg_page_addr, event_page_addr); 53 } 54 55 static void synic_realize(DeviceState *dev, Error **errp) 56 { 57 } 58 59 static void synic_reset(DeviceState *dev) 60 { 61 SynICState *synic = SYNIC(dev); 62 synic_update(synic, false, 0, 0); 63 } 64 65 static void synic_class_init(ObjectClass *klass, void *data) 66 { 67 DeviceClass *dc = DEVICE_CLASS(klass); 68 69 dc->realize = synic_realize; 70 dc->reset = synic_reset; 71 dc->user_creatable = false; 72 } 73 74 void hyperv_synic_add(CPUState *cs) 75 { 76 Object *obj; 77 SynICState *synic; 78 79 obj = object_new(TYPE_SYNIC); 80 synic = SYNIC(obj); 81 synic->cs = cs; 82 object_property_add_child(OBJECT(cs), "synic", obj, &error_abort); 83 object_unref(obj); 84 object_property_set_bool(obj, true, "realized", &error_abort); 85 } 86 87 void hyperv_synic_reset(CPUState *cs) 88 { 89 device_reset(DEVICE(get_synic(cs))); 90 } 91 92 static const TypeInfo synic_type_info = { 93 .name = TYPE_SYNIC, 94 .parent = TYPE_DEVICE, 95 .instance_size = sizeof(SynICState), 96 .class_init = synic_class_init, 97 }; 98 99 static void synic_register_types(void) 100 { 101 type_register_static(&synic_type_info); 102 } 103 104 type_init(synic_register_types) 105 106 struct HvSintRoute { 107 uint32_t sint; 108 SynICState *synic; 109 int gsi; 110 EventNotifier sint_set_notifier; 111 EventNotifier sint_ack_notifier; 112 HvSintAckClb sint_ack_clb; 113 void *sint_ack_clb_data; 114 unsigned refcount; 115 }; 116 117 static CPUState *hyperv_find_vcpu(uint32_t vp_index) 118 { 119 CPUState *cs = qemu_get_cpu(vp_index); 120 assert(hyperv_vp_index(cs) == vp_index); 121 return cs; 122 } 123 124 static void kvm_hv_sint_ack_handler(EventNotifier *notifier) 125 { 126 HvSintRoute *sint_route = container_of(notifier, HvSintRoute, 127 sint_ack_notifier); 128 event_notifier_test_and_clear(notifier); 129 sint_route->sint_ack_clb(sint_route->sint_ack_clb_data); 130 } 131 132 HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, 133 HvSintAckClb sint_ack_clb, 134 void *sint_ack_clb_data) 135 { 136 HvSintRoute *sint_route; 137 EventNotifier *ack_notifier; 138 int r, gsi; 139 CPUState *cs; 140 SynICState *synic; 141 142 cs = hyperv_find_vcpu(vp_index); 143 if (!cs) { 144 return NULL; 145 } 146 147 synic = get_synic(cs); 148 if (!synic) { 149 return NULL; 150 } 151 152 sint_route = g_new0(HvSintRoute, 1); 153 r = event_notifier_init(&sint_route->sint_set_notifier, false); 154 if (r) { 155 goto err; 156 } 157 158 ack_notifier = sint_ack_clb ? &sint_route->sint_ack_notifier : NULL; 159 if (ack_notifier) { 160 r = event_notifier_init(ack_notifier, false); 161 if (r) { 162 goto err_sint_set_notifier; 163 } 164 165 event_notifier_set_handler(ack_notifier, kvm_hv_sint_ack_handler); 166 } 167 168 gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint); 169 if (gsi < 0) { 170 goto err_gsi; 171 } 172 173 r = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, 174 &sint_route->sint_set_notifier, 175 ack_notifier, gsi); 176 if (r) { 177 goto err_irqfd; 178 } 179 sint_route->gsi = gsi; 180 sint_route->sint_ack_clb = sint_ack_clb; 181 sint_route->sint_ack_clb_data = sint_ack_clb_data; 182 sint_route->synic = synic; 183 sint_route->sint = sint; 184 sint_route->refcount = 1; 185 186 return sint_route; 187 188 err_irqfd: 189 kvm_irqchip_release_virq(kvm_state, gsi); 190 err_gsi: 191 if (ack_notifier) { 192 event_notifier_set_handler(ack_notifier, NULL); 193 event_notifier_cleanup(ack_notifier); 194 } 195 err_sint_set_notifier: 196 event_notifier_cleanup(&sint_route->sint_set_notifier); 197 err: 198 g_free(sint_route); 199 200 return NULL; 201 } 202 203 void hyperv_sint_route_ref(HvSintRoute *sint_route) 204 { 205 sint_route->refcount++; 206 } 207 208 void hyperv_sint_route_unref(HvSintRoute *sint_route) 209 { 210 if (!sint_route) { 211 return; 212 } 213 214 assert(sint_route->refcount > 0); 215 216 if (--sint_route->refcount) { 217 return; 218 } 219 220 kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, 221 &sint_route->sint_set_notifier, 222 sint_route->gsi); 223 kvm_irqchip_release_virq(kvm_state, sint_route->gsi); 224 if (sint_route->sint_ack_clb) { 225 event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL); 226 event_notifier_cleanup(&sint_route->sint_ack_notifier); 227 } 228 event_notifier_cleanup(&sint_route->sint_set_notifier); 229 g_free(sint_route); 230 } 231 232 int hyperv_sint_route_set_sint(HvSintRoute *sint_route) 233 { 234 return event_notifier_set(&sint_route->sint_set_notifier); 235 } 236