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