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>
This commit is contained in:
parent
f5642f8b45
commit
e6ea9f45b7
@ -13,6 +13,9 @@
|
|||||||
#include "exec/address-spaces.h"
|
#include "exec/address-spaces.h"
|
||||||
#include "sysemu/kvm.h"
|
#include "sysemu/kvm.h"
|
||||||
#include "qemu/bitops.h"
|
#include "qemu/bitops.h"
|
||||||
|
#include "qemu/queue.h"
|
||||||
|
#include "qemu/rcu.h"
|
||||||
|
#include "qemu/rcu_queue.h"
|
||||||
#include "hw/hyperv/hyperv.h"
|
#include "hw/hyperv/hyperv.h"
|
||||||
|
|
||||||
typedef struct SynICState {
|
typedef struct SynICState {
|
||||||
@ -450,3 +453,93 @@ int hyperv_sint_route_set_sint(HvSintRoute *sint_route)
|
|||||||
{
|
{
|
||||||
return event_notifier_set(&sint_route->sint_set_notifier);
|
return event_notifier_set(&sint_route->sint_set_notifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct EventFlagHandler {
|
||||||
|
struct rcu_head rcu;
|
||||||
|
QLIST_ENTRY(EventFlagHandler) link;
|
||||||
|
uint32_t conn_id;
|
||||||
|
EventNotifier *notifier;
|
||||||
|
} EventFlagHandler;
|
||||||
|
|
||||||
|
static QLIST_HEAD(, EventFlagHandler) event_flag_handlers;
|
||||||
|
static QemuMutex handlers_mutex;
|
||||||
|
|
||||||
|
static void __attribute__((constructor)) hv_init(void)
|
||||||
|
{
|
||||||
|
QLIST_INIT(&event_flag_handlers);
|
||||||
|
qemu_mutex_init(&handlers_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hyperv_set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
EventFlagHandler *handler;
|
||||||
|
|
||||||
|
qemu_mutex_lock(&handlers_mutex);
|
||||||
|
QLIST_FOREACH(handler, &event_flag_handlers, link) {
|
||||||
|
if (handler->conn_id == conn_id) {
|
||||||
|
if (notifier) {
|
||||||
|
ret = -EEXIST;
|
||||||
|
} else {
|
||||||
|
QLIST_REMOVE_RCU(handler, link);
|
||||||
|
g_free_rcu(handler, rcu);
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notifier) {
|
||||||
|
handler = g_new(EventFlagHandler, 1);
|
||||||
|
handler->conn_id = conn_id;
|
||||||
|
handler->notifier = notifier;
|
||||||
|
QLIST_INSERT_HEAD_RCU(&event_flag_handlers, handler, link);
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
ret = -ENOENT;
|
||||||
|
}
|
||||||
|
unlock:
|
||||||
|
qemu_mutex_unlock(&handlers_mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast)
|
||||||
|
{
|
||||||
|
uint16_t ret;
|
||||||
|
EventFlagHandler *handler;
|
||||||
|
|
||||||
|
if (unlikely(!fast)) {
|
||||||
|
hwaddr addr = param;
|
||||||
|
|
||||||
|
if (addr & (__alignof__(addr) - 1)) {
|
||||||
|
return HV_STATUS_INVALID_ALIGNMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
param = ldq_phys(&address_space_memory, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Per spec, bits 32-47 contain the extra "flag number". However, we
|
||||||
|
* have no use for it, and in all known usecases it is zero, so just
|
||||||
|
* report lookup failure if it isn't.
|
||||||
|
*/
|
||||||
|
if (param & 0xffff00000000ULL) {
|
||||||
|
return HV_STATUS_INVALID_PORT_ID;
|
||||||
|
}
|
||||||
|
/* remaining bits are reserved-zero */
|
||||||
|
if (param & ~HV_CONNECTION_ID_MASK) {
|
||||||
|
return HV_STATUS_INVALID_HYPERCALL_INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = HV_STATUS_INVALID_CONNECTION_ID;
|
||||||
|
rcu_read_lock();
|
||||||
|
QLIST_FOREACH_RCU(handler, &event_flag_handlers, link) {
|
||||||
|
if (handler->conn_id == param) {
|
||||||
|
event_notifier_set(handler->notifier);
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#define HV_STATUS_INVALID_ALIGNMENT 4
|
#define HV_STATUS_INVALID_ALIGNMENT 4
|
||||||
#define HV_STATUS_INVALID_PARAMETER 5
|
#define HV_STATUS_INVALID_PARAMETER 5
|
||||||
#define HV_STATUS_INSUFFICIENT_MEMORY 11
|
#define HV_STATUS_INSUFFICIENT_MEMORY 11
|
||||||
|
#define HV_STATUS_INVALID_PORT_ID 17
|
||||||
#define HV_STATUS_INVALID_CONNECTION_ID 18
|
#define HV_STATUS_INVALID_CONNECTION_ID 18
|
||||||
#define HV_STATUS_INSUFFICIENT_BUFFERS 19
|
#define HV_STATUS_INSUFFICIENT_BUFFERS 19
|
||||||
|
|
||||||
|
@ -39,6 +39,19 @@ int hyperv_post_msg(HvSintRoute *sint_route, struct hyperv_message *msg);
|
|||||||
*/
|
*/
|
||||||
int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno);
|
int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Associate @notifier with the event connection @conn_id, such that @notifier
|
||||||
|
* is signaled when the guest executes HV_SIGNAL_EVENT hypercall on @conn_id.
|
||||||
|
* If @notifier is NULL clear the association.
|
||||||
|
*/
|
||||||
|
int hyperv_set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process HV_SIGNAL_EVENT hypercall: signal the EventNotifier associated with
|
||||||
|
* the connection as specified in @param.
|
||||||
|
*/
|
||||||
|
uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast);
|
||||||
|
|
||||||
static inline uint32_t hyperv_vp_index(CPUState *cs)
|
static inline uint32_t hyperv_vp_index(CPUState *cs)
|
||||||
{
|
{
|
||||||
return cs->cpu_index;
|
return cs->cpu_index;
|
||||||
|
@ -79,16 +79,18 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
case KVM_EXIT_HYPERV_HCALL: {
|
case KVM_EXIT_HYPERV_HCALL: {
|
||||||
uint16_t code;
|
uint16_t code = exit->u.hcall.input & 0xffff;
|
||||||
|
bool fast = exit->u.hcall.input & HV_HYPERCALL_FAST;
|
||||||
|
uint64_t param = exit->u.hcall.params[0];
|
||||||
|
|
||||||
code = exit->u.hcall.input & 0xffff;
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case HV_POST_MESSAGE:
|
|
||||||
case HV_SIGNAL_EVENT:
|
case HV_SIGNAL_EVENT:
|
||||||
|
exit->u.hcall.result = hyperv_hcall_signal_event(param, fast);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE;
|
exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user