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