root/tools/hv/vmbus_bufring.h
/* SPDX-License-Identifier: BSD-3-Clause */

#ifndef _VMBUS_BUF_H_
#define _VMBUS_BUF_H_

#include <stdbool.h>
#include <stdint.h>

#define __packed   __attribute__((__packed__))
#define unlikely(x)     __builtin_expect(!!(x), 0)

#define ICMSGHDRFLAG_TRANSACTION        1
#define ICMSGHDRFLAG_REQUEST            2
#define ICMSGHDRFLAG_RESPONSE           4

#define IC_VERSION_NEGOTIATION_MAX_VER_COUNT 100
#define ICMSG_HDR (sizeof(struct vmbuspipe_hdr) + sizeof(struct icmsg_hdr))
#define ICMSG_NEGOTIATE_PKT_SIZE(icframe_vercnt, icmsg_vercnt) \
        (ICMSG_HDR + sizeof(struct icmsg_negotiate) + \
         (((icframe_vercnt) + (icmsg_vercnt)) * sizeof(struct ic_version)))

/*
 * Channel packets
 */

/* Channel packet flags */
#define VMBUS_CHANPKT_TYPE_INBAND       0x0006
#define VMBUS_CHANPKT_TYPE_RXBUF        0x0007
#define VMBUS_CHANPKT_TYPE_GPA          0x0009
#define VMBUS_CHANPKT_TYPE_COMP         0x000b

#define VMBUS_CHANPKT_FLAG_NONE         0
#define VMBUS_CHANPKT_FLAG_RC           0x0001  /* report completion */

#define VMBUS_CHANPKT_SIZE_SHIFT        3
#define VMBUS_CHANPKT_SIZE_ALIGN        BIT(VMBUS_CHANPKT_SIZE_SHIFT)
#define VMBUS_CHANPKT_HLEN_MIN          \
        (sizeof(struct vmbus_chanpkt_hdr) >> VMBUS_CHANPKT_SIZE_SHIFT)

/*
 * Buffer ring
 */
struct vmbus_bufring {
        volatile uint32_t windex;
        volatile uint32_t rindex;

        /*
         * Interrupt mask {0,1}
         *
         * For TX bufring, host set this to 1, when it is processing
         * the TX bufring, so that we can safely skip the TX event
         * notification to host.
         *
         * For RX bufring, once this is set to 1 by us, host will not
         * further dispatch interrupts to us, even if there are data
         * pending on the RX bufring.  This effectively disables the
         * interrupt of the channel to which this RX bufring is attached.
         */
        volatile uint32_t imask;

        /*
         * Win8 uses some of the reserved bits to implement
         * interrupt driven flow management. On the send side
         * we can request that the receiver interrupt the sender
         * when the ring transitions from being full to being able
         * to handle a message of size "pending_send_sz".
         *
         * Add necessary state for this enhancement.
         */
        volatile uint32_t pending_send;
        uint32_t reserved1[12];

        union {
                struct {
                        uint32_t feat_pending_send_sz:1;
                };
                uint32_t value;
        } feature_bits;

        /* Pad it to rte_mem_page_size() so that data starts on page boundary */
        uint8_t reserved2[4028];

        /*
         * Ring data starts here + RingDataStartOffset
         * !!! DO NOT place any fields below this !!!
         */
        uint8_t data[];
} __packed;

struct vmbus_br {
        struct vmbus_bufring *vbr;
        uint32_t        dsize;
        uint32_t        windex; /* next available location */
};

struct vmbus_chanpkt_hdr {
        uint16_t        type;   /* VMBUS_CHANPKT_TYPE_ */
        uint16_t        hlen;   /* header len, in 8 bytes */
        uint16_t        tlen;   /* total len, in 8 bytes */
        uint16_t        flags;  /* VMBUS_CHANPKT_FLAG_ */
        uint64_t        xactid;
} __packed;

struct vmbus_chanpkt {
        struct vmbus_chanpkt_hdr hdr;
} __packed;

struct vmbuspipe_hdr {
        unsigned int flags;
        unsigned int msgsize;
} __packed;

struct ic_version {
        unsigned short major;
        unsigned short minor;
} __packed;

struct icmsg_negotiate {
        unsigned short icframe_vercnt;
        unsigned short icmsg_vercnt;
        unsigned int reserved;
        struct ic_version icversion_data[]; /* any size array */
} __packed;

struct icmsg_hdr {
        struct ic_version icverframe;
        unsigned short icmsgtype;
        struct ic_version icvermsg;
        unsigned short icmsgsize;
        unsigned int status;
        unsigned char ictransaction_id;
        unsigned char icflags;
        unsigned char reserved[2];
} __packed;

int rte_vmbus_chan_recv_raw(struct vmbus_br *rxbr, void *data, uint32_t *len);
int rte_vmbus_chan_send(struct vmbus_br *txbr, uint16_t type, void *data,
                        uint32_t dlen, uint32_t flags);
void vmbus_br_setup(struct vmbus_br *br, void *buf, unsigned int blen);
void *vmbus_uio_map(int *fd, int size);

/* Amount of space available for write */
static inline uint32_t vmbus_br_availwrite(const struct vmbus_br *br, uint32_t windex)
{
        uint32_t rindex = br->vbr->rindex;

        if (windex >= rindex)
                return br->dsize - (windex - rindex);
        else
                return rindex - windex;
}

static inline uint32_t vmbus_br_availread(const struct vmbus_br *br)
{
        return br->dsize - vmbus_br_availwrite(br, br->vbr->windex);
}

#endif  /* !_VMBUS_BUF_H_ */