Files
yttrium-virtio-gpu/NetKVM/DebugTools/VirtioConsoleSimulation/engine.c
T

521 lines
11 KiB
C

#include "osdep.h"
#include "VirtIO_Win.h"
#include "testcommands.h"
#include "IONetDescriptor.h"
#include "Hardware\Hardware.h"
#define VIRTIO_NET_INVALID_INTERRUPT_STATUS 0xFF
#define MAX_RX_PACKET 1024
#define MAX_TX_PACKET 1024
BOOLEAN bUseMergedBuffers = TRUE;
BOOLEAN bUsePublishedIndices = TRUE;
BOOLEAN bHostHasVnetHdr = TRUE;
BOOLEAN bVirtioF_NotifyOnEmpty = FALSE;
BOOLEAN bAsyncTransmit = FALSE;
BOOLEAN bUseIndirectTx = FALSE;
int debugLevel = 4;
typedef struct
{
virtio_net_hdr_basic header;
UCHAR buffer[MAX_RX_PACKET];
} tRxPacketBasic;
typedef struct
{
virtio_net_hdr_ext extheader;
UCHAR buffer[MAX_RX_PACKET];
} tRxPacketExt;
typedef struct
{
union
{
tRxPacketBasic basic;
tRxPacketExt ext;
};
ULONG serial;
} tRxPacket;
typedef struct
{
virtio_net_hdr_ext header;
ULONG serial;
UCHAR buffer[MAX_TX_PACKET];
void *storageForIndirect;
} tTxPacket;
typedef struct
{
VirtIODevice dev;
ULONG hostFeatures;
} tHost;
typedef struct
{
PVOID originalAddress;
PVOID hardwareDevice;
// USHORT queueSizes[4];
UCHAR queueSelect;
} tDevice, *ptDevice;
static void DeviceReset(tDevice *device)
{
device->queueSelect = 0;
}
u32 ReadVirtIODeviceRegister(ULONG_PTR ulRegister)
{
u32 val = 0xffffffff;
ULONG reg = ulRegister & 0x1f;
tDevice *device = (tDevice *)(ulRegister - reg);
switch (reg)
{
case VIRTIO_PCI_QUEUE_PFN:
val = hwGetQueuePfn(device->hardwareDevice, device->queueSelect);
break;
default:
FailCase("%s(%d) - not supported", __FUNCTION__, reg);
break;
}
DPrintf(0, ("DW[%d] => %x\n", reg, val));
return val;
}
void WriteVirtIODeviceRegister(ULONG_PTR ulRegister, u32 ulValue)
{
ULONG reg = ulRegister & 0x1f;
tDevice *device = (tDevice *)(ulRegister - reg);
DPrintf(0, ("DW %x => %d\n", ulValue, reg));
switch (reg)
{
case VIRTIO_PCI_QUEUE_PFN:
hwSetQueuePfn(device->hardwareDevice, device->queueSelect, ulValue);
break;
default:
FailCase("%s(%d) - not supported", __FUNCTION__, reg);
break;
}
}
u8 ReadVirtIODeviceByte(ULONG_PTR ulRegister)
{
UCHAR val = 0xff;
ULONG reg = ulRegister & 0x1f;
tDevice *device = (tDevice *)(ulRegister - reg);
switch (reg)
{
case VIRTIO_PCI_ISR:
val = hwReadInterruptStatus(device->hardwareDevice);
break;
case VIRTIO_PCI_STATUS:
val = hwGetDeviceStatus(device->hardwareDevice);
break;
default:
FailCase("%s(%d) - not supported", __FUNCTION__, reg);
break;
}
DPrintf(0, ("B[%d] => %x\n", reg, val));
return val;
}
void WriteVirtIODeviceByte(ULONG_PTR ulRegister, u8 bValue)
{
ULONG reg = ulRegister & 0x1f;
tDevice *device = (tDevice *)(ulRegister - reg);
DPrintf(0, ("B %x => %d\n", bValue, reg));
switch (reg)
{
case VIRTIO_PCI_STATUS:
hwSetDeviceStatus(device->hardwareDevice, bValue);
if (bValue) DeviceReset(device);
break;
default:
FailCase("%s(%d) - not supported", __FUNCTION__, reg);
break;
}
}
u16 ReadVirtIODeviceWord(ULONG_PTR ulRegister)
{
u16 val = 0xffff;
ULONG reg = ulRegister & 0x1f;
tDevice *device = (tDevice *)(ulRegister - reg);
switch (reg)
{
case VIRTIO_PCI_QUEUE_SEL:
val = device->queueSelect;
break;
case VIRTIO_PCI_QUEUE_NUM:
val = hwGetQueueSize(device->hardwareDevice, device->queueSelect);
break;
default:
FailCase("%s(%d) - not supported", __FUNCTION__, reg);
break;
}
DPrintf(0, ("W[%d] => %x\n", reg, val));
return val;
}
void WriteVirtIODeviceWord(ULONG_PTR ulRegister, u16 wValue)
{
ULONG reg = ulRegister & 0x1f;
tDevice *device = (tDevice *)(ulRegister - reg);
DPrintf(0, ("W %x => %d\n", wValue, reg));
switch (reg)
{
case VIRTIO_PCI_QUEUE_SEL:
device->queueSelect = wValue & 3;
break;
case VIRTIO_PCI_QUEUE_NOTIFY:
hwQueueNotify(device->hardwareDevice, wValue);
break;
default:
FailCase("%s(%d) - not supported", __FUNCTION__, reg);
break;
}
}
#if 0
PVOID AllocatePhysical(ULONG size)
{
ULONG_PTR addr, base;
PVOID pRet, p = malloc(size + 2 * PAGE_SIZE);
DPrintf(0, ("asked for %d, allocated %p\n", size, p));
addr = (ULONG_PTR)p;
base = (addr + PAGE_SIZE) & ~(PAGE_SIZE - 1);
*(PVOID *)base = p;
pRet = (PVOID)(base + PAGE_SIZE);
DPrintf(0, ("returning %p\n", pRet));
return pRet;
}
static void MmFreeContiguousMemory(PVOID virtualAddress)
{
PVOID actualAddr;
ULONG_PTR addr = (ULONG_PTR)virtualAddress;
DPrintf(0, ("asked to free %p\n", addr));
addr -= PAGE_SIZE;
actualAddr = *(PVOID *)addr;
DPrintf(0, ("freeing %p\n", actualAddr));
free(actualAddr);
}
#else
PVOID AllocatePhysical(ULONG size)
{
ULONG_PTR addr, base;
PVOID pRet, p = malloc(size + 2 * PAGE_SIZE);
DPrintf(0, ("asked for %d, allocated %p\n", size, p));
addr = (ULONG_PTR)p;
base = (addr + PAGE_SIZE) & ~(PAGE_SIZE - 1);
*(PVOID *)base = p;
pRet = (PVOID)(base + PAGE_SIZE);
DPrintf(0, ("returning %p\n", pRet));
return pRet;
}
static void MmFreeContiguousMemory(PVOID virtualAddress)
{
PVOID actualAddr;
ULONG_PTR addr = (ULONG_PTR)virtualAddress;
DPrintf(0, ("asked to free %p\n", addr));
addr -= PAGE_SIZE;
actualAddr = *(PVOID *)addr;
DPrintf(0, ("freeing %p\n", actualAddr));
free(actualAddr);
}
#endif
static PHYSICAL_ADDRESS MmGetPhysicalAddress(PVOID virtualAddress)
{
PHYSICAL_ADDRESS pa;
pa.QuadPart = (UINT_PTR)virtualAddress;
return pa;
}
static void InitializeDevice(tDevice *pDev)
{
pDev->hardwareDevice = hwCreateDevice(pDev);
}
static struct virtqueue *TxQ;
static struct virtqueue *RxQ;
tDevice *pDevice;
static tHost Host;
void GetRxBuffer(PULONG pLenght)
{
UINT len;
ULONG serial = 0;
tRxPacket *p = (tRxPacket *)RxQ->vq_ops->get_buf(RxQ, &len);
if (p)
{
serial = p->serial;
if (bUseMergedBuffers) *pLenght = len - sizeof(p->ext.extheader);
else *pLenght = len - sizeof(p->basic.header);
KeepRxPacket(p, serial);
}
else
{
FailCase("%s", __FUNCTION__);
}
}
static BOOLEAN AddRxBuffer(tRxPacket *pPacket, BOOLEAN bKick)
{
BOOLEAN bOK = TRUE;
if (bUseMergedBuffers)
{
struct VirtIOBufferDescriptor sg;
sg.physAddr.QuadPart = (ULONG_PTR)pPacket;
sg.ulSize = sizeof(pPacket->ext);
bOK = 0 <= RxQ->vq_ops->add_buf(RxQ, &sg, 0, 1, pPacket, NULL, 0);
}
else
{
struct VirtIOBufferDescriptor sg[2];
sg[0].physAddr.QuadPart = (ULONG_PTR)&pPacket->basic.header;
sg[0].ulSize = sizeof(pPacket->basic.header);
sg[1].physAddr.QuadPart = (ULONG_PTR)&pPacket->basic.buffer;
sg[1].ulSize = sizeof(pPacket->basic.buffer);
bOK = 0 <= RxQ->vq_ops->add_buf(RxQ, sg, 0, 2, pPacket, NULL, 0);
}
if (!bOK)
{
LogTestFlow("[%s] Can't add buffer %d\n", __FUNCTION__, pPacket->serial);
}
else if (bKick)
{
RxQ->vq_ops->kick(RxQ);
}
return bOK;
}
void ReturnRxBuffer(ULONG serial)
{
tRxPacket *pPacket = (tRxPacket *)GetRxPacket(serial);
if (pPacket) AddRxBuffer(pPacket, TRUE);
}
static BOOLEAN AddTxBuffer(ULONG serial)
{
BOOLEAN bOK = TRUE;
ULONGLONG hwIndirectAddress = 0;
ULONG headerSize = bUseMergedBuffers ? sizeof(virtio_net_hdr_ext) : sizeof(virtio_net_hdr_basic);
struct VirtIOBufferDescriptor sg[2];
tTxPacket *pPacket = (tTxPacket *)malloc(sizeof(tTxPacket));
memset(pPacket, 0, sizeof(tTxPacket));
pPacket->serial = serial;
sg[0].physAddr.QuadPart = (ULONG_PTR)(PVOID)&pPacket->header;
sg[0].ulSize = headerSize;
sg[1].physAddr.QuadPart = (ULONG_PTR)(PVOID)pPacket->buffer;
sg[1].ulSize = sizeof(pPacket->buffer);
if (bUseIndirectTx)
{
pPacket->storageForIndirect = AllocatePhysical(2 * 16);
hwIndirectAddress = MmGetPhysicalAddress(pPacket->storageForIndirect).QuadPart;
}
if (0 > TxQ->vq_ops->add_buf(TxQ, sg, 2, 0, pPacket, pPacket->storageForIndirect, hwIndirectAddress))
{
free(pPacket);
bOK = FALSE;
}
return bOK;
}
void AddTxBuffers(ULONG startSerial, ULONG num)
{
BOOLEAN bOK = TRUE;
while (num-- && bOK)
{
bOK = AddTxBuffer(startSerial);
if (bOK) startSerial++;
}
if (!bOK) FailCase("[%s] can't add buffer %d", __FUNCTION__, startSerial);
else
TxQ->vq_ops->kick(TxQ);
hwCheckInterrupt(pDevice->hardwareDevice);
}
void GetTxBuffer(ULONG serial)
{
UINT len = 0;
tTxPacket *p = (tTxPacket *)TxQ->vq_ops->get_buf(TxQ, &len);
if (p)
{
if (serial != p->serial)
{
FailCase("[%s] got %d, expected %d", __FUNCTION__, p->serial, serial);
}
else if (!len)
{
//FailCase("[%s] got invalid packet", __FUNCTION__);
}
if (p->storageForIndirect) free(p->storageForIndirect);
free(p);
}
else
{
FailCase("[%s] - no buffer", __FUNCTION__);
}
}
void SimulationPrepare()
{
ULONG i, size, allocSize;
pDevice = (tDevice *)AllocatePhysical(sizeof(tDevice));
memset(pDevice, 0, sizeof(*pDevice));
InitializeDevice(pDevice);
VirtIODeviceInitialize(&Host.dev, (ULONG_PTR)pDevice);
VirtIODeviceQueryQueueAllocation(&Host.dev, 1, &size, &allocSize);
if (allocSize)
{
PVOID p = AllocatePhysical(allocSize);
if (p)
{
PHYSICAL_ADDRESS phys = MmGetPhysicalAddress(p);
TxQ = VirtIODevicePrepareQueue(&Host.dev, 1, phys, p, allocSize, p);
}
}
VirtIODeviceQueryQueueAllocation(&Host.dev, 0, &size, &allocSize);
if (allocSize)
{
PVOID p = AllocatePhysical(allocSize);
if (p)
{
PHYSICAL_ADDRESS phys = MmGetPhysicalAddress(p);
RxQ = VirtIODevicePrepareQueue(&Host.dev, 0, phys, p, allocSize, (PUCHAR)p + 1);
}
}
if (TxQ && RxQ)
{
size = VirtIODeviceGetQueueSize(RxQ);
for (i = 0; i < size; ++i)
{
tRxPacket *pPacket = (tRxPacket *)malloc(sizeof(tRxPacket));
memset(pPacket, 0, sizeof(tRxPacket));
pPacket->serial = i;
if (!AddRxBuffer(pPacket, FALSE))
{
//FailCase("[%s] - filling Rx", __FUNCTION__);
break;
}
}
DPrintf(0, ("added %d blocks\n", i));
RxQ->vq_ops->kick(RxQ);
}
else
{
FailCase("[%s] - queues", __FUNCTION__);
}
}
void SimulationFinish()
{
if (TxQ)
{
TxQ->vq_ops->shutdown(TxQ);
VirtIODeviceDeleteQueue(TxQ, NULL);
}
if (RxQ)
{
PVOID va = NULL;
RxQ->vq_ops->shutdown(RxQ);
VirtIODeviceDeleteQueue(RxQ, &va);
if (va) MmFreeContiguousMemory((PUCHAR)va - 1);
}
if (pDevice)
{
ULONG tx = 0, rx = 0;
hwGetInterrups(pDevice->hardwareDevice, &tx, &rx);
if (tx || rx)
{
LogTestFlow("Interrupts: TX:%d, RX:%d\n", tx, rx);
}
hwDestroyDevice(pDevice->hardwareDevice);
MmFreeContiguousMemory(pDevice);
}
}
void KickTx(void)
{
TxQ->vq_ops->kick(TxQ);
}
void KickTxAlways(void)
{
TxQ->vq_ops->kick_always(TxQ);
}
BOOLEAN TxRestart(void)
{
return TxQ->vq_ops->restart(TxQ);
}
BOOLEAN RxRestart(void)
{
return RxQ->vq_ops->restart(RxQ);
}
void TxEnableInterrupt(BOOLEAN bEnable)
{
TxQ->vq_ops->enable_interrupt(TxQ, bEnable);
}
void RxEnableInterrupt(BOOLEAN bEnable)
{
RxQ->vq_ops->enable_interrupt(RxQ, bEnable);
}
EXTERN_C void RxReceivePacket(UCHAR fill)
{
void *buffer;
ULONG size;
if (!bHostHasVnetHdr)
{
size = MAX_RX_PACKET;
buffer = malloc(MAX_RX_PACKET);
memset(buffer, fill, size);
}
else
{
tRxPacketBasic *pp;
size = sizeof(tRxPacketBasic);
pp = (tRxPacketBasic *)malloc(size);
memset(&pp->header, 0, sizeof(pp->header));
memset(&pp->buffer, fill, sizeof(pp->buffer));
buffer = pp;
}
hwReceiveBuffer(pDevice->hardwareDevice, buffer, size);
free(buffer);
}
EXTERN_C void CompleteTx(int num)
{
hwCompleteTx(pDevice->hardwareDevice, num);
}