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 "exec/address-spaces.h" 14 #include "sysemu/kvm.h" 15 #include "hw/hyperv/hyperv.h" 16 17 typedef struct SynICState { 18 DeviceState parent_obj; 19 20 CPUState *cs; 21 22 bool enabled; 23 hwaddr msg_page_addr; 24 hwaddr event_page_addr; 25 MemoryRegion msg_page_mr; 26 MemoryRegion event_page_mr; 27 struct hyperv_message_page *msg_page; 28 struct hyperv_event_flags_page *event_page; 29 } SynICState; 30 31 #define TYPE_SYNIC "hyperv-synic" 32 #define SYNIC(obj) OBJECT_CHECK(SynICState, (obj), TYPE_SYNIC) 33 34 static SynICState *get_synic(CPUState *cs) 35 { 36 return SYNIC(object_resolve_path_component(OBJECT(cs), "synic")); 37 } 38 39 static void synic_update(SynICState *synic, bool enable, 40 hwaddr msg_page_addr, hwaddr event_page_addr) 41 { 42 43 synic->enabled = enable; 44 if (synic->msg_page_addr != msg_page_addr) { 45 if (synic->msg_page_addr) { 46 memory_region_del_subregion(get_system_memory(), 47 &synic->msg_page_mr); 48 } 49 if (msg_page_addr) { 50 memory_region_add_subregion(get_system_memory(), msg_page_addr, 51 &synic->msg_page_mr); 52 } 53 synic->msg_page_addr = msg_page_addr; 54 } 55 if (synic->event_page_addr != event_page_addr) { 56 if (synic->event_page_addr) { 57 memory_region_del_subregion(get_system_memory(), 58 &synic->event_page_mr); 59 } 60 if (event_page_addr) { 61 memory_region_add_subregion(get_system_memory(), event_page_addr, 62 &synic->event_page_mr); 63 } 64 synic->event_page_addr = event_page_addr; 65 } 66 } 67 68 void hyperv_synic_update(CPUState *cs, bool enable, 69 hwaddr msg_page_addr, hwaddr event_page_addr) 70 { 71 SynICState *synic = get_synic(cs); 72 73 if (!synic) { 74 return; 75 } 76 77 synic_update(synic, enable, msg_page_addr, event_page_addr); 78 } 79 80 static void synic_realize(DeviceState *dev, Error **errp) 81 { 82 Object *obj = OBJECT(dev); 83 SynICState *synic = SYNIC(dev); 84 char *msgp_name, *eventp_name; 85 uint32_t vp_index; 86 87 /* memory region names have to be globally unique */ 88 vp_index = hyperv_vp_index(synic->cs); 89 msgp_name = g_strdup_printf("synic-%u-msg-page", vp_index); 90 eventp_name = g_strdup_printf("synic-%u-event-page", vp_index); 91 92 memory_region_init_ram(&synic->msg_page_mr, obj, msgp_name, 93 sizeof(*synic->msg_page), &error_abort); 94 memory_region_init_ram(&synic->event_page_mr, obj, eventp_name, 95 sizeof(*synic->event_page), &error_abort); 96 synic->msg_page = memory_region_get_ram_ptr(&synic->msg_page_mr); 97 synic->event_page = memory_region_get_ram_ptr(&synic->event_page_mr); 98 99 g_free(msgp_name); 100 g_free(eventp_name); 101 } 102 static void synic_reset(DeviceState *dev) 103 { 104 SynICState *synic = SYNIC(dev); 105 memset(synic->msg_page, 0, sizeof(*synic->msg_page)); 106 memset(synic->event_page, 0, sizeof(*synic->event_page)); 107 synic_update(synic, false, 0, 0); 108 } 109 110 static void synic_class_init(ObjectClass *klass, void *data) 111 { 112 DeviceClass *dc = DEVICE_CLASS(klass); 113 114 dc->realize = synic_realize; 115 dc->reset = synic_reset; 116 dc->user_creatable = false; 117 } 118 119 void hyperv_synic_add(CPUState *cs) 120 { 121 Object *obj; 122 SynICState *synic; 123 124 obj = object_new(TYPE_SYNIC); 125 synic = SYNIC(obj); 126 synic->cs = cs; 127 object_property_add_child(OBJECT(cs), "synic", obj, &error_abort); 128 object_unref(obj); 129 object_property_set_bool(obj, true, "realized", &error_abort); 130 } 131 132 void hyperv_synic_reset(CPUState *cs) 133 { 134 device_reset(DEVICE(get_synic(cs))); 135 } 136 137 static const TypeInfo synic_type_info = { 138 .name = TYPE_SYNIC, 139 .parent = TYPE_DEVICE, 140 .instance_size = sizeof(SynICState), 141 .class_init = synic_class_init, 142 }; 143 144 static void synic_register_types(void) 145 { 146 type_register_static(&synic_type_info); 147 } 148 149 type_init(synic_register_types) 150 151 struct HvSintRoute { 152 uint32_t sint; 153 SynICState *synic; 154 int gsi; 155 EventNotifier sint_set_notifier; 156 EventNotifier sint_ack_notifier; 157 HvSintAckClb sint_ack_clb; 158 void *sint_ack_clb_data; 159 unsigned refcount; 160 }; 161 162 static CPUState *hyperv_find_vcpu(uint32_t vp_index) 163 { 164 CPUState *cs = qemu_get_cpu(vp_index); 165 assert(hyperv_vp_index(cs) == vp_index); 166 return cs; 167 } 168 169 static void kvm_hv_sint_ack_handler(EventNotifier *notifier) 170 { 171 HvSintRoute *sint_route = container_of(notifier, HvSintRoute, 172 sint_ack_notifier); 173 event_notifier_test_and_clear(notifier); 174 sint_route->sint_ack_clb(sint_route->sint_ack_clb_data); 175 } 176 177 HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, 178 HvSintAckClb sint_ack_clb, 179 void *sint_ack_clb_data) 180 { 181 HvSintRoute *sint_route; 182 EventNotifier *ack_notifier; 183 int r, gsi; 184 CPUState *cs; 185 SynICState *synic; 186 187 cs = hyperv_find_vcpu(vp_index); 188 if (!cs) { 189 return NULL; 190 } 191 192 synic = get_synic(cs); 193 if (!synic) { 194 return NULL; 195 } 196 197 sint_route = g_new0(HvSintRoute, 1); 198 r = event_notifier_init(&sint_route->sint_set_notifier, false); 199 if (r) { 200 goto err; 201 } 202 203 ack_notifier = sint_ack_clb ? &sint_route->sint_ack_notifier : NULL; 204 if (ack_notifier) { 205 r = event_notifier_init(ack_notifier, false); 206 if (r) { 207 goto err_sint_set_notifier; 208 } 209 210 event_notifier_set_handler(ack_notifier, kvm_hv_sint_ack_handler); 211 } 212 213 gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint); 214 if (gsi < 0) { 215 goto err_gsi; 216 } 217 218 r = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, 219 &sint_route->sint_set_notifier, 220 ack_notifier, gsi); 221 if (r) { 222 goto err_irqfd; 223 } 224 sint_route->gsi = gsi; 225 sint_route->sint_ack_clb = sint_ack_clb; 226 sint_route->sint_ack_clb_data = sint_ack_clb_data; 227 sint_route->synic = synic; 228 sint_route->sint = sint; 229 sint_route->refcount = 1; 230 231 return sint_route; 232 233 err_irqfd: 234 kvm_irqchip_release_virq(kvm_state, gsi); 235 err_gsi: 236 if (ack_notifier) { 237 event_notifier_set_handler(ack_notifier, NULL); 238 event_notifier_cleanup(ack_notifier); 239 } 240 err_sint_set_notifier: 241 event_notifier_cleanup(&sint_route->sint_set_notifier); 242 err: 243 g_free(sint_route); 244 245 return NULL; 246 } 247 248 void hyperv_sint_route_ref(HvSintRoute *sint_route) 249 { 250 sint_route->refcount++; 251 } 252 253 void hyperv_sint_route_unref(HvSintRoute *sint_route) 254 { 255 if (!sint_route) { 256 return; 257 } 258 259 assert(sint_route->refcount > 0); 260 261 if (--sint_route->refcount) { 262 return; 263 } 264 265 kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, 266 &sint_route->sint_set_notifier, 267 sint_route->gsi); 268 kvm_irqchip_release_virq(kvm_state, sint_route->gsi); 269 if (sint_route->sint_ack_clb) { 270 event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL); 271 event_notifier_cleanup(&sint_route->sint_ack_notifier); 272 } 273 event_notifier_cleanup(&sint_route->sint_set_notifier); 274 g_free(sint_route); 275 } 276 277 int hyperv_sint_route_set_sint(HvSintRoute *sint_route) 278 { 279 return event_notifier_set(&sint_route->sint_set_notifier); 280 } 281