/** @file

  Generic type and macro definitions corresponding to the virtio-0.9.5
  specification.

  Copyright (C) 2012-2016, Red Hat, Inc.
  Portion of Copyright (C) 2013, ARM Ltd.

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#ifndef _VIRTIO_0_9_5_H_
#define _VIRTIO_0_9_5_H_

#include <Base.h>

//
// VirtIo Subsystem Device IDs
//
#define VIRTIO_SUBSYSTEM_NETWORK_CARD       1
#define VIRTIO_SUBSYSTEM_BLOCK_DEVICE       2
#define VIRTIO_SUBSYSTEM_CONSOLE            3
#define VIRTIO_SUBSYSTEM_ENTROPY_SOURCE     4
#define VIRTIO_SUBSYSTEM_MEMORY_BALLOONING  5
#define VIRTIO_SUBSYSTEM_IO_MEMORY          6
#define VIRTIO_SUBSYSTEM_RPMSG              7
#define VIRTIO_SUBSYSTEM_SCSI_HOST          8
#define VIRTIO_SUBSYSTEM_9P_TRANSPORT       9
#define VIRTIO_SUBSYSTEM_MAC80211_WLAN      10

//
// Virtio IDs
//
#define VIRTIO_VENDOR_ID                    0x1AF4
#define VIRTIO_MMIO_MAGIC                   0x74726976 // "virt"


//
// VirtIo Device Specific Configuration Offsets
//
#define VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_PCI             20
#define VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_PCI_WITH_MSI_X  24
#define VIRTIO_DEVICE_SPECIFIC_CONFIGURATION_OFFSET_MMIO            0x100

//
// PCI VirtIo Header Offsets
//
#define VIRTIO_PCI_OFFSET_DEVICE_FEATURES           0x00
#define VIRTIO_PCI_OFFSET_GUEST_FEATURES            0x04
#define VIRTIO_PCI_OFFSET_QUEUE_ADDRESS             0x08
#define VIRTIO_PCI_OFFSET_QUEUE_SIZE                0x0C
#define VIRTIO_PCI_OFFSET_QUEUE_SELECT              0x0E
#define VIRTIO_PCI_OFFSET_QUEUE_NOTIFY              0x10
#define VIRTIO_PCI_OFFSET_QUEUE_DEVICE_STATUS       0x12
#define VIRTIO_PCI_OFFSET_QUEUE_DEVICE_ISR          0x13

//
// MMIO VirtIo Header Offsets
//
#define VIRTIO_MMIO_OFFSET_MAGIC                    0x00
#define VIRTIO_MMIO_OFFSET_VERSION                  0x04
#define VIRTIO_MMIO_OFFSET_DEVICE_ID                0x08
#define VIRTIO_MMIO_OFFSET_VENDOR_ID                0x0C
#define VIRTIO_MMIO_OFFSET_HOST_FEATURES            0x10
#define VIRTIO_MMIO_OFFSET_HOST_FEATURES_SEL        0x14
#define VIRTIO_MMIO_OFFSET_GUEST_FEATURES           0x20
#define VIRTIO_MMIO_OFFSET_GUEST_FEATURES_SEL       0x24
#define VIRTIO_MMIO_OFFSET_GUEST_PAGE_SIZE          0x28
#define VIRTIO_MMIO_OFFSET_QUEUE_SEL                0x30
#define VIRTIO_MMIO_OFFSET_QUEUE_NUM_MAX            0x34
#define VIRTIO_MMIO_OFFSET_QUEUE_NUM                0x38
#define VIRTIO_MMIO_OFFSET_QUEUE_ALIGN              0x3C
#define VIRTIO_MMIO_OFFSET_QUEUE_PFN                0x40
#define VIRTIO_MMIO_OFFSET_QUEUE_NOTIFY             0x50
#define VIRTIO_MMIO_OFFSET_INTERRUPT_STATUS         0x60
#define VIRTIO_MMIO_OFFSET_INTERRUPT_ACK            0x64
#define VIRTIO_MMIO_OFFSET_STATUS                   0x70

//
// Data in the communication area is defined as packed and accessed as
// volatile.
//
// Some structures contain arrays with dynamically determined size. In such
// cases the array and its sibling fields are replaced with pointers.
//
// All indices (variables and fields named *Idx) are free-running and wrap
// around after 0xFFFF. The queue size reported by the host is always an
// integral power of 2, not greater than 32768. Actual array indices are
// consistently calculated by taking the remainder of a given Idx object modulo
// QueueSize. Since 0x10000 is an integral multiple of the QueueSize, UINT16
// wraparound is a correct wraparound modulo QueueSize too (it doesn't offset
// the remainder class).
//
// virtio-0.9.5, 2.3.4 Available Ring
//
#define VRING_AVAIL_F_NO_INTERRUPT BIT0

typedef struct {
  volatile UINT16 *Flags;
  volatile UINT16 *Idx;

  volatile UINT16 *Ring;      // QueueSize elements
  volatile UINT16 *UsedEvent; // unused as per negotiation
} VRING_AVAIL;


//
// virtio-0.9.5, 2.3.5 Used Ring
//
#define VRING_USED_F_NO_NOTIFY BIT0

#pragma pack(1)
typedef struct {
  UINT32 Id;
  UINT32 Len;
} VRING_USED_ELEM;
#pragma pack()

typedef struct {
  volatile UINT16          *Flags;
  volatile UINT16          *Idx;
  volatile VRING_USED_ELEM *UsedElem;   // QueueSize elements
  volatile UINT16          *AvailEvent; // unused as per negotiation
} VRING_USED;


//
// virtio-0.9.5, 2.3.2 Descriptor Table
//
#define VRING_DESC_F_NEXT     BIT0 // more descriptors in this request
#define VRING_DESC_F_WRITE    BIT1 // buffer to be written *by the host*
#define VRING_DESC_F_INDIRECT BIT2 // unused

#pragma pack(1)
typedef struct {
  UINT64 Addr;
  UINT32 Len;
  UINT16 Flags;
  UINT16 Next;
} VRING_DESC;
#pragma pack()

typedef struct {
  UINTN               NumPages;
  VOID                *Base;     // deallocate only this field
  volatile VRING_DESC *Desc;     // QueueSize elements
  VRING_AVAIL         Avail;
  VRING_USED          Used;
  UINT16              QueueSize;
} VRING;

//
// virtio-0.9.5, 2.2.2.1 Device Status
//
#define VSTAT_ACK       BIT0
#define VSTAT_DRIVER    BIT1
#define VSTAT_DRIVER_OK BIT2
#define VSTAT_FAILED    BIT7

//
// virtio-0.9.5, Appendix B: Reserved (Device-Independent) Feature Bits
//
#define VIRTIO_F_NOTIFY_ON_EMPTY    BIT24
#define VIRTIO_F_RING_INDIRECT_DESC BIT28
#define VIRTIO_F_RING_EVENT_IDX     BIT29


#endif // _VIRTIO_0_9_5_H_
