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
;
51 #define TYPE_HYPERV_TEST_DEV "hyperv-testdev"
52 OBJECT_DECLARE_SIMPLE_TYPE(HypervTestDev
, HYPERV_TEST_DEV
)
55 HV_TEST_DEV_SINT_ROUTE_CREATE
= 1,
56 HV_TEST_DEV_SINT_ROUTE_DESTROY
,
57 HV_TEST_DEV_SINT_ROUTE_SET_SINT
,
58 HV_TEST_DEV_MSG_CONN_CREATE
,
59 HV_TEST_DEV_MSG_CONN_DESTROY
,
60 HV_TEST_DEV_EVT_CONN_CREATE
,
61 HV_TEST_DEV_EVT_CONN_DESTROY
,
64 static void sint_route_create(HypervTestDev
*dev
,
65 uint8_t vp_index
, uint8_t sint
)
67 TestSintRoute
*sint_route
;
69 sint_route
= g_new0(TestSintRoute
, 1);
72 sint_route
->vp_index
= vp_index
;
73 sint_route
->sint
= sint
;
75 sint_route
->sint_route
= hyperv_sint_route_new(vp_index
, sint
, NULL
, NULL
);
76 assert(sint_route
->sint_route
);
78 QLIST_INSERT_HEAD(&dev
->sint_routes
, sint_route
, le
);
81 static TestSintRoute
*sint_route_find(HypervTestDev
*dev
,
82 uint8_t vp_index
, uint8_t sint
)
84 TestSintRoute
*sint_route
;
86 QLIST_FOREACH(sint_route
, &dev
->sint_routes
, le
) {
87 if (sint_route
->vp_index
== vp_index
&& sint_route
->sint
== sint
) {
95 static void sint_route_destroy(HypervTestDev
*dev
,
96 uint8_t vp_index
, uint8_t sint
)
98 TestSintRoute
*sint_route
;
100 sint_route
= sint_route_find(dev
, vp_index
, sint
);
101 QLIST_REMOVE(sint_route
, le
);
102 hyperv_sint_route_unref(sint_route
->sint_route
);
106 static void sint_route_set_sint(HypervTestDev
*dev
,
107 uint8_t vp_index
, uint8_t sint
)
109 TestSintRoute
*sint_route
;
111 sint_route
= sint_route_find(dev
, vp_index
, sint
);
113 hyperv_sint_route_set_sint(sint_route
->sint_route
);
116 static void msg_retry(void *opaque
)
118 TestMsgConn
*conn
= opaque
;
119 assert(!hyperv_post_msg(conn
->sint_route
, &conn
->msg
));
122 static void msg_cb(void *data
, int status
)
124 TestMsgConn
*conn
= data
;
130 assert(status
== -EAGAIN
);
132 aio_bh_schedule_oneshot(qemu_get_aio_context(), msg_retry
, conn
);
135 static uint16_t msg_handler(const struct hyperv_post_message_input
*msg
,
139 TestMsgConn
*conn
= data
;
141 /* post the same message we've got */
142 conn
->msg
.header
.message_type
= msg
->message_type
;
143 assert(msg
->payload_size
< sizeof(conn
->msg
.payload
));
144 conn
->msg
.header
.payload_size
= msg
->payload_size
;
145 memcpy(&conn
->msg
.payload
, msg
->payload
, msg
->payload_size
);
147 ret
= hyperv_post_msg(conn
->sint_route
, &conn
->msg
);
151 return HV_STATUS_SUCCESS
;
153 return HV_STATUS_INSUFFICIENT_BUFFERS
;
155 return HV_STATUS_INVALID_HYPERCALL_INPUT
;
159 static void msg_conn_create(HypervTestDev
*dev
, uint8_t vp_index
,
160 uint8_t sint
, uint8_t conn_id
)
164 conn
= g_new0(TestMsgConn
, 1);
167 conn
->conn_id
= conn_id
;
169 conn
->sint_route
= hyperv_sint_route_new(vp_index
, sint
, msg_cb
, conn
);
170 assert(conn
->sint_route
);
172 assert(!hyperv_set_msg_handler(conn
->conn_id
, msg_handler
, conn
));
174 QLIST_INSERT_HEAD(&dev
->msg_conns
, conn
, le
);
177 static void msg_conn_destroy(HypervTestDev
*dev
, uint8_t conn_id
)
181 QLIST_FOREACH(conn
, &dev
->msg_conns
, le
) {
182 if (conn
->conn_id
== conn_id
) {
183 QLIST_REMOVE(conn
, le
);
184 hyperv_set_msg_handler(conn
->conn_id
, NULL
, NULL
);
185 hyperv_sint_route_unref(conn
->sint_route
);
193 static void evt_conn_handler(EventNotifier
*notifier
)
195 TestEvtConn
*conn
= container_of(notifier
, TestEvtConn
, notifier
);
197 event_notifier_test_and_clear(notifier
);
199 /* signal the same event flag we've got */
200 assert(!hyperv_set_event_flag(conn
->sint_route
, conn
->conn_id
));
203 static void evt_conn_create(HypervTestDev
*dev
, uint8_t vp_index
,
204 uint8_t sint
, uint8_t conn_id
)
208 conn
= g_new0(TestEvtConn
, 1);
211 conn
->conn_id
= conn_id
;
213 conn
->sint_route
= hyperv_sint_route_new(vp_index
, sint
, NULL
, NULL
);
214 assert(conn
->sint_route
);
216 assert(!event_notifier_init(&conn
->notifier
, false));
218 event_notifier_set_handler(&conn
->notifier
, evt_conn_handler
);
220 assert(!hyperv_set_event_flag_handler(conn_id
, &conn
->notifier
));
222 QLIST_INSERT_HEAD(&dev
->evt_conns
, conn
, le
);
225 static void evt_conn_destroy(HypervTestDev
*dev
, uint8_t conn_id
)
229 QLIST_FOREACH(conn
, &dev
->evt_conns
, le
) {
230 if (conn
->conn_id
== conn_id
) {
231 QLIST_REMOVE(conn
, le
);
232 hyperv_set_event_flag_handler(conn
->conn_id
, NULL
);
233 event_notifier_set_handler(&conn
->notifier
, NULL
);
234 event_notifier_cleanup(&conn
->notifier
);
235 hyperv_sint_route_unref(conn
->sint_route
);
243 static uint64_t hv_test_dev_read(void *opaque
, hwaddr addr
, unsigned size
)
248 static void hv_test_dev_write(void *opaque
, hwaddr addr
, uint64_t data
,
251 HypervTestDev
*dev
= HYPERV_TEST_DEV(opaque
);
252 uint8_t sint
= data
& 0xFF;
253 uint8_t vp_index
= (data
>> 8ULL) & 0xFF;
254 uint8_t ctl
= (data
>> 16ULL) & 0xFF;
255 uint8_t conn_id
= (data
>> 24ULL) & 0xFF;
258 case HV_TEST_DEV_SINT_ROUTE_CREATE
:
259 sint_route_create(dev
, vp_index
, sint
);
261 case HV_TEST_DEV_SINT_ROUTE_DESTROY
:
262 sint_route_destroy(dev
, vp_index
, sint
);
264 case HV_TEST_DEV_SINT_ROUTE_SET_SINT
:
265 sint_route_set_sint(dev
, vp_index
, sint
);
267 case HV_TEST_DEV_MSG_CONN_CREATE
:
268 msg_conn_create(dev
, vp_index
, sint
, conn_id
);
270 case HV_TEST_DEV_MSG_CONN_DESTROY
:
271 msg_conn_destroy(dev
, conn_id
);
273 case HV_TEST_DEV_EVT_CONN_CREATE
:
274 evt_conn_create(dev
, vp_index
, sint
, conn_id
);
276 case HV_TEST_DEV_EVT_CONN_DESTROY
:
277 evt_conn_destroy(dev
, conn_id
);
284 static const MemoryRegionOps synic_test_sint_ops
= {
285 .read
= hv_test_dev_read
,
286 .write
= hv_test_dev_write
,
287 .valid
.min_access_size
= 4,
288 .valid
.max_access_size
= 4,
289 .endianness
= DEVICE_LITTLE_ENDIAN
,
292 static void hv_test_dev_realizefn(DeviceState
*d
, Error
**errp
)
294 ISADevice
*isa
= ISA_DEVICE(d
);
295 HypervTestDev
*dev
= HYPERV_TEST_DEV(d
);
296 MemoryRegion
*io
= isa_address_space_io(isa
);
298 QLIST_INIT(&dev
->sint_routes
);
299 QLIST_INIT(&dev
->msg_conns
);
300 QLIST_INIT(&dev
->evt_conns
);
301 memory_region_init_io(&dev
->sint_control
, OBJECT(dev
),
302 &synic_test_sint_ops
, dev
,
303 "hyperv-testdev-ctl", 4);
304 memory_region_add_subregion(io
, 0x3000, &dev
->sint_control
);
307 static void hv_test_dev_class_init(ObjectClass
*klass
, void *data
)
309 DeviceClass
*dc
= DEVICE_CLASS(klass
);
311 set_bit(DEVICE_CATEGORY_MISC
, dc
->categories
);
312 dc
->realize
= hv_test_dev_realizefn
;
315 static const TypeInfo hv_test_dev_info
= {
316 .name
= TYPE_HYPERV_TEST_DEV
,
317 .parent
= TYPE_ISA_DEVICE
,
318 .instance_size
= sizeof(HypervTestDev
),
319 .class_init
= hv_test_dev_class_init
,
322 static void hv_test_dev_register_types(void)
324 type_register_static(&hv_test_dev_info
);
326 type_init(hv_test_dev_register_types
);