473 lines
15 KiB
HolyC
473 lines
15 KiB
HolyC
// Virtio.HC
|
||
|
||
//
|
||
// PCI virtio I/O registers.
|
||
//
|
||
|
||
#define VIRTIO_PCI_HOST_FEATURES 0 // Features supported by the host
|
||
#define VIRTIO_PCI_GUEST_FEATURES 4 // Features activated by the guest
|
||
#define VIRTIO_PCI_QUEUE_PFN 8 // PFN for the currently selected queue
|
||
#define VIRTIO_PCI_QUEUE_SIZE 12 // Queue size for the currently selected queue
|
||
#define VIRTIO_PCI_QUEUE_SEL 14 // Queue selector
|
||
#define VIRTIO_PCI_QUEUE_NOTIFY 16 // Queue notifier
|
||
#define VIRTIO_PCI_STATUS 18 // Device status register
|
||
#define VIRTIO_PCI_ISR 19 // Interrupt status register
|
||
#define VIRTIO_PCI_CONFIG 20 // Configuration data block
|
||
|
||
//
|
||
// PCI virtio status register bits
|
||
//
|
||
|
||
#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
|
||
#define VIRTIO_CONFIG_S_DRIVER 2
|
||
#define VIRTIO_CONFIG_S_DRIVER_OK 4
|
||
#define VIRTIO_CONFIG_S_FAILED 0x80
|
||
|
||
//
|
||
// Ring descriptor flags
|
||
//
|
||
|
||
#define VRING_DESC_F_NEXT 1 // Buffer continues via the next field
|
||
#define VRING_DESC_F_WRITE 2 // Buffer is write-only (otherwise read-only)
|
||
#define VRING_DESC_F_INDIRECT 4 // Buffer contains a list of buffer descriptors
|
||
|
||
class @virtio_queue_buf
|
||
{
|
||
U64 address;
|
||
U32 length;
|
||
U16 flags;
|
||
U16 next;
|
||
};
|
||
class @virtio_avail
|
||
{
|
||
U16 flags;
|
||
U16 index;
|
||
U16 ring[256];
|
||
U16 int_index;
|
||
};
|
||
class @virtio_used_item
|
||
{
|
||
U32 index;
|
||
U32 length;
|
||
};
|
||
class @virtio_used
|
||
{
|
||
U16 flags;
|
||
U16 index;
|
||
@virtio_used_item ring[256];
|
||
U16 int_index;
|
||
};
|
||
class @virtio_queue
|
||
{
|
||
@virtio_queue_buf buffers[256];
|
||
@virtio_avail available;
|
||
U8 padding[3578];
|
||
@virtio_used used;
|
||
};
|
||
|
||
class @virtio_avail_buf
|
||
{
|
||
U32 index;
|
||
U64 address;
|
||
U32 length;
|
||
};
|
||
|
||
class @virtio_buf_info
|
||
{
|
||
U8* buffer;
|
||
U64 size;
|
||
U8 flags;
|
||
|
||
// If the user wants to keep same buffer as passed in this struct, use "true".
|
||
// otherwise, the supplied buffer will be copied in the queues' buffer
|
||
Bool copy;
|
||
};
|
||
|
||
// Virtio-blk.HC
|
||
|
||
#define BDT_VIRTIO_BLK 10
|
||
|
||
#define VIRTIO_BLK_T_IN 0
|
||
#define VIRTIO_BLK_T_OUT 1
|
||
#define VIRTIO_BLK_T_FLUSH 4
|
||
|
||
#define VIRTIO_BLK_MAX_BLK 0x400000 // Limit blkdev to 2G max, set to NULL to use entire disk (not recommended for RedSea)
|
||
|
||
class @virtio_blk
|
||
{
|
||
U16 port;
|
||
U32 blks;
|
||
@virtio_queue* vq;
|
||
I64 vq_size;
|
||
I64 vq_index;
|
||
U8 status;
|
||
};
|
||
|
||
class @virtio_blk_request
|
||
{
|
||
U32 type;
|
||
U32 priority;
|
||
U64 sector;
|
||
};
|
||
|
||
@virtio_blk virtio_blk;
|
||
MemSet(&virtio_blk, 0, sizeof(@virtio_blk));
|
||
|
||
I64 VirtioBlkInit()
|
||
{
|
||
I64 j;
|
||
|
||
// Scan for device
|
||
j = PCIClassFind(0x010000, 0);
|
||
if (j < 0) {
|
||
"\n[virtio-blk] No device found\n";
|
||
return -1;
|
||
}
|
||
virtio_blk.port = PCIReadU32(j.u8[2],
|
||
j.u8[1], j.u8[0], 0x10)
|
||
& 0xFFFFFFFC;
|
||
|
||
virtio_blk.blks = InU32(virtio_blk.port + VIRTIO_PCI_CONFIG);
|
||
|
||
// Reset Device
|
||
OutU8(virtio_blk.port + VIRTIO_PCI_STATUS, 0);
|
||
|
||
// Found Driver
|
||
OutU8(virtio_blk.port + VIRTIO_PCI_STATUS, InU8(virtio_blk.port + VIRTIO_PCI_STATUS) | VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER);
|
||
|
||
// Set up virt queue
|
||
OutU16(virtio_blk.port + VIRTIO_PCI_QUEUE_SEL, 0);
|
||
virtio_blk.vq_size = InU16(virtio_blk.port + VIRTIO_PCI_QUEUE_SIZE); // 256
|
||
virtio_blk.vq = CAllocAligned(sizeof(@virtio_queue), 4096, slon_mem_task->code_heap);
|
||
OutU32(virtio_blk.port + VIRTIO_PCI_QUEUE_PFN, virtio_blk.vq / 4096);
|
||
|
||
// Init OK
|
||
OutU8(virtio_blk.port + VIRTIO_PCI_STATUS, InU8(virtio_blk.port + VIRTIO_PCI_STATUS) | VIRTIO_CONFIG_S_DRIVER_OK);
|
||
virtio_blk.vq_index = 0;
|
||
}
|
||
|
||
// DskVIO.HC
|
||
|
||
U0 VIOFlush()
|
||
{
|
||
I64 j;
|
||
I64 vq_idx;
|
||
@virtio_blk_request* brq = CAlloc(sizeof(@virtio_blk_request), slon_mem_task);
|
||
brq->type = VIRTIO_BLK_T_FLUSH;
|
||
brq->sector = NULL;
|
||
vq_idx = virtio_blk.vq->available.index % 256;
|
||
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].address = brq;
|
||
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].length = sizeof(@virtio_blk_request);
|
||
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].flags = VRING_DESC_F_NEXT;
|
||
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].next = (virtio_blk.vq_index + 1) % 256;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].address = &virtio_blk.status;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].length = 1;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].flags = VRING_DESC_F_WRITE;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].next = 0;
|
||
virtio_blk.vq->available.ring[vq_idx] = virtio_blk.vq_index % 256;
|
||
virtio_blk.vq_index += 2;
|
||
j = virtio_blk.vq->used.index;
|
||
virtio_blk.vq->available.index++;
|
||
OutU16(virtio_blk.port + VIRTIO_PCI_QUEUE_NOTIFY, 0);
|
||
while (j == virtio_blk.vq->used.index) {
|
||
Yield;
|
||
}
|
||
Free(brq);
|
||
}
|
||
|
||
Bool VIORBlks(CDrv* dv, U8* buf, I64 blk, I64 cnt)
|
||
{
|
||
no_warn dv;
|
||
I64 i, j;
|
||
I64 vq_idx;
|
||
U64 addr;
|
||
@virtio_blk_request* brq = CAlloc(sizeof(@virtio_blk_request), slon_mem_task);
|
||
for (i = 0; i < cnt; i++) {
|
||
brq->type = VIRTIO_BLK_T_IN;
|
||
brq->sector = blk + i;
|
||
vq_idx = virtio_blk.vq->available.index % 256;
|
||
addr = buf + (BLK_SIZE * i);
|
||
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].address = brq;
|
||
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].length = sizeof(@virtio_blk_request);
|
||
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].flags = VRING_DESC_F_NEXT;
|
||
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].next = (virtio_blk.vq_index + 1) % 256;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].address = addr;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].length = BLK_SIZE;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].flags = VRING_DESC_F_WRITE | VRING_DESC_F_NEXT;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].next = (virtio_blk.vq_index + 2) % 256;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].address = &virtio_blk.status;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].length = 1;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].flags = VRING_DESC_F_WRITE;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].next = 0;
|
||
virtio_blk.vq->available.ring[vq_idx] = virtio_blk.vq_index % 256;
|
||
virtio_blk.vq_index += 3;
|
||
j = virtio_blk.vq->used.index;
|
||
virtio_blk.vq->available.index++;
|
||
OutU16(virtio_blk.port + VIRTIO_PCI_QUEUE_NOTIFY, 0);
|
||
while (j == virtio_blk.vq->used.index) {
|
||
Yield;
|
||
}
|
||
}
|
||
Free(brq);
|
||
return TRUE;
|
||
}
|
||
|
||
Bool VIOWBlks(CDrv* dv, U8* buf, I64 blk, I64 cnt)
|
||
{
|
||
no_warn dv;
|
||
I64 i, j;
|
||
I64 vq_idx;
|
||
U64 addr;
|
||
@virtio_blk_request* brq = CAlloc(sizeof(@virtio_blk_request), slon_mem_task);
|
||
for (i = 0; i < cnt; i++) {
|
||
brq->type = VIRTIO_BLK_T_OUT;
|
||
brq->sector = blk + i;
|
||
vq_idx = virtio_blk.vq->available.index % 256;
|
||
addr = buf + (BLK_SIZE * i);
|
||
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].address = brq;
|
||
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].length = sizeof(@virtio_blk_request);
|
||
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].flags = VRING_DESC_F_NEXT;
|
||
virtio_blk.vq->buffers[virtio_blk.vq_index % 256].next = (virtio_blk.vq_index + 1) % 256;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].address = addr;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].length = BLK_SIZE;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].flags = VRING_DESC_F_NEXT;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 1) % 256].next = (virtio_blk.vq_index + 2) % 256;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].address = &virtio_blk.status;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].length = 1;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].flags = VRING_DESC_F_WRITE;
|
||
virtio_blk.vq->buffers[(virtio_blk.vq_index + 2) % 256].next = 0;
|
||
virtio_blk.vq->available.ring[vq_idx] = virtio_blk.vq_index % 256;
|
||
virtio_blk.vq_index += 3;
|
||
j = virtio_blk.vq->used.index;
|
||
virtio_blk.vq->available.index++;
|
||
OutU16(virtio_blk.port + VIRTIO_PCI_QUEUE_NOTIFY, 0);
|
||
while (j == virtio_blk.vq->used.index) {
|
||
Yield;
|
||
}
|
||
}
|
||
Free(brq);
|
||
VIOFlush;
|
||
return TRUE;
|
||
}
|
||
|
||
U0 RedSeaTryInit(CDrv* dv)
|
||
{
|
||
CRedSeaBoot br;
|
||
Bool unlock;
|
||
try {
|
||
unlock = DrvLock(dv);
|
||
BlkRead(dv, &br, dv->drv_offset, 1);
|
||
if (br.signature != MBR_PT_REDSEA || br.signature2 != 0xAA55)
|
||
return;
|
||
dv->fs_type = FSt_REDSEA;
|
||
CallExtStr("RedSeaFreeFreeLst", dv);
|
||
dv->spc = 1;
|
||
dv->size = br.sects;
|
||
dv->data_area = dv->drv_offset + br.bitmap_sects;
|
||
dv->root_clus = br.root_clus;
|
||
dv->fat1 = dv->fat2 = dv->drv_offset + 1;
|
||
CallExtStr("DrvFATBlkAlloc", dv);
|
||
if (unlock)
|
||
DrvUnlock(dv);
|
||
} catch if (unlock)
|
||
DrvUnlock(dv);
|
||
}
|
||
|
||
U8 MountVirtioBlk()
|
||
{ // Mount Virtio-blk device
|
||
CDrv* dv = DrvMakeFreeSlot(DrvNextFreeLet('A'));
|
||
CBlkDev* bd = BlkDevNextFreeSlot(dv->drv_let, BDT_RAM);
|
||
CRedSeaBoot* bs = CAlloc(BLK_SIZE, slon_mem_task);
|
||
bd->max_blk = 512;
|
||
BlkDevAdd(bd, , TRUE, TRUE);
|
||
bd->type = BDT_VIRTIO_BLK;
|
||
if (VIRTIO_BLK_MAX_BLK) {
|
||
bd->max_blk = Min(VIRTIO_BLK_MAX_BLK, virtio_blk.blks);
|
||
} else {
|
||
bd->max_blk = virtio_blk.blks;
|
||
}
|
||
Free(bd->RAM_dsk);
|
||
dv->size = bd->max_blk + 1 - bd->drv_offset;
|
||
VIORBlks(dv, bs, 0, 1);
|
||
dv->root_clus = bs->root_clus;
|
||
dv->data_area = bs->bitmap_sects;
|
||
dv->next_free = NULL;
|
||
dv->last_free = NULL;
|
||
Free(bs);
|
||
RedSeaTryInit(dv);
|
||
return dv->drv_let;
|
||
}
|
||
|
||
// DskBlk2.HC
|
||
|
||
Bool BlkRead2(CDrv* dv, U8* buf, I64 blk, I64 cnt)
|
||
{ // Read blk cnt from Drv to buf.
|
||
Bool res = TRUE, unlock;
|
||
CBlkDev* bd = dv->bd;
|
||
if (cnt <= 0)
|
||
return TRUE;
|
||
DrvChk(dv);
|
||
try {
|
||
unlock = DrvLock(dv);
|
||
CallExtStr("BlkDevInit", bd);
|
||
if (dv->drv_offset && blk < dv->drv_offset || blk + cnt > dv->drv_offset + dv->size)
|
||
throw('Drv');
|
||
if (bd->flags & BDF_READ_CACHE)
|
||
CallExtStr("RCache", dv, &buf, &blk, &cnt);
|
||
if (cnt > 0) {
|
||
switch (bd->type) {
|
||
case BDT_RAM:
|
||
MemCpy(buf, bd->RAM_dsk + blk << BLK_SIZE_BITS, cnt << BLK_SIZE_BITS);
|
||
break;
|
||
case BDT_ISO_FILE_READ:
|
||
case BDT_ISO_FILE_WRITE:
|
||
FBlkRead(bd->file_dsk, buf, blk, cnt);
|
||
break;
|
||
case BDT_ATA:
|
||
case BDT_ATAPI:
|
||
res = CallExtStr("ATARBlks", dv, buf, blk, cnt);
|
||
break;
|
||
case BDT_VIRTIO_BLK:
|
||
res = VIORBlks(dv, buf, blk, cnt);
|
||
break;
|
||
}
|
||
bd->last_time = tS;
|
||
if (bd->flags & BDF_READ_CACHE)
|
||
CallExtStr("DskCacheAdd", dv, buf, blk, cnt);
|
||
}
|
||
if (unlock)
|
||
DrvUnlock(dv);
|
||
} catch if (unlock)
|
||
DrvUnlock(dv);
|
||
return res;
|
||
}
|
||
|
||
Bool BlkWrite2(CDrv* dv, U8* buf, I64 blk, I64 cnt)
|
||
{ // Write blk cnt from buf to Drv.
|
||
Bool res = TRUE, unlock;
|
||
CBlkDev* bd = dv->bd;
|
||
if (cnt <= 0)
|
||
return TRUE;
|
||
DrvChk(dv);
|
||
try {
|
||
unlock = DrvLock(dv);
|
||
CallExtStr("BlkDevInit", bd);
|
||
if (bd->flags & BDF_READ_ONLY && !(bd->flags & BDF_READ_ONLY_OVERRIDE))
|
||
throw('BlkDev');
|
||
if (dv->drv_offset && blk < dv->drv_offset || blk + cnt > dv->drv_offset + dv->size)
|
||
throw('Drv');
|
||
if (cnt > 0) {
|
||
switch (bd->type) {
|
||
case BDT_RAM:
|
||
MemCpy(bd->RAM_dsk + blk << BLK_SIZE_BITS, buf, cnt << BLK_SIZE_BITS);
|
||
break;
|
||
case BDT_ISO_FILE_READ:
|
||
case BDT_ISO_FILE_WRITE:
|
||
FBlkWrite(bd->file_dsk, buf, blk, cnt);
|
||
break;
|
||
case BDT_ATA:
|
||
case BDT_ATAPI:
|
||
res = CallExtStr("ATAWBlks", dv, buf, blk, cnt);
|
||
break;
|
||
case BDT_VIRTIO_BLK:
|
||
res = VIOWBlks(dv, buf, blk, cnt);
|
||
break;
|
||
}
|
||
bd->last_time = tS;
|
||
if (bd->flags & BDF_READ_CACHE)
|
||
CallExtStr("DskCacheAdd", dv, buf, blk, cnt);
|
||
}
|
||
if (unlock)
|
||
DrvUnlock(dv);
|
||
} catch if (unlock)
|
||
DrvUnlock(dv);
|
||
return res;
|
||
}
|
||
|
||
@patch_jmp_rel32(&BlkRead, &BlkRead2);
|
||
@patch_jmp_rel32(&BlkWrite, &BlkWrite2);
|
||
|
||
// DskBlkDev2.HC
|
||
|
||
CBlkDev* BlkDevChk2(CBlkDev* bd, Bool except = TRUE)
|
||
{ // Check for valid BlkDev. Throw exception.
|
||
if (bd->type == BDT_VIRTIO_BLK)
|
||
return bd;
|
||
if (!bd || bd->bd_signature != BD_SIGNATURE_VAL || !(BDT_NULL < bd->type < BDT_TYPES_NUM)) {
|
||
if (except)
|
||
throw('BlkDev');
|
||
else
|
||
return NULL;
|
||
} else
|
||
return bd;
|
||
}
|
||
|
||
@patch_jmp_rel32(&BlkDevChk, &BlkDevChk2);
|
||
|
||
// DskDrv2.HC
|
||
|
||
DefineLstLoad("ST_BLKDEV_TYPES2",
|
||
"NULL\0RAM\0ATA\0FILE_READ\0FILE_WRITE\0ATAPI\0NULL\0NULL\0NULL\0NULL\0VIRTIO\0");
|
||
|
||
U8 DrvTextAttrGet2(U8 drv_let = 0)
|
||
{ // Get color of drive.
|
||
U8* blkdev_text_attr2 = blkdev_text_attr;
|
||
U8* drv_text_attr2 = drv_text_attr;
|
||
I64 dta_size = 3;
|
||
drv_let = Let2Let(drv_let);
|
||
if (drv_let == 'A')
|
||
return BLACK << 4 | WHITE;
|
||
if ('A' <= drv_let <= 'Z')
|
||
return blkdev_text_attr2[Let2BlkDevType(drv_let)] << 4 | drv_text_attr2[drv_let % dta_size];
|
||
else
|
||
return BLACK << 4 | WHITE;
|
||
}
|
||
|
||
U0 DrvRep2()
|
||
{ // Drive report.
|
||
CDrv* dv;
|
||
CBlkDev* bd;
|
||
I64 ch, i, drv_let, attr;
|
||
U8* st;
|
||
"\nDefined Drives:\n";
|
||
for (i = 0, dv = blkdev.drvs; i < DRVS_NUM; i++, dv++) {
|
||
if (dv->dv_signature == DRV_SIGNATURE_VAL) {
|
||
bd = dv->bd;
|
||
drv_let = Drv2Let(dv);
|
||
if (Bt(&dv->fs_type, FStf_DISABLE))
|
||
ch = '-';
|
||
else if (drv_let == blkdev.boot_drv_let)
|
||
ch = ':';
|
||
else
|
||
ch = '+';
|
||
attr = DrvTextAttrGet(drv_let);
|
||
"\dFG,%d\d\dBG,%d\d%C %-8Z %-10Z %04X %04X %02X\n",
|
||
attr & 15, attr >> 4, drv_let, dv->fs_type &FSG_TYPE_MASK, "ST_DRV_TYPES",
|
||
bd->type, "ST_BLKDEV_TYPES2", bd->base0, bd->base1, bd->unit;
|
||
if (st = DrvModelNum(drv_let)) {
|
||
"Model#:%s\n", st;
|
||
Free(st);
|
||
}
|
||
if (st = DrvSerialNum(drv_let)) {
|
||
"Serial#:%s\n", st;
|
||
Free(st);
|
||
}
|
||
if (bd->type == BDT_ISO_FILE_READ || bd->type == BDT_ISO_FILE_WRITE)
|
||
"File=\"%s\"\n", bd->file_dsk_name;
|
||
"%016X-%016X\n\dFG\d\dBG\d", dv->drv_offset, dv->drv_offset + dv->size - 1;
|
||
}
|
||
}
|
||
"Home Dir:\"%s\"\n", blkdev.home_dir;
|
||
}
|
||
|
||
@patch_jmp_rel32(&DrvTextAttrGet, &DrvTextAttrGet2);
|
||
@patch_jmp_rel32(&DrvRep, &DrvRep2);
|
||
|
||
VirtioBlkInit;
|
||
MountVirtioBlk;
|
||
|
||
if (Let2Drv('A', 0) && !Let2Drv('A')->root_clus) {
|
||
"[virtio-blk] RedSea filesystem not initialized, formatting.\n";
|
||
Fmt('A', , FALSE, FSt_REDSEA);
|
||
Cd("M:/System/");
|
||
}
|