xref: /qemu/hw/hyperv/vmbus.c (revision ad80e367)
10d71f708SJon Doron /*
20d71f708SJon Doron  * QEMU Hyper-V VMBus
30d71f708SJon Doron  *
40d71f708SJon Doron  * Copyright (c) 2017-2018 Virtuozzo International GmbH.
50d71f708SJon Doron  *
60d71f708SJon Doron  * This work is licensed under the terms of the GNU GPL, version 2 or later.
70d71f708SJon Doron  * See the COPYING file in the top-level directory.
80d71f708SJon Doron  */
90d71f708SJon Doron 
100d71f708SJon Doron #include "qemu/osdep.h"
110d71f708SJon Doron #include "qemu/error-report.h"
120d71f708SJon Doron #include "qemu/main-loop.h"
130d71f708SJon Doron #include "qapi/error.h"
140d71f708SJon Doron #include "migration/vmstate.h"
150d71f708SJon Doron #include "hw/qdev-properties.h"
16ce35e229SEduardo Habkost #include "hw/qdev-properties-system.h"
170d71f708SJon Doron #include "hw/hyperv/hyperv.h"
180d71f708SJon Doron #include "hw/hyperv/vmbus.h"
190d71f708SJon Doron #include "hw/hyperv/vmbus-bridge.h"
200d71f708SJon Doron #include "hw/sysbus.h"
210d71f708SJon Doron #include "cpu.h"
220d71f708SJon Doron #include "trace.h"
230d71f708SJon Doron 
240d71f708SJon Doron enum {
250d71f708SJon Doron     VMGPADL_INIT,
260d71f708SJon Doron     VMGPADL_ALIVE,
270d71f708SJon Doron     VMGPADL_TEARINGDOWN,
280d71f708SJon Doron     VMGPADL_TORNDOWN,
290d71f708SJon Doron };
300d71f708SJon Doron 
310d71f708SJon Doron struct VMBusGpadl {
320d71f708SJon Doron     /* GPADL id */
330d71f708SJon Doron     uint32_t id;
340d71f708SJon Doron     /* associated channel id (rudimentary?) */
350d71f708SJon Doron     uint32_t child_relid;
360d71f708SJon Doron 
370d71f708SJon Doron     /* number of pages in the GPADL as declared in GPADL_HEADER message */
380d71f708SJon Doron     uint32_t num_gfns;
390d71f708SJon Doron     /*
400d71f708SJon Doron      * Due to limited message size, GPADL may not fit fully in a single
410d71f708SJon Doron      * GPADL_HEADER message, and is further popluated using GPADL_BODY
420d71f708SJon Doron      * messages.  @seen_gfns is the number of pages seen so far; once it
430d71f708SJon Doron      * reaches @num_gfns, the GPADL is ready to use.
440d71f708SJon Doron      */
450d71f708SJon Doron     uint32_t seen_gfns;
460d71f708SJon Doron     /* array of GFNs (of size @num_gfns once allocated) */
470d71f708SJon Doron     uint64_t *gfns;
480d71f708SJon Doron 
490d71f708SJon Doron     uint8_t state;
500d71f708SJon Doron 
510d71f708SJon Doron     QTAILQ_ENTRY(VMBusGpadl) link;
520d71f708SJon Doron     VMBus *vmbus;
530d71f708SJon Doron     unsigned refcount;
540d71f708SJon Doron };
550d71f708SJon Doron 
560d71f708SJon Doron /*
570d71f708SJon Doron  * Wrap sequential read from / write to GPADL.
580d71f708SJon Doron  */
590d71f708SJon Doron typedef struct GpadlIter {
600d71f708SJon Doron     VMBusGpadl *gpadl;
610d71f708SJon Doron     AddressSpace *as;
620d71f708SJon Doron     DMADirection dir;
630d71f708SJon Doron     /* offset into GPADL where the next i/o will be performed */
640d71f708SJon Doron     uint32_t off;
650d71f708SJon Doron     /*
660d71f708SJon Doron      * Cached mapping of the currently accessed page, up to page boundary.
670d71f708SJon Doron      * Updated lazily on i/o.
680d71f708SJon Doron      * Note: MemoryRegionCache can not be used here because pages in the GPADL
690d71f708SJon Doron      * are non-contiguous and may belong to different memory regions.
700d71f708SJon Doron      */
710d71f708SJon Doron     void *map;
720d71f708SJon Doron     /* offset after last i/o (i.e. not affected by seek) */
730d71f708SJon Doron     uint32_t last_off;
740d71f708SJon Doron     /*
750d71f708SJon Doron      * Indicator that the iterator is active and may have a cached mapping.
760d71f708SJon Doron      * Allows to enforce bracketing of all i/o (which may create cached
770d71f708SJon Doron      * mappings) and thus exclude mapping leaks.
780d71f708SJon Doron      */
790d71f708SJon Doron     bool active;
800d71f708SJon Doron } GpadlIter;
810d71f708SJon Doron 
820d71f708SJon Doron /*
830d71f708SJon Doron  * Ring buffer.  There are two of them, sitting in the same GPADL, for each
840d71f708SJon Doron  * channel.
850d71f708SJon Doron  * Each ring buffer consists of a set of pages, with the first page containing
860d71f708SJon Doron  * the ring buffer header, and the remaining pages being for data packets.
870d71f708SJon Doron  */
880d71f708SJon Doron typedef struct VMBusRingBufCommon {
890d71f708SJon Doron     AddressSpace *as;
900d71f708SJon Doron     /* GPA of the ring buffer header */
910d71f708SJon Doron     dma_addr_t rb_addr;
920d71f708SJon Doron     /* start and length of the ring buffer data area within GPADL */
930d71f708SJon Doron     uint32_t base;
940d71f708SJon Doron     uint32_t len;
950d71f708SJon Doron 
960d71f708SJon Doron     GpadlIter iter;
970d71f708SJon Doron } VMBusRingBufCommon;
980d71f708SJon Doron 
990d71f708SJon Doron typedef struct VMBusSendRingBuf {
1000d71f708SJon Doron     VMBusRingBufCommon common;
1010d71f708SJon Doron     /* current write index, to be committed at the end of send */
1020d71f708SJon Doron     uint32_t wr_idx;
1030d71f708SJon Doron     /* write index at the start of send */
1040d71f708SJon Doron     uint32_t last_wr_idx;
1050d71f708SJon Doron     /* space to be requested from the guest */
1060d71f708SJon Doron     uint32_t wanted;
1070d71f708SJon Doron     /* space reserved for planned sends */
1080d71f708SJon Doron     uint32_t reserved;
1090d71f708SJon Doron     /* last seen read index */
1100d71f708SJon Doron     uint32_t last_seen_rd_idx;
1110d71f708SJon Doron } VMBusSendRingBuf;
1120d71f708SJon Doron 
1130d71f708SJon Doron typedef struct VMBusRecvRingBuf {
1140d71f708SJon Doron     VMBusRingBufCommon common;
1150d71f708SJon Doron     /* current read index, to be committed at the end of receive */
1160d71f708SJon Doron     uint32_t rd_idx;
1170d71f708SJon Doron     /* read index at the start of receive */
1180d71f708SJon Doron     uint32_t last_rd_idx;
1190d71f708SJon Doron     /* last seen write index */
1200d71f708SJon Doron     uint32_t last_seen_wr_idx;
1210d71f708SJon Doron } VMBusRecvRingBuf;
1220d71f708SJon Doron 
1230d71f708SJon Doron 
1240d71f708SJon Doron enum {
1250d71f708SJon Doron     VMOFFER_INIT,
1260d71f708SJon Doron     VMOFFER_SENDING,
1270d71f708SJon Doron     VMOFFER_SENT,
1280d71f708SJon Doron };
1290d71f708SJon Doron 
1300d71f708SJon Doron enum {
1310d71f708SJon Doron     VMCHAN_INIT,
1320d71f708SJon Doron     VMCHAN_OPENING,
1330d71f708SJon Doron     VMCHAN_OPEN,
1340d71f708SJon Doron };
1350d71f708SJon Doron 
1360d71f708SJon Doron struct VMBusChannel {
1370d71f708SJon Doron     VMBusDevice *dev;
1380d71f708SJon Doron 
1390d71f708SJon Doron     /* channel id */
1400d71f708SJon Doron     uint32_t id;
1410d71f708SJon Doron     /*
1420d71f708SJon Doron      * subchannel index within the device; subchannel #0 is "primary" and
1430d71f708SJon Doron      * always exists
1440d71f708SJon Doron      */
1450d71f708SJon Doron     uint16_t subchan_idx;
1460d71f708SJon Doron     uint32_t open_id;
1470d71f708SJon Doron     /* VP_INDEX of the vCPU to notify with (synthetic) interrupts */
1480d71f708SJon Doron     uint32_t target_vp;
1490d71f708SJon Doron     /* GPADL id to use for the ring buffers */
1500d71f708SJon Doron     uint32_t ringbuf_gpadl;
1510d71f708SJon Doron     /* start (in pages) of the send ring buffer within @ringbuf_gpadl */
1520d71f708SJon Doron     uint32_t ringbuf_send_offset;
1530d71f708SJon Doron 
1540d71f708SJon Doron     uint8_t offer_state;
1550d71f708SJon Doron     uint8_t state;
1560d71f708SJon Doron     bool is_open;
1570d71f708SJon Doron 
1580d71f708SJon Doron     /* main device worker; copied from the device class */
1590d71f708SJon Doron     VMBusChannelNotifyCb notify_cb;
1600d71f708SJon Doron     /*
1610d71f708SJon Doron      * guest->host notifications, either sent directly or dispatched via
1620d71f708SJon Doron      * interrupt page (older VMBus)
1630d71f708SJon Doron      */
1640d71f708SJon Doron     EventNotifier notifier;
1650d71f708SJon Doron 
1660d71f708SJon Doron     VMBus *vmbus;
1670d71f708SJon Doron     /*
1680d71f708SJon Doron      * SINT route to signal with host->guest notifications; may be shared with
1690d71f708SJon Doron      * the main VMBus SINT route
1700d71f708SJon Doron      */
1710d71f708SJon Doron     HvSintRoute *notify_route;
1720d71f708SJon Doron     VMBusGpadl *gpadl;
1730d71f708SJon Doron 
1740d71f708SJon Doron     VMBusSendRingBuf send_ringbuf;
1750d71f708SJon Doron     VMBusRecvRingBuf recv_ringbuf;
1760d71f708SJon Doron 
1770d71f708SJon Doron     QTAILQ_ENTRY(VMBusChannel) link;
1780d71f708SJon Doron };
1790d71f708SJon Doron 
1800d71f708SJon Doron /*
1810d71f708SJon Doron  * Hyper-V spec mandates that every message port has 16 buffers, which means
1820d71f708SJon Doron  * that the guest can post up to this many messages without blocking.
1830d71f708SJon Doron  * Therefore a queue for incoming messages has to be provided.
1840d71f708SJon Doron  * For outgoing (i.e. host->guest) messages there's no queue; the VMBus just
1850d71f708SJon Doron  * doesn't transition to a new state until the message is known to have been
1860d71f708SJon Doron  * successfully delivered to the respective SynIC message slot.
1870d71f708SJon Doron  */
1880d71f708SJon Doron #define HV_MSG_QUEUE_LEN     16
1890d71f708SJon Doron 
1900d71f708SJon Doron /* Hyper-V devices never use channel #0.  Must be something special. */
1910d71f708SJon Doron #define VMBUS_FIRST_CHANID      1
1920d71f708SJon Doron /* Each channel occupies one bit within a single event page sint slot. */
1930d71f708SJon Doron #define VMBUS_CHANID_COUNT      (HV_EVENT_FLAGS_COUNT - VMBUS_FIRST_CHANID)
1940d71f708SJon Doron /* Leave a few connection numbers for other purposes. */
1950d71f708SJon Doron #define VMBUS_CHAN_CONNECTION_OFFSET     16
1960d71f708SJon Doron 
1970d71f708SJon Doron /*
1980d71f708SJon Doron  * Since the success or failure of sending a message is reported
1990d71f708SJon Doron  * asynchronously, the VMBus state machine has effectively two entry points:
2000d71f708SJon Doron  * vmbus_run and vmbus_msg_cb (the latter is called when the host->guest
2010d71f708SJon Doron  * message delivery status becomes known).  Both are run as oneshot BHs on the
2020d71f708SJon Doron  * main aio context, ensuring serialization.
2030d71f708SJon Doron  */
2040d71f708SJon Doron enum {
2050d71f708SJon Doron     VMBUS_LISTEN,
2060d71f708SJon Doron     VMBUS_HANDSHAKE,
2070d71f708SJon Doron     VMBUS_OFFER,
2080d71f708SJon Doron     VMBUS_CREATE_GPADL,
2090d71f708SJon Doron     VMBUS_TEARDOWN_GPADL,
2100d71f708SJon Doron     VMBUS_OPEN_CHANNEL,
2110d71f708SJon Doron     VMBUS_UNLOAD,
2120d71f708SJon Doron     VMBUS_STATE_MAX
2130d71f708SJon Doron };
2140d71f708SJon Doron 
2150d71f708SJon Doron struct VMBus {
2160d71f708SJon Doron     BusState parent;
2170d71f708SJon Doron 
2180d71f708SJon Doron     uint8_t state;
2190d71f708SJon Doron     /* protection against recursive aio_poll (see vmbus_run) */
2200d71f708SJon Doron     bool in_progress;
2210d71f708SJon Doron     /* whether there's a message being delivered to the guest */
2220d71f708SJon Doron     bool msg_in_progress;
2230d71f708SJon Doron     uint32_t version;
2240d71f708SJon Doron     /* VP_INDEX of the vCPU to send messages and interrupts to */
2250d71f708SJon Doron     uint32_t target_vp;
2260d71f708SJon Doron     HvSintRoute *sint_route;
2270d71f708SJon Doron     /*
2280d71f708SJon Doron      * interrupt page for older protocol versions; newer ones use SynIC event
2290d71f708SJon Doron      * flags directly
2300d71f708SJon Doron      */
2310d71f708SJon Doron     hwaddr int_page_gpa;
2320d71f708SJon Doron 
2330d71f708SJon Doron     DECLARE_BITMAP(chanid_bitmap, VMBUS_CHANID_COUNT);
2340d71f708SJon Doron 
2350d71f708SJon Doron     /* incoming message queue */
2360d71f708SJon Doron     struct hyperv_post_message_input rx_queue[HV_MSG_QUEUE_LEN];
2370d71f708SJon Doron     uint8_t rx_queue_head;
2380d71f708SJon Doron     uint8_t rx_queue_size;
2390d71f708SJon Doron     QemuMutex rx_queue_lock;
2400d71f708SJon Doron 
2410d71f708SJon Doron     QTAILQ_HEAD(, VMBusGpadl) gpadl_list;
2420d71f708SJon Doron     QTAILQ_HEAD(, VMBusChannel) channel_list;
2430d71f708SJon Doron 
2440d71f708SJon Doron     /*
2450d71f708SJon Doron      * guest->host notifications for older VMBus, to be dispatched via
2460d71f708SJon Doron      * interrupt page
2470d71f708SJon Doron      */
2480d71f708SJon Doron     EventNotifier notifier;
2490d71f708SJon Doron };
2500d71f708SJon Doron 
gpadl_full(VMBusGpadl * gpadl)2510d71f708SJon Doron static bool gpadl_full(VMBusGpadl *gpadl)
2520d71f708SJon Doron {
2530d71f708SJon Doron     return gpadl->seen_gfns == gpadl->num_gfns;
2540d71f708SJon Doron }
2550d71f708SJon Doron 
create_gpadl(VMBus * vmbus,uint32_t id,uint32_t child_relid,uint32_t num_gfns)2560d71f708SJon Doron static VMBusGpadl *create_gpadl(VMBus *vmbus, uint32_t id,
2570d71f708SJon Doron                                 uint32_t child_relid, uint32_t num_gfns)
2580d71f708SJon Doron {
2590d71f708SJon Doron     VMBusGpadl *gpadl = g_new0(VMBusGpadl, 1);
2600d71f708SJon Doron 
2610d71f708SJon Doron     gpadl->id = id;
2620d71f708SJon Doron     gpadl->child_relid = child_relid;
2630d71f708SJon Doron     gpadl->num_gfns = num_gfns;
2640d71f708SJon Doron     gpadl->gfns = g_new(uint64_t, num_gfns);
2650d71f708SJon Doron     QTAILQ_INSERT_HEAD(&vmbus->gpadl_list, gpadl, link);
2660d71f708SJon Doron     gpadl->vmbus = vmbus;
2670d71f708SJon Doron     gpadl->refcount = 1;
2680d71f708SJon Doron     return gpadl;
2690d71f708SJon Doron }
2700d71f708SJon Doron 
free_gpadl(VMBusGpadl * gpadl)2710d71f708SJon Doron static void free_gpadl(VMBusGpadl *gpadl)
2720d71f708SJon Doron {
2730d71f708SJon Doron     QTAILQ_REMOVE(&gpadl->vmbus->gpadl_list, gpadl, link);
2740d71f708SJon Doron     g_free(gpadl->gfns);
2750d71f708SJon Doron     g_free(gpadl);
2760d71f708SJon Doron }
2770d71f708SJon Doron 
find_gpadl(VMBus * vmbus,uint32_t gpadl_id)2780d71f708SJon Doron static VMBusGpadl *find_gpadl(VMBus *vmbus, uint32_t gpadl_id)
2790d71f708SJon Doron {
2800d71f708SJon Doron     VMBusGpadl *gpadl;
2810d71f708SJon Doron     QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) {
2820d71f708SJon Doron         if (gpadl->id == gpadl_id) {
2830d71f708SJon Doron             return gpadl;
2840d71f708SJon Doron         }
2850d71f708SJon Doron     }
2860d71f708SJon Doron     return NULL;
2870d71f708SJon Doron }
2880d71f708SJon Doron 
vmbus_get_gpadl(VMBusChannel * chan,uint32_t gpadl_id)2890d71f708SJon Doron VMBusGpadl *vmbus_get_gpadl(VMBusChannel *chan, uint32_t gpadl_id)
2900d71f708SJon Doron {
2910d71f708SJon Doron     VMBusGpadl *gpadl = find_gpadl(chan->vmbus, gpadl_id);
2920d71f708SJon Doron     if (!gpadl || !gpadl_full(gpadl)) {
2930d71f708SJon Doron         return NULL;
2940d71f708SJon Doron     }
2950d71f708SJon Doron     gpadl->refcount++;
2960d71f708SJon Doron     return gpadl;
2970d71f708SJon Doron }
2980d71f708SJon Doron 
vmbus_put_gpadl(VMBusGpadl * gpadl)2990d71f708SJon Doron void vmbus_put_gpadl(VMBusGpadl *gpadl)
3000d71f708SJon Doron {
3010d71f708SJon Doron     if (!gpadl) {
3020d71f708SJon Doron         return;
3030d71f708SJon Doron     }
3040d71f708SJon Doron     if (--gpadl->refcount) {
3050d71f708SJon Doron         return;
3060d71f708SJon Doron     }
3070d71f708SJon Doron     free_gpadl(gpadl);
3080d71f708SJon Doron }
3090d71f708SJon Doron 
vmbus_gpadl_len(VMBusGpadl * gpadl)3100d71f708SJon Doron uint32_t vmbus_gpadl_len(VMBusGpadl *gpadl)
3110d71f708SJon Doron {
3120d71f708SJon Doron     return gpadl->num_gfns * TARGET_PAGE_SIZE;
3130d71f708SJon Doron }
3140d71f708SJon Doron 
gpadl_iter_init(GpadlIter * iter,VMBusGpadl * gpadl,AddressSpace * as,DMADirection dir)3150d71f708SJon Doron static void gpadl_iter_init(GpadlIter *iter, VMBusGpadl *gpadl,
3160d71f708SJon Doron                             AddressSpace *as, DMADirection dir)
3170d71f708SJon Doron {
3180d71f708SJon Doron     iter->gpadl = gpadl;
3190d71f708SJon Doron     iter->as = as;
3200d71f708SJon Doron     iter->dir = dir;
3210d71f708SJon Doron     iter->active = false;
3220d71f708SJon Doron }
3230d71f708SJon Doron 
gpadl_iter_cache_unmap(GpadlIter * iter)3240d71f708SJon Doron static inline void gpadl_iter_cache_unmap(GpadlIter *iter)
3250d71f708SJon Doron {
3260d71f708SJon Doron     uint32_t map_start_in_page = (uintptr_t)iter->map & ~TARGET_PAGE_MASK;
3270d71f708SJon Doron     uint32_t io_end_in_page = ((iter->last_off - 1) & ~TARGET_PAGE_MASK) + 1;
3280d71f708SJon Doron 
3290d71f708SJon Doron     /* mapping is only done to do non-zero amount of i/o */
3300d71f708SJon Doron     assert(iter->last_off > 0);
3310d71f708SJon Doron     assert(map_start_in_page < io_end_in_page);
3320d71f708SJon Doron 
3330d71f708SJon Doron     dma_memory_unmap(iter->as, iter->map, TARGET_PAGE_SIZE - map_start_in_page,
3340d71f708SJon Doron                      iter->dir, io_end_in_page - map_start_in_page);
3350d71f708SJon Doron }
3360d71f708SJon Doron 
3370d71f708SJon Doron /*
3380d71f708SJon Doron  * Copy exactly @len bytes between the GPADL pointed to by @iter and @buf.
3390d71f708SJon Doron  * The direction of the copy is determined by @iter->dir.
3400d71f708SJon Doron  * The caller must ensure the operation overflows neither @buf nor the GPADL
3410d71f708SJon Doron  * (there's an assert for the latter).
3420d71f708SJon Doron  * Reuse the currently mapped page in the GPADL if possible.
3430d71f708SJon Doron  */
gpadl_iter_io(GpadlIter * iter,void * buf,uint32_t len)3440d71f708SJon Doron static ssize_t gpadl_iter_io(GpadlIter *iter, void *buf, uint32_t len)
3450d71f708SJon Doron {
3460d71f708SJon Doron     ssize_t ret = len;
3470d71f708SJon Doron 
3480d71f708SJon Doron     assert(iter->active);
3490d71f708SJon Doron 
3500d71f708SJon Doron     while (len) {
3510d71f708SJon Doron         uint32_t off_in_page = iter->off & ~TARGET_PAGE_MASK;
3520d71f708SJon Doron         uint32_t pgleft = TARGET_PAGE_SIZE - off_in_page;
3530d71f708SJon Doron         uint32_t cplen = MIN(pgleft, len);
3540d71f708SJon Doron         void *p;
3550d71f708SJon Doron 
3560d71f708SJon Doron         /* try to reuse the cached mapping */
3570d71f708SJon Doron         if (iter->map) {
3580d71f708SJon Doron             uint32_t map_start_in_page =
3590d71f708SJon Doron                 (uintptr_t)iter->map & ~TARGET_PAGE_MASK;
3600d71f708SJon Doron             uint32_t off_base = iter->off & ~TARGET_PAGE_MASK;
3610d71f708SJon Doron             uint32_t mapped_base = (iter->last_off - 1) & ~TARGET_PAGE_MASK;
3620d71f708SJon Doron             if (off_base != mapped_base || off_in_page < map_start_in_page) {
3630d71f708SJon Doron                 gpadl_iter_cache_unmap(iter);
3640d71f708SJon Doron                 iter->map = NULL;
3650d71f708SJon Doron             }
3660d71f708SJon Doron         }
3670d71f708SJon Doron 
3680d71f708SJon Doron         if (!iter->map) {
3690d71f708SJon Doron             dma_addr_t maddr;
3700d71f708SJon Doron             dma_addr_t mlen = pgleft;
3710d71f708SJon Doron             uint32_t idx = iter->off >> TARGET_PAGE_BITS;
3720d71f708SJon Doron             assert(idx < iter->gpadl->num_gfns);
3730d71f708SJon Doron 
3740d71f708SJon Doron             maddr = (iter->gpadl->gfns[idx] << TARGET_PAGE_BITS) | off_in_page;
3750d71f708SJon Doron 
376a1d4b0a3SPhilippe Mathieu-Daudé             iter->map = dma_memory_map(iter->as, maddr, &mlen, iter->dir,
377a1d4b0a3SPhilippe Mathieu-Daudé                                        MEMTXATTRS_UNSPECIFIED);
3780d71f708SJon Doron             if (mlen != pgleft) {
3790d71f708SJon Doron                 dma_memory_unmap(iter->as, iter->map, mlen, iter->dir, 0);
3800d71f708SJon Doron                 iter->map = NULL;
3810d71f708SJon Doron                 return -EFAULT;
3820d71f708SJon Doron             }
3830d71f708SJon Doron         }
3840d71f708SJon Doron 
3858b39aa90SJon Doron         p = (void *)(uintptr_t)(((uintptr_t)iter->map & TARGET_PAGE_MASK) |
3868b39aa90SJon Doron                 off_in_page);
3870d71f708SJon Doron         if (iter->dir == DMA_DIRECTION_FROM_DEVICE) {
3880d71f708SJon Doron             memcpy(p, buf, cplen);
3890d71f708SJon Doron         } else {
3900d71f708SJon Doron             memcpy(buf, p, cplen);
3910d71f708SJon Doron         }
3920d71f708SJon Doron 
3930d71f708SJon Doron         buf += cplen;
3940d71f708SJon Doron         len -= cplen;
3950d71f708SJon Doron         iter->off += cplen;
3960d71f708SJon Doron         iter->last_off = iter->off;
3970d71f708SJon Doron     }
3980d71f708SJon Doron 
3990d71f708SJon Doron     return ret;
4000d71f708SJon Doron }
4010d71f708SJon Doron 
4020d71f708SJon Doron /*
4030d71f708SJon Doron  * Position the iterator @iter at new offset @new_off.
4040d71f708SJon Doron  * If this results in the cached mapping being unusable with the new offset,
4050d71f708SJon Doron  * unmap it.
4060d71f708SJon Doron  */
gpadl_iter_seek(GpadlIter * iter,uint32_t new_off)4070d71f708SJon Doron static inline void gpadl_iter_seek(GpadlIter *iter, uint32_t new_off)
4080d71f708SJon Doron {
4090d71f708SJon Doron     assert(iter->active);
4100d71f708SJon Doron     iter->off = new_off;
4110d71f708SJon Doron }
4120d71f708SJon Doron 
4130d71f708SJon Doron /*
4140d71f708SJon Doron  * Start a series of i/o on the GPADL.
4150d71f708SJon Doron  * After this i/o and seek operations on @iter become legal.
4160d71f708SJon Doron  */
gpadl_iter_start_io(GpadlIter * iter)4170d71f708SJon Doron static inline void gpadl_iter_start_io(GpadlIter *iter)
4180d71f708SJon Doron {
4190d71f708SJon Doron     assert(!iter->active);
4200d71f708SJon Doron     /* mapping is cached lazily on i/o */
4210d71f708SJon Doron     iter->map = NULL;
4220d71f708SJon Doron     iter->active = true;
4230d71f708SJon Doron }
4240d71f708SJon Doron 
4250d71f708SJon Doron /*
4260d71f708SJon Doron  * End the eariler started series of i/o on the GPADL and release the cached
4270d71f708SJon Doron  * mapping if any.
4280d71f708SJon Doron  */
gpadl_iter_end_io(GpadlIter * iter)4290d71f708SJon Doron static inline void gpadl_iter_end_io(GpadlIter *iter)
4300d71f708SJon Doron {
4310d71f708SJon Doron     assert(iter->active);
4320d71f708SJon Doron 
4330d71f708SJon Doron     if (iter->map) {
4340d71f708SJon Doron         gpadl_iter_cache_unmap(iter);
4350d71f708SJon Doron     }
4360d71f708SJon Doron 
4370d71f708SJon Doron     iter->active = false;
4380d71f708SJon Doron }
4390d71f708SJon Doron 
4400d71f708SJon Doron static void vmbus_resched(VMBus *vmbus);
4410d71f708SJon Doron static void vmbus_msg_cb(void *data, int status);
4420d71f708SJon Doron 
vmbus_iov_to_gpadl(VMBusChannel * chan,VMBusGpadl * gpadl,uint32_t off,const struct iovec * iov,size_t iov_cnt)4430d71f708SJon Doron ssize_t vmbus_iov_to_gpadl(VMBusChannel *chan, VMBusGpadl *gpadl, uint32_t off,
4440d71f708SJon Doron                            const struct iovec *iov, size_t iov_cnt)
4450d71f708SJon Doron {
4460d71f708SJon Doron     GpadlIter iter;
4470d71f708SJon Doron     size_t i;
4480d71f708SJon Doron     ssize_t ret = 0;
4490d71f708SJon Doron 
4500d71f708SJon Doron     gpadl_iter_init(&iter, gpadl, chan->dev->dma_as,
4510d71f708SJon Doron                     DMA_DIRECTION_FROM_DEVICE);
4520d71f708SJon Doron     gpadl_iter_start_io(&iter);
4530d71f708SJon Doron     gpadl_iter_seek(&iter, off);
4540d71f708SJon Doron     for (i = 0; i < iov_cnt; i++) {
4550d71f708SJon Doron         ret = gpadl_iter_io(&iter, iov[i].iov_base, iov[i].iov_len);
4560d71f708SJon Doron         if (ret < 0) {
4570d71f708SJon Doron             goto out;
4580d71f708SJon Doron         }
4590d71f708SJon Doron     }
4600d71f708SJon Doron out:
4610d71f708SJon Doron     gpadl_iter_end_io(&iter);
4620d71f708SJon Doron     return ret;
4630d71f708SJon Doron }
4640d71f708SJon Doron 
vmbus_map_sgl(VMBusChanReq * req,DMADirection dir,struct iovec * iov,unsigned iov_cnt,size_t len,size_t off)4650d71f708SJon Doron int vmbus_map_sgl(VMBusChanReq *req, DMADirection dir, struct iovec *iov,
4660d71f708SJon Doron                   unsigned iov_cnt, size_t len, size_t off)
4670d71f708SJon Doron {
4680d71f708SJon Doron     int ret_cnt = 0, ret;
4690d71f708SJon Doron     unsigned i;
4700d71f708SJon Doron     QEMUSGList *sgl = &req->sgl;
4710d71f708SJon Doron     ScatterGatherEntry *sg = sgl->sg;
4720d71f708SJon Doron 
4730d71f708SJon Doron     for (i = 0; i < sgl->nsg; i++) {
4740d71f708SJon Doron         if (sg[i].len > off) {
4750d71f708SJon Doron             break;
4760d71f708SJon Doron         }
4770d71f708SJon Doron         off -= sg[i].len;
4780d71f708SJon Doron     }
4790d71f708SJon Doron     for (; len && i < sgl->nsg; i++) {
4800d71f708SJon Doron         dma_addr_t mlen = MIN(sg[i].len - off, len);
4810d71f708SJon Doron         dma_addr_t addr = sg[i].base + off;
4820d71f708SJon Doron         len -= mlen;
4830d71f708SJon Doron         off = 0;
4840d71f708SJon Doron 
4850d71f708SJon Doron         for (; mlen; ret_cnt++) {
4860d71f708SJon Doron             dma_addr_t l = mlen;
4870d71f708SJon Doron             dma_addr_t a = addr;
4880d71f708SJon Doron 
4890d71f708SJon Doron             if (ret_cnt == iov_cnt) {
4900d71f708SJon Doron                 ret = -ENOBUFS;
4910d71f708SJon Doron                 goto err;
4920d71f708SJon Doron             }
4930d71f708SJon Doron 
494a1d4b0a3SPhilippe Mathieu-Daudé             iov[ret_cnt].iov_base = dma_memory_map(sgl->as, a, &l, dir,
495a1d4b0a3SPhilippe Mathieu-Daudé                                                    MEMTXATTRS_UNSPECIFIED);
4960d71f708SJon Doron             if (!l) {
4970d71f708SJon Doron                 ret = -EFAULT;
4980d71f708SJon Doron                 goto err;
4990d71f708SJon Doron             }
5000d71f708SJon Doron             iov[ret_cnt].iov_len = l;
5010d71f708SJon Doron             addr += l;
5020d71f708SJon Doron             mlen -= l;
5030d71f708SJon Doron         }
5040d71f708SJon Doron     }
5050d71f708SJon Doron 
5060d71f708SJon Doron     return ret_cnt;
5070d71f708SJon Doron err:
5080d71f708SJon Doron     vmbus_unmap_sgl(req, dir, iov, ret_cnt, 0);
5090d71f708SJon Doron     return ret;
5100d71f708SJon Doron }
5110d71f708SJon Doron 
vmbus_unmap_sgl(VMBusChanReq * req,DMADirection dir,struct iovec * iov,unsigned iov_cnt,size_t accessed)5120d71f708SJon Doron void vmbus_unmap_sgl(VMBusChanReq *req, DMADirection dir, struct iovec *iov,
5130d71f708SJon Doron                      unsigned iov_cnt, size_t accessed)
5140d71f708SJon Doron {
5150d71f708SJon Doron     QEMUSGList *sgl = &req->sgl;
5160d71f708SJon Doron     unsigned i;
5170d71f708SJon Doron 
5180d71f708SJon Doron     for (i = 0; i < iov_cnt; i++) {
5190d71f708SJon Doron         size_t acsd = MIN(accessed, iov[i].iov_len);
5200d71f708SJon Doron         dma_memory_unmap(sgl->as, iov[i].iov_base, iov[i].iov_len, dir, acsd);
5210d71f708SJon Doron         accessed -= acsd;
5220d71f708SJon Doron     }
5230d71f708SJon Doron }
5240d71f708SJon Doron 
5250d71f708SJon Doron static const VMStateDescription vmstate_gpadl = {
5260d71f708SJon Doron     .name = "vmbus/gpadl",
5270d71f708SJon Doron     .version_id = 0,
5280d71f708SJon Doron     .minimum_version_id = 0,
5292ebfd1c4SRichard Henderson     .fields = (const VMStateField[]) {
5300d71f708SJon Doron         VMSTATE_UINT32(id, VMBusGpadl),
5310d71f708SJon Doron         VMSTATE_UINT32(child_relid, VMBusGpadl),
5320d71f708SJon Doron         VMSTATE_UINT32(num_gfns, VMBusGpadl),
5330d71f708SJon Doron         VMSTATE_UINT32(seen_gfns, VMBusGpadl),
5340d71f708SJon Doron         VMSTATE_VARRAY_UINT32_ALLOC(gfns, VMBusGpadl, num_gfns, 0,
5350d71f708SJon Doron                                     vmstate_info_uint64, uint64_t),
5360d71f708SJon Doron         VMSTATE_UINT8(state, VMBusGpadl),
5370d71f708SJon Doron         VMSTATE_END_OF_LIST()
5380d71f708SJon Doron     }
5390d71f708SJon Doron };
5400d71f708SJon Doron 
5410d71f708SJon Doron /*
5420d71f708SJon Doron  * Wrap the index into a ring buffer of @len bytes.
5430d71f708SJon Doron  * @idx is assumed not to exceed twice the size of the ringbuffer, so only
5440d71f708SJon Doron  * single wraparound is considered.
5450d71f708SJon Doron  */
rb_idx_wrap(uint32_t idx,uint32_t len)5460d71f708SJon Doron static inline uint32_t rb_idx_wrap(uint32_t idx, uint32_t len)
5470d71f708SJon Doron {
5480d71f708SJon Doron     if (idx >= len) {
5490d71f708SJon Doron         idx -= len;
5500d71f708SJon Doron     }
5510d71f708SJon Doron     return idx;
5520d71f708SJon Doron }
5530d71f708SJon Doron 
5540d71f708SJon Doron /*
5550d71f708SJon Doron  * Circular difference between two indices into a ring buffer of @len bytes.
5560d71f708SJon Doron  * @allow_catchup - whether @idx1 may catch up @idx2; e.g. read index may catch
5570d71f708SJon Doron  * up write index but not vice versa.
5580d71f708SJon Doron  */
rb_idx_delta(uint32_t idx1,uint32_t idx2,uint32_t len,bool allow_catchup)5590d71f708SJon Doron static inline uint32_t rb_idx_delta(uint32_t idx1, uint32_t idx2, uint32_t len,
5600d71f708SJon Doron                                     bool allow_catchup)
5610d71f708SJon Doron {
5620d71f708SJon Doron     return rb_idx_wrap(idx2 + len - idx1 - !allow_catchup, len);
5630d71f708SJon Doron }
5640d71f708SJon Doron 
ringbuf_map_hdr(VMBusRingBufCommon * ringbuf)5650d71f708SJon Doron static vmbus_ring_buffer *ringbuf_map_hdr(VMBusRingBufCommon *ringbuf)
5660d71f708SJon Doron {
5670d71f708SJon Doron     vmbus_ring_buffer *rb;
5680d71f708SJon Doron     dma_addr_t mlen = sizeof(*rb);
5690d71f708SJon Doron 
5700d71f708SJon Doron     rb = dma_memory_map(ringbuf->as, ringbuf->rb_addr, &mlen,
571a1d4b0a3SPhilippe Mathieu-Daudé                         DMA_DIRECTION_FROM_DEVICE, MEMTXATTRS_UNSPECIFIED);
5720d71f708SJon Doron     if (mlen != sizeof(*rb)) {
5730d71f708SJon Doron         dma_memory_unmap(ringbuf->as, rb, mlen,
5740d71f708SJon Doron                          DMA_DIRECTION_FROM_DEVICE, 0);
5750d71f708SJon Doron         return NULL;
5760d71f708SJon Doron     }
5770d71f708SJon Doron     return rb;
5780d71f708SJon Doron }
5790d71f708SJon Doron 
ringbuf_unmap_hdr(VMBusRingBufCommon * ringbuf,vmbus_ring_buffer * rb,bool dirty)5800d71f708SJon Doron static void ringbuf_unmap_hdr(VMBusRingBufCommon *ringbuf,
5810d71f708SJon Doron                               vmbus_ring_buffer *rb, bool dirty)
5820d71f708SJon Doron {
5830d71f708SJon Doron     assert(rb);
5840d71f708SJon Doron 
5850d71f708SJon Doron     dma_memory_unmap(ringbuf->as, rb, sizeof(*rb), DMA_DIRECTION_FROM_DEVICE,
5860d71f708SJon Doron                      dirty ? sizeof(*rb) : 0);
5870d71f708SJon Doron }
5880d71f708SJon Doron 
ringbuf_init_common(VMBusRingBufCommon * ringbuf,VMBusGpadl * gpadl,AddressSpace * as,DMADirection dir,uint32_t begin,uint32_t end)5890d71f708SJon Doron static void ringbuf_init_common(VMBusRingBufCommon *ringbuf, VMBusGpadl *gpadl,
5900d71f708SJon Doron                                 AddressSpace *as, DMADirection dir,
5910d71f708SJon Doron                                 uint32_t begin, uint32_t end)
5920d71f708SJon Doron {
5930d71f708SJon Doron     ringbuf->as = as;
5940d71f708SJon Doron     ringbuf->rb_addr = gpadl->gfns[begin] << TARGET_PAGE_BITS;
5950d71f708SJon Doron     ringbuf->base = (begin + 1) << TARGET_PAGE_BITS;
5960d71f708SJon Doron     ringbuf->len = (end - begin - 1) << TARGET_PAGE_BITS;
5970d71f708SJon Doron     gpadl_iter_init(&ringbuf->iter, gpadl, as, dir);
5980d71f708SJon Doron }
5990d71f708SJon Doron 
ringbufs_init(VMBusChannel * chan)6000d71f708SJon Doron static int ringbufs_init(VMBusChannel *chan)
6010d71f708SJon Doron {
6020d71f708SJon Doron     vmbus_ring_buffer *rb;
6030d71f708SJon Doron     VMBusSendRingBuf *send_ringbuf = &chan->send_ringbuf;
6040d71f708SJon Doron     VMBusRecvRingBuf *recv_ringbuf = &chan->recv_ringbuf;
6050d71f708SJon Doron 
6060d71f708SJon Doron     if (chan->ringbuf_send_offset <= 1 ||
6070d71f708SJon Doron         chan->gpadl->num_gfns <= chan->ringbuf_send_offset + 1) {
6080d71f708SJon Doron         return -EINVAL;
6090d71f708SJon Doron     }
6100d71f708SJon Doron 
6110d71f708SJon Doron     ringbuf_init_common(&recv_ringbuf->common, chan->gpadl, chan->dev->dma_as,
6120d71f708SJon Doron                         DMA_DIRECTION_TO_DEVICE, 0, chan->ringbuf_send_offset);
6130d71f708SJon Doron     ringbuf_init_common(&send_ringbuf->common, chan->gpadl, chan->dev->dma_as,
6140d71f708SJon Doron                         DMA_DIRECTION_FROM_DEVICE, chan->ringbuf_send_offset,
6150d71f708SJon Doron                         chan->gpadl->num_gfns);
6160d71f708SJon Doron     send_ringbuf->wanted = 0;
6170d71f708SJon Doron     send_ringbuf->reserved = 0;
6180d71f708SJon Doron 
6190d71f708SJon Doron     rb = ringbuf_map_hdr(&recv_ringbuf->common);
6200d71f708SJon Doron     if (!rb) {
6210d71f708SJon Doron         return -EFAULT;
6220d71f708SJon Doron     }
6230d71f708SJon Doron     recv_ringbuf->rd_idx = recv_ringbuf->last_rd_idx = rb->read_index;
6240d71f708SJon Doron     ringbuf_unmap_hdr(&recv_ringbuf->common, rb, false);
6250d71f708SJon Doron 
6260d71f708SJon Doron     rb = ringbuf_map_hdr(&send_ringbuf->common);
6270d71f708SJon Doron     if (!rb) {
6280d71f708SJon Doron         return -EFAULT;
6290d71f708SJon Doron     }
6300d71f708SJon Doron     send_ringbuf->wr_idx = send_ringbuf->last_wr_idx = rb->write_index;
6310d71f708SJon Doron     send_ringbuf->last_seen_rd_idx = rb->read_index;
6320d71f708SJon Doron     rb->feature_bits |= VMBUS_RING_BUFFER_FEAT_PENDING_SZ;
6330d71f708SJon Doron     ringbuf_unmap_hdr(&send_ringbuf->common, rb, true);
6340d71f708SJon Doron 
6350d71f708SJon Doron     if (recv_ringbuf->rd_idx >= recv_ringbuf->common.len ||
6360d71f708SJon Doron         send_ringbuf->wr_idx >= send_ringbuf->common.len) {
6370d71f708SJon Doron         return -EOVERFLOW;
6380d71f708SJon Doron     }
6390d71f708SJon Doron 
6400d71f708SJon Doron     return 0;
6410d71f708SJon Doron }
6420d71f708SJon Doron 
6430d71f708SJon Doron /*
6440d71f708SJon Doron  * Perform io between the GPADL-backed ringbuffer @ringbuf and @buf, wrapping
6450d71f708SJon Doron  * around if needed.
6460d71f708SJon Doron  * @len is assumed not to exceed the size of the ringbuffer, so only single
6470d71f708SJon Doron  * wraparound is considered.
6480d71f708SJon Doron  */
ringbuf_io(VMBusRingBufCommon * ringbuf,void * buf,uint32_t len)6490d71f708SJon Doron static ssize_t ringbuf_io(VMBusRingBufCommon *ringbuf, void *buf, uint32_t len)
6500d71f708SJon Doron {
6510d71f708SJon Doron     ssize_t ret1 = 0, ret2 = 0;
6520d71f708SJon Doron     uint32_t remain = ringbuf->len + ringbuf->base - ringbuf->iter.off;
6530d71f708SJon Doron 
6540d71f708SJon Doron     if (len >= remain) {
6550d71f708SJon Doron         ret1 = gpadl_iter_io(&ringbuf->iter, buf, remain);
6560d71f708SJon Doron         if (ret1 < 0) {
6570d71f708SJon Doron             return ret1;
6580d71f708SJon Doron         }
6590d71f708SJon Doron         gpadl_iter_seek(&ringbuf->iter, ringbuf->base);
6600d71f708SJon Doron         buf += remain;
6610d71f708SJon Doron         len -= remain;
6620d71f708SJon Doron     }
6630d71f708SJon Doron     ret2 = gpadl_iter_io(&ringbuf->iter, buf, len);
6640d71f708SJon Doron     if (ret2 < 0) {
6650d71f708SJon Doron         return ret2;
6660d71f708SJon Doron     }
6670d71f708SJon Doron     return ret1 + ret2;
6680d71f708SJon Doron }
6690d71f708SJon Doron 
6700d71f708SJon Doron /*
6710d71f708SJon Doron  * Position the circular iterator within @ringbuf to offset @new_off, wrapping
6720d71f708SJon Doron  * around if needed.
6730d71f708SJon Doron  * @new_off is assumed not to exceed twice the size of the ringbuffer, so only
6740d71f708SJon Doron  * single wraparound is considered.
6750d71f708SJon Doron  */
ringbuf_seek(VMBusRingBufCommon * ringbuf,uint32_t new_off)6760d71f708SJon Doron static inline void ringbuf_seek(VMBusRingBufCommon *ringbuf, uint32_t new_off)
6770d71f708SJon Doron {
6780d71f708SJon Doron     gpadl_iter_seek(&ringbuf->iter,
6790d71f708SJon Doron                     ringbuf->base + rb_idx_wrap(new_off, ringbuf->len));
6800d71f708SJon Doron }
6810d71f708SJon Doron 
ringbuf_tell(VMBusRingBufCommon * ringbuf)6820d71f708SJon Doron static inline uint32_t ringbuf_tell(VMBusRingBufCommon *ringbuf)
6830d71f708SJon Doron {
6840d71f708SJon Doron     return ringbuf->iter.off - ringbuf->base;
6850d71f708SJon Doron }
6860d71f708SJon Doron 
ringbuf_start_io(VMBusRingBufCommon * ringbuf)6870d71f708SJon Doron static inline void ringbuf_start_io(VMBusRingBufCommon *ringbuf)
6880d71f708SJon Doron {
6890d71f708SJon Doron     gpadl_iter_start_io(&ringbuf->iter);
6900d71f708SJon Doron }
6910d71f708SJon Doron 
ringbuf_end_io(VMBusRingBufCommon * ringbuf)6920d71f708SJon Doron static inline void ringbuf_end_io(VMBusRingBufCommon *ringbuf)
6930d71f708SJon Doron {
6940d71f708SJon Doron     gpadl_iter_end_io(&ringbuf->iter);
6950d71f708SJon Doron }
6960d71f708SJon Doron 
vmbus_channel_device(VMBusChannel * chan)6970d71f708SJon Doron VMBusDevice *vmbus_channel_device(VMBusChannel *chan)
6980d71f708SJon Doron {
6990d71f708SJon Doron     return chan->dev;
7000d71f708SJon Doron }
7010d71f708SJon Doron 
vmbus_device_channel(VMBusDevice * dev,uint32_t chan_idx)7020d71f708SJon Doron VMBusChannel *vmbus_device_channel(VMBusDevice *dev, uint32_t chan_idx)
7030d71f708SJon Doron {
7040d71f708SJon Doron     if (chan_idx >= dev->num_channels) {
7050d71f708SJon Doron         return NULL;
7060d71f708SJon Doron     }
7070d71f708SJon Doron     return &dev->channels[chan_idx];
7080d71f708SJon Doron }
7090d71f708SJon Doron 
vmbus_channel_idx(VMBusChannel * chan)7100d71f708SJon Doron uint32_t vmbus_channel_idx(VMBusChannel *chan)
7110d71f708SJon Doron {
7120d71f708SJon Doron     return chan - chan->dev->channels;
7130d71f708SJon Doron }
7140d71f708SJon Doron 
vmbus_channel_notify_host(VMBusChannel * chan)7150d71f708SJon Doron void vmbus_channel_notify_host(VMBusChannel *chan)
7160d71f708SJon Doron {
7170d71f708SJon Doron     event_notifier_set(&chan->notifier);
7180d71f708SJon Doron }
7190d71f708SJon Doron 
vmbus_channel_is_open(VMBusChannel * chan)7200d71f708SJon Doron bool vmbus_channel_is_open(VMBusChannel *chan)
7210d71f708SJon Doron {
7220d71f708SJon Doron     return chan->is_open;
7230d71f708SJon Doron }
7240d71f708SJon Doron 
7250d71f708SJon Doron /*
7260d71f708SJon Doron  * Notify the guest side about the data to work on in the channel ring buffer.
7270d71f708SJon Doron  * The notification is done by signaling a dedicated per-channel SynIC event
7280d71f708SJon Doron  * flag (more recent guests) or setting a bit in the interrupt page and firing
7290d71f708SJon Doron  * the VMBus SINT (older guests).
7300d71f708SJon Doron  */
vmbus_channel_notify_guest(VMBusChannel * chan)7310d71f708SJon Doron static int vmbus_channel_notify_guest(VMBusChannel *chan)
7320d71f708SJon Doron {
7330d71f708SJon Doron     int res = 0;
7340d71f708SJon Doron     unsigned long *int_map, mask;
7350d71f708SJon Doron     unsigned idx;
7360d71f708SJon Doron     hwaddr addr = chan->vmbus->int_page_gpa;
7370d71f708SJon Doron     hwaddr len = TARGET_PAGE_SIZE / 2, dirty = 0;
7380d71f708SJon Doron 
7390d71f708SJon Doron     trace_vmbus_channel_notify_guest(chan->id);
7400d71f708SJon Doron 
7410d71f708SJon Doron     if (!addr) {
7420d71f708SJon Doron         return hyperv_set_event_flag(chan->notify_route, chan->id);
7430d71f708SJon Doron     }
7440d71f708SJon Doron 
7450d71f708SJon Doron     int_map = cpu_physical_memory_map(addr, &len, 1);
7460d71f708SJon Doron     if (len != TARGET_PAGE_SIZE / 2) {
7470d71f708SJon Doron         res = -ENXIO;
7480d71f708SJon Doron         goto unmap;
7490d71f708SJon Doron     }
7500d71f708SJon Doron 
7510d71f708SJon Doron     idx = BIT_WORD(chan->id);
7520d71f708SJon Doron     mask = BIT_MASK(chan->id);
753d73415a3SStefan Hajnoczi     if ((qatomic_fetch_or(&int_map[idx], mask) & mask) != mask) {
7540d71f708SJon Doron         res = hyperv_sint_route_set_sint(chan->notify_route);
7550d71f708SJon Doron         dirty = len;
7560d71f708SJon Doron     }
7570d71f708SJon Doron 
7580d71f708SJon Doron unmap:
7590d71f708SJon Doron     cpu_physical_memory_unmap(int_map, len, 1, dirty);
7600d71f708SJon Doron     return res;
7610d71f708SJon Doron }
7620d71f708SJon Doron 
7630d71f708SJon Doron #define VMBUS_PKT_TRAILER      sizeof(uint64_t)
7640d71f708SJon Doron 
vmbus_pkt_hdr_set_offsets(vmbus_packet_hdr * hdr,uint32_t desclen,uint32_t msglen)7650d71f708SJon Doron static uint32_t vmbus_pkt_hdr_set_offsets(vmbus_packet_hdr *hdr,
7660d71f708SJon Doron                                           uint32_t desclen, uint32_t msglen)
7670d71f708SJon Doron {
7680d71f708SJon Doron     hdr->offset_qwords = sizeof(*hdr) / sizeof(uint64_t) +
7690d71f708SJon Doron         DIV_ROUND_UP(desclen, sizeof(uint64_t));
7700d71f708SJon Doron     hdr->len_qwords = hdr->offset_qwords +
7710d71f708SJon Doron         DIV_ROUND_UP(msglen, sizeof(uint64_t));
7720d71f708SJon Doron     return hdr->len_qwords * sizeof(uint64_t) + VMBUS_PKT_TRAILER;
7730d71f708SJon Doron }
7740d71f708SJon Doron 
7750d71f708SJon Doron /*
7760d71f708SJon Doron  * Simplified ring buffer operation with paired barriers annotations in the
7770d71f708SJon Doron  * producer and consumer loops:
7780d71f708SJon Doron  *
7790d71f708SJon Doron  * producer                           * consumer
7800d71f708SJon Doron  * ~~~~~~~~                           * ~~~~~~~~
7810d71f708SJon Doron  * write pending_send_sz              * read write_index
7820d71f708SJon Doron  * smp_mb                       [A]   * smp_mb                       [C]
7830d71f708SJon Doron  * read read_index                    * read packet
7840d71f708SJon Doron  * smp_mb                       [B]   * read/write out-of-band data
7850d71f708SJon Doron  * read/write out-of-band data        * smp_mb                       [B]
7860d71f708SJon Doron  * write packet                       * write read_index
7870d71f708SJon Doron  * smp_mb                       [C]   * smp_mb                       [A]
7880d71f708SJon Doron  * write write_index                  * read pending_send_sz
7890d71f708SJon Doron  * smp_wmb                      [D]   * smp_rmb                      [D]
7900d71f708SJon Doron  * write pending_send_sz              * read write_index
7910d71f708SJon Doron  * ...                                * ...
7920d71f708SJon Doron  */
7930d71f708SJon Doron 
ringbuf_send_avail(VMBusSendRingBuf * ringbuf)7940d71f708SJon Doron static inline uint32_t ringbuf_send_avail(VMBusSendRingBuf *ringbuf)
7950d71f708SJon Doron {
7960d71f708SJon Doron     /* don't trust guest data */
7970d71f708SJon Doron     if (ringbuf->last_seen_rd_idx >= ringbuf->common.len) {
7980d71f708SJon Doron         return 0;
7990d71f708SJon Doron     }
8000d71f708SJon Doron     return rb_idx_delta(ringbuf->wr_idx, ringbuf->last_seen_rd_idx,
8010d71f708SJon Doron                         ringbuf->common.len, false);
8020d71f708SJon Doron }
8030d71f708SJon Doron 
ringbuf_send_update_idx(VMBusChannel * chan)8040d71f708SJon Doron static ssize_t ringbuf_send_update_idx(VMBusChannel *chan)
8050d71f708SJon Doron {
8060d71f708SJon Doron     VMBusSendRingBuf *ringbuf = &chan->send_ringbuf;
8070d71f708SJon Doron     vmbus_ring_buffer *rb;
8080d71f708SJon Doron     uint32_t written;
8090d71f708SJon Doron 
8100d71f708SJon Doron     written = rb_idx_delta(ringbuf->last_wr_idx, ringbuf->wr_idx,
8110d71f708SJon Doron                            ringbuf->common.len, true);
8120d71f708SJon Doron     if (!written) {
8130d71f708SJon Doron         return 0;
8140d71f708SJon Doron     }
8150d71f708SJon Doron 
8160d71f708SJon Doron     rb = ringbuf_map_hdr(&ringbuf->common);
8170d71f708SJon Doron     if (!rb) {
8180d71f708SJon Doron         return -EFAULT;
8190d71f708SJon Doron     }
8200d71f708SJon Doron 
8210d71f708SJon Doron     ringbuf->reserved -= written;
8220d71f708SJon Doron 
8230d71f708SJon Doron     /* prevent reorder with the data operation and packet write */
8240d71f708SJon Doron     smp_mb();                   /* barrier pair [C] */
8250d71f708SJon Doron     rb->write_index = ringbuf->wr_idx;
8260d71f708SJon Doron 
8270d71f708SJon Doron     /*
8280d71f708SJon Doron      * If the producer earlier indicated that it wants to be notified when the
8290d71f708SJon Doron      * consumer frees certain amount of space in the ring buffer, that amount
8300d71f708SJon Doron      * is reduced by the size of the completed write.
8310d71f708SJon Doron      */
8320d71f708SJon Doron     if (ringbuf->wanted) {
8330d71f708SJon Doron         /* otherwise reservation would fail */
8340d71f708SJon Doron         assert(ringbuf->wanted < written);
8350d71f708SJon Doron         ringbuf->wanted -= written;
8360d71f708SJon Doron         /* prevent reorder with write_index write */
8370d71f708SJon Doron         smp_wmb();              /* barrier pair [D] */
8380d71f708SJon Doron         rb->pending_send_sz = ringbuf->wanted;
8390d71f708SJon Doron     }
8400d71f708SJon Doron 
8410d71f708SJon Doron     /* prevent reorder with write_index or pending_send_sz write */
8420d71f708SJon Doron     smp_mb();                   /* barrier pair [A] */
8430d71f708SJon Doron     ringbuf->last_seen_rd_idx = rb->read_index;
8440d71f708SJon Doron 
8450d71f708SJon Doron     /*
8460d71f708SJon Doron      * The consumer may have missed the reduction of pending_send_sz and skip
8470d71f708SJon Doron      * notification, so re-check the blocking condition, and, if it's no longer
8480d71f708SJon Doron      * true, ensure processing another iteration by simulating consumer's
8490d71f708SJon Doron      * notification.
8500d71f708SJon Doron      */
8510d71f708SJon Doron     if (ringbuf_send_avail(ringbuf) >= ringbuf->wanted) {
8520d71f708SJon Doron         vmbus_channel_notify_host(chan);
8530d71f708SJon Doron     }
8540d71f708SJon Doron 
8550d71f708SJon Doron     /* skip notification by consumer's request */
8560d71f708SJon Doron     if (rb->interrupt_mask) {
8570d71f708SJon Doron         goto out;
8580d71f708SJon Doron     }
8590d71f708SJon Doron 
8600d71f708SJon Doron     /*
8610d71f708SJon Doron      * The consumer hasn't caught up with the producer's previous state so it's
8620d71f708SJon Doron      * not blocked.
8630d71f708SJon Doron      * (last_seen_rd_idx comes from the guest but it's safe to use w/o
8640d71f708SJon Doron      * validation here as it only affects notification.)
8650d71f708SJon Doron      */
8660d71f708SJon Doron     if (rb_idx_delta(ringbuf->last_seen_rd_idx, ringbuf->wr_idx,
8670d71f708SJon Doron                      ringbuf->common.len, true) > written) {
8680d71f708SJon Doron         goto out;
8690d71f708SJon Doron     }
8700d71f708SJon Doron 
8710d71f708SJon Doron     vmbus_channel_notify_guest(chan);
8720d71f708SJon Doron out:
8730d71f708SJon Doron     ringbuf_unmap_hdr(&ringbuf->common, rb, true);
8740d71f708SJon Doron     ringbuf->last_wr_idx = ringbuf->wr_idx;
8750d71f708SJon Doron     return written;
8760d71f708SJon Doron }
8770d71f708SJon Doron 
vmbus_channel_reserve(VMBusChannel * chan,uint32_t desclen,uint32_t msglen)8780d71f708SJon Doron int vmbus_channel_reserve(VMBusChannel *chan,
8790d71f708SJon Doron                           uint32_t desclen, uint32_t msglen)
8800d71f708SJon Doron {
8810d71f708SJon Doron     VMBusSendRingBuf *ringbuf = &chan->send_ringbuf;
8820d71f708SJon Doron     vmbus_ring_buffer *rb = NULL;
8830d71f708SJon Doron     vmbus_packet_hdr hdr;
8840d71f708SJon Doron     uint32_t needed = ringbuf->reserved +
8850d71f708SJon Doron         vmbus_pkt_hdr_set_offsets(&hdr, desclen, msglen);
8860d71f708SJon Doron 
8870d71f708SJon Doron     /* avoid touching the guest memory if possible */
8880d71f708SJon Doron     if (likely(needed <= ringbuf_send_avail(ringbuf))) {
8890d71f708SJon Doron         goto success;
8900d71f708SJon Doron     }
8910d71f708SJon Doron 
8920d71f708SJon Doron     rb = ringbuf_map_hdr(&ringbuf->common);
8930d71f708SJon Doron     if (!rb) {
8940d71f708SJon Doron         return -EFAULT;
8950d71f708SJon Doron     }
8960d71f708SJon Doron 
8970d71f708SJon Doron     /* fetch read index from guest memory and try again */
8980d71f708SJon Doron     ringbuf->last_seen_rd_idx = rb->read_index;
8990d71f708SJon Doron 
9000d71f708SJon Doron     if (likely(needed <= ringbuf_send_avail(ringbuf))) {
9010d71f708SJon Doron         goto success;
9020d71f708SJon Doron     }
9030d71f708SJon Doron 
9040d71f708SJon Doron     rb->pending_send_sz = needed;
9050d71f708SJon Doron 
9060d71f708SJon Doron     /*
9070d71f708SJon Doron      * The consumer may have made progress and freed up some space before
9080d71f708SJon Doron      * seeing updated pending_send_sz, so re-read read_index (preventing
9090d71f708SJon Doron      * reorder with the pending_send_sz write) and try again.
9100d71f708SJon Doron      */
9110d71f708SJon Doron     smp_mb();                   /* barrier pair [A] */
9120d71f708SJon Doron     ringbuf->last_seen_rd_idx = rb->read_index;
9130d71f708SJon Doron 
9140d71f708SJon Doron     if (needed > ringbuf_send_avail(ringbuf)) {
9150d71f708SJon Doron         goto out;
9160d71f708SJon Doron     }
9170d71f708SJon Doron 
9180d71f708SJon Doron success:
9190d71f708SJon Doron     ringbuf->reserved = needed;
9200d71f708SJon Doron     needed = 0;
9210d71f708SJon Doron 
9220d71f708SJon Doron     /* clear pending_send_sz if it was set */
9230d71f708SJon Doron     if (ringbuf->wanted) {
9240d71f708SJon Doron         if (!rb) {
9250d71f708SJon Doron             rb = ringbuf_map_hdr(&ringbuf->common);
9260d71f708SJon Doron             if (!rb) {
9270d71f708SJon Doron                 /* failure to clear pending_send_sz is non-fatal */
9280d71f708SJon Doron                 goto out;
9290d71f708SJon Doron             }
9300d71f708SJon Doron         }
9310d71f708SJon Doron 
9320d71f708SJon Doron         rb->pending_send_sz = 0;
9330d71f708SJon Doron     }
9340d71f708SJon Doron 
9350d71f708SJon Doron     /* prevent reorder of the following data operation with read_index read */
9360d71f708SJon Doron     smp_mb();                   /* barrier pair [B] */
9370d71f708SJon Doron 
9380d71f708SJon Doron out:
9390d71f708SJon Doron     if (rb) {
9400d71f708SJon Doron         ringbuf_unmap_hdr(&ringbuf->common, rb, ringbuf->wanted == needed);
9410d71f708SJon Doron     }
9420d71f708SJon Doron     ringbuf->wanted = needed;
9430d71f708SJon Doron     return needed ? -ENOSPC : 0;
9440d71f708SJon Doron }
9450d71f708SJon Doron 
vmbus_channel_send(VMBusChannel * chan,uint16_t pkt_type,void * desc,uint32_t desclen,void * msg,uint32_t msglen,bool need_comp,uint64_t transaction_id)9460d71f708SJon Doron ssize_t vmbus_channel_send(VMBusChannel *chan, uint16_t pkt_type,
9470d71f708SJon Doron                            void *desc, uint32_t desclen,
9480d71f708SJon Doron                            void *msg, uint32_t msglen,
9490d71f708SJon Doron                            bool need_comp, uint64_t transaction_id)
9500d71f708SJon Doron {
9510d71f708SJon Doron     ssize_t ret = 0;
9520d71f708SJon Doron     vmbus_packet_hdr hdr;
9530d71f708SJon Doron     uint32_t totlen;
9540d71f708SJon Doron     VMBusSendRingBuf *ringbuf = &chan->send_ringbuf;
9550d71f708SJon Doron 
9560d71f708SJon Doron     if (!vmbus_channel_is_open(chan)) {
9570d71f708SJon Doron         return -EINVAL;
9580d71f708SJon Doron     }
9590d71f708SJon Doron 
9600d71f708SJon Doron     totlen = vmbus_pkt_hdr_set_offsets(&hdr, desclen, msglen);
9610d71f708SJon Doron     hdr.type = pkt_type;
9620d71f708SJon Doron     hdr.flags = need_comp ? VMBUS_PACKET_FLAG_REQUEST_COMPLETION : 0;
9630d71f708SJon Doron     hdr.transaction_id = transaction_id;
9640d71f708SJon Doron 
9650d71f708SJon Doron     assert(totlen <= ringbuf->reserved);
9660d71f708SJon Doron 
9670d71f708SJon Doron     ringbuf_start_io(&ringbuf->common);
9680d71f708SJon Doron     ringbuf_seek(&ringbuf->common, ringbuf->wr_idx);
9690d71f708SJon Doron     ret = ringbuf_io(&ringbuf->common, &hdr, sizeof(hdr));
9700d71f708SJon Doron     if (ret < 0) {
9710d71f708SJon Doron         goto out;
9720d71f708SJon Doron     }
9730d71f708SJon Doron     if (desclen) {
9740d71f708SJon Doron         assert(desc);
9750d71f708SJon Doron         ret = ringbuf_io(&ringbuf->common, desc, desclen);
9760d71f708SJon Doron         if (ret < 0) {
9770d71f708SJon Doron             goto out;
9780d71f708SJon Doron         }
9790d71f708SJon Doron         ringbuf_seek(&ringbuf->common,
9800d71f708SJon Doron                      ringbuf->wr_idx + hdr.offset_qwords * sizeof(uint64_t));
9810d71f708SJon Doron     }
9820d71f708SJon Doron     ret = ringbuf_io(&ringbuf->common, msg, msglen);
9830d71f708SJon Doron     if (ret < 0) {
9840d71f708SJon Doron         goto out;
9850d71f708SJon Doron     }
9860d71f708SJon Doron     ringbuf_seek(&ringbuf->common, ringbuf->wr_idx + totlen);
9870d71f708SJon Doron     ringbuf->wr_idx = ringbuf_tell(&ringbuf->common);
9880d71f708SJon Doron     ret = 0;
9890d71f708SJon Doron out:
9900d71f708SJon Doron     ringbuf_end_io(&ringbuf->common);
9910d71f708SJon Doron     if (ret) {
9920d71f708SJon Doron         return ret;
9930d71f708SJon Doron     }
9940d71f708SJon Doron     return ringbuf_send_update_idx(chan);
9950d71f708SJon Doron }
9960d71f708SJon Doron 
vmbus_channel_send_completion(VMBusChanReq * req,void * msg,uint32_t msglen)9970d71f708SJon Doron ssize_t vmbus_channel_send_completion(VMBusChanReq *req,
9980d71f708SJon Doron                                       void *msg, uint32_t msglen)
9990d71f708SJon Doron {
10000d71f708SJon Doron     assert(req->need_comp);
10010d71f708SJon Doron     return vmbus_channel_send(req->chan, VMBUS_PACKET_COMP, NULL, 0,
10020d71f708SJon Doron                               msg, msglen, false, req->transaction_id);
10030d71f708SJon Doron }
10040d71f708SJon Doron 
sgl_from_gpa_ranges(QEMUSGList * sgl,VMBusDevice * dev,VMBusRingBufCommon * ringbuf,uint32_t len)10050d71f708SJon Doron static int sgl_from_gpa_ranges(QEMUSGList *sgl, VMBusDevice *dev,
10060d71f708SJon Doron                                VMBusRingBufCommon *ringbuf, uint32_t len)
10070d71f708SJon Doron {
10080d71f708SJon Doron     int ret;
10090d71f708SJon Doron     vmbus_pkt_gpa_direct hdr;
10100d71f708SJon Doron     hwaddr curaddr = 0;
10110d71f708SJon Doron     hwaddr curlen = 0;
10120d71f708SJon Doron     int num;
10130d71f708SJon Doron 
10140d71f708SJon Doron     if (len < sizeof(hdr)) {
10150d71f708SJon Doron         return -EIO;
10160d71f708SJon Doron     }
10170d71f708SJon Doron     ret = ringbuf_io(ringbuf, &hdr, sizeof(hdr));
10180d71f708SJon Doron     if (ret < 0) {
10190d71f708SJon Doron         return ret;
10200d71f708SJon Doron     }
10210d71f708SJon Doron     len -= sizeof(hdr);
10220d71f708SJon Doron 
10230d71f708SJon Doron     num = (len - hdr.rangecount * sizeof(vmbus_gpa_range)) / sizeof(uint64_t);
10240d71f708SJon Doron     if (num < 0) {
10250d71f708SJon Doron         return -EIO;
10260d71f708SJon Doron     }
10270d71f708SJon Doron     qemu_sglist_init(sgl, DEVICE(dev), num, ringbuf->as);
10280d71f708SJon Doron 
10290d71f708SJon Doron     for (; hdr.rangecount; hdr.rangecount--) {
10300d71f708SJon Doron         vmbus_gpa_range range;
10310d71f708SJon Doron 
10320d71f708SJon Doron         if (len < sizeof(range)) {
10330d71f708SJon Doron             goto eio;
10340d71f708SJon Doron         }
10350d71f708SJon Doron         ret = ringbuf_io(ringbuf, &range, sizeof(range));
10360d71f708SJon Doron         if (ret < 0) {
10370d71f708SJon Doron             goto err;
10380d71f708SJon Doron         }
10390d71f708SJon Doron         len -= sizeof(range);
10400d71f708SJon Doron 
10410d71f708SJon Doron         if (range.byte_offset & TARGET_PAGE_MASK) {
10420d71f708SJon Doron             goto eio;
10430d71f708SJon Doron         }
10440d71f708SJon Doron 
10450d71f708SJon Doron         for (; range.byte_count; range.byte_offset = 0) {
10460d71f708SJon Doron             uint64_t paddr;
10470d71f708SJon Doron             uint32_t plen = MIN(range.byte_count,
10480d71f708SJon Doron                                 TARGET_PAGE_SIZE - range.byte_offset);
10490d71f708SJon Doron 
10500d71f708SJon Doron             if (len < sizeof(uint64_t)) {
10510d71f708SJon Doron                 goto eio;
10520d71f708SJon Doron             }
10530d71f708SJon Doron             ret = ringbuf_io(ringbuf, &paddr, sizeof(paddr));
10540d71f708SJon Doron             if (ret < 0) {
10550d71f708SJon Doron                 goto err;
10560d71f708SJon Doron             }
10570d71f708SJon Doron             len -= sizeof(uint64_t);
10580d71f708SJon Doron             paddr <<= TARGET_PAGE_BITS;
10590d71f708SJon Doron             paddr |= range.byte_offset;
10600d71f708SJon Doron             range.byte_count -= plen;
10610d71f708SJon Doron 
10620d71f708SJon Doron             if (curaddr + curlen == paddr) {
10630d71f708SJon Doron                 /* consecutive fragments - join */
10640d71f708SJon Doron                 curlen += plen;
10650d71f708SJon Doron             } else {
10660d71f708SJon Doron                 if (curlen) {
10670d71f708SJon Doron                     qemu_sglist_add(sgl, curaddr, curlen);
10680d71f708SJon Doron                 }
10690d71f708SJon Doron 
10700d71f708SJon Doron                 curaddr = paddr;
10710d71f708SJon Doron                 curlen = plen;
10720d71f708SJon Doron             }
10730d71f708SJon Doron         }
10740d71f708SJon Doron     }
10750d71f708SJon Doron 
10760d71f708SJon Doron     if (curlen) {
10770d71f708SJon Doron         qemu_sglist_add(sgl, curaddr, curlen);
10780d71f708SJon Doron     }
10790d71f708SJon Doron 
10800d71f708SJon Doron     return 0;
10810d71f708SJon Doron eio:
10820d71f708SJon Doron     ret = -EIO;
10830d71f708SJon Doron err:
10840d71f708SJon Doron     qemu_sglist_destroy(sgl);
10850d71f708SJon Doron     return ret;
10860d71f708SJon Doron }
10870d71f708SJon Doron 
vmbus_alloc_req(VMBusChannel * chan,uint32_t size,uint16_t pkt_type,uint32_t msglen,uint64_t transaction_id,bool need_comp)10880d71f708SJon Doron static VMBusChanReq *vmbus_alloc_req(VMBusChannel *chan,
10890d71f708SJon Doron                                      uint32_t size, uint16_t pkt_type,
10900d71f708SJon Doron                                      uint32_t msglen, uint64_t transaction_id,
10910d71f708SJon Doron                                      bool need_comp)
10920d71f708SJon Doron {
10930d71f708SJon Doron     VMBusChanReq *req;
10940d71f708SJon Doron     uint32_t msgoff = QEMU_ALIGN_UP(size, __alignof__(*req->msg));
10950d71f708SJon Doron     uint32_t totlen = msgoff + msglen;
10960d71f708SJon Doron 
10970d71f708SJon Doron     req = g_malloc0(totlen);
10980d71f708SJon Doron     req->chan = chan;
10990d71f708SJon Doron     req->pkt_type = pkt_type;
11000d71f708SJon Doron     req->msg = (void *)req + msgoff;
11010d71f708SJon Doron     req->msglen = msglen;
11020d71f708SJon Doron     req->transaction_id = transaction_id;
11030d71f708SJon Doron     req->need_comp = need_comp;
11040d71f708SJon Doron     return req;
11050d71f708SJon Doron }
11060d71f708SJon Doron 
vmbus_channel_recv_start(VMBusChannel * chan)11070d71f708SJon Doron int vmbus_channel_recv_start(VMBusChannel *chan)
11080d71f708SJon Doron {
11090d71f708SJon Doron     VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
11100d71f708SJon Doron     vmbus_ring_buffer *rb;
11110d71f708SJon Doron 
11120d71f708SJon Doron     rb = ringbuf_map_hdr(&ringbuf->common);
11130d71f708SJon Doron     if (!rb) {
11140d71f708SJon Doron         return -EFAULT;
11150d71f708SJon Doron     }
11160d71f708SJon Doron     ringbuf->last_seen_wr_idx = rb->write_index;
11170d71f708SJon Doron     ringbuf_unmap_hdr(&ringbuf->common, rb, false);
11180d71f708SJon Doron 
11190d71f708SJon Doron     if (ringbuf->last_seen_wr_idx >= ringbuf->common.len) {
11200d71f708SJon Doron         return -EOVERFLOW;
11210d71f708SJon Doron     }
11220d71f708SJon Doron 
11230d71f708SJon Doron     /* prevent reorder of the following data operation with write_index read */
11240d71f708SJon Doron     smp_mb();                   /* barrier pair [C] */
11250d71f708SJon Doron     return 0;
11260d71f708SJon Doron }
11270d71f708SJon Doron 
vmbus_channel_recv_peek(VMBusChannel * chan,uint32_t size)11280d71f708SJon Doron void *vmbus_channel_recv_peek(VMBusChannel *chan, uint32_t size)
11290d71f708SJon Doron {
11300d71f708SJon Doron     VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
11310d71f708SJon Doron     vmbus_packet_hdr hdr = {};
11320d71f708SJon Doron     VMBusChanReq *req;
11330d71f708SJon Doron     uint32_t avail;
11340d71f708SJon Doron     uint32_t totlen, pktlen, msglen, msgoff, desclen;
11350d71f708SJon Doron 
11360d71f708SJon Doron     assert(size >= sizeof(*req));
11370d71f708SJon Doron 
11380d71f708SJon Doron     /* safe as last_seen_wr_idx is validated in vmbus_channel_recv_start */
11390d71f708SJon Doron     avail = rb_idx_delta(ringbuf->rd_idx, ringbuf->last_seen_wr_idx,
11400d71f708SJon Doron                          ringbuf->common.len, true);
11410d71f708SJon Doron     if (avail < sizeof(hdr)) {
11420d71f708SJon Doron         return NULL;
11430d71f708SJon Doron     }
11440d71f708SJon Doron 
11450d71f708SJon Doron     ringbuf_seek(&ringbuf->common, ringbuf->rd_idx);
11460d71f708SJon Doron     if (ringbuf_io(&ringbuf->common, &hdr, sizeof(hdr)) < 0) {
11470d71f708SJon Doron         return NULL;
11480d71f708SJon Doron     }
11490d71f708SJon Doron 
11500d71f708SJon Doron     pktlen = hdr.len_qwords * sizeof(uint64_t);
11510d71f708SJon Doron     totlen = pktlen + VMBUS_PKT_TRAILER;
11520d71f708SJon Doron     if (totlen > avail) {
11530d71f708SJon Doron         return NULL;
11540d71f708SJon Doron     }
11550d71f708SJon Doron 
11560d71f708SJon Doron     msgoff = hdr.offset_qwords * sizeof(uint64_t);
11570d71f708SJon Doron     if (msgoff > pktlen || msgoff < sizeof(hdr)) {
11580d71f708SJon Doron         error_report("%s: malformed packet: %u %u", __func__, msgoff, pktlen);
11590d71f708SJon Doron         return NULL;
11600d71f708SJon Doron     }
11610d71f708SJon Doron 
11620d71f708SJon Doron     msglen = pktlen - msgoff;
11630d71f708SJon Doron 
11640d71f708SJon Doron     req = vmbus_alloc_req(chan, size, hdr.type, msglen, hdr.transaction_id,
11650d71f708SJon Doron                           hdr.flags & VMBUS_PACKET_FLAG_REQUEST_COMPLETION);
11660d71f708SJon Doron 
11670d71f708SJon Doron     switch (hdr.type) {
11680d71f708SJon Doron     case VMBUS_PACKET_DATA_USING_GPA_DIRECT:
11690d71f708SJon Doron         desclen = msgoff - sizeof(hdr);
11700d71f708SJon Doron         if (sgl_from_gpa_ranges(&req->sgl, chan->dev, &ringbuf->common,
11710d71f708SJon Doron                                 desclen) < 0) {
11720d71f708SJon Doron             error_report("%s: failed to convert GPA ranges to SGL", __func__);
11730d71f708SJon Doron             goto free_req;
11740d71f708SJon Doron         }
11750d71f708SJon Doron         break;
11760d71f708SJon Doron     case VMBUS_PACKET_DATA_INBAND:
11770d71f708SJon Doron     case VMBUS_PACKET_COMP:
11780d71f708SJon Doron         break;
11790d71f708SJon Doron     default:
11800d71f708SJon Doron         error_report("%s: unexpected msg type: %x", __func__, hdr.type);
11810d71f708SJon Doron         goto free_req;
11820d71f708SJon Doron     }
11830d71f708SJon Doron 
11840d71f708SJon Doron     ringbuf_seek(&ringbuf->common, ringbuf->rd_idx + msgoff);
11850d71f708SJon Doron     if (ringbuf_io(&ringbuf->common, req->msg, msglen) < 0) {
11860d71f708SJon Doron         goto free_req;
11870d71f708SJon Doron     }
11880d71f708SJon Doron     ringbuf_seek(&ringbuf->common, ringbuf->rd_idx + totlen);
11890d71f708SJon Doron 
11900d71f708SJon Doron     return req;
11910d71f708SJon Doron free_req:
11920d71f708SJon Doron     vmbus_free_req(req);
11930d71f708SJon Doron     return NULL;
11940d71f708SJon Doron }
11950d71f708SJon Doron 
vmbus_channel_recv_pop(VMBusChannel * chan)11960d71f708SJon Doron void vmbus_channel_recv_pop(VMBusChannel *chan)
11970d71f708SJon Doron {
11980d71f708SJon Doron     VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
11990d71f708SJon Doron     ringbuf->rd_idx = ringbuf_tell(&ringbuf->common);
12000d71f708SJon Doron }
12010d71f708SJon Doron 
vmbus_channel_recv_done(VMBusChannel * chan)12020d71f708SJon Doron ssize_t vmbus_channel_recv_done(VMBusChannel *chan)
12030d71f708SJon Doron {
12040d71f708SJon Doron     VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
12050d71f708SJon Doron     vmbus_ring_buffer *rb;
12060d71f708SJon Doron     uint32_t read;
12070d71f708SJon Doron 
12080d71f708SJon Doron     read = rb_idx_delta(ringbuf->last_rd_idx, ringbuf->rd_idx,
12090d71f708SJon Doron                         ringbuf->common.len, true);
12100d71f708SJon Doron     if (!read) {
12110d71f708SJon Doron         return 0;
12120d71f708SJon Doron     }
12130d71f708SJon Doron 
12140d71f708SJon Doron     rb = ringbuf_map_hdr(&ringbuf->common);
12150d71f708SJon Doron     if (!rb) {
12160d71f708SJon Doron         return -EFAULT;
12170d71f708SJon Doron     }
12180d71f708SJon Doron 
12190d71f708SJon Doron     /* prevent reorder with the data operation and packet read */
12200d71f708SJon Doron     smp_mb();                   /* barrier pair [B] */
12210d71f708SJon Doron     rb->read_index = ringbuf->rd_idx;
12220d71f708SJon Doron 
12230d71f708SJon Doron     /* prevent reorder of the following pending_send_sz read */
12240d71f708SJon Doron     smp_mb();                   /* barrier pair [A] */
12250d71f708SJon Doron 
12260d71f708SJon Doron     if (rb->interrupt_mask) {
12270d71f708SJon Doron         goto out;
12280d71f708SJon Doron     }
12290d71f708SJon Doron 
12300d71f708SJon Doron     if (rb->feature_bits & VMBUS_RING_BUFFER_FEAT_PENDING_SZ) {
12310d71f708SJon Doron         uint32_t wr_idx, wr_avail;
12320d71f708SJon Doron         uint32_t wanted = rb->pending_send_sz;
12330d71f708SJon Doron 
12340d71f708SJon Doron         if (!wanted) {
12350d71f708SJon Doron             goto out;
12360d71f708SJon Doron         }
12370d71f708SJon Doron 
12380d71f708SJon Doron         /* prevent reorder with pending_send_sz read */
12390d71f708SJon Doron         smp_rmb();              /* barrier pair [D] */
12400d71f708SJon Doron         wr_idx = rb->write_index;
12410d71f708SJon Doron 
12420d71f708SJon Doron         wr_avail = rb_idx_delta(wr_idx, ringbuf->rd_idx, ringbuf->common.len,
12430d71f708SJon Doron                                 true);
12440d71f708SJon Doron 
12450d71f708SJon Doron         /* the producer wasn't blocked on the consumer state */
12460d71f708SJon Doron         if (wr_avail >= read + wanted) {
12470d71f708SJon Doron             goto out;
12480d71f708SJon Doron         }
12490d71f708SJon Doron         /* there's not enough space for the producer to make progress */
12500d71f708SJon Doron         if (wr_avail < wanted) {
12510d71f708SJon Doron             goto out;
12520d71f708SJon Doron         }
12530d71f708SJon Doron     }
12540d71f708SJon Doron 
12550d71f708SJon Doron     vmbus_channel_notify_guest(chan);
12560d71f708SJon Doron out:
12570d71f708SJon Doron     ringbuf_unmap_hdr(&ringbuf->common, rb, true);
12580d71f708SJon Doron     ringbuf->last_rd_idx = ringbuf->rd_idx;
12590d71f708SJon Doron     return read;
12600d71f708SJon Doron }
12610d71f708SJon Doron 
vmbus_free_req(void * req)12620d71f708SJon Doron void vmbus_free_req(void *req)
12630d71f708SJon Doron {
12640d71f708SJon Doron     VMBusChanReq *r = req;
12650d71f708SJon Doron 
12660d71f708SJon Doron     if (!req) {
12670d71f708SJon Doron         return;
12680d71f708SJon Doron     }
12690d71f708SJon Doron 
12700d71f708SJon Doron     if (r->sgl.dev) {
12710d71f708SJon Doron         qemu_sglist_destroy(&r->sgl);
12720d71f708SJon Doron     }
12730d71f708SJon Doron     g_free(req);
12740d71f708SJon Doron }
12750d71f708SJon Doron 
channel_event_cb(EventNotifier * e)12760d71f708SJon Doron static void channel_event_cb(EventNotifier *e)
12770d71f708SJon Doron {
12780d71f708SJon Doron     VMBusChannel *chan = container_of(e, VMBusChannel, notifier);
12790d71f708SJon Doron     if (event_notifier_test_and_clear(e)) {
12800d71f708SJon Doron         /*
12810d71f708SJon Doron          * All receives are supposed to happen within the device worker, so
12820d71f708SJon Doron          * bracket it with ringbuf_start/end_io on the receive ringbuffer, and
12830d71f708SJon Doron          * potentially reuse the cached mapping throughout the worker.
12840d71f708SJon Doron          * Can't do this for sends as they may happen outside the device
12850d71f708SJon Doron          * worker.
12860d71f708SJon Doron          */
12870d71f708SJon Doron         VMBusRecvRingBuf *ringbuf = &chan->recv_ringbuf;
12880d71f708SJon Doron         ringbuf_start_io(&ringbuf->common);
12890d71f708SJon Doron         chan->notify_cb(chan);
12900d71f708SJon Doron         ringbuf_end_io(&ringbuf->common);
12910d71f708SJon Doron 
12920d71f708SJon Doron     }
12930d71f708SJon Doron }
12940d71f708SJon Doron 
alloc_chan_id(VMBus * vmbus)12950d71f708SJon Doron static int alloc_chan_id(VMBus *vmbus)
12960d71f708SJon Doron {
12970d71f708SJon Doron     int ret;
12980d71f708SJon Doron 
12990d71f708SJon Doron     ret = find_next_zero_bit(vmbus->chanid_bitmap, VMBUS_CHANID_COUNT, 0);
13000d71f708SJon Doron     if (ret == VMBUS_CHANID_COUNT) {
13010d71f708SJon Doron         return -ENOMEM;
13020d71f708SJon Doron     }
13030d71f708SJon Doron     return ret + VMBUS_FIRST_CHANID;
13040d71f708SJon Doron }
13050d71f708SJon Doron 
register_chan_id(VMBusChannel * chan)13060d71f708SJon Doron static int register_chan_id(VMBusChannel *chan)
13070d71f708SJon Doron {
13080d71f708SJon Doron     return test_and_set_bit(chan->id - VMBUS_FIRST_CHANID,
13090d71f708SJon Doron                             chan->vmbus->chanid_bitmap) ? -EEXIST : 0;
13100d71f708SJon Doron }
13110d71f708SJon Doron 
unregister_chan_id(VMBusChannel * chan)13120d71f708SJon Doron static void unregister_chan_id(VMBusChannel *chan)
13130d71f708SJon Doron {
13140d71f708SJon Doron     clear_bit(chan->id - VMBUS_FIRST_CHANID, chan->vmbus->chanid_bitmap);
13150d71f708SJon Doron }
13160d71f708SJon Doron 
chan_connection_id(VMBusChannel * chan)13170d71f708SJon Doron static uint32_t chan_connection_id(VMBusChannel *chan)
13180d71f708SJon Doron {
13190d71f708SJon Doron     return VMBUS_CHAN_CONNECTION_OFFSET + chan->id;
13200d71f708SJon Doron }
13210d71f708SJon Doron 
init_channel(VMBus * vmbus,VMBusDevice * dev,VMBusDeviceClass * vdc,VMBusChannel * chan,uint16_t idx,Error ** errp)13220d71f708SJon Doron static void init_channel(VMBus *vmbus, VMBusDevice *dev, VMBusDeviceClass *vdc,
13230d71f708SJon Doron                          VMBusChannel *chan, uint16_t idx, Error **errp)
13240d71f708SJon Doron {
13250d71f708SJon Doron     int res;
13260d71f708SJon Doron 
13270d71f708SJon Doron     chan->dev = dev;
13280d71f708SJon Doron     chan->notify_cb = vdc->chan_notify_cb;
13290d71f708SJon Doron     chan->subchan_idx = idx;
13300d71f708SJon Doron     chan->vmbus = vmbus;
13310d71f708SJon Doron 
13320d71f708SJon Doron     res = alloc_chan_id(vmbus);
13330d71f708SJon Doron     if (res < 0) {
13340d71f708SJon Doron         error_setg(errp, "no spare channel id");
13350d71f708SJon Doron         return;
13360d71f708SJon Doron     }
13370d71f708SJon Doron     chan->id = res;
13380d71f708SJon Doron     register_chan_id(chan);
13390d71f708SJon Doron 
13400d71f708SJon Doron     /*
13410d71f708SJon Doron      * The guest drivers depend on the device subchannels (idx #1+) to be
13420d71f708SJon Doron      * offered after the primary channel (idx #0) of that device.  To ensure
13430d71f708SJon Doron      * that, record the channels on the channel list in the order they appear
13440d71f708SJon Doron      * within the device.
13450d71f708SJon Doron      */
13460d71f708SJon Doron     QTAILQ_INSERT_TAIL(&vmbus->channel_list, chan, link);
13470d71f708SJon Doron }
13480d71f708SJon Doron 
deinit_channel(VMBusChannel * chan)13490d71f708SJon Doron static void deinit_channel(VMBusChannel *chan)
13500d71f708SJon Doron {
13510d71f708SJon Doron     assert(chan->state == VMCHAN_INIT);
13520d71f708SJon Doron     QTAILQ_REMOVE(&chan->vmbus->channel_list, chan, link);
13530d71f708SJon Doron     unregister_chan_id(chan);
13540d71f708SJon Doron }
13550d71f708SJon Doron 
create_channels(VMBus * vmbus,VMBusDevice * dev,Error ** errp)13560d71f708SJon Doron static void create_channels(VMBus *vmbus, VMBusDevice *dev, Error **errp)
13570d71f708SJon Doron {
13580d71f708SJon Doron     uint16_t i;
13590d71f708SJon Doron     VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(dev);
13600d71f708SJon Doron     Error *err = NULL;
13610d71f708SJon Doron 
13620d71f708SJon Doron     dev->num_channels = vdc->num_channels ? vdc->num_channels(dev) : 1;
13630d71f708SJon Doron     if (dev->num_channels < 1) {
1364dcfe4805SMarkus Armbruster         error_setg(errp, "invalid #channels: %u", dev->num_channels);
1365dcfe4805SMarkus Armbruster         return;
13660d71f708SJon Doron     }
13670d71f708SJon Doron 
13680d71f708SJon Doron     dev->channels = g_new0(VMBusChannel, dev->num_channels);
13690d71f708SJon Doron     for (i = 0; i < dev->num_channels; i++) {
13700d71f708SJon Doron         init_channel(vmbus, dev, vdc, &dev->channels[i], i, &err);
13710d71f708SJon Doron         if (err) {
13720d71f708SJon Doron             goto err_init;
13730d71f708SJon Doron         }
13740d71f708SJon Doron     }
13750d71f708SJon Doron 
13760d71f708SJon Doron     return;
13770d71f708SJon Doron 
13780d71f708SJon Doron err_init:
13790d71f708SJon Doron     while (i--) {
13800d71f708SJon Doron         deinit_channel(&dev->channels[i]);
13810d71f708SJon Doron     }
13820d71f708SJon Doron     error_propagate(errp, err);
13830d71f708SJon Doron }
13840d71f708SJon Doron 
free_channels(VMBusDevice * dev)13850d71f708SJon Doron static void free_channels(VMBusDevice *dev)
13860d71f708SJon Doron {
13870d71f708SJon Doron     uint16_t i;
13880d71f708SJon Doron     for (i = 0; i < dev->num_channels; i++) {
13890d71f708SJon Doron         deinit_channel(&dev->channels[i]);
13900d71f708SJon Doron     }
13910d71f708SJon Doron     g_free(dev->channels);
13920d71f708SJon Doron }
13930d71f708SJon Doron 
make_sint_route(VMBus * vmbus,uint32_t vp_index)13940d71f708SJon Doron static HvSintRoute *make_sint_route(VMBus *vmbus, uint32_t vp_index)
13950d71f708SJon Doron {
13960d71f708SJon Doron     VMBusChannel *chan;
13970d71f708SJon Doron 
13980d71f708SJon Doron     if (vp_index == vmbus->target_vp) {
13990d71f708SJon Doron         hyperv_sint_route_ref(vmbus->sint_route);
14000d71f708SJon Doron         return vmbus->sint_route;
14010d71f708SJon Doron     }
14020d71f708SJon Doron 
14030d71f708SJon Doron     QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
14040d71f708SJon Doron         if (chan->target_vp == vp_index && vmbus_channel_is_open(chan)) {
14050d71f708SJon Doron             hyperv_sint_route_ref(chan->notify_route);
14060d71f708SJon Doron             return chan->notify_route;
14070d71f708SJon Doron         }
14080d71f708SJon Doron     }
14090d71f708SJon Doron 
14100d71f708SJon Doron     return hyperv_sint_route_new(vp_index, VMBUS_SINT, NULL, NULL);
14110d71f708SJon Doron }
14120d71f708SJon Doron 
open_channel(VMBusChannel * chan)14130d71f708SJon Doron static void open_channel(VMBusChannel *chan)
14140d71f708SJon Doron {
14150d71f708SJon Doron     VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(chan->dev);
14160d71f708SJon Doron 
14170d71f708SJon Doron     chan->gpadl = vmbus_get_gpadl(chan, chan->ringbuf_gpadl);
14180d71f708SJon Doron     if (!chan->gpadl) {
14190d71f708SJon Doron         return;
14200d71f708SJon Doron     }
14210d71f708SJon Doron 
14220d71f708SJon Doron     if (ringbufs_init(chan)) {
14230d71f708SJon Doron         goto put_gpadl;
14240d71f708SJon Doron     }
14250d71f708SJon Doron 
14260d71f708SJon Doron     if (event_notifier_init(&chan->notifier, 0)) {
14270d71f708SJon Doron         goto put_gpadl;
14280d71f708SJon Doron     }
14290d71f708SJon Doron 
14300d71f708SJon Doron     event_notifier_set_handler(&chan->notifier, channel_event_cb);
14310d71f708SJon Doron 
14320d71f708SJon Doron     if (hyperv_set_event_flag_handler(chan_connection_id(chan),
14330d71f708SJon Doron                                       &chan->notifier)) {
14340d71f708SJon Doron         goto cleanup_notifier;
14350d71f708SJon Doron     }
14360d71f708SJon Doron 
14370d71f708SJon Doron     chan->notify_route = make_sint_route(chan->vmbus, chan->target_vp);
14380d71f708SJon Doron     if (!chan->notify_route) {
14390d71f708SJon Doron         goto clear_event_flag_handler;
14400d71f708SJon Doron     }
14410d71f708SJon Doron 
14420d71f708SJon Doron     if (vdc->open_channel && vdc->open_channel(chan)) {
14430d71f708SJon Doron         goto unref_sint_route;
14440d71f708SJon Doron     }
14450d71f708SJon Doron 
14460d71f708SJon Doron     chan->is_open = true;
14470d71f708SJon Doron     return;
14480d71f708SJon Doron 
14490d71f708SJon Doron unref_sint_route:
14500d71f708SJon Doron     hyperv_sint_route_unref(chan->notify_route);
14510d71f708SJon Doron clear_event_flag_handler:
14520d71f708SJon Doron     hyperv_set_event_flag_handler(chan_connection_id(chan), NULL);
14530d71f708SJon Doron cleanup_notifier:
14540d71f708SJon Doron     event_notifier_set_handler(&chan->notifier, NULL);
14550d71f708SJon Doron     event_notifier_cleanup(&chan->notifier);
14560d71f708SJon Doron put_gpadl:
14570d71f708SJon Doron     vmbus_put_gpadl(chan->gpadl);
14580d71f708SJon Doron }
14590d71f708SJon Doron 
close_channel(VMBusChannel * chan)14600d71f708SJon Doron static void close_channel(VMBusChannel *chan)
14610d71f708SJon Doron {
14620d71f708SJon Doron     VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(chan->dev);
14630d71f708SJon Doron 
14640d71f708SJon Doron     if (!chan->is_open) {
14650d71f708SJon Doron         return;
14660d71f708SJon Doron     }
14670d71f708SJon Doron 
14680d71f708SJon Doron     if (vdc->close_channel) {
14690d71f708SJon Doron         vdc->close_channel(chan);
14700d71f708SJon Doron     }
14710d71f708SJon Doron 
14720d71f708SJon Doron     hyperv_sint_route_unref(chan->notify_route);
14730d71f708SJon Doron     hyperv_set_event_flag_handler(chan_connection_id(chan), NULL);
14740d71f708SJon Doron     event_notifier_set_handler(&chan->notifier, NULL);
14750d71f708SJon Doron     event_notifier_cleanup(&chan->notifier);
14760d71f708SJon Doron     vmbus_put_gpadl(chan->gpadl);
14770d71f708SJon Doron     chan->is_open = false;
14780d71f708SJon Doron }
14790d71f708SJon Doron 
channel_post_load(void * opaque,int version_id)14800d71f708SJon Doron static int channel_post_load(void *opaque, int version_id)
14810d71f708SJon Doron {
14820d71f708SJon Doron     VMBusChannel *chan = opaque;
14830d71f708SJon Doron 
14840d71f708SJon Doron     return register_chan_id(chan);
14850d71f708SJon Doron }
14860d71f708SJon Doron 
14870d71f708SJon Doron static const VMStateDescription vmstate_channel = {
14880d71f708SJon Doron     .name = "vmbus/channel",
14890d71f708SJon Doron     .version_id = 0,
14900d71f708SJon Doron     .minimum_version_id = 0,
14910d71f708SJon Doron     .post_load = channel_post_load,
14922ebfd1c4SRichard Henderson     .fields = (const VMStateField[]) {
14930d71f708SJon Doron         VMSTATE_UINT32(id, VMBusChannel),
14940d71f708SJon Doron         VMSTATE_UINT16(subchan_idx, VMBusChannel),
14950d71f708SJon Doron         VMSTATE_UINT32(open_id, VMBusChannel),
14960d71f708SJon Doron         VMSTATE_UINT32(target_vp, VMBusChannel),
14970d71f708SJon Doron         VMSTATE_UINT32(ringbuf_gpadl, VMBusChannel),
14980d71f708SJon Doron         VMSTATE_UINT32(ringbuf_send_offset, VMBusChannel),
14990d71f708SJon Doron         VMSTATE_UINT8(offer_state, VMBusChannel),
15000d71f708SJon Doron         VMSTATE_UINT8(state, VMBusChannel),
15010d71f708SJon Doron         VMSTATE_END_OF_LIST()
15020d71f708SJon Doron     }
15030d71f708SJon Doron };
15040d71f708SJon Doron 
find_channel(VMBus * vmbus,uint32_t id)15050d71f708SJon Doron static VMBusChannel *find_channel(VMBus *vmbus, uint32_t id)
15060d71f708SJon Doron {
15070d71f708SJon Doron     VMBusChannel *chan;
15080d71f708SJon Doron     QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
15090d71f708SJon Doron         if (chan->id == id) {
15100d71f708SJon Doron             return chan;
15110d71f708SJon Doron         }
15120d71f708SJon Doron     }
15130d71f708SJon Doron     return NULL;
15140d71f708SJon Doron }
15150d71f708SJon Doron 
enqueue_incoming_message(VMBus * vmbus,const struct hyperv_post_message_input * msg)15160d71f708SJon Doron static int enqueue_incoming_message(VMBus *vmbus,
15170d71f708SJon Doron                                     const struct hyperv_post_message_input *msg)
15180d71f708SJon Doron {
15190d71f708SJon Doron     int ret = 0;
15200d71f708SJon Doron     uint8_t idx, prev_size;
15210d71f708SJon Doron 
15220d71f708SJon Doron     qemu_mutex_lock(&vmbus->rx_queue_lock);
15230d71f708SJon Doron 
15240d71f708SJon Doron     if (vmbus->rx_queue_size == HV_MSG_QUEUE_LEN) {
15250d71f708SJon Doron         ret = -ENOBUFS;
15260d71f708SJon Doron         goto out;
15270d71f708SJon Doron     }
15280d71f708SJon Doron 
15290d71f708SJon Doron     prev_size = vmbus->rx_queue_size;
15300d71f708SJon Doron     idx = (vmbus->rx_queue_head + vmbus->rx_queue_size) % HV_MSG_QUEUE_LEN;
15310d71f708SJon Doron     memcpy(&vmbus->rx_queue[idx], msg, sizeof(*msg));
15320d71f708SJon Doron     vmbus->rx_queue_size++;
15330d71f708SJon Doron 
15340d71f708SJon Doron     /* only need to resched if the queue was empty before */
15350d71f708SJon Doron     if (!prev_size) {
15360d71f708SJon Doron         vmbus_resched(vmbus);
15370d71f708SJon Doron     }
15380d71f708SJon Doron out:
15390d71f708SJon Doron     qemu_mutex_unlock(&vmbus->rx_queue_lock);
15400d71f708SJon Doron     return ret;
15410d71f708SJon Doron }
15420d71f708SJon Doron 
vmbus_recv_message(const struct hyperv_post_message_input * msg,void * data)15430d71f708SJon Doron static uint16_t vmbus_recv_message(const struct hyperv_post_message_input *msg,
15440d71f708SJon Doron                                    void *data)
15450d71f708SJon Doron {
15460d71f708SJon Doron     VMBus *vmbus = data;
15470d71f708SJon Doron     struct vmbus_message_header *vmbus_msg;
15480d71f708SJon Doron 
15490d71f708SJon Doron     if (msg->message_type != HV_MESSAGE_VMBUS) {
15500d71f708SJon Doron         return HV_STATUS_INVALID_HYPERCALL_INPUT;
15510d71f708SJon Doron     }
15520d71f708SJon Doron 
15530d71f708SJon Doron     if (msg->payload_size < sizeof(struct vmbus_message_header)) {
15540d71f708SJon Doron         return HV_STATUS_INVALID_HYPERCALL_INPUT;
15550d71f708SJon Doron     }
15560d71f708SJon Doron 
15570d71f708SJon Doron     vmbus_msg = (struct vmbus_message_header *)msg->payload;
15580d71f708SJon Doron 
15590d71f708SJon Doron     trace_vmbus_recv_message(vmbus_msg->message_type, msg->payload_size);
15600d71f708SJon Doron 
15610d71f708SJon Doron     if (vmbus_msg->message_type == VMBUS_MSG_INVALID ||
15620d71f708SJon Doron         vmbus_msg->message_type >= VMBUS_MSG_COUNT) {
15630d71f708SJon Doron         error_report("vmbus: unknown message type %#x",
15640d71f708SJon Doron                      vmbus_msg->message_type);
15650d71f708SJon Doron         return HV_STATUS_INVALID_HYPERCALL_INPUT;
15660d71f708SJon Doron     }
15670d71f708SJon Doron 
15680d71f708SJon Doron     if (enqueue_incoming_message(vmbus, msg)) {
15690d71f708SJon Doron         return HV_STATUS_INSUFFICIENT_BUFFERS;
15700d71f708SJon Doron     }
15710d71f708SJon Doron     return HV_STATUS_SUCCESS;
15720d71f708SJon Doron }
15730d71f708SJon Doron 
vmbus_initialized(VMBus * vmbus)15740d71f708SJon Doron static bool vmbus_initialized(VMBus *vmbus)
15750d71f708SJon Doron {
15760d71f708SJon Doron     return vmbus->version > 0 && vmbus->version <= VMBUS_VERSION_CURRENT;
15770d71f708SJon Doron }
15780d71f708SJon Doron 
vmbus_reset_all(VMBus * vmbus)15790d71f708SJon Doron static void vmbus_reset_all(VMBus *vmbus)
15800d71f708SJon Doron {
15818cadd251SPeter Maydell     bus_cold_reset(BUS(vmbus));
15820d71f708SJon Doron }
15830d71f708SJon Doron 
post_msg(VMBus * vmbus,void * msgdata,uint32_t msglen)15840d71f708SJon Doron static void post_msg(VMBus *vmbus, void *msgdata, uint32_t msglen)
15850d71f708SJon Doron {
15860d71f708SJon Doron     int ret;
15870d71f708SJon Doron     struct hyperv_message msg = {
15880d71f708SJon Doron         .header.message_type = HV_MESSAGE_VMBUS,
15890d71f708SJon Doron     };
15900d71f708SJon Doron 
15910d71f708SJon Doron     assert(!vmbus->msg_in_progress);
15920d71f708SJon Doron     assert(msglen <= sizeof(msg.payload));
15930d71f708SJon Doron     assert(msglen >= sizeof(struct vmbus_message_header));
15940d71f708SJon Doron 
15950d71f708SJon Doron     vmbus->msg_in_progress = true;
15960d71f708SJon Doron 
15970d71f708SJon Doron     trace_vmbus_post_msg(((struct vmbus_message_header *)msgdata)->message_type,
15980d71f708SJon Doron                          msglen);
15990d71f708SJon Doron 
16000d71f708SJon Doron     memcpy(msg.payload, msgdata, msglen);
16010d71f708SJon Doron     msg.header.payload_size = ROUND_UP(msglen, VMBUS_MESSAGE_SIZE_ALIGN);
16020d71f708SJon Doron 
16030d71f708SJon Doron     ret = hyperv_post_msg(vmbus->sint_route, &msg);
16040d71f708SJon Doron     if (ret == 0 || ret == -EAGAIN) {
16050d71f708SJon Doron         return;
16060d71f708SJon Doron     }
16070d71f708SJon Doron 
16080d71f708SJon Doron     error_report("message delivery fatal failure: %d; aborting vmbus", ret);
16090d71f708SJon Doron     vmbus_reset_all(vmbus);
16100d71f708SJon Doron }
16110d71f708SJon Doron 
vmbus_init(VMBus * vmbus)16120d71f708SJon Doron static int vmbus_init(VMBus *vmbus)
16130d71f708SJon Doron {
16140d71f708SJon Doron     if (vmbus->target_vp != (uint32_t)-1) {
16150d71f708SJon Doron         vmbus->sint_route = hyperv_sint_route_new(vmbus->target_vp, VMBUS_SINT,
16160d71f708SJon Doron                                                   vmbus_msg_cb, vmbus);
16170d71f708SJon Doron         if (!vmbus->sint_route) {
16180d71f708SJon Doron             error_report("failed to set up SINT route");
16190d71f708SJon Doron             return -ENOMEM;
16200d71f708SJon Doron         }
16210d71f708SJon Doron     }
16220d71f708SJon Doron     return 0;
16230d71f708SJon Doron }
16240d71f708SJon Doron 
vmbus_deinit(VMBus * vmbus)16250d71f708SJon Doron static void vmbus_deinit(VMBus *vmbus)
16260d71f708SJon Doron {
16270d71f708SJon Doron     VMBusGpadl *gpadl, *tmp_gpadl;
16280d71f708SJon Doron     VMBusChannel *chan;
16290d71f708SJon Doron 
16300d71f708SJon Doron     QTAILQ_FOREACH_SAFE(gpadl, &vmbus->gpadl_list, link, tmp_gpadl) {
16310d71f708SJon Doron         if (gpadl->state == VMGPADL_TORNDOWN) {
16320d71f708SJon Doron             continue;
16330d71f708SJon Doron         }
16340d71f708SJon Doron         vmbus_put_gpadl(gpadl);
16350d71f708SJon Doron     }
16360d71f708SJon Doron 
16370d71f708SJon Doron     QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
16380d71f708SJon Doron         chan->offer_state = VMOFFER_INIT;
16390d71f708SJon Doron     }
16400d71f708SJon Doron 
16410d71f708SJon Doron     hyperv_sint_route_unref(vmbus->sint_route);
16420d71f708SJon Doron     vmbus->sint_route = NULL;
16430d71f708SJon Doron     vmbus->int_page_gpa = 0;
16440d71f708SJon Doron     vmbus->target_vp = (uint32_t)-1;
16450d71f708SJon Doron     vmbus->version = 0;
16460d71f708SJon Doron     vmbus->state = VMBUS_LISTEN;
16470d71f708SJon Doron     vmbus->msg_in_progress = false;
16480d71f708SJon Doron }
16490d71f708SJon Doron 
handle_initiate_contact(VMBus * vmbus,vmbus_message_initiate_contact * msg,uint32_t msglen)16500d71f708SJon Doron static void handle_initiate_contact(VMBus *vmbus,
16510d71f708SJon Doron                                     vmbus_message_initiate_contact *msg,
16520d71f708SJon Doron                                     uint32_t msglen)
16530d71f708SJon Doron {
16540d71f708SJon Doron     if (msglen < sizeof(*msg)) {
16550d71f708SJon Doron         return;
16560d71f708SJon Doron     }
16570d71f708SJon Doron 
16580d71f708SJon Doron     trace_vmbus_initiate_contact(msg->version_requested >> 16,
16590d71f708SJon Doron                                  msg->version_requested & 0xffff,
16600d71f708SJon Doron                                  msg->target_vcpu, msg->monitor_page1,
16610d71f708SJon Doron                                  msg->monitor_page2, msg->interrupt_page);
16620d71f708SJon Doron 
16630d71f708SJon Doron     /*
16640d71f708SJon Doron      * Reset vmbus on INITIATE_CONTACT regardless of its previous state.
16650d71f708SJon Doron      * Useful, in particular, with vmbus-aware BIOS which can't shut vmbus down
16660d71f708SJon Doron      * before handing over to OS loader.
16670d71f708SJon Doron      */
16680d71f708SJon Doron     vmbus_reset_all(vmbus);
16690d71f708SJon Doron 
16700d71f708SJon Doron     vmbus->target_vp = msg->target_vcpu;
16710d71f708SJon Doron     vmbus->version = msg->version_requested;
16720d71f708SJon Doron     if (vmbus->version < VMBUS_VERSION_WIN8) {
16730d71f708SJon Doron         /* linux passes interrupt page even when it doesn't need it */
16740d71f708SJon Doron         vmbus->int_page_gpa = msg->interrupt_page;
16750d71f708SJon Doron     }
16760d71f708SJon Doron     vmbus->state = VMBUS_HANDSHAKE;
16770d71f708SJon Doron 
16780d71f708SJon Doron     if (vmbus_init(vmbus)) {
16790d71f708SJon Doron         error_report("failed to init vmbus; aborting");
16800d71f708SJon Doron         vmbus_deinit(vmbus);
16810d71f708SJon Doron         return;
16820d71f708SJon Doron     }
16830d71f708SJon Doron }
16840d71f708SJon Doron 
send_handshake(VMBus * vmbus)16850d71f708SJon Doron static void send_handshake(VMBus *vmbus)
16860d71f708SJon Doron {
16870d71f708SJon Doron     struct vmbus_message_version_response msg = {
16880d71f708SJon Doron         .header.message_type = VMBUS_MSG_VERSION_RESPONSE,
16890d71f708SJon Doron         .version_supported = vmbus_initialized(vmbus),
16900d71f708SJon Doron     };
16910d71f708SJon Doron 
16920d71f708SJon Doron     post_msg(vmbus, &msg, sizeof(msg));
16930d71f708SJon Doron }
16940d71f708SJon Doron 
handle_request_offers(VMBus * vmbus,void * msgdata,uint32_t msglen)16950d71f708SJon Doron static void handle_request_offers(VMBus *vmbus, void *msgdata, uint32_t msglen)
16960d71f708SJon Doron {
16970d71f708SJon Doron     VMBusChannel *chan;
16980d71f708SJon Doron 
16990d71f708SJon Doron     if (!vmbus_initialized(vmbus)) {
17000d71f708SJon Doron         return;
17010d71f708SJon Doron     }
17020d71f708SJon Doron 
17030d71f708SJon Doron     QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
17040d71f708SJon Doron         if (chan->offer_state == VMOFFER_INIT) {
17050d71f708SJon Doron             chan->offer_state = VMOFFER_SENDING;
17060d71f708SJon Doron             break;
17070d71f708SJon Doron         }
17080d71f708SJon Doron     }
17090d71f708SJon Doron 
17100d71f708SJon Doron     vmbus->state = VMBUS_OFFER;
17110d71f708SJon Doron }
17120d71f708SJon Doron 
send_offer(VMBus * vmbus)17130d71f708SJon Doron static void send_offer(VMBus *vmbus)
17140d71f708SJon Doron {
17150d71f708SJon Doron     VMBusChannel *chan;
17160d71f708SJon Doron     struct vmbus_message_header alloffers_msg = {
17170d71f708SJon Doron         .message_type = VMBUS_MSG_ALLOFFERS_DELIVERED,
17180d71f708SJon Doron     };
17190d71f708SJon Doron 
17200d71f708SJon Doron     QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
17210d71f708SJon Doron         if (chan->offer_state == VMOFFER_SENDING) {
17220d71f708SJon Doron             VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(chan->dev);
17230d71f708SJon Doron             /* Hyper-V wants LE GUIDs */
17240d71f708SJon Doron             QemuUUID classid = qemu_uuid_bswap(vdc->classid);
17250d71f708SJon Doron             QemuUUID instanceid = qemu_uuid_bswap(chan->dev->instanceid);
17260d71f708SJon Doron             struct vmbus_message_offer_channel msg = {
17270d71f708SJon Doron                 .header.message_type = VMBUS_MSG_OFFERCHANNEL,
17280d71f708SJon Doron                 .child_relid = chan->id,
17290d71f708SJon Doron                 .connection_id = chan_connection_id(chan),
17300d71f708SJon Doron                 .channel_flags = vdc->channel_flags,
17310d71f708SJon Doron                 .mmio_size_mb = vdc->mmio_size_mb,
17320d71f708SJon Doron                 .sub_channel_index = vmbus_channel_idx(chan),
17330d71f708SJon Doron                 .interrupt_flags = VMBUS_OFFER_INTERRUPT_DEDICATED,
17340d71f708SJon Doron             };
17350d71f708SJon Doron 
17360d71f708SJon Doron             memcpy(msg.type_uuid, &classid, sizeof(classid));
17370d71f708SJon Doron             memcpy(msg.instance_uuid, &instanceid, sizeof(instanceid));
17380d71f708SJon Doron 
17390d71f708SJon Doron             trace_vmbus_send_offer(chan->id, chan->dev);
17400d71f708SJon Doron 
17410d71f708SJon Doron             post_msg(vmbus, &msg, sizeof(msg));
17420d71f708SJon Doron             return;
17430d71f708SJon Doron         }
17440d71f708SJon Doron     }
17450d71f708SJon Doron 
17460d71f708SJon Doron     /* no more offers, send terminator message */
17470d71f708SJon Doron     trace_vmbus_terminate_offers();
17480d71f708SJon Doron     post_msg(vmbus, &alloffers_msg, sizeof(alloffers_msg));
17490d71f708SJon Doron }
17500d71f708SJon Doron 
complete_offer(VMBus * vmbus)17510d71f708SJon Doron static bool complete_offer(VMBus *vmbus)
17520d71f708SJon Doron {
17530d71f708SJon Doron     VMBusChannel *chan;
17540d71f708SJon Doron 
17550d71f708SJon Doron     QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
17560d71f708SJon Doron         if (chan->offer_state == VMOFFER_SENDING) {
17570d71f708SJon Doron             chan->offer_state = VMOFFER_SENT;
17580d71f708SJon Doron             goto next_offer;
17590d71f708SJon Doron         }
17600d71f708SJon Doron     }
17610d71f708SJon Doron     /*
17620d71f708SJon Doron      * no transitioning channels found so this is completing the terminator
17630d71f708SJon Doron      * message, and vmbus can move to the next state
17640d71f708SJon Doron      */
17650d71f708SJon Doron     return true;
17660d71f708SJon Doron 
17670d71f708SJon Doron next_offer:
17680d71f708SJon Doron     /* try to mark another channel for offering */
17690d71f708SJon Doron     QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
17700d71f708SJon Doron         if (chan->offer_state == VMOFFER_INIT) {
17710d71f708SJon Doron             chan->offer_state = VMOFFER_SENDING;
17720d71f708SJon Doron             break;
17730d71f708SJon Doron         }
17740d71f708SJon Doron     }
17750d71f708SJon Doron     /*
17760d71f708SJon Doron      * if an offer has been sent there are more offers or the terminator yet to
17770d71f708SJon Doron      * send, so no state transition for vmbus
17780d71f708SJon Doron      */
17790d71f708SJon Doron     return false;
17800d71f708SJon Doron }
17810d71f708SJon Doron 
17820d71f708SJon Doron 
handle_gpadl_header(VMBus * vmbus,vmbus_message_gpadl_header * msg,uint32_t msglen)17830d71f708SJon Doron static void handle_gpadl_header(VMBus *vmbus, vmbus_message_gpadl_header *msg,
17840d71f708SJon Doron                                 uint32_t msglen)
17850d71f708SJon Doron {
17860d71f708SJon Doron     VMBusGpadl *gpadl;
17870d71f708SJon Doron     uint32_t num_gfns, i;
17880d71f708SJon Doron 
17890d71f708SJon Doron     /* must include at least one gpa range */
17900d71f708SJon Doron     if (msglen < sizeof(*msg) + sizeof(msg->range[0]) ||
17910d71f708SJon Doron         !vmbus_initialized(vmbus)) {
17920d71f708SJon Doron         return;
17930d71f708SJon Doron     }
17940d71f708SJon Doron 
17950d71f708SJon Doron     num_gfns = (msg->range_buflen - msg->rangecount * sizeof(msg->range[0])) /
17960d71f708SJon Doron                sizeof(msg->range[0].pfn_array[0]);
17970d71f708SJon Doron 
17980d71f708SJon Doron     trace_vmbus_gpadl_header(msg->gpadl_id, num_gfns);
17990d71f708SJon Doron 
18000d71f708SJon Doron     /*
18010d71f708SJon Doron      * In theory the GPADL_HEADER message can define a GPADL with multiple GPA
18020d71f708SJon Doron      * ranges each with arbitrary size and alignment.  However in practice only
18030d71f708SJon Doron      * single-range page-aligned GPADLs have been observed so just ignore
18040d71f708SJon Doron      * anything else and simplify things greatly.
18050d71f708SJon Doron      */
18060d71f708SJon Doron     if (msg->rangecount != 1 || msg->range[0].byte_offset ||
18070d71f708SJon Doron         (msg->range[0].byte_count != (num_gfns << TARGET_PAGE_BITS))) {
18080d71f708SJon Doron         return;
18090d71f708SJon Doron     }
18100d71f708SJon Doron 
18110d71f708SJon Doron     /* ignore requests to create already existing GPADLs */
18120d71f708SJon Doron     if (find_gpadl(vmbus, msg->gpadl_id)) {
18130d71f708SJon Doron         return;
18140d71f708SJon Doron     }
18150d71f708SJon Doron 
18160d71f708SJon Doron     gpadl = create_gpadl(vmbus, msg->gpadl_id, msg->child_relid, num_gfns);
18170d71f708SJon Doron 
18180d71f708SJon Doron     for (i = 0; i < num_gfns &&
18190d71f708SJon Doron          (void *)&msg->range[0].pfn_array[i + 1] <= (void *)msg + msglen;
18200d71f708SJon Doron          i++) {
18210d71f708SJon Doron         gpadl->gfns[gpadl->seen_gfns++] = msg->range[0].pfn_array[i];
18220d71f708SJon Doron     }
18230d71f708SJon Doron 
18240d71f708SJon Doron     if (gpadl_full(gpadl)) {
18250d71f708SJon Doron         vmbus->state = VMBUS_CREATE_GPADL;
18260d71f708SJon Doron     }
18270d71f708SJon Doron }
18280d71f708SJon Doron 
handle_gpadl_body(VMBus * vmbus,vmbus_message_gpadl_body * msg,uint32_t msglen)18290d71f708SJon Doron static void handle_gpadl_body(VMBus *vmbus, vmbus_message_gpadl_body *msg,
18300d71f708SJon Doron                               uint32_t msglen)
18310d71f708SJon Doron {
18320d71f708SJon Doron     VMBusGpadl *gpadl;
18330d71f708SJon Doron     uint32_t num_gfns_left, i;
18340d71f708SJon Doron 
18350d71f708SJon Doron     if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) {
18360d71f708SJon Doron         return;
18370d71f708SJon Doron     }
18380d71f708SJon Doron 
18390d71f708SJon Doron     trace_vmbus_gpadl_body(msg->gpadl_id);
18400d71f708SJon Doron 
18410d71f708SJon Doron     gpadl = find_gpadl(vmbus, msg->gpadl_id);
18420d71f708SJon Doron     if (!gpadl) {
18430d71f708SJon Doron         return;
18440d71f708SJon Doron     }
18450d71f708SJon Doron 
18460d71f708SJon Doron     num_gfns_left = gpadl->num_gfns - gpadl->seen_gfns;
18470d71f708SJon Doron     assert(num_gfns_left);
18480d71f708SJon Doron 
18490d71f708SJon Doron     for (i = 0; i < num_gfns_left &&
18500d71f708SJon Doron          (void *)&msg->pfn_array[i + 1] <= (void *)msg + msglen; i++) {
18510d71f708SJon Doron         gpadl->gfns[gpadl->seen_gfns++] = msg->pfn_array[i];
18520d71f708SJon Doron     }
18530d71f708SJon Doron 
18540d71f708SJon Doron     if (gpadl_full(gpadl)) {
18550d71f708SJon Doron         vmbus->state = VMBUS_CREATE_GPADL;
18560d71f708SJon Doron     }
18570d71f708SJon Doron }
18580d71f708SJon Doron 
send_create_gpadl(VMBus * vmbus)18590d71f708SJon Doron static void send_create_gpadl(VMBus *vmbus)
18600d71f708SJon Doron {
18610d71f708SJon Doron     VMBusGpadl *gpadl;
18620d71f708SJon Doron 
18630d71f708SJon Doron     QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) {
18640d71f708SJon Doron         if (gpadl_full(gpadl) && gpadl->state == VMGPADL_INIT) {
18650d71f708SJon Doron             struct vmbus_message_gpadl_created msg = {
18660d71f708SJon Doron                 .header.message_type = VMBUS_MSG_GPADL_CREATED,
18670d71f708SJon Doron                 .gpadl_id = gpadl->id,
18680d71f708SJon Doron                 .child_relid = gpadl->child_relid,
18690d71f708SJon Doron             };
18700d71f708SJon Doron 
18710d71f708SJon Doron             trace_vmbus_gpadl_created(gpadl->id);
18720d71f708SJon Doron             post_msg(vmbus, &msg, sizeof(msg));
18730d71f708SJon Doron             return;
18740d71f708SJon Doron         }
18750d71f708SJon Doron     }
18760d71f708SJon Doron 
18770d71f708SJon Doron     assert(false);
18780d71f708SJon Doron }
18790d71f708SJon Doron 
complete_create_gpadl(VMBus * vmbus)18800d71f708SJon Doron static bool complete_create_gpadl(VMBus *vmbus)
18810d71f708SJon Doron {
18820d71f708SJon Doron     VMBusGpadl *gpadl;
18830d71f708SJon Doron 
18840d71f708SJon Doron     QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) {
18850d71f708SJon Doron         if (gpadl_full(gpadl) && gpadl->state == VMGPADL_INIT) {
18860d71f708SJon Doron             gpadl->state = VMGPADL_ALIVE;
18870d71f708SJon Doron 
18880d71f708SJon Doron             return true;
18890d71f708SJon Doron         }
18900d71f708SJon Doron     }
18910d71f708SJon Doron 
18920d71f708SJon Doron     assert(false);
18930d71f708SJon Doron     return false;
18940d71f708SJon Doron }
18950d71f708SJon Doron 
handle_gpadl_teardown(VMBus * vmbus,vmbus_message_gpadl_teardown * msg,uint32_t msglen)18960d71f708SJon Doron static void handle_gpadl_teardown(VMBus *vmbus,
18970d71f708SJon Doron                                   vmbus_message_gpadl_teardown *msg,
18980d71f708SJon Doron                                   uint32_t msglen)
18990d71f708SJon Doron {
19000d71f708SJon Doron     VMBusGpadl *gpadl;
19010d71f708SJon Doron 
19020d71f708SJon Doron     if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) {
19030d71f708SJon Doron         return;
19040d71f708SJon Doron     }
19050d71f708SJon Doron 
19060d71f708SJon Doron     trace_vmbus_gpadl_teardown(msg->gpadl_id);
19070d71f708SJon Doron 
19080d71f708SJon Doron     gpadl = find_gpadl(vmbus, msg->gpadl_id);
19090d71f708SJon Doron     if (!gpadl || gpadl->state == VMGPADL_TORNDOWN) {
19100d71f708SJon Doron         return;
19110d71f708SJon Doron     }
19120d71f708SJon Doron 
19130d71f708SJon Doron     gpadl->state = VMGPADL_TEARINGDOWN;
19140d71f708SJon Doron     vmbus->state = VMBUS_TEARDOWN_GPADL;
19150d71f708SJon Doron }
19160d71f708SJon Doron 
send_teardown_gpadl(VMBus * vmbus)19170d71f708SJon Doron static void send_teardown_gpadl(VMBus *vmbus)
19180d71f708SJon Doron {
19190d71f708SJon Doron     VMBusGpadl *gpadl;
19200d71f708SJon Doron 
19210d71f708SJon Doron     QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) {
19220d71f708SJon Doron         if (gpadl->state == VMGPADL_TEARINGDOWN) {
19230d71f708SJon Doron             struct vmbus_message_gpadl_torndown msg = {
19240d71f708SJon Doron                 .header.message_type = VMBUS_MSG_GPADL_TORNDOWN,
19250d71f708SJon Doron                 .gpadl_id = gpadl->id,
19260d71f708SJon Doron             };
19270d71f708SJon Doron 
19280d71f708SJon Doron             trace_vmbus_gpadl_torndown(gpadl->id);
19290d71f708SJon Doron             post_msg(vmbus, &msg, sizeof(msg));
19300d71f708SJon Doron             return;
19310d71f708SJon Doron         }
19320d71f708SJon Doron     }
19330d71f708SJon Doron 
19340d71f708SJon Doron     assert(false);
19350d71f708SJon Doron }
19360d71f708SJon Doron 
complete_teardown_gpadl(VMBus * vmbus)19370d71f708SJon Doron static bool complete_teardown_gpadl(VMBus *vmbus)
19380d71f708SJon Doron {
19390d71f708SJon Doron     VMBusGpadl *gpadl;
19400d71f708SJon Doron 
19410d71f708SJon Doron     QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) {
19420d71f708SJon Doron         if (gpadl->state == VMGPADL_TEARINGDOWN) {
19430d71f708SJon Doron             gpadl->state = VMGPADL_TORNDOWN;
19440d71f708SJon Doron             vmbus_put_gpadl(gpadl);
19450d71f708SJon Doron             return true;
19460d71f708SJon Doron         }
19470d71f708SJon Doron     }
19480d71f708SJon Doron 
19490d71f708SJon Doron     assert(false);
19500d71f708SJon Doron     return false;
19510d71f708SJon Doron }
19520d71f708SJon Doron 
handle_open_channel(VMBus * vmbus,vmbus_message_open_channel * msg,uint32_t msglen)19530d71f708SJon Doron static void handle_open_channel(VMBus *vmbus, vmbus_message_open_channel *msg,
19540d71f708SJon Doron                                 uint32_t msglen)
19550d71f708SJon Doron {
19560d71f708SJon Doron     VMBusChannel *chan;
19570d71f708SJon Doron 
19580d71f708SJon Doron     if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) {
19590d71f708SJon Doron         return;
19600d71f708SJon Doron     }
19610d71f708SJon Doron 
19620d71f708SJon Doron     trace_vmbus_open_channel(msg->child_relid, msg->ring_buffer_gpadl_id,
19630d71f708SJon Doron                              msg->target_vp);
19640d71f708SJon Doron     chan = find_channel(vmbus, msg->child_relid);
19650d71f708SJon Doron     if (!chan || chan->state != VMCHAN_INIT) {
19660d71f708SJon Doron         return;
19670d71f708SJon Doron     }
19680d71f708SJon Doron 
19690d71f708SJon Doron     chan->ringbuf_gpadl = msg->ring_buffer_gpadl_id;
19700d71f708SJon Doron     chan->ringbuf_send_offset = msg->ring_buffer_offset;
19710d71f708SJon Doron     chan->target_vp = msg->target_vp;
19720d71f708SJon Doron     chan->open_id = msg->open_id;
19730d71f708SJon Doron 
19740d71f708SJon Doron     open_channel(chan);
19750d71f708SJon Doron 
19760d71f708SJon Doron     chan->state = VMCHAN_OPENING;
19770d71f708SJon Doron     vmbus->state = VMBUS_OPEN_CHANNEL;
19780d71f708SJon Doron }
19790d71f708SJon Doron 
send_open_channel(VMBus * vmbus)19800d71f708SJon Doron static void send_open_channel(VMBus *vmbus)
19810d71f708SJon Doron {
19820d71f708SJon Doron     VMBusChannel *chan;
19830d71f708SJon Doron 
19840d71f708SJon Doron     QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
19850d71f708SJon Doron         if (chan->state == VMCHAN_OPENING) {
19860d71f708SJon Doron             struct vmbus_message_open_result msg = {
19870d71f708SJon Doron                 .header.message_type = VMBUS_MSG_OPENCHANNEL_RESULT,
19880d71f708SJon Doron                 .child_relid = chan->id,
19890d71f708SJon Doron                 .open_id = chan->open_id,
19900d71f708SJon Doron                 .status = !vmbus_channel_is_open(chan),
19910d71f708SJon Doron             };
19920d71f708SJon Doron 
19930d71f708SJon Doron             trace_vmbus_channel_open(chan->id, msg.status);
19940d71f708SJon Doron             post_msg(vmbus, &msg, sizeof(msg));
19950d71f708SJon Doron             return;
19960d71f708SJon Doron         }
19970d71f708SJon Doron     }
19980d71f708SJon Doron 
19990d71f708SJon Doron     assert(false);
20000d71f708SJon Doron }
20010d71f708SJon Doron 
complete_open_channel(VMBus * vmbus)20020d71f708SJon Doron static bool complete_open_channel(VMBus *vmbus)
20030d71f708SJon Doron {
20040d71f708SJon Doron     VMBusChannel *chan;
20050d71f708SJon Doron 
20060d71f708SJon Doron     QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
20070d71f708SJon Doron         if (chan->state == VMCHAN_OPENING) {
20080d71f708SJon Doron             if (vmbus_channel_is_open(chan)) {
20090d71f708SJon Doron                 chan->state = VMCHAN_OPEN;
20100d71f708SJon Doron                 /*
20110d71f708SJon Doron                  * simulate guest notification of ringbuffer space made
20120d71f708SJon Doron                  * available, for the channel protocols where the host
20130d71f708SJon Doron                  * initiates the communication
20140d71f708SJon Doron                  */
20150d71f708SJon Doron                 vmbus_channel_notify_host(chan);
20160d71f708SJon Doron             } else {
20170d71f708SJon Doron                 chan->state = VMCHAN_INIT;
20180d71f708SJon Doron             }
20190d71f708SJon Doron             return true;
20200d71f708SJon Doron         }
20210d71f708SJon Doron     }
20220d71f708SJon Doron 
20230d71f708SJon Doron     assert(false);
20240d71f708SJon Doron     return false;
20250d71f708SJon Doron }
20260d71f708SJon Doron 
vdev_reset_on_close(VMBusDevice * vdev)20270d71f708SJon Doron static void vdev_reset_on_close(VMBusDevice *vdev)
20280d71f708SJon Doron {
20290d71f708SJon Doron     uint16_t i;
20300d71f708SJon Doron 
20310d71f708SJon Doron     for (i = 0; i < vdev->num_channels; i++) {
20320d71f708SJon Doron         if (vmbus_channel_is_open(&vdev->channels[i])) {
20330d71f708SJon Doron             return;
20340d71f708SJon Doron         }
20350d71f708SJon Doron     }
20360d71f708SJon Doron 
20370d71f708SJon Doron     /* all channels closed -- reset device */
20388cadd251SPeter Maydell     device_cold_reset(DEVICE(vdev));
20390d71f708SJon Doron }
20400d71f708SJon Doron 
handle_close_channel(VMBus * vmbus,vmbus_message_close_channel * msg,uint32_t msglen)20410d71f708SJon Doron static void handle_close_channel(VMBus *vmbus, vmbus_message_close_channel *msg,
20420d71f708SJon Doron                                  uint32_t msglen)
20430d71f708SJon Doron {
20440d71f708SJon Doron     VMBusChannel *chan;
20450d71f708SJon Doron 
20460d71f708SJon Doron     if (msglen < sizeof(*msg) || !vmbus_initialized(vmbus)) {
20470d71f708SJon Doron         return;
20480d71f708SJon Doron     }
20490d71f708SJon Doron 
20500d71f708SJon Doron     trace_vmbus_close_channel(msg->child_relid);
20510d71f708SJon Doron 
20520d71f708SJon Doron     chan = find_channel(vmbus, msg->child_relid);
20530d71f708SJon Doron     if (!chan) {
20540d71f708SJon Doron         return;
20550d71f708SJon Doron     }
20560d71f708SJon Doron 
20570d71f708SJon Doron     close_channel(chan);
20580d71f708SJon Doron     chan->state = VMCHAN_INIT;
20590d71f708SJon Doron 
20600d71f708SJon Doron     vdev_reset_on_close(chan->dev);
20610d71f708SJon Doron }
20620d71f708SJon Doron 
handle_unload(VMBus * vmbus,void * msg,uint32_t msglen)20630d71f708SJon Doron static void handle_unload(VMBus *vmbus, void *msg, uint32_t msglen)
20640d71f708SJon Doron {
20650d71f708SJon Doron     vmbus->state = VMBUS_UNLOAD;
20660d71f708SJon Doron }
20670d71f708SJon Doron 
send_unload(VMBus * vmbus)20680d71f708SJon Doron static void send_unload(VMBus *vmbus)
20690d71f708SJon Doron {
20700d71f708SJon Doron     vmbus_message_header msg = {
20710d71f708SJon Doron         .message_type = VMBUS_MSG_UNLOAD_RESPONSE,
20720d71f708SJon Doron     };
20730d71f708SJon Doron 
20740d71f708SJon Doron     qemu_mutex_lock(&vmbus->rx_queue_lock);
20750d71f708SJon Doron     vmbus->rx_queue_size = 0;
20760d71f708SJon Doron     qemu_mutex_unlock(&vmbus->rx_queue_lock);
20770d71f708SJon Doron 
20780d71f708SJon Doron     post_msg(vmbus, &msg, sizeof(msg));
20790d71f708SJon Doron     return;
20800d71f708SJon Doron }
20810d71f708SJon Doron 
complete_unload(VMBus * vmbus)20820d71f708SJon Doron static bool complete_unload(VMBus *vmbus)
20830d71f708SJon Doron {
20840d71f708SJon Doron     vmbus_reset_all(vmbus);
20850d71f708SJon Doron     return true;
20860d71f708SJon Doron }
20870d71f708SJon Doron 
process_message(VMBus * vmbus)20880d71f708SJon Doron static void process_message(VMBus *vmbus)
20890d71f708SJon Doron {
20900d71f708SJon Doron     struct hyperv_post_message_input *hv_msg;
20910d71f708SJon Doron     struct vmbus_message_header *msg;
20920d71f708SJon Doron     void *msgdata;
20930d71f708SJon Doron     uint32_t msglen;
20940d71f708SJon Doron 
20950d71f708SJon Doron     qemu_mutex_lock(&vmbus->rx_queue_lock);
20960d71f708SJon Doron 
20970d71f708SJon Doron     if (!vmbus->rx_queue_size) {
20980d71f708SJon Doron         goto unlock;
20990d71f708SJon Doron     }
21000d71f708SJon Doron 
21010d71f708SJon Doron     hv_msg = &vmbus->rx_queue[vmbus->rx_queue_head];
21020d71f708SJon Doron     msglen =  hv_msg->payload_size;
21030d71f708SJon Doron     if (msglen < sizeof(*msg)) {
21040d71f708SJon Doron         goto out;
21050d71f708SJon Doron     }
21060d71f708SJon Doron     msgdata = hv_msg->payload;
21073d558330SMarkus Armbruster     msg = msgdata;
21080d71f708SJon Doron 
21090d71f708SJon Doron     trace_vmbus_process_incoming_message(msg->message_type);
21100d71f708SJon Doron 
21110d71f708SJon Doron     switch (msg->message_type) {
21120d71f708SJon Doron     case VMBUS_MSG_INITIATE_CONTACT:
21130d71f708SJon Doron         handle_initiate_contact(vmbus, msgdata, msglen);
21140d71f708SJon Doron         break;
21150d71f708SJon Doron     case VMBUS_MSG_REQUESTOFFERS:
21160d71f708SJon Doron         handle_request_offers(vmbus, msgdata, msglen);
21170d71f708SJon Doron         break;
21180d71f708SJon Doron     case VMBUS_MSG_GPADL_HEADER:
21190d71f708SJon Doron         handle_gpadl_header(vmbus, msgdata, msglen);
21200d71f708SJon Doron         break;
21210d71f708SJon Doron     case VMBUS_MSG_GPADL_BODY:
21220d71f708SJon Doron         handle_gpadl_body(vmbus, msgdata, msglen);
21230d71f708SJon Doron         break;
21240d71f708SJon Doron     case VMBUS_MSG_GPADL_TEARDOWN:
21250d71f708SJon Doron         handle_gpadl_teardown(vmbus, msgdata, msglen);
21260d71f708SJon Doron         break;
21270d71f708SJon Doron     case VMBUS_MSG_OPENCHANNEL:
21280d71f708SJon Doron         handle_open_channel(vmbus, msgdata, msglen);
21290d71f708SJon Doron         break;
21300d71f708SJon Doron     case VMBUS_MSG_CLOSECHANNEL:
21310d71f708SJon Doron         handle_close_channel(vmbus, msgdata, msglen);
21320d71f708SJon Doron         break;
21330d71f708SJon Doron     case VMBUS_MSG_UNLOAD:
21340d71f708SJon Doron         handle_unload(vmbus, msgdata, msglen);
21350d71f708SJon Doron         break;
21360d71f708SJon Doron     default:
21370d71f708SJon Doron         error_report("unknown message type %#x", msg->message_type);
21380d71f708SJon Doron         break;
21390d71f708SJon Doron     }
21400d71f708SJon Doron 
21410d71f708SJon Doron out:
21420d71f708SJon Doron     vmbus->rx_queue_size--;
21430d71f708SJon Doron     vmbus->rx_queue_head++;
21440d71f708SJon Doron     vmbus->rx_queue_head %= HV_MSG_QUEUE_LEN;
21450d71f708SJon Doron 
21460d71f708SJon Doron     vmbus_resched(vmbus);
21470d71f708SJon Doron unlock:
21480d71f708SJon Doron     qemu_mutex_unlock(&vmbus->rx_queue_lock);
21490d71f708SJon Doron }
21500d71f708SJon Doron 
21510d71f708SJon Doron static const struct {
21520d71f708SJon Doron     void (*run)(VMBus *vmbus);
21530d71f708SJon Doron     bool (*complete)(VMBus *vmbus);
21540d71f708SJon Doron } state_runner[] = {
21550d71f708SJon Doron     [VMBUS_LISTEN]         = {process_message,     NULL},
21560d71f708SJon Doron     [VMBUS_HANDSHAKE]      = {send_handshake,      NULL},
21570d71f708SJon Doron     [VMBUS_OFFER]          = {send_offer,          complete_offer},
21580d71f708SJon Doron     [VMBUS_CREATE_GPADL]   = {send_create_gpadl,   complete_create_gpadl},
21590d71f708SJon Doron     [VMBUS_TEARDOWN_GPADL] = {send_teardown_gpadl, complete_teardown_gpadl},
21600d71f708SJon Doron     [VMBUS_OPEN_CHANNEL]   = {send_open_channel,   complete_open_channel},
21610d71f708SJon Doron     [VMBUS_UNLOAD]         = {send_unload,         complete_unload},
21620d71f708SJon Doron };
21630d71f708SJon Doron 
vmbus_do_run(VMBus * vmbus)21640d71f708SJon Doron static void vmbus_do_run(VMBus *vmbus)
21650d71f708SJon Doron {
21660d71f708SJon Doron     if (vmbus->msg_in_progress) {
21670d71f708SJon Doron         return;
21680d71f708SJon Doron     }
21690d71f708SJon Doron 
21700d71f708SJon Doron     assert(vmbus->state < VMBUS_STATE_MAX);
21710d71f708SJon Doron     assert(state_runner[vmbus->state].run);
21720d71f708SJon Doron     state_runner[vmbus->state].run(vmbus);
21730d71f708SJon Doron }
21740d71f708SJon Doron 
vmbus_run(void * opaque)21750d71f708SJon Doron static void vmbus_run(void *opaque)
21760d71f708SJon Doron {
21770d71f708SJon Doron     VMBus *vmbus = opaque;
21780d71f708SJon Doron 
21790d71f708SJon Doron     /* make sure no recursion happens (e.g. due to recursive aio_poll()) */
21800d71f708SJon Doron     if (vmbus->in_progress) {
21810d71f708SJon Doron         return;
21820d71f708SJon Doron     }
21830d71f708SJon Doron 
21840d71f708SJon Doron     vmbus->in_progress = true;
21850d71f708SJon Doron     /*
21860d71f708SJon Doron      * FIXME: if vmbus_resched() is called from within vmbus_do_run(), it
21870d71f708SJon Doron      * should go *after* the code that can result in aio_poll; otherwise
21880d71f708SJon Doron      * reschedules can be missed.  No idea how to enforce that.
21890d71f708SJon Doron      */
21900d71f708SJon Doron     vmbus_do_run(vmbus);
21910d71f708SJon Doron     vmbus->in_progress = false;
21920d71f708SJon Doron }
21930d71f708SJon Doron 
vmbus_msg_cb(void * data,int status)21940d71f708SJon Doron static void vmbus_msg_cb(void *data, int status)
21950d71f708SJon Doron {
21960d71f708SJon Doron     VMBus *vmbus = data;
21970d71f708SJon Doron     bool (*complete)(VMBus *vmbus);
21980d71f708SJon Doron 
21990d71f708SJon Doron     assert(vmbus->msg_in_progress);
22000d71f708SJon Doron 
22010d71f708SJon Doron     trace_vmbus_msg_cb(status);
22020d71f708SJon Doron 
22030d71f708SJon Doron     if (status == -EAGAIN) {
22040d71f708SJon Doron         goto out;
22050d71f708SJon Doron     }
22060d71f708SJon Doron     if (status) {
22070d71f708SJon Doron         error_report("message delivery fatal failure: %d; aborting vmbus",
22080d71f708SJon Doron                      status);
22090d71f708SJon Doron         vmbus_reset_all(vmbus);
22100d71f708SJon Doron         return;
22110d71f708SJon Doron     }
22120d71f708SJon Doron 
22130d71f708SJon Doron     assert(vmbus->state < VMBUS_STATE_MAX);
22140d71f708SJon Doron     complete = state_runner[vmbus->state].complete;
22150d71f708SJon Doron     if (!complete || complete(vmbus)) {
22160d71f708SJon Doron         vmbus->state = VMBUS_LISTEN;
22170d71f708SJon Doron     }
22180d71f708SJon Doron out:
22190d71f708SJon Doron     vmbus->msg_in_progress = false;
22200d71f708SJon Doron     vmbus_resched(vmbus);
22210d71f708SJon Doron }
22220d71f708SJon Doron 
vmbus_resched(VMBus * vmbus)22230d71f708SJon Doron static void vmbus_resched(VMBus *vmbus)
22240d71f708SJon Doron {
22250d71f708SJon Doron     aio_bh_schedule_oneshot(qemu_get_aio_context(), vmbus_run, vmbus);
22260d71f708SJon Doron }
22270d71f708SJon Doron 
vmbus_signal_event(EventNotifier * e)22280d71f708SJon Doron static void vmbus_signal_event(EventNotifier *e)
22290d71f708SJon Doron {
22300d71f708SJon Doron     VMBusChannel *chan;
22310d71f708SJon Doron     VMBus *vmbus = container_of(e, VMBus, notifier);
22320d71f708SJon Doron     unsigned long *int_map;
22330d71f708SJon Doron     hwaddr addr, len;
22340d71f708SJon Doron     bool is_dirty = false;
22350d71f708SJon Doron 
22360d71f708SJon Doron     if (!event_notifier_test_and_clear(e)) {
22370d71f708SJon Doron         return;
22380d71f708SJon Doron     }
22390d71f708SJon Doron 
22400d71f708SJon Doron     trace_vmbus_signal_event();
22410d71f708SJon Doron 
22420d71f708SJon Doron     if (!vmbus->int_page_gpa) {
22430d71f708SJon Doron         return;
22440d71f708SJon Doron     }
22450d71f708SJon Doron 
22460d71f708SJon Doron     addr = vmbus->int_page_gpa + TARGET_PAGE_SIZE / 2;
22470d71f708SJon Doron     len = TARGET_PAGE_SIZE / 2;
22480d71f708SJon Doron     int_map = cpu_physical_memory_map(addr, &len, 1);
22490d71f708SJon Doron     if (len != TARGET_PAGE_SIZE / 2) {
22500d71f708SJon Doron         goto unmap;
22510d71f708SJon Doron     }
22520d71f708SJon Doron 
22530d71f708SJon Doron     QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
22540d71f708SJon Doron         if (bitmap_test_and_clear_atomic(int_map, chan->id, 1)) {
22550d71f708SJon Doron             if (!vmbus_channel_is_open(chan)) {
22560d71f708SJon Doron                 continue;
22570d71f708SJon Doron             }
22580d71f708SJon Doron             vmbus_channel_notify_host(chan);
22590d71f708SJon Doron             is_dirty = true;
22600d71f708SJon Doron         }
22610d71f708SJon Doron     }
22620d71f708SJon Doron 
22630d71f708SJon Doron unmap:
22640d71f708SJon Doron     cpu_physical_memory_unmap(int_map, len, 1, is_dirty);
22650d71f708SJon Doron }
22660d71f708SJon Doron 
vmbus_dev_realize(DeviceState * dev,Error ** errp)22670d71f708SJon Doron static void vmbus_dev_realize(DeviceState *dev, Error **errp)
22680d71f708SJon Doron {
22690d71f708SJon Doron     VMBusDevice *vdev = VMBUS_DEVICE(dev);
22700d71f708SJon Doron     VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(vdev);
22710d71f708SJon Doron     VMBus *vmbus = VMBUS(qdev_get_parent_bus(dev));
22720d71f708SJon Doron     BusChild *child;
22730d71f708SJon Doron     Error *err = NULL;
2274721da039SCédric Le Goater     char idstr[UUID_STR_LEN];
22750d71f708SJon Doron 
22760d71f708SJon Doron     assert(!qemu_uuid_is_null(&vdev->instanceid));
22770d71f708SJon Doron 
227880cc1a0dSEduardo Habkost     if (!qemu_uuid_is_null(&vdc->instanceid)) {
227980cc1a0dSEduardo Habkost         /* Class wants to only have a single instance with a fixed UUID */
228080cc1a0dSEduardo Habkost         if (!qemu_uuid_is_equal(&vdev->instanceid, &vdc->instanceid)) {
228180cc1a0dSEduardo Habkost             error_setg(&err, "instance id can't be changed");
228280cc1a0dSEduardo Habkost             goto error_out;
228380cc1a0dSEduardo Habkost         }
228480cc1a0dSEduardo Habkost     }
228580cc1a0dSEduardo Habkost 
22860d71f708SJon Doron     /* Check for instance id collision for this class id */
22870d71f708SJon Doron     QTAILQ_FOREACH(child, &BUS(vmbus)->children, sibling) {
22880d71f708SJon Doron         VMBusDevice *child_dev = VMBUS_DEVICE(child->child);
22890d71f708SJon Doron 
22900d71f708SJon Doron         if (child_dev == vdev) {
22910d71f708SJon Doron             continue;
22920d71f708SJon Doron         }
22930d71f708SJon Doron 
22940d71f708SJon Doron         if (qemu_uuid_is_equal(&child_dev->instanceid, &vdev->instanceid)) {
22950d71f708SJon Doron             qemu_uuid_unparse(&vdev->instanceid, idstr);
22960d71f708SJon Doron             error_setg(&err, "duplicate vmbus device instance id %s", idstr);
22970d71f708SJon Doron             goto error_out;
22980d71f708SJon Doron         }
22990d71f708SJon Doron     }
23000d71f708SJon Doron 
23010d71f708SJon Doron     vdev->dma_as = &address_space_memory;
23020d71f708SJon Doron 
23030d71f708SJon Doron     create_channels(vmbus, vdev, &err);
23040d71f708SJon Doron     if (err) {
23050d71f708SJon Doron         goto error_out;
23060d71f708SJon Doron     }
23070d71f708SJon Doron 
23080d71f708SJon Doron     if (vdc->vmdev_realize) {
23090d71f708SJon Doron         vdc->vmdev_realize(vdev, &err);
23100d71f708SJon Doron         if (err) {
23110d71f708SJon Doron             goto err_vdc_realize;
23120d71f708SJon Doron         }
23130d71f708SJon Doron     }
23140d71f708SJon Doron     return;
23150d71f708SJon Doron 
23160d71f708SJon Doron err_vdc_realize:
23170d71f708SJon Doron     free_channels(vdev);
23180d71f708SJon Doron error_out:
23190d71f708SJon Doron     error_propagate(errp, err);
23200d71f708SJon Doron }
23210d71f708SJon Doron 
vmbus_dev_reset(DeviceState * dev)23220d71f708SJon Doron static void vmbus_dev_reset(DeviceState *dev)
23230d71f708SJon Doron {
23240d71f708SJon Doron     uint16_t i;
23250d71f708SJon Doron     VMBusDevice *vdev = VMBUS_DEVICE(dev);
23260d71f708SJon Doron     VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(vdev);
23270d71f708SJon Doron 
23280d71f708SJon Doron     if (vdev->channels) {
23290d71f708SJon Doron         for (i = 0; i < vdev->num_channels; i++) {
23300d71f708SJon Doron             VMBusChannel *chan = &vdev->channels[i];
23310d71f708SJon Doron             close_channel(chan);
23320d71f708SJon Doron             chan->state = VMCHAN_INIT;
23330d71f708SJon Doron         }
23340d71f708SJon Doron     }
23350d71f708SJon Doron 
23360d71f708SJon Doron     if (vdc->vmdev_reset) {
23370d71f708SJon Doron         vdc->vmdev_reset(vdev);
23380d71f708SJon Doron     }
23390d71f708SJon Doron }
23400d71f708SJon Doron 
vmbus_dev_unrealize(DeviceState * dev)23410d71f708SJon Doron static void vmbus_dev_unrealize(DeviceState *dev)
23420d71f708SJon Doron {
23430d71f708SJon Doron     VMBusDevice *vdev = VMBUS_DEVICE(dev);
23440d71f708SJon Doron     VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(vdev);
23450d71f708SJon Doron 
23460d71f708SJon Doron     if (vdc->vmdev_unrealize) {
23470d71f708SJon Doron         vdc->vmdev_unrealize(vdev);
23480d71f708SJon Doron     }
23490d71f708SJon Doron     free_channels(vdev);
23500d71f708SJon Doron }
23510d71f708SJon Doron 
235280cc1a0dSEduardo Habkost static Property vmbus_dev_props[] = {
235380cc1a0dSEduardo Habkost     DEFINE_PROP_UUID("instanceid", VMBusDevice, instanceid),
235480cc1a0dSEduardo Habkost     DEFINE_PROP_END_OF_LIST()
235580cc1a0dSEduardo Habkost };
235680cc1a0dSEduardo Habkost 
235780cc1a0dSEduardo Habkost 
vmbus_dev_class_init(ObjectClass * klass,void * data)23580d71f708SJon Doron static void vmbus_dev_class_init(ObjectClass *klass, void *data)
23590d71f708SJon Doron {
23600d71f708SJon Doron     DeviceClass *kdev = DEVICE_CLASS(klass);
236180cc1a0dSEduardo Habkost     device_class_set_props(kdev, vmbus_dev_props);
23620d71f708SJon Doron     kdev->bus_type = TYPE_VMBUS;
23630d71f708SJon Doron     kdev->realize = vmbus_dev_realize;
23640d71f708SJon Doron     kdev->unrealize = vmbus_dev_unrealize;
23650d71f708SJon Doron     kdev->reset = vmbus_dev_reset;
23660d71f708SJon Doron }
23670d71f708SJon Doron 
vmbus_dev_instance_init(Object * obj)23680d71f708SJon Doron static void vmbus_dev_instance_init(Object *obj)
23690d71f708SJon Doron {
23700d71f708SJon Doron     VMBusDevice *vdev = VMBUS_DEVICE(obj);
23710d71f708SJon Doron     VMBusDeviceClass *vdc = VMBUS_DEVICE_GET_CLASS(vdev);
23720d71f708SJon Doron 
23730d71f708SJon Doron     if (!qemu_uuid_is_null(&vdc->instanceid)) {
23740d71f708SJon Doron         /* Class wants to only have a single instance with a fixed UUID */
23750d71f708SJon Doron         vdev->instanceid = vdc->instanceid;
23760d71f708SJon Doron     }
23770d71f708SJon Doron }
23780d71f708SJon Doron 
23790d71f708SJon Doron const VMStateDescription vmstate_vmbus_dev = {
23800d71f708SJon Doron     .name = TYPE_VMBUS_DEVICE,
23810d71f708SJon Doron     .version_id = 0,
23820d71f708SJon Doron     .minimum_version_id = 0,
23832ebfd1c4SRichard Henderson     .fields = (const VMStateField[]) {
23840d71f708SJon Doron         VMSTATE_UINT8_ARRAY(instanceid.data, VMBusDevice, 16),
23850d71f708SJon Doron         VMSTATE_UINT16(num_channels, VMBusDevice),
23860d71f708SJon Doron         VMSTATE_STRUCT_VARRAY_POINTER_UINT16(channels, VMBusDevice,
23870d71f708SJon Doron                                              num_channels, vmstate_channel,
23880d71f708SJon Doron                                              VMBusChannel),
23890d71f708SJon Doron         VMSTATE_END_OF_LIST()
23900d71f708SJon Doron     }
23910d71f708SJon Doron };
23920d71f708SJon Doron 
23930d71f708SJon Doron /* vmbus generic device base */
23940d71f708SJon Doron static const TypeInfo vmbus_dev_type_info = {
23950d71f708SJon Doron     .name = TYPE_VMBUS_DEVICE,
23960d71f708SJon Doron     .parent = TYPE_DEVICE,
23970d71f708SJon Doron     .abstract = true,
23980d71f708SJon Doron     .instance_size = sizeof(VMBusDevice),
23990d71f708SJon Doron     .class_size = sizeof(VMBusDeviceClass),
24000d71f708SJon Doron     .class_init = vmbus_dev_class_init,
24010d71f708SJon Doron     .instance_init = vmbus_dev_instance_init,
24020d71f708SJon Doron };
24030d71f708SJon Doron 
vmbus_realize(BusState * bus,Error ** errp)24040d71f708SJon Doron static void vmbus_realize(BusState *bus, Error **errp)
24050d71f708SJon Doron {
24060d71f708SJon Doron     int ret = 0;
24070d71f708SJon Doron     VMBus *vmbus = VMBUS(bus);
24080d71f708SJon Doron 
24090d71f708SJon Doron     qemu_mutex_init(&vmbus->rx_queue_lock);
24100d71f708SJon Doron 
24110d71f708SJon Doron     QTAILQ_INIT(&vmbus->gpadl_list);
24120d71f708SJon Doron     QTAILQ_INIT(&vmbus->channel_list);
24130d71f708SJon Doron 
24140d71f708SJon Doron     ret = hyperv_set_msg_handler(VMBUS_MESSAGE_CONNECTION_ID,
24150d71f708SJon Doron                                  vmbus_recv_message, vmbus);
24160d71f708SJon Doron     if (ret != 0) {
24176c37ebf3SMarkus Armbruster         error_setg(errp, "hyperv set message handler failed: %d", ret);
24180d71f708SJon Doron         goto error_out;
24190d71f708SJon Doron     }
24200d71f708SJon Doron 
24210d71f708SJon Doron     ret = event_notifier_init(&vmbus->notifier, 0);
24220d71f708SJon Doron     if (ret != 0) {
24236c37ebf3SMarkus Armbruster         error_setg(errp, "event notifier failed to init with %d", ret);
24240d71f708SJon Doron         goto remove_msg_handler;
24250d71f708SJon Doron     }
24260d71f708SJon Doron 
24270d71f708SJon Doron     event_notifier_set_handler(&vmbus->notifier, vmbus_signal_event);
24280d71f708SJon Doron     ret = hyperv_set_event_flag_handler(VMBUS_EVENT_CONNECTION_ID,
24290d71f708SJon Doron                                         &vmbus->notifier);
24300d71f708SJon Doron     if (ret != 0) {
24316c37ebf3SMarkus Armbruster         error_setg(errp, "hyperv set event handler failed with %d", ret);
24320d71f708SJon Doron         goto clear_event_notifier;
24330d71f708SJon Doron     }
24340d71f708SJon Doron 
24350d71f708SJon Doron     return;
24360d71f708SJon Doron 
24370d71f708SJon Doron clear_event_notifier:
24380d71f708SJon Doron     event_notifier_cleanup(&vmbus->notifier);
24390d71f708SJon Doron remove_msg_handler:
24400d71f708SJon Doron     hyperv_set_msg_handler(VMBUS_MESSAGE_CONNECTION_ID, NULL, NULL);
24410d71f708SJon Doron error_out:
24420d71f708SJon Doron     qemu_mutex_destroy(&vmbus->rx_queue_lock);
24430d71f708SJon Doron }
24440d71f708SJon Doron 
vmbus_unrealize(BusState * bus)24450d71f708SJon Doron static void vmbus_unrealize(BusState *bus)
24460d71f708SJon Doron {
24470d71f708SJon Doron     VMBus *vmbus = VMBUS(bus);
24480d71f708SJon Doron 
24490d71f708SJon Doron     hyperv_set_msg_handler(VMBUS_MESSAGE_CONNECTION_ID, NULL, NULL);
24500d71f708SJon Doron     hyperv_set_event_flag_handler(VMBUS_EVENT_CONNECTION_ID, NULL);
24510d71f708SJon Doron     event_notifier_cleanup(&vmbus->notifier);
24520d71f708SJon Doron 
24530d71f708SJon Doron     qemu_mutex_destroy(&vmbus->rx_queue_lock);
24540d71f708SJon Doron }
24550d71f708SJon Doron 
vmbus_reset_hold(Object * obj,ResetType type)2456ad80e367SPeter Maydell static void vmbus_reset_hold(Object *obj, ResetType type)
24570d71f708SJon Doron {
2458d7f35529SPeter Maydell     vmbus_deinit(VMBUS(obj));
24590d71f708SJon Doron }
24600d71f708SJon Doron 
vmbus_get_dev_path(DeviceState * dev)24610d71f708SJon Doron static char *vmbus_get_dev_path(DeviceState *dev)
24620d71f708SJon Doron {
24630d71f708SJon Doron     BusState *bus = qdev_get_parent_bus(dev);
24640d71f708SJon Doron     return qdev_get_dev_path(bus->parent);
24650d71f708SJon Doron }
24660d71f708SJon Doron 
vmbus_get_fw_dev_path(DeviceState * dev)24670d71f708SJon Doron static char *vmbus_get_fw_dev_path(DeviceState *dev)
24680d71f708SJon Doron {
24690d71f708SJon Doron     VMBusDevice *vdev = VMBUS_DEVICE(dev);
2470721da039SCédric Le Goater     char uuid[UUID_STR_LEN];
24710d71f708SJon Doron 
24720d71f708SJon Doron     qemu_uuid_unparse(&vdev->instanceid, uuid);
24730d71f708SJon Doron     return g_strdup_printf("%s@%s", qdev_fw_name(dev), uuid);
24740d71f708SJon Doron }
24750d71f708SJon Doron 
vmbus_class_init(ObjectClass * klass,void * data)24760d71f708SJon Doron static void vmbus_class_init(ObjectClass *klass, void *data)
24770d71f708SJon Doron {
24780d71f708SJon Doron     BusClass *k = BUS_CLASS(klass);
2479d7f35529SPeter Maydell     ResettableClass *rc = RESETTABLE_CLASS(klass);
24800d71f708SJon Doron 
24810d71f708SJon Doron     k->get_dev_path = vmbus_get_dev_path;
24820d71f708SJon Doron     k->get_fw_dev_path = vmbus_get_fw_dev_path;
24830d71f708SJon Doron     k->realize = vmbus_realize;
24840d71f708SJon Doron     k->unrealize = vmbus_unrealize;
2485d7f35529SPeter Maydell     rc->phases.hold = vmbus_reset_hold;
24860d71f708SJon Doron }
24870d71f708SJon Doron 
vmbus_pre_load(void * opaque)24880d71f708SJon Doron static int vmbus_pre_load(void *opaque)
24890d71f708SJon Doron {
24900d71f708SJon Doron     VMBusChannel *chan;
24910d71f708SJon Doron     VMBus *vmbus = VMBUS(opaque);
24920d71f708SJon Doron 
24930d71f708SJon Doron     /*
24940d71f708SJon Doron      * channel IDs allocated by the source will come in the migration stream
24950d71f708SJon Doron      * for each channel, so clean up the ones allocated at realize
24960d71f708SJon Doron      */
24970d71f708SJon Doron     QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
24980d71f708SJon Doron         unregister_chan_id(chan);
24990d71f708SJon Doron     }
25000d71f708SJon Doron 
25010d71f708SJon Doron     return 0;
25020d71f708SJon Doron }
vmbus_post_load(void * opaque,int version_id)25030d71f708SJon Doron static int vmbus_post_load(void *opaque, int version_id)
25040d71f708SJon Doron {
25050d71f708SJon Doron     int ret;
25060d71f708SJon Doron     VMBus *vmbus = VMBUS(opaque);
25070d71f708SJon Doron     VMBusGpadl *gpadl;
25080d71f708SJon Doron     VMBusChannel *chan;
25090d71f708SJon Doron 
25100d71f708SJon Doron     ret = vmbus_init(vmbus);
25110d71f708SJon Doron     if (ret) {
25120d71f708SJon Doron         return ret;
25130d71f708SJon Doron     }
25140d71f708SJon Doron 
25150d71f708SJon Doron     QTAILQ_FOREACH(gpadl, &vmbus->gpadl_list, link) {
25160d71f708SJon Doron         gpadl->vmbus = vmbus;
25170d71f708SJon Doron         gpadl->refcount = 1;
25180d71f708SJon Doron     }
25190d71f708SJon Doron 
25200d71f708SJon Doron     /*
25210d71f708SJon Doron      * reopening channels depends on initialized vmbus so it's done here
25220d71f708SJon Doron      * instead of channel_post_load()
25230d71f708SJon Doron      */
25240d71f708SJon Doron     QTAILQ_FOREACH(chan, &vmbus->channel_list, link) {
25250d71f708SJon Doron 
25260d71f708SJon Doron         if (chan->state == VMCHAN_OPENING || chan->state == VMCHAN_OPEN) {
25270d71f708SJon Doron             open_channel(chan);
25280d71f708SJon Doron         }
25290d71f708SJon Doron 
25300d71f708SJon Doron         if (chan->state != VMCHAN_OPEN) {
25310d71f708SJon Doron             continue;
25320d71f708SJon Doron         }
25330d71f708SJon Doron 
25340d71f708SJon Doron         if (!vmbus_channel_is_open(chan)) {
25350d71f708SJon Doron             /* reopen failed, abort loading */
25360d71f708SJon Doron             return -1;
25370d71f708SJon Doron         }
25380d71f708SJon Doron 
25390d71f708SJon Doron         /* resume processing on the guest side if it missed the notification */
25400d71f708SJon Doron         hyperv_sint_route_set_sint(chan->notify_route);
25410d71f708SJon Doron         /* ditto on the host side */
25420d71f708SJon Doron         vmbus_channel_notify_host(chan);
25430d71f708SJon Doron     }
25440d71f708SJon Doron 
25450d71f708SJon Doron     vmbus_resched(vmbus);
25460d71f708SJon Doron     return 0;
25470d71f708SJon Doron }
25480d71f708SJon Doron 
25490d71f708SJon Doron static const VMStateDescription vmstate_post_message_input = {
25500d71f708SJon Doron     .name = "vmbus/hyperv_post_message_input",
25510d71f708SJon Doron     .version_id = 0,
25520d71f708SJon Doron     .minimum_version_id = 0,
25532ebfd1c4SRichard Henderson     .fields = (const VMStateField[]) {
25540d71f708SJon Doron         /*
25550d71f708SJon Doron          * skip connection_id and message_type as they are validated before
25560d71f708SJon Doron          * queueing and ignored on dequeueing
25570d71f708SJon Doron          */
25580d71f708SJon Doron         VMSTATE_UINT32(payload_size, struct hyperv_post_message_input),
25590d71f708SJon Doron         VMSTATE_UINT8_ARRAY(payload, struct hyperv_post_message_input,
25600d71f708SJon Doron                             HV_MESSAGE_PAYLOAD_SIZE),
25610d71f708SJon Doron         VMSTATE_END_OF_LIST()
25620d71f708SJon Doron     }
25630d71f708SJon Doron };
25640d71f708SJon Doron 
vmbus_rx_queue_needed(void * opaque)25650d71f708SJon Doron static bool vmbus_rx_queue_needed(void *opaque)
25660d71f708SJon Doron {
25670d71f708SJon Doron     VMBus *vmbus = VMBUS(opaque);
25680d71f708SJon Doron     return vmbus->rx_queue_size;
25690d71f708SJon Doron }
25700d71f708SJon Doron 
25710d71f708SJon Doron static const VMStateDescription vmstate_rx_queue = {
25720d71f708SJon Doron     .name = "vmbus/rx_queue",
25730d71f708SJon Doron     .version_id = 0,
25740d71f708SJon Doron     .minimum_version_id = 0,
25750d71f708SJon Doron     .needed = vmbus_rx_queue_needed,
25762ebfd1c4SRichard Henderson     .fields = (const VMStateField[]) {
25770d71f708SJon Doron         VMSTATE_UINT8(rx_queue_head, VMBus),
25780d71f708SJon Doron         VMSTATE_UINT8(rx_queue_size, VMBus),
25790d71f708SJon Doron         VMSTATE_STRUCT_ARRAY(rx_queue, VMBus,
25800d71f708SJon Doron                              HV_MSG_QUEUE_LEN, 0,
25810d71f708SJon Doron                              vmstate_post_message_input,
25820d71f708SJon Doron                              struct hyperv_post_message_input),
25830d71f708SJon Doron         VMSTATE_END_OF_LIST()
25840d71f708SJon Doron     }
25850d71f708SJon Doron };
25860d71f708SJon Doron 
25870d71f708SJon Doron static const VMStateDescription vmstate_vmbus = {
25880d71f708SJon Doron     .name = TYPE_VMBUS,
25890d71f708SJon Doron     .version_id = 0,
25900d71f708SJon Doron     .minimum_version_id = 0,
25910d71f708SJon Doron     .pre_load = vmbus_pre_load,
25920d71f708SJon Doron     .post_load = vmbus_post_load,
25932ebfd1c4SRichard Henderson     .fields = (const VMStateField[]) {
25940d71f708SJon Doron         VMSTATE_UINT8(state, VMBus),
25950d71f708SJon Doron         VMSTATE_UINT32(version, VMBus),
25960d71f708SJon Doron         VMSTATE_UINT32(target_vp, VMBus),
25970d71f708SJon Doron         VMSTATE_UINT64(int_page_gpa, VMBus),
25980d71f708SJon Doron         VMSTATE_QTAILQ_V(gpadl_list, VMBus, 0,
25990d71f708SJon Doron                          vmstate_gpadl, VMBusGpadl, link),
26000d71f708SJon Doron         VMSTATE_END_OF_LIST()
26010d71f708SJon Doron     },
26022ebfd1c4SRichard Henderson     .subsections = (const VMStateDescription * const []) {
26030d71f708SJon Doron         &vmstate_rx_queue,
26040d71f708SJon Doron         NULL
26050d71f708SJon Doron     }
26060d71f708SJon Doron };
26070d71f708SJon Doron 
26080d71f708SJon Doron static const TypeInfo vmbus_type_info = {
26090d71f708SJon Doron     .name = TYPE_VMBUS,
26100d71f708SJon Doron     .parent = TYPE_BUS,
26110d71f708SJon Doron     .instance_size = sizeof(VMBus),
26120d71f708SJon Doron     .class_init = vmbus_class_init,
26130d71f708SJon Doron };
26140d71f708SJon Doron 
vmbus_bridge_realize(DeviceState * dev,Error ** errp)26150d71f708SJon Doron static void vmbus_bridge_realize(DeviceState *dev, Error **errp)
26160d71f708SJon Doron {
26170d71f708SJon Doron     VMBusBridge *bridge = VMBUS_BRIDGE(dev);
26180d71f708SJon Doron 
26190d71f708SJon Doron     /*
26200d71f708SJon Doron      * here there's at least one vmbus bridge that is being realized, so
26210d71f708SJon Doron      * vmbus_bridge_find can only return NULL if it's not unique
26220d71f708SJon Doron      */
26230d71f708SJon Doron     if (!vmbus_bridge_find()) {
26240d71f708SJon Doron         error_setg(errp, "there can be at most one %s in the system",
26250d71f708SJon Doron                    TYPE_VMBUS_BRIDGE);
26260d71f708SJon Doron         return;
26270d71f708SJon Doron     }
26280d71f708SJon Doron 
26290d71f708SJon Doron     if (!hyperv_is_synic_enabled()) {
26300d71f708SJon Doron         error_report("VMBus requires usable Hyper-V SynIC and VP_INDEX");
26310d71f708SJon Doron         return;
26320d71f708SJon Doron     }
26330d71f708SJon Doron 
26346093637bSMaciej S. Szmigiero     if (!hyperv_are_vmbus_recommended_features_enabled()) {
26356093637bSMaciej S. Szmigiero         warn_report("VMBus enabled without the recommended set of Hyper-V features: "
26366093637bSMaciej S. Szmigiero                     "hv-stimer, hv-vapic and hv-runtime. "
26376093637bSMaciej S. Szmigiero                     "Some Windows versions might not boot or enable the VMBus device");
26386093637bSMaciej S. Szmigiero     }
26396093637bSMaciej S. Szmigiero 
26409388d170SPeter Maydell     bridge->bus = VMBUS(qbus_new(TYPE_VMBUS, dev, "vmbus"));
26410d71f708SJon Doron }
26420d71f708SJon Doron 
vmbus_bridge_ofw_unit_address(const SysBusDevice * dev)26430d71f708SJon Doron static char *vmbus_bridge_ofw_unit_address(const SysBusDevice *dev)
26440d71f708SJon Doron {
26450d71f708SJon Doron     /* there can be only one VMBus */
26460d71f708SJon Doron     return g_strdup("0");
26470d71f708SJon Doron }
26480d71f708SJon Doron 
26490d71f708SJon Doron static const VMStateDescription vmstate_vmbus_bridge = {
26500d71f708SJon Doron     .name = TYPE_VMBUS_BRIDGE,
26510d71f708SJon Doron     .version_id = 0,
26520d71f708SJon Doron     .minimum_version_id = 0,
26532ebfd1c4SRichard Henderson     .fields = (const VMStateField[]) {
26540d71f708SJon Doron         VMSTATE_STRUCT_POINTER(bus, VMBusBridge, vmstate_vmbus, VMBus),
26550d71f708SJon Doron         VMSTATE_END_OF_LIST()
26560d71f708SJon Doron     },
26570d71f708SJon Doron };
26580d71f708SJon Doron 
26596775d15dSJon Doron static Property vmbus_bridge_props[] = {
26608f06f22fSJon Doron     DEFINE_PROP_UINT8("irq", VMBusBridge, irq, 7),
26616775d15dSJon Doron     DEFINE_PROP_END_OF_LIST()
26626775d15dSJon Doron };
26636775d15dSJon Doron 
vmbus_bridge_class_init(ObjectClass * klass,void * data)26640d71f708SJon Doron static void vmbus_bridge_class_init(ObjectClass *klass, void *data)
26650d71f708SJon Doron {
26660d71f708SJon Doron     DeviceClass *k = DEVICE_CLASS(klass);
26670d71f708SJon Doron     SysBusDeviceClass *sk = SYS_BUS_DEVICE_CLASS(klass);
26680d71f708SJon Doron 
26690d71f708SJon Doron     k->realize = vmbus_bridge_realize;
26700d71f708SJon Doron     k->fw_name = "vmbus";
26710d71f708SJon Doron     sk->explicit_ofw_unit_address = vmbus_bridge_ofw_unit_address;
26720d71f708SJon Doron     set_bit(DEVICE_CATEGORY_BRIDGE, k->categories);
26730d71f708SJon Doron     k->vmsd = &vmstate_vmbus_bridge;
26746775d15dSJon Doron     device_class_set_props(k, vmbus_bridge_props);
26750d71f708SJon Doron     /* override SysBusDevice's default */
26760d71f708SJon Doron     k->user_creatable = true;
26770d71f708SJon Doron }
26780d71f708SJon Doron 
26790d71f708SJon Doron static const TypeInfo vmbus_bridge_type_info = {
26800d71f708SJon Doron     .name = TYPE_VMBUS_BRIDGE,
26810d71f708SJon Doron     .parent = TYPE_SYS_BUS_DEVICE,
26820d71f708SJon Doron     .instance_size = sizeof(VMBusBridge),
26830d71f708SJon Doron     .class_init = vmbus_bridge_class_init,
26840d71f708SJon Doron };
26850d71f708SJon Doron 
vmbus_register_types(void)26860d71f708SJon Doron static void vmbus_register_types(void)
26870d71f708SJon Doron {
26880d71f708SJon Doron     type_register_static(&vmbus_bridge_type_info);
26890d71f708SJon Doron     type_register_static(&vmbus_dev_type_info);
26900d71f708SJon Doron     type_register_static(&vmbus_type_info);
26910d71f708SJon Doron }
26920d71f708SJon Doron 
26930d71f708SJon Doron type_init(vmbus_register_types)
2694