/* $Id: Virtio.h $ */ /** @file * Virtio.h - Virtio Declarations */ /* * Copyright (C) 2009-2020 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ #ifndef VBOX_INCLUDED_SRC_VirtIO_Virtio_h #define VBOX_INCLUDED_SRC_VirtIO_Virtio_h #ifndef RT_WITHOUT_PRAGMA_ONCE # pragma once #endif #include /** Pointer to the core shared state of a VirtIO PCI device */ typedef struct VPCISTATE *PVPCISTATE; /** Pointer to the core ring-3 state of a VirtIO PCI device */ typedef struct VPCISTATER3 *PVPCISTATER3; /** Pointer to the core ring-0 state of a VirtIO PCI device */ typedef struct VPCISTATER0 *PVPCISTATER0; /** Pointer to the core raw-mode state of a VirtIO PCI device */ typedef struct VPCISTATERC *PVPCISTATERC; /** Pointer to the core current context state of a VirtIO PCI device */ typedef CTX_SUFF(PVPCISTATE) PVPCISTATECC; /** The core current context state of a VirtIO PCI device */ typedef struct CTX_SUFF(VPCISTATE) VPCISTATECC; /** @name Saved state versions. * The saved state version is changed if either common or any of specific * parts are changed. That is, it is perfectly possible that the version * of saved vnet state will increase as a result of change in vblk structure * for example. */ #define VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1 1 #define VIRTIO_SAVEDSTATE_VERSION 2 /** @} */ #define DEVICE_PCI_VENDOR_ID 0x1AF4 #define DEVICE_PCI_BASE_ID 0x1000 #define DEVICE_PCI_SUBSYSTEM_VENDOR_ID 0x1AF4 #define DEVICE_PCI_SUBSYSTEM_BASE_ID 1 #define VIRTIO_MAX_NQUEUES 3 #define VPCI_HOST_FEATURES 0x0 #define VPCI_GUEST_FEATURES 0x4 #define VPCI_QUEUE_PFN 0x8 #define VPCI_QUEUE_NUM 0xC #define VPCI_QUEUE_SEL 0xE #define VPCI_QUEUE_NOTIFY 0x10 #define VPCI_STATUS 0x12 #define VPCI_ISR 0x13 #define VPCI_CONFIG 0x14 #define VPCI_ISR_QUEUE 0x1 #define VPCI_ISR_CONFIG 0x3 #define VPCI_STATUS_ACK 0x01 #define VPCI_STATUS_DRV 0x02 #define VPCI_STATUS_DRV_OK 0x04 #define VPCI_STATUS_FAILED 0x80 #define VPCI_F_NOTIFY_ON_EMPTY UINT32_C(0x01000000) #define VPCI_F_ANY_LAYOUT UINT32_C(0x08000000) #define VPCI_F_RING_INDIRECT_DESC UINT32_C(0x10000000) #define VPCI_F_RING_EVENT_IDX UINT32_C(0x20000000) #define VPCI_F_BAD_FEATURE UINT32_C(0x40000000) #define VRINGDESC_MAX_SIZE (2 * 1024 * 1024) #define VRINGDESC_F_NEXT 0x01 #define VRINGDESC_F_WRITE 0x02 #define VRINGDESC_F_INDIRECT 0x04 typedef struct VRINGDESC { uint64_t u64Addr; uint32_t uLen; uint16_t u16Flags; uint16_t u16Next; } VRINGDESC; typedef VRINGDESC *PVRINGDESC; #define VRINGAVAIL_F_NO_INTERRUPT 0x01 typedef struct VRINGAVAIL { uint16_t uFlags; uint16_t uNextFreeIndex; uint16_t auRing[1]; } VRINGAVAIL; typedef struct VRINGUSEDELEM { uint32_t uId; uint32_t uLen; } VRINGUSEDELEM; #define VRINGUSED_F_NO_NOTIFY 0x01 typedef struct VRingUsed { uint16_t uFlags; uint16_t uIndex; VRINGUSEDELEM aRing[1]; } VRINGUSED; typedef VRINGUSED *PVRINGUSED; #define VRING_MAX_SIZE 1024 typedef struct VRING { uint16_t uSize; uint16_t padding[3]; RTGCPHYS addrDescriptors; RTGCPHYS addrAvail; RTGCPHYS addrUsed; } VRING; typedef VRING *PVRING; typedef struct VQUEUE { VRING VRing; uint16_t uNextAvailIndex; uint16_t uNextUsedIndex; uint32_t uPageNumber; char szName[16]; } VQUEUE; typedef VQUEUE *PVQUEUE; /** * Queue callback (consumer?). * * @param pDevIns The device instance. * @param pQueue Pointer to the queue structure. */ typedef DECLCALLBACK(void) FNVPCIQUEUECALLBACK(PPDMDEVINS pDevIns, PVQUEUE pQueue); /** Pointer to a VQUEUE callback function. */ typedef FNVPCIQUEUECALLBACK *PFNVPCIQUEUECALLBACK; typedef struct VQUEUER3 { R3PTRTYPE(PFNVPCIQUEUECALLBACK) pfnCallback; } VQUEUER3; typedef VQUEUER3 *PVQUEUER3; typedef struct VQUEUESEG { RTGCPHYS addr; void *pv; uint32_t cb; } VQUEUESEG; typedef struct VQUEUEELEM { uint32_t uIndex; uint32_t cIn; uint32_t cOut; VQUEUESEG aSegsIn[VRING_MAX_SIZE]; VQUEUESEG aSegsOut[VRING_MAX_SIZE]; } VQUEUEELEM; typedef VQUEUEELEM *PVQUEUEELEM; enum VirtioDeviceType { VIRTIO_NET_ID = 0, VIRTIO_BLK_ID = 1, VIRTIO_32BIT_HACK = 0x7fffffff }; /** * The core shared state of a VirtIO PCI device */ typedef struct VPCISTATE { PDMCRITSECT cs; /**< Critical section - what is it protecting? */ /** Read-only part, never changes after initialization. */ char szInstance[8]; /**< Instance name, e.g. VNet#1. */ /* Read/write part, protected with critical section. */ /** Status LED. */ PDMLED led; uint32_t uGuestFeatures; uint16_t uQueueSelector; /**< An index in aQueues array. */ uint8_t uStatus; /**< Device Status (bits are device-specific). */ uint8_t uISR; /**< Interrupt Status Register. */ /** Number of queues actually used. */ uint32_t cQueues; uint32_t u32Padding; /** Shared queue data. */ VQUEUE Queues[VIRTIO_MAX_NQUEUES]; STAMCOUNTER StatIntsRaised; STAMCOUNTER StatIntsSkipped; #ifdef VBOX_WITH_STATISTICS STAMPROFILEADV StatIOReadR3; STAMPROFILEADV StatIOReadR0; STAMPROFILEADV StatIOReadRC; STAMPROFILEADV StatIOWriteR3; STAMPROFILEADV StatIOWriteR0; STAMPROFILEADV StatIOWriteRC; STAMPROFILE StatCsR3; STAMPROFILE StatCsR0; STAMPROFILE StatCsRC; #endif } VPCISTATE; /** * The core ring-3 state of a VirtIO PCI device * * @implements PDMILEDPORTS */ typedef struct VPCISTATER3 { /** Status LUN: Base interface. */ PDMIBASE IBase; /** Status LUN: LED port interface. */ PDMILEDPORTS ILeds; /** Status LUN: LED connector (peer). */ R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector; /** Pointer to the shared state. */ R3PTRTYPE(PVPCISTATE) pShared; /** Ring-3 per-queue data. */ VQUEUER3 Queues[VIRTIO_MAX_NQUEUES]; } VPCISTATER3; /** * The core ring-0 state of a VirtIO PCI device */ typedef struct VPCISTATER0 { uint64_t uUnused; } VPCISTATER0; /** * The core raw-mode state of a VirtIO PCI device */ typedef struct VPCISTATERC { uint64_t uUnused; } VPCISTATERC; /** @name VirtIO port I/O callbacks. * @{ */ typedef struct VPCIIOCALLBACKS { DECLCALLBACKMEMBER(uint32_t, pfnGetHostFeatures)(PVPCISTATE pVPciState); DECLCALLBACKMEMBER(uint32_t, pfnGetHostMinimalFeatures)(PVPCISTATE pVPciState); DECLCALLBACKMEMBER(void, pfnSetHostFeatures)(PVPCISTATE pVPciState, uint32_t fFeatures); DECLCALLBACKMEMBER(int, pfnGetConfig)(PVPCISTATE pVPciState, uint32_t offCfg, uint32_t cb, void *pvData); DECLCALLBACKMEMBER(int, pfnSetConfig)(PVPCISTATE pVPciState, uint32_t offCfg, uint32_t cb, void *pvData); DECLCALLBACKMEMBER(int, pfnReset)(PPDMDEVINS pDevIns); DECLCALLBACKMEMBER(void, pfnReady)(PPDMDEVINS pDevIns); } VPCIIOCALLBACKS; /** Pointer to a const VirtIO port I/O callback structure. */ typedef const VPCIIOCALLBACKS *PCVPCIIOCALLBACKS; /** @} */ int vpciR3Init(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVPCISTATECC pThisCC, uint16_t uDeviceId, uint16_t uClass, uint32_t cQueues); int vpciRZInit(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVPCISTATECC pThisCC); int vpciR3Term(PPDMDEVINS pDevIns, PVPCISTATE pThis); PVQUEUE vpciR3AddQueue(PVPCISTATE pThis, PVPCISTATECC pThisCC, unsigned uSize, PFNVPCIQUEUECALLBACK pfnCallback, const char *pcszName); void *vpciR3QueryInterface(PVPCISTATECC pThisCC, const char *pszIID); void vpciR3SetWriteLed(PVPCISTATE pThis, bool fOn); void vpciR3SetReadLed(PVPCISTATE pThis, bool fOn); int vpciR3SaveExec(PCPDMDEVHLPR3 pHlp, PVPCISTATE pThis, PSSMHANDLE pSSM); int vpciR3LoadExec(PCPDMDEVHLPR3 pHlp, PVPCISTATE pThis, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass, uint32_t cQueues); void vpciR3DumpStateWorker(PVPCISTATE pThis, PCDBGFINFOHLP pHlp); void vpciReset(PPDMDEVINS pDevIns, PVPCISTATE pThis); int vpciRaiseInterrupt(PPDMDEVINS pDevIns, PVPCISTATE pThis, int rcBusy, uint8_t u8IntCause); int vpciIOPortIn(PPDMDEVINS pDevIns, PVPCISTATE pThis, RTIOPORT offPort, uint32_t *pu32, unsigned cb,PCVPCIIOCALLBACKS pCallbacks); int vpciIOPortOut(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVPCISTATECC pThisCC, RTIOPORT offPort, uint32_t u32, unsigned cb, PCVPCIIOCALLBACKS pCallbacks); #define VPCI_CS DECLINLINE(int) vpciCsEnter(PPDMDEVINS pDevIns, PVPCISTATE pThis, int rcBusy) { #ifdef VPCI_CS STAM_PROFILE_START(&pThis->CTX_SUFF(StatCs), a); int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->cs, rcBusy); STAM_PROFILE_STOP(&pThis->CTX_SUFF(StatCs), a); return rc; #else RT_NOREF(pDevIns, pThis, rcBusy); return VINF_SUCCESS; #endif } DECLINLINE(void) vpciCsLeave(PPDMDEVINS pDevIns, PVPCISTATE pThis) { #ifdef VPCI_CS PDMDevHlpCritSectLeave(pDevIns, &pThis->cs); #endif } void vringSetNotification(PPDMDEVINS pDevIns, PVRING pVRing, bool fEnabled); DECLINLINE(uint16_t) vringReadAvailIndex(PPDMDEVINS pDevIns, PVRING pVRing) { uint16_t idx = 0; PDMDevHlpPhysRead(pDevIns, pVRing->addrAvail + RT_UOFFSETOF(VRINGAVAIL, uNextFreeIndex), &idx, sizeof(idx)); return idx; } bool vqueueSkip(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue); bool vqueueGet(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue, PVQUEUEELEM pElem, bool fRemove = true); void vqueuePut(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue, PVQUEUEELEM pElem, uint32_t uLen, uint32_t uReserved = 0); void vqueueSync(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue); DECLINLINE(bool) vqueuePeek(PPDMDEVINS pDevIns, PVPCISTATE pThis, PVQUEUE pQueue, PVQUEUEELEM pElem) { return vqueueGet(pDevIns, pThis, pQueue, pElem, /* fRemove */ false); } DECLINLINE(bool) vqueueIsReady(PVQUEUE pQueue) { return !!pQueue->VRing.addrAvail; } DECLINLINE(bool) vqueueIsEmpty(PPDMDEVINS pDevIns, PVQUEUE pQueue) { return vringReadAvailIndex(pDevIns, &pQueue->VRing) == pQueue->uNextAvailIndex; } #endif /* !VBOX_INCLUDED_SRC_VirtIO_Virtio_h */