2 * QEMU KVM Hyper-V test device to support Hyper-V kvm-unit-tests
4 * Copyright (C) 2015 Andrey Smetanin <asmetanin@virtuozzo.com>
7 * Andrey Smetanin <asmetanin@virtuozzo.com>
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
14 #include "qemu/osdep.h"
15 #include "qemu/main-loop.h"
16 #include "qemu/module.h"
17 #include "qemu/queue.h"
18 #include "hw/isa/isa.h"
19 #include "hw/hyperv/hyperv.h"
20 #include "qom/object.h"
22 typedef struct TestSintRoute
{
23 QLIST_ENTRY(TestSintRoute
) le
;
26 HvSintRoute
*sint_route
;
29 typedef struct TestMsgConn
{
30 QLIST_ENTRY(TestMsgConn
) le
;
32 HvSintRoute
*sint_route
;
33 struct hyperv_message msg
;
36 typedef struct TestEvtConn
{
37 QLIST_ENTRY(TestEvtConn
) le
;
39 HvSintRoute
*sint_route
;
40 EventNotifier notifier
;
43 struct HypervTestDev
{
45 MemoryRegion sint_control
;
46 QLIST_HEAD(, TestSintRoute
) sint_routes
;
47 QLIST_HEAD(, TestMsgConn
) msg_conns
;
48 QLIST_HEAD(, TestEvtConn
) evt_conns
;
50 typedef struct HypervTestDev HypervTestDev
;
52 #define TYPE_HYPERV_TEST_DEV "hyperv-testdev"
53 DECLARE_INSTANCE_CHECKER(HypervTestDev
, HYPERV_TEST_DEV
,
57 HV_TEST_DEV_SINT_ROUTE_CREATE
= 1,
58 HV_TEST_DEV_SINT_ROUTE_DESTROY
,
59 HV_TEST_DEV_SINT_ROUTE_SET_SINT
,
60 HV_TEST_DEV_MSG_CONN_CREATE
,
61 HV_TEST_DEV_MSG_CONN_DESTROY
,
62 HV_TEST_DEV_EVT_CONN_CREATE
,
63 HV_TEST_DEV_EVT_CONN_DESTROY
,
66 static void sint_route_create(HypervTestDev
*dev
,
67 uint8_t vp_index
, uint8_t sint
)
69 TestSintRoute
*sint_route
;
71 sint_route
= g_new0(TestSintRoute
, 1);
74 sint_route
->vp_index
= vp_index
;
75 sint_route
->sint
= sint
;
77 sint_route
->sint_route
= hyperv_sint_route_new(vp_index
, sint
, NULL
, NULL
);
78 assert(sint_route
->sint_route
);
80 QLIST_INSERT_HEAD(&dev
->sint_routes
, sint_route
, le
);
83 static TestSintRoute
*sint_route_find(HypervTestDev
*dev
,
84 uint8_t vp_index
, uint8_t sint
)
86 TestSintRoute
*sint_route
;
88 QLIST_FOREACH(sint_route
, &dev
->sint_routes
, le
) {
89 if (sint_route
->vp_index
== vp_index
&& sint_route
->sint
== sint
) {
97 static void sint_route_destroy(HypervTestDev
*dev
,
98 uint8_t vp_index
, uint8_t sint
)
100 TestSintRoute
*sint_route
;
102 sint_route
= sint_route_find(dev
, vp_index
, sint
);
103 QLIST_REMOVE(sint_route
, le
);
104 hyperv_sint_route_unref(sint_route
->sint_route
);
108 static void sint_route_set_sint(HypervTestDev
*dev
,
109 uint8_t vp_index
, uint8_t sint
)
111 TestSintRoute
*sint_route
;
113 sint_route
= sint_route_find(dev
, vp_index
, sint
);
115 hyperv_sint_route_set_sint(sint_route
->sint_route
);
118 static void msg_retry(void *opaque
)
120 TestMsgConn
*conn
= opaque
;
121 assert(!hyperv_post_msg(conn
->sint_route
, &conn
->msg
));
124 static void msg_cb(void *data
, int status
)
126 TestMsgConn
*conn
= data
;
132 assert(status
== -EAGAIN
);
134 aio_bh_schedule_oneshot(qemu_get_aio_context(), msg_retry
, conn
);
137 static uint16_t msg_handler(const struct hyperv_post_message_input
*msg
,
141 TestMsgConn
*conn
= data
;
143 /* post the same message we've got */
144 conn
->msg
.header
.message_type
= msg
->message_type
;
145 assert(msg
->payload_size
< sizeof(conn
->msg
.payload
));
146 conn
->msg
.header
.payload_size
= msg
->payload_size
;
147 memcpy(&conn
->msg
.payload
, msg
->payload
, msg
->payload_size
);
149 ret
= hyperv_post_msg(conn
->sint_route
, &conn
->msg
);
153 return HV_STATUS_SUCCESS
;
155 return HV_STATUS_INSUFFICIENT_BUFFERS
;
157 return HV_STATUS_INVALID_HYPERCALL_INPUT
;
161 static void msg_conn_create(HypervTestDev
*dev
, uint8_t vp_index
,
162 uint8_t sint
, uint8_t conn_id
)
166 conn
= g_new0(TestMsgConn
, 1);
169 conn
->conn_id
= conn_id
;
171 conn
->sint_route
= hyperv_sint_route_new(vp_index
, sint
, msg_cb
, conn
);
172 assert(conn
->sint_route
);
174 assert(!hyperv_set_msg_handler(conn
->conn_id
, msg_handler
, conn
));
176 QLIST_INSERT_HEAD(&dev
->msg_conns
, conn
, le
);
179 static void msg_conn_destroy(HypervTestDev
*dev
, uint8_t conn_id
)
183 QLIST_FOREACH(conn
, &dev
->msg_conns
, le
) {
184 if (conn
->conn_id
== conn_id
) {
185 QLIST_REMOVE(conn
, le
);
186 hyperv_set_msg_handler(conn
->conn_id
, NULL
, NULL
);
187 hyperv_sint_route_unref(conn
->sint_route
);
195 static void evt_conn_handler(EventNotifier
*notifier
)
197 TestEvtConn
*conn
= container_of(notifier
, TestEvtConn
, notifier
);
199 event_notifier_test_and_clear(notifier
);
201 /* signal the same event flag we've got */
202 assert(!hyperv_set_event_flag(conn
->sint_route
, conn
->conn_id
));
205 static void evt_conn_create(HypervTestDev
*dev
, uint8_t vp_index
,
206 uint8_t sint
, uint8_t conn_id
)
210 conn
= g_new0(TestEvtConn
, 1);
213 conn
->conn_id
= conn_id
;
215 conn
->sint_route
= hyperv_sint_route_new(vp_index
, sint
, NULL
, NULL
);
216 assert(conn
->sint_route
);
218 assert(!event_notifier_init(&conn
->notifier
, false));
220 event_notifier_set_handler(&conn
->notifier
, evt_conn_handler
);
222 assert(!hyperv_set_event_flag_handler(conn_id
, &conn
->notifier
));
224 QLIST_INSERT_HEAD(&dev
->evt_conns
, conn
, le
);
227 static void evt_conn_destroy(HypervTestDev
*dev
, uint8_t conn_id
)
231 QLIST_FOREACH(conn
, &dev
->evt_conns
, le
) {
232 if (conn
->conn_id
== conn_id
) {
233 QLIST_REMOVE(conn
, le
);
234 hyperv_set_event_flag_handler(conn
->conn_id
, NULL
);
235 event_notifier_set_handler(&conn
->notifier
, NULL
);
236 event_notifier_cleanup(&conn
->notifier
);
237 hyperv_sint_route_unref(conn
->sint_route
);
245 static uint64_t hv_test_dev_read(void *opaque
, hwaddr addr
, unsigned size
)
250 static void hv_test_dev_write(void *opaque
, hwaddr addr
, uint64_t data
,
253 HypervTestDev
*dev
= HYPERV_TEST_DEV(opaque
);
254 uint8_t sint
= data
& 0xFF;
255 uint8_t vp_index
= (data
>> 8ULL) & 0xFF;
256 uint8_t ctl
= (data
>> 16ULL) & 0xFF;
257 uint8_t conn_id
= (data
>> 24ULL) & 0xFF;
260 case HV_TEST_DEV_SINT_ROUTE_CREATE
:
261 sint_route_create(dev
, vp_index
, sint
);
263 case HV_TEST_DEV_SINT_ROUTE_DESTROY
:
264 sint_route_destroy(dev
, vp_index
, sint
);
266 case HV_TEST_DEV_SINT_ROUTE_SET_SINT
:
267 sint_route_set_sint(dev
, vp_index
, sint
);
269 case HV_TEST_DEV_MSG_CONN_CREATE
:
270 msg_conn_create(dev
, vp_index
, sint
, conn_id
);
272 case HV_TEST_DEV_MSG_CONN_DESTROY
:
273 msg_conn_destroy(dev
, conn_id
);
275 case HV_TEST_DEV_EVT_CONN_CREATE
:
276 evt_conn_create(dev
, vp_index
, sint
, conn_id
);
278 case HV_TEST_DEV_EVT_CONN_DESTROY
:
279 evt_conn_destroy(dev
, conn_id
);
286 static const MemoryRegionOps synic_test_sint_ops
= {
287 .read
= hv_test_dev_read
,
288 .write
= hv_test_dev_write
,
289 .valid
.min_access_size
= 4,
290 .valid
.max_access_size
= 4,
291 .endianness
= DEVICE_LITTLE_ENDIAN
,
294 static void hv_test_dev_realizefn(DeviceState
*d
, Error
**errp
)
296 ISADevice
*isa
= ISA_DEVICE(d
);
297 HypervTestDev
*dev
= HYPERV_TEST_DEV(d
);
298 MemoryRegion
*io
= isa_address_space_io(isa
);
300 QLIST_INIT(&dev
->sint_routes
);
301 QLIST_INIT(&dev
->msg_conns
);
302 QLIST_INIT(&dev
->evt_conns
);
303 memory_region_init_io(&dev
->sint_control
, OBJECT(dev
),
304 &synic_test_sint_ops
, dev
,
305 "hyperv-testdev-ctl", 4);
306 memory_region_add_subregion(io
, 0x3000, &dev
->sint_control
);
309 static void hv_test_dev_class_init(ObjectClass
*klass
, void *data
)
311 DeviceClass
*dc
= DEVICE_CLASS(klass
);
313 set_bit(DEVICE_CATEGORY_MISC
, dc
->categories
);
314 dc
->realize
= hv_test_dev_realizefn
;
317 static const TypeInfo hv_test_dev_info
= {
318 .name
= TYPE_HYPERV_TEST_DEV
,
319 .parent
= TYPE_ISA_DEVICE
,
320 .instance_size
= sizeof(HypervTestDev
),
321 .class_init
= hv_test_dev_class_init
,
324 static void hv_test_dev_register_types(void)
326 type_register_static(&hv_test_dev_info
);
328 type_init(hv_test_dev_register_types
);