slon/System/Drivers/Virtio-blk.HC

473 lines
15 KiB
HolyC
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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/");
}