qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

hyperv: process SIGNAL_EVENT hypercall

Add handling of SIGNAL_EVENT hypercall. For that, provide an interface
to associate an EventNotifier with an event connection number, so that
it's signaled when the SIGNAL_EVENT hypercall with the matching
connection ID is called by the guest.

Support for using KVM functionality for this will be added in a followup
patch.

Signed-off-by: Roman Kagan <rkagan@virtuozzo.com>
Message-Id: <20180921082217.29481-8-rkagan@virtuozzo.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

authored by

Roman Kagan and committed by
Paolo Bonzini
e6ea9f45 f5642f8b

+113 -4
+93
hw/hyperv/hyperv.c
··· 13 13 #include "exec/address-spaces.h" 14 14 #include "sysemu/kvm.h" 15 15 #include "qemu/bitops.h" 16 + #include "qemu/queue.h" 17 + #include "qemu/rcu.h" 18 + #include "qemu/rcu_queue.h" 16 19 #include "hw/hyperv/hyperv.h" 17 20 18 21 typedef struct SynICState { ··· 450 453 { 451 454 return event_notifier_set(&sint_route->sint_set_notifier); 452 455 } 456 + 457 + typedef struct EventFlagHandler { 458 + struct rcu_head rcu; 459 + QLIST_ENTRY(EventFlagHandler) link; 460 + uint32_t conn_id; 461 + EventNotifier *notifier; 462 + } EventFlagHandler; 463 + 464 + static QLIST_HEAD(, EventFlagHandler) event_flag_handlers; 465 + static QemuMutex handlers_mutex; 466 + 467 + static void __attribute__((constructor)) hv_init(void) 468 + { 469 + QLIST_INIT(&event_flag_handlers); 470 + qemu_mutex_init(&handlers_mutex); 471 + } 472 + 473 + int hyperv_set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier) 474 + { 475 + int ret; 476 + EventFlagHandler *handler; 477 + 478 + qemu_mutex_lock(&handlers_mutex); 479 + QLIST_FOREACH(handler, &event_flag_handlers, link) { 480 + if (handler->conn_id == conn_id) { 481 + if (notifier) { 482 + ret = -EEXIST; 483 + } else { 484 + QLIST_REMOVE_RCU(handler, link); 485 + g_free_rcu(handler, rcu); 486 + ret = 0; 487 + } 488 + goto unlock; 489 + } 490 + } 491 + 492 + if (notifier) { 493 + handler = g_new(EventFlagHandler, 1); 494 + handler->conn_id = conn_id; 495 + handler->notifier = notifier; 496 + QLIST_INSERT_HEAD_RCU(&event_flag_handlers, handler, link); 497 + ret = 0; 498 + } else { 499 + ret = -ENOENT; 500 + } 501 + unlock: 502 + qemu_mutex_unlock(&handlers_mutex); 503 + return ret; 504 + } 505 + 506 + uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast) 507 + { 508 + uint16_t ret; 509 + EventFlagHandler *handler; 510 + 511 + if (unlikely(!fast)) { 512 + hwaddr addr = param; 513 + 514 + if (addr & (__alignof__(addr) - 1)) { 515 + return HV_STATUS_INVALID_ALIGNMENT; 516 + } 517 + 518 + param = ldq_phys(&address_space_memory, addr); 519 + } 520 + 521 + /* 522 + * Per spec, bits 32-47 contain the extra "flag number". However, we 523 + * have no use for it, and in all known usecases it is zero, so just 524 + * report lookup failure if it isn't. 525 + */ 526 + if (param & 0xffff00000000ULL) { 527 + return HV_STATUS_INVALID_PORT_ID; 528 + } 529 + /* remaining bits are reserved-zero */ 530 + if (param & ~HV_CONNECTION_ID_MASK) { 531 + return HV_STATUS_INVALID_HYPERCALL_INPUT; 532 + } 533 + 534 + ret = HV_STATUS_INVALID_CONNECTION_ID; 535 + rcu_read_lock(); 536 + QLIST_FOREACH_RCU(handler, &event_flag_handlers, link) { 537 + if (handler->conn_id == param) { 538 + event_notifier_set(handler->notifier); 539 + ret = 0; 540 + break; 541 + } 542 + } 543 + rcu_read_unlock(); 544 + return ret; 545 + }
+1
include/hw/hyperv/hyperv-proto.h
··· 21 21 #define HV_STATUS_INVALID_ALIGNMENT 4 22 22 #define HV_STATUS_INVALID_PARAMETER 5 23 23 #define HV_STATUS_INSUFFICIENT_MEMORY 11 24 + #define HV_STATUS_INVALID_PORT_ID 17 24 25 #define HV_STATUS_INVALID_CONNECTION_ID 18 25 26 #define HV_STATUS_INSUFFICIENT_BUFFERS 19 26 27
+13
include/hw/hyperv/hyperv.h
··· 39 39 */ 40 40 int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno); 41 41 42 + /* 43 + * Associate @notifier with the event connection @conn_id, such that @notifier 44 + * is signaled when the guest executes HV_SIGNAL_EVENT hypercall on @conn_id. 45 + * If @notifier is NULL clear the association. 46 + */ 47 + int hyperv_set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier); 48 + 49 + /* 50 + * Process HV_SIGNAL_EVENT hypercall: signal the EventNotifier associated with 51 + * the connection as specified in @param. 52 + */ 53 + uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast); 54 + 42 55 static inline uint32_t hyperv_vp_index(CPUState *cs) 43 56 { 44 57 return cs->cpu_index;
+6 -4
target/i386/hyperv.c
··· 79 79 80 80 return 0; 81 81 case KVM_EXIT_HYPERV_HCALL: { 82 - uint16_t code; 82 + uint16_t code = exit->u.hcall.input & 0xffff; 83 + bool fast = exit->u.hcall.input & HV_HYPERCALL_FAST; 84 + uint64_t param = exit->u.hcall.params[0]; 83 85 84 - code = exit->u.hcall.input & 0xffff; 85 86 switch (code) { 86 - case HV_POST_MESSAGE: 87 87 case HV_SIGNAL_EVENT: 88 + exit->u.hcall.result = hyperv_hcall_signal_event(param, fast); 89 + break; 88 90 default: 89 91 exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE; 90 - return 0; 91 92 } 93 + return 0; 92 94 } 93 95 default: 94 96 return -1;