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"
21 typedef struct TestSintRoute
{
22 QLIST_ENTRY(TestSintRoute
) le
;
25 HvSintRoute
*sint_route
;
28 typedef struct TestMsgConn
{
29 QLIST_ENTRY(TestMsgConn
) le
;
31 HvSintRoute
*sint_route
;
32 struct hyperv_message msg
;
35 typedef struct TestEvtConn
{
36 QLIST_ENTRY(TestEvtConn
) le
;
38 HvSintRoute
*sint_route
;
39 EventNotifier notifier
;
42 struct HypervTestDev
{
44 MemoryRegion sint_control
;
45 QLIST_HEAD(, TestSintRoute
) sint_routes
;
46 QLIST_HEAD(, TestMsgConn
) msg_conns
;
47 QLIST_HEAD(, TestEvtConn
) evt_conns
;
49 typedef struct HypervTestDev HypervTestDev
;
51 #define TYPE_HYPERV_TEST_DEV "hyperv-testdev"
52 #define HYPERV_TEST_DEV(obj) \
53 OBJECT_CHECK(HypervTestDev, (obj), TYPE_HYPERV_TEST_DEV)
56 HV_TEST_DEV_SINT_ROUTE_CREATE
= 1,
57 HV_TEST_DEV_SINT_ROUTE_DESTROY
,
58 HV_TEST_DEV_SINT_ROUTE_SET_SINT
,
59 HV_TEST_DEV_MSG_CONN_CREATE
,
60 HV_TEST_DEV_MSG_CONN_DESTROY
,
61 HV_TEST_DEV_EVT_CONN_CREATE
,
62 HV_TEST_DEV_EVT_CONN_DESTROY
,
65 static void sint_route_create(HypervTestDev
*dev
,
66 uint8_t vp_index
, uint8_t sint
)
68 TestSintRoute
*sint_route
;
70 sint_route
= g_new0(TestSintRoute
, 1);
73 sint_route
->vp_index
= vp_index
;
74 sint_route
->sint
= sint
;
76 sint_route
->sint_route
= hyperv_sint_route_new(vp_index
, sint
, NULL
, NULL
);
77 assert(sint_route
->sint_route
);
79 QLIST_INSERT_HEAD(&dev
->sint_routes
, sint_route
, le
);
82 static TestSintRoute
*sint_route_find(HypervTestDev
*dev
,
83 uint8_t vp_index
, uint8_t sint
)
85 TestSintRoute
*sint_route
;
87 QLIST_FOREACH(sint_route
, &dev
->sint_routes
, le
) {
88 if (sint_route
->vp_index
== vp_index
&& sint_route
->sint
== sint
) {
96 static void sint_route_destroy(HypervTestDev
*dev
,
97 uint8_t vp_index
, uint8_t sint
)
99 TestSintRoute
*sint_route
;
101 sint_route
= sint_route_find(dev
, vp_index
, sint
);
102 QLIST_REMOVE(sint_route
, le
);
103 hyperv_sint_route_unref(sint_route
->sint_route
);
107 static void sint_route_set_sint(HypervTestDev
*dev
,
108 uint8_t vp_index
, uint8_t sint
)
110 TestSintRoute
*sint_route
;
112 sint_route
= sint_route_find(dev
, vp_index
, sint
);
114 hyperv_sint_route_set_sint(sint_route
->sint_route
);
117 static void msg_retry(void *opaque
)
119 TestMsgConn
*conn
= opaque
;
120 assert(!hyperv_post_msg(conn
->sint_route
, &conn
->msg
));
123 static void msg_cb(void *data
, int status
)
125 TestMsgConn
*conn
= data
;
131 assert(status
== -EAGAIN
);
133 aio_bh_schedule_oneshot(qemu_get_aio_context(), msg_retry
, conn
);
136 static uint16_t msg_handler(const struct hyperv_post_message_input
*msg
,
140 TestMsgConn
*conn
= data
;
142 /* post the same message we've got */
143 conn
->msg
.header
.message_type
= msg
->message_type
;
144 assert(msg
->payload_size
< sizeof(conn
->msg
.payload
));
145 conn
->msg
.header
.payload_size
= msg
->payload_size
;
146 memcpy(&conn
->msg
.payload
, msg
->payload
, msg
->payload_size
);
148 ret
= hyperv_post_msg(conn
->sint_route
, &conn
->msg
);
152 return HV_STATUS_SUCCESS
;
154 return HV_STATUS_INSUFFICIENT_BUFFERS
;
156 return HV_STATUS_INVALID_HYPERCALL_INPUT
;
160 static void msg_conn_create(HypervTestDev
*dev
, uint8_t vp_index
,
161 uint8_t sint
, uint8_t conn_id
)
165 conn
= g_new0(TestMsgConn
, 1);
168 conn
->conn_id
= conn_id
;
170 conn
->sint_route
= hyperv_sint_route_new(vp_index
, sint
, msg_cb
, conn
);
171 assert(conn
->sint_route
);
173 assert(!hyperv_set_msg_handler(conn
->conn_id
, msg_handler
, conn
));
175 QLIST_INSERT_HEAD(&dev
->msg_conns
, conn
, le
);
178 static void msg_conn_destroy(HypervTestDev
*dev
, uint8_t conn_id
)
182 QLIST_FOREACH(conn
, &dev
->msg_conns
, le
) {
183 if (conn
->conn_id
== conn_id
) {
184 QLIST_REMOVE(conn
, le
);
185 hyperv_set_msg_handler(conn
->conn_id
, NULL
, NULL
);
186 hyperv_sint_route_unref(conn
->sint_route
);
194 static void evt_conn_handler(EventNotifier
*notifier
)
196 TestEvtConn
*conn
= container_of(notifier
, TestEvtConn
, notifier
);
198 event_notifier_test_and_clear(notifier
);
200 /* signal the same event flag we've got */
201 assert(!hyperv_set_event_flag(conn
->sint_route
, conn
->conn_id
));
204 static void evt_conn_create(HypervTestDev
*dev
, uint8_t vp_index
,
205 uint8_t sint
, uint8_t conn_id
)
209 conn
= g_new0(TestEvtConn
, 1);
212 conn
->conn_id
= conn_id
;
214 conn
->sint_route
= hyperv_sint_route_new(vp_index
, sint
, NULL
, NULL
);
215 assert(conn
->sint_route
);
217 assert(!event_notifier_init(&conn
->notifier
, false));
219 event_notifier_set_handler(&conn
->notifier
, evt_conn_handler
);
221 assert(!hyperv_set_event_flag_handler(conn_id
, &conn
->notifier
));
223 QLIST_INSERT_HEAD(&dev
->evt_conns
, conn
, le
);
226 static void evt_conn_destroy(HypervTestDev
*dev
, uint8_t conn_id
)
230 QLIST_FOREACH(conn
, &dev
->evt_conns
, le
) {
231 if (conn
->conn_id
== conn_id
) {
232 QLIST_REMOVE(conn
, le
);
233 hyperv_set_event_flag_handler(conn
->conn_id
, NULL
);
234 event_notifier_set_handler(&conn
->notifier
, NULL
);
235 event_notifier_cleanup(&conn
->notifier
);
236 hyperv_sint_route_unref(conn
->sint_route
);
244 static uint64_t hv_test_dev_read(void *opaque
, hwaddr addr
, unsigned size
)
249 static void hv_test_dev_write(void *opaque
, hwaddr addr
, uint64_t data
,
252 HypervTestDev
*dev
= HYPERV_TEST_DEV(opaque
);
253 uint8_t sint
= data
& 0xFF;
254 uint8_t vp_index
= (data
>> 8ULL) & 0xFF;
255 uint8_t ctl
= (data
>> 16ULL) & 0xFF;
256 uint8_t conn_id
= (data
>> 24ULL) & 0xFF;
259 case HV_TEST_DEV_SINT_ROUTE_CREATE
:
260 sint_route_create(dev
, vp_index
, sint
);
262 case HV_TEST_DEV_SINT_ROUTE_DESTROY
:
263 sint_route_destroy(dev
, vp_index
, sint
);
265 case HV_TEST_DEV_SINT_ROUTE_SET_SINT
:
266 sint_route_set_sint(dev
, vp_index
, sint
);
268 case HV_TEST_DEV_MSG_CONN_CREATE
:
269 msg_conn_create(dev
, vp_index
, sint
, conn_id
);
271 case HV_TEST_DEV_MSG_CONN_DESTROY
:
272 msg_conn_destroy(dev
, conn_id
);
274 case HV_TEST_DEV_EVT_CONN_CREATE
:
275 evt_conn_create(dev
, vp_index
, sint
, conn_id
);
277 case HV_TEST_DEV_EVT_CONN_DESTROY
:
278 evt_conn_destroy(dev
, conn_id
);
285 static const MemoryRegionOps synic_test_sint_ops
= {
286 .read
= hv_test_dev_read
,
287 .write
= hv_test_dev_write
,
288 .valid
.min_access_size
= 4,
289 .valid
.max_access_size
= 4,
290 .endianness
= DEVICE_LITTLE_ENDIAN
,
293 static void hv_test_dev_realizefn(DeviceState
*d
, Error
**errp
)
295 ISADevice
*isa
= ISA_DEVICE(d
);
296 HypervTestDev
*dev
= HYPERV_TEST_DEV(d
);
297 MemoryRegion
*io
= isa_address_space_io(isa
);
299 QLIST_INIT(&dev
->sint_routes
);
300 QLIST_INIT(&dev
->msg_conns
);
301 QLIST_INIT(&dev
->evt_conns
);
302 memory_region_init_io(&dev
->sint_control
, OBJECT(dev
),
303 &synic_test_sint_ops
, dev
,
304 "hyperv-testdev-ctl", 4);
305 memory_region_add_subregion(io
, 0x3000, &dev
->sint_control
);
308 static void hv_test_dev_class_init(ObjectClass
*klass
, void *data
)
310 DeviceClass
*dc
= DEVICE_CLASS(klass
);
312 set_bit(DEVICE_CATEGORY_MISC
, dc
->categories
);
313 dc
->realize
= hv_test_dev_realizefn
;
316 static const TypeInfo hv_test_dev_info
= {
317 .name
= TYPE_HYPERV_TEST_DEV
,
318 .parent
= TYPE_ISA_DEVICE
,
319 .instance_size
= sizeof(HypervTestDev
),
320 .class_init
= hv_test_dev_class_init
,
323 static void hv_test_dev_register_types(void)
325 type_register_static(&hv_test_dev_info
);
327 type_init(hv_test_dev_register_types
);