root/usr/src/uts/common/io/virtio/virtio_spec.h
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2019 Joyent, Inc.
 * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
 * Copyright 2025 Oxide Computer Company
 */

#ifndef _VIRTIO_SPEC_H
#define _VIRTIO_SPEC_H

/*
 * VIRTIO FRAMEWORK: DEFINITIONS from the VirtIO specification
 *
 * For design and usage documentation, see the comments in "virtio.h".
 *
 * NOTE: Client drivers should not use definitions from this file.
 */

#include <sys/types.h>
#include <sys/dditypes.h>
#include <sys/list.h>
#include <sys/ccompile.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct virtio_vq_desc virtio_vq_desc_t;
typedef struct virtio_vq_driver virtio_vq_driver_t;
typedef struct virtio_vq_device virtio_vq_device_t;
typedef struct virtio_vq_elem virtio_vq_elem_t;

#define VIRTIO_MIN_MODERN_DEVID         0x1040

/*
 * PCI capability types as defined by the specification.
 */
typedef enum virtio_pci_cap_type {
        VPC_COMMON_CFG =                1,
        VPC_NOTIFY_CFG =                2,
        VPC_ISR_CFG =                   3,
        VPC_DEVICE_CFG =                4,
        VPC_PCI_CFG =                   5,
} virtio_pci_cap_type_t;

/*
 * PACKED STRUCTS FOR DEVICE ACCESS
 */

struct virtio_vq_desc {
        /*
         * Buffer physical address and length.
         */
        uint64_t                        vqd_addr;
        uint32_t                        vqd_len;

        /*
         * Flags.  Use with the VIRTQ_DESC_F_* family of constants.  See below.
         */
        uint16_t                        vqd_flags;

        /*
         * If VIRTQ_DESC_F_NEXT is set in flags, this refers to the next
         * descriptor in the chain by table index.
         */
        uint16_t                        vqd_next;
} __packed;

/*
 * VIRTIO DESCRIPTOR FLAGS (vqd_flags)
 */

/*
 * NEXT:
 *      Signals that this descriptor (direct or indirect) is part of a chain.
 *      If populated, "vqd_next" names the next descriptor in the chain by its
 *      table index.
 */
#define VIRTQ_DESC_F_NEXT               (1 << 0)

/*
 * WRITE:
 *      Determines whether this buffer is to be written by the device (WRITE is
 *      set) or by the driver (WRITE is not set).
 */
#define VIRTQ_DESC_F_WRITE              (1 << 1)

/*
 * INDIRECT:
 *      This bit signals that a direct descriptor refers to an indirect
 *      descriptor list, rather than directly to a buffer.  This bit may only
 *      be used in a direct descriptor; indirect descriptors are not allowed to
 *      refer to additional layers of indirect tables.  If this bit is set,
 *      NEXT must be clear; indirect descriptors may not be chained.
 */
#define VIRTQ_DESC_F_INDIRECT           (1 << 2)

/*
 * This structure is variously known as the "available" or "avail" ring, or the
 * driver-owned portion of the queue structure.  It is used by the driver to
 * submit descriptor chains to the device.
 */
struct virtio_vq_driver {
        uint16_t                        vqdr_flags;
        uint16_t                        vqdr_index;
        uint16_t                        vqdr_ring[];
} __packed;

#define VIRTQ_AVAIL_F_NO_INTERRUPT      (1 << 0)

/*
 * We use the sizeof operator on this packed struct to calculate the offset of
 * subsequent structs.  Ensure the compiler is not adding any padding to the
 * end of the struct.
 */
CTASSERT(sizeof (virtio_vq_driver_t) ==
    offsetof(virtio_vq_driver_t, vqdr_ring));

struct virtio_vq_elem {
        /*
         * The device returns chains of descriptors by specifying the table
         * index of the first descriptor in the chain.
         */
        uint32_t                        vqe_start;
        uint32_t                        vqe_len;
} __packed;

/*
 * This structure is variously known as the "used" ring, or the device-owned
 * portion of the queue structure.  It is used by the device to return
 * completed descriptor chains to the driver.
 */
struct virtio_vq_device {
        uint16_t                        vqde_flags;
        uint16_t                        vqde_index;
        virtio_vq_elem_t                vqde_ring[];
} __packed;

#define VIRTQ_USED_F_NO_NOTIFY          (1 << 0)

/*
 * BASIC CONFIGURATION
 *
 * Legacy devices expose both their generic and their device-specific
 * configuration through PCI BAR0.
 */
#define VIRTIO_LEGACY_BAR               0

/*
 * These are offsets into the base configuration space available through the
 * virtio_lget*() and virtio_lput*() family of functions.  These offsets are for
 * what the specification describes as the "legacy" mode of device operation.
 */
#define VIRTIO_LEGACY_FEATURES_DEVICE   0x00    /* 32 R   */
#define VIRTIO_LEGACY_FEATURES_DRIVER   0x04    /* 32 R/W */
#define VIRTIO_LEGACY_QUEUE_ADDRESS     0x08    /* 32 R/W */
#define VIRTIO_LEGACY_QUEUE_SIZE        0x0C    /* 16 R   */
#define VIRTIO_LEGACY_QUEUE_SELECT      0x0E    /* 16 R/W */
#define VIRTIO_LEGACY_QUEUE_NOTIFY      0x10    /* 16 R/W */
#define VIRTIO_LEGACY_DEVICE_STATUS     0x12    /* 8  R/W */
#define VIRTIO_LEGACY_ISR_STATUS        0x13    /* 8  R   */

#define VIRTIO_LEGACY_MSIX_CONFIG       0x14    /* 16 R/W */
#define VIRTIO_LEGACY_MSIX_QUEUE        0x16    /* 16 R/W */

#define VIRTIO_LEGACY_CFG_OFFSET        (VIRTIO_LEGACY_ISR_STATUS + 1)
#define VIRTIO_LEGACY_CFG_OFFSET_MSIX   (VIRTIO_LEGACY_MSIX_QUEUE + 2)

#define VIRTIO_LEGACY_MSI_NO_VECTOR     0xFFFF

/*
 * These are offsets into the common configuration space available through the
 * virtio_cmnget*() and virtio_cmnput*() family of functions. These offsets are
 * for what the specification describes as the "modern" mode of device
 * operation.
 */
#define VIRTIO_MODERN_COMMON_DFSELECT           0x00    /* 32 R/W */
#define VIRTIO_MODERN_COMMON_DF                 0x04    /* 32 R   */
#define VIRTIO_MODERN_COMMON_GFSELECT           0x08    /* 32 R/W */
#define VIRTIO_MODERN_COMMON_GF                 0x0C    /* 32 R/W */
#define VIRTIO_MODERN_COMMON_MSIX               0x10    /* 16 R/W */
#define VIRTIO_MODERN_COMMON_NUMQ               0x12    /* 16 R   */
#define VIRTIO_MODERN_COMMON_STATUS             0x14    /* 8  R/W */
#define VIRTIO_MODERN_COMMON_CFGGENERATION      0x15    /* 8  R   */
#define VIRTIO_MODERN_COMMON_Q_SELECT           0x16    /* 16 R/W */
#define VIRTIO_MODERN_COMMON_Q_SIZE             0x18    /* 16 R/W */
#define VIRTIO_MODERN_COMMON_Q_MSIX             0x1a    /* 16 R/W */
#define VIRTIO_MODERN_COMMON_Q_ENABLE           0x1c    /* 16 R/W */
#define VIRTIO_MODERN_COMMON_Q_NOFF             0x1e    /* 16 R   */
#define VIRTIO_MODERN_COMMON_Q_DESCLO           0x20    /* 32 R/W */
#define VIRTIO_MODERN_COMMON_Q_DESCHI           0x24    /* 32 R/W */
#define VIRTIO_MODERN_COMMON_Q_AVAILLO          0x28    /* 32 R/W */
#define VIRTIO_MODERN_COMMON_Q_AVAILHI          0x2c    /* 32 R/W */
#define VIRTIO_MODERN_COMMON_Q_USEDLO           0x30    /* 32 R/W */
#define VIRTIO_MODERN_COMMON_Q_USEDHI           0x34    /* 32 R/W */

/*
 * Offsets into a VirtIO PCI capability:
 */
#define VIRTIO_PCI_CAP_LEN              2       /* 8  R   */
#define VIRTIO_PCI_CAP_TYPE             3       /* 8  R   */
#define VIRTIO_PCI_CAP_BAR              4       /* 8  R   */
#define VIRTIO_PCI_CAP_ID               5       /* 32 R   */
#define VIRTIO_PCI_CAP_BAROFF           8       /* 32 R   */
#define VIRTIO_PCI_CAP_BARLEN           12      /* 32 R   */
#define VIRTIO_PCI_CAP_MULTIPLIER       16      /* 32 R  ; NOTIFY CFG ONLY */

/*
 * Bits in the Device Status byte:
 */
#define VIRTIO_STATUS_RESET             0
#define VIRTIO_STATUS_ACKNOWLEDGE       (1 << 0)
#define VIRTIO_STATUS_DRIVER            (1 << 1)
#define VIRTIO_STATUS_DRIVER_OK         (1 << 2)
#define VIRTIO_STATUS_FEAT_OK           (1 << 3)
#define VIRTIO_STATUS_DEV_NEEDS_RESET   (1 << 6)
#define VIRTIO_STATUS_FAILED            (1 << 7)

/*
 * Bits in the Interrupt Service Routine Status byte:
 */
#define VIRTIO_ISR_CHECK_QUEUES         (1 << 0)
#define VIRTIO_ISR_CHECK_CONFIG         (1 << 1)

/*
 * Bits in the Features fields:
 */
#define VIRTIO_F_ANY_LAYOUT             (1ULL << 27)    /* Legacy only */
#define VIRTIO_F_RING_INDIRECT_DESC     (1ULL << 28)
#define VIRTIO_F_VERSION_1              (1ULL << 32)

/*
 * For devices operating in the legacy mode, virtqueues must be aligned on a
 * "page size" of 4096 bytes; this is also called the "Queue Align" value in
 * newer versions of the specification.
 */
#define VIRTIO_PAGE_SHIFT               12
#define VIRTIO_PAGE_SIZE                (1 << VIRTIO_PAGE_SHIFT)
CTASSERT(VIRTIO_PAGE_SIZE == 4096);
CTASSERT(ISP2(VIRTIO_PAGE_SIZE));

/*
 * For devices operating via the modern interface, the virtqueue layout is more
 * flexible with each component having different alignment requirements.
 */
#define MODERN_VQ_ALIGN_DESC            16
#define MODERN_VQ_ALIGN_DRIVER          2
#define MODERN_VQ_ALIGN_DEVICE          4

#ifdef __cplusplus
}
#endif

#endif /* _VIRTIO_SPEC_H */