xref: /openbmc/qemu/hw/hyperv/hyperv.c (revision 701189e31140a7c82ec02a7f4ca632cfd6a8559d)
1*701189e3SRoman Kagan /*
2*701189e3SRoman Kagan  * Hyper-V guest/hypervisor interaction
3*701189e3SRoman Kagan  *
4*701189e3SRoman Kagan  * Copyright (c) 2015-2018 Virtuozzo International GmbH.
5*701189e3SRoman Kagan  *
6*701189e3SRoman Kagan  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7*701189e3SRoman Kagan  * See the COPYING file in the top-level directory.
8*701189e3SRoman Kagan  */
9*701189e3SRoman Kagan 
10*701189e3SRoman Kagan #include "qemu/osdep.h"
11*701189e3SRoman Kagan #include "qemu/main-loop.h"
12*701189e3SRoman Kagan #include "sysemu/kvm.h"
13*701189e3SRoman Kagan #include "hw/hyperv/hyperv.h"
14*701189e3SRoman Kagan 
15*701189e3SRoman Kagan struct HvSintRoute {
16*701189e3SRoman Kagan     uint32_t sint;
17*701189e3SRoman Kagan     CPUState *cs;
18*701189e3SRoman Kagan     int gsi;
19*701189e3SRoman Kagan     EventNotifier sint_set_notifier;
20*701189e3SRoman Kagan     EventNotifier sint_ack_notifier;
21*701189e3SRoman Kagan     HvSintAckClb sint_ack_clb;
22*701189e3SRoman Kagan     void *sint_ack_clb_data;
23*701189e3SRoman Kagan     unsigned refcount;
24*701189e3SRoman Kagan };
25*701189e3SRoman Kagan 
26*701189e3SRoman Kagan static CPUState *hyperv_find_vcpu(uint32_t vp_index)
27*701189e3SRoman Kagan {
28*701189e3SRoman Kagan     CPUState *cs = qemu_get_cpu(vp_index);
29*701189e3SRoman Kagan     assert(hyperv_vp_index(cs) == vp_index);
30*701189e3SRoman Kagan     return cs;
31*701189e3SRoman Kagan }
32*701189e3SRoman Kagan 
33*701189e3SRoman Kagan static void kvm_hv_sint_ack_handler(EventNotifier *notifier)
34*701189e3SRoman Kagan {
35*701189e3SRoman Kagan     HvSintRoute *sint_route = container_of(notifier, HvSintRoute,
36*701189e3SRoman Kagan                                            sint_ack_notifier);
37*701189e3SRoman Kagan     event_notifier_test_and_clear(notifier);
38*701189e3SRoman Kagan     sint_route->sint_ack_clb(sint_route->sint_ack_clb_data);
39*701189e3SRoman Kagan }
40*701189e3SRoman Kagan 
41*701189e3SRoman Kagan HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint,
42*701189e3SRoman Kagan                                    HvSintAckClb sint_ack_clb,
43*701189e3SRoman Kagan                                    void *sint_ack_clb_data)
44*701189e3SRoman Kagan {
45*701189e3SRoman Kagan     HvSintRoute *sint_route;
46*701189e3SRoman Kagan     EventNotifier *ack_notifier;
47*701189e3SRoman Kagan     int r, gsi;
48*701189e3SRoman Kagan     CPUState *cs;
49*701189e3SRoman Kagan 
50*701189e3SRoman Kagan     cs = hyperv_find_vcpu(vp_index);
51*701189e3SRoman Kagan     if (!cs) {
52*701189e3SRoman Kagan         return NULL;
53*701189e3SRoman Kagan     }
54*701189e3SRoman Kagan 
55*701189e3SRoman Kagan     sint_route = g_new0(HvSintRoute, 1);
56*701189e3SRoman Kagan     r = event_notifier_init(&sint_route->sint_set_notifier, false);
57*701189e3SRoman Kagan     if (r) {
58*701189e3SRoman Kagan         goto err;
59*701189e3SRoman Kagan     }
60*701189e3SRoman Kagan 
61*701189e3SRoman Kagan     ack_notifier = sint_ack_clb ? &sint_route->sint_ack_notifier : NULL;
62*701189e3SRoman Kagan     if (ack_notifier) {
63*701189e3SRoman Kagan         r = event_notifier_init(ack_notifier, false);
64*701189e3SRoman Kagan         if (r) {
65*701189e3SRoman Kagan             goto err_sint_set_notifier;
66*701189e3SRoman Kagan         }
67*701189e3SRoman Kagan 
68*701189e3SRoman Kagan         event_notifier_set_handler(ack_notifier, kvm_hv_sint_ack_handler);
69*701189e3SRoman Kagan     }
70*701189e3SRoman Kagan 
71*701189e3SRoman Kagan     gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint);
72*701189e3SRoman Kagan     if (gsi < 0) {
73*701189e3SRoman Kagan         goto err_gsi;
74*701189e3SRoman Kagan     }
75*701189e3SRoman Kagan 
76*701189e3SRoman Kagan     r = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state,
77*701189e3SRoman Kagan                                            &sint_route->sint_set_notifier,
78*701189e3SRoman Kagan                                            ack_notifier, gsi);
79*701189e3SRoman Kagan     if (r) {
80*701189e3SRoman Kagan         goto err_irqfd;
81*701189e3SRoman Kagan     }
82*701189e3SRoman Kagan     sint_route->gsi = gsi;
83*701189e3SRoman Kagan     sint_route->sint_ack_clb = sint_ack_clb;
84*701189e3SRoman Kagan     sint_route->sint_ack_clb_data = sint_ack_clb_data;
85*701189e3SRoman Kagan     sint_route->cs = cs;
86*701189e3SRoman Kagan     sint_route->sint = sint;
87*701189e3SRoman Kagan     sint_route->refcount = 1;
88*701189e3SRoman Kagan 
89*701189e3SRoman Kagan     return sint_route;
90*701189e3SRoman Kagan 
91*701189e3SRoman Kagan err_irqfd:
92*701189e3SRoman Kagan     kvm_irqchip_release_virq(kvm_state, gsi);
93*701189e3SRoman Kagan err_gsi:
94*701189e3SRoman Kagan     if (ack_notifier) {
95*701189e3SRoman Kagan         event_notifier_set_handler(ack_notifier, NULL);
96*701189e3SRoman Kagan         event_notifier_cleanup(ack_notifier);
97*701189e3SRoman Kagan     }
98*701189e3SRoman Kagan err_sint_set_notifier:
99*701189e3SRoman Kagan     event_notifier_cleanup(&sint_route->sint_set_notifier);
100*701189e3SRoman Kagan err:
101*701189e3SRoman Kagan     g_free(sint_route);
102*701189e3SRoman Kagan 
103*701189e3SRoman Kagan     return NULL;
104*701189e3SRoman Kagan }
105*701189e3SRoman Kagan 
106*701189e3SRoman Kagan void hyperv_sint_route_ref(HvSintRoute *sint_route)
107*701189e3SRoman Kagan {
108*701189e3SRoman Kagan     sint_route->refcount++;
109*701189e3SRoman Kagan }
110*701189e3SRoman Kagan 
111*701189e3SRoman Kagan void hyperv_sint_route_unref(HvSintRoute *sint_route)
112*701189e3SRoman Kagan {
113*701189e3SRoman Kagan     if (!sint_route) {
114*701189e3SRoman Kagan         return;
115*701189e3SRoman Kagan     }
116*701189e3SRoman Kagan 
117*701189e3SRoman Kagan     assert(sint_route->refcount > 0);
118*701189e3SRoman Kagan 
119*701189e3SRoman Kagan     if (--sint_route->refcount) {
120*701189e3SRoman Kagan         return;
121*701189e3SRoman Kagan     }
122*701189e3SRoman Kagan 
123*701189e3SRoman Kagan     kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state,
124*701189e3SRoman Kagan                                           &sint_route->sint_set_notifier,
125*701189e3SRoman Kagan                                           sint_route->gsi);
126*701189e3SRoman Kagan     kvm_irqchip_release_virq(kvm_state, sint_route->gsi);
127*701189e3SRoman Kagan     if (sint_route->sint_ack_clb) {
128*701189e3SRoman Kagan         event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL);
129*701189e3SRoman Kagan         event_notifier_cleanup(&sint_route->sint_ack_notifier);
130*701189e3SRoman Kagan     }
131*701189e3SRoman Kagan     event_notifier_cleanup(&sint_route->sint_set_notifier);
132*701189e3SRoman Kagan     g_free(sint_route);
133*701189e3SRoman Kagan }
134*701189e3SRoman Kagan 
135*701189e3SRoman Kagan int hyperv_sint_route_set_sint(HvSintRoute *sint_route)
136*701189e3SRoman Kagan {
137*701189e3SRoman Kagan     return event_notifier_set(&sint_route->sint_set_notifier);
138*701189e3SRoman Kagan }
139