Skip to content

Commit

Permalink
Refactor VirtIO MMIO register map handling
Browse files Browse the repository at this point in the history
1. Replace the hard-coded register address checking with enum.
2. Handle the configuration space read / write with device-specific structures.
  • Loading branch information
shengwen-tw committed Jul 26, 2023
1 parent f0a24fe commit 8d69110
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 86 deletions.
6 changes: 5 additions & 1 deletion device.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "riscv.h"
#include "virtio.h"

/* RAM */

Expand Down Expand Up @@ -94,6 +95,8 @@ typedef struct {
/* queue config */
uint32_t QueueSel;
virtio_net_queue_t queues[2];
/* device configuration */
struct virtio_net_config config;
/* status */
uint32_t Status;
uint32_t InterruptStatus;
Expand Down Expand Up @@ -141,13 +144,14 @@ typedef struct {
/* queue config */
uint32_t QueueSel;
virtio_blk_queue_t queues[2];
/* device configuration */
struct virtio_blk_config config;
/* status */
uint32_t Status;
uint32_t InterruptStatus;
/* supplied by environment */
uint32_t *ram;
uint32_t *disk;
uint64_t capacity;
} virtio_blk_state_t;

void virtio_blk_read(vm_t *vm,
Expand Down
113 changes: 60 additions & 53 deletions virtio-blk.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,6 @@
#define VBLK_QUEUE_NUM_MAX 1024
#define VBLK_QUEUE (vblk->queues[vblk->QueueSel])

struct vblk_req_header {
uint32_t type;
uint32_t reserved;
uint64_t sector;
uint8_t status;
} __attribute__((packed));

static void virtio_blk_set_fail(virtio_blk_state_t *vblk)
{
vblk->Status |= VIRTIO_STATUS__DEVICE_NEEDS_RESET;
Expand All @@ -52,11 +45,11 @@ static void virtio_blk_update_status(virtio_blk_state_t *vblk, uint32_t status)
/* Reset */
uint32_t *ram = vblk->ram;
uint32_t *disk = vblk->disk;
uint32_t capacity = vblk->capacity;
uint32_t capacity = vblk->config.capacity;
memset(vblk, 0, sizeof(*vblk));
vblk->ram = ram;
vblk->disk = disk;
vblk->capacity = capacity;
vblk->config.capacity = capacity;
}

static void virtio_blk_write_handler(virtio_blk_state_t *vblk,
Expand Down Expand Up @@ -128,7 +121,7 @@ static int virtio_blk_desc_handler(virtio_blk_state_t *vblk,
uint8_t *status = (uint8_t *) ((uintptr_t) vblk->ram + vq_desc[2].addr);

/* Check sector index is valid */
if (sector > (vblk->capacity - 1)) {
if (sector > (vblk->config.capacity - 1)) {
*status = VIRTIO_BLK_S_IOERR;
return -1;
}
Expand Down Expand Up @@ -219,125 +212,139 @@ static bool virtio_blk_reg_read(virtio_blk_state_t *vblk,
uint32_t addr,
uint32_t *value)
{
/* TODO: replace the register address with enum.
* For the address after the configuration space, it should be
* handled by structure pointer depend on the device.
*/
#define _(reg) VIRTIO_##reg
switch (addr) {
case 0: /* MagicValue (R) */
case _(MagicValue):
*value = 0x74726976;
return true;
case 1: /* Version (R) */
case _(Version):
*value = 2;
return true;
case 2: /* DeviceID (R) */
case _(DeviceID):
*value = 2;
return true;
case 3: /* VendorID (R) */
case _(VendorID):
*value = VIRTIO_VENDOR_ID;
return true;
case 4: /* DeviceFeatures (R) */
case _(DeviceFeatures):
*value = vblk->DeviceFeaturesSel == 0
? VBLK_FEATURES_0
: (vblk->DeviceFeaturesSel == 1 ? VBLK_FEATURES_1 : 0);
return true;
case 13: /* QueueNumMax (R) */
case _(QueueNumMax):
*value = VBLK_QUEUE_NUM_MAX;
return true;
case 17: /* QueueReady (RW) */
case _(QueueReady):
*value = VBLK_QUEUE.ready ? 1 : 0;
return true;
case 24: /* InterruptStatus (R) */
case _(InterruptStatus):
*value = vblk->InterruptStatus;
return true;
case 28: /* Status (RW) */
case _(Status):
*value = vblk->Status;
return true;
case 63: /* ConfigGeneration (R) */
case _(ConfigGeneration):
*value = 0;
return true;
case 64: /* CapacityLow (R) */
*value = 0x00000000ffffffff & vblk->capacity;
return true;
case 65: /* CapacityHigh (R) */
*value = vblk->capacity >> 32;
return true;
default:
return false;
/* Undefined register before the configuration space */
if (addr < _(Config))
return false;

/* Invalid address which exceeded the max range */
if (addr > (_(Config) + sizeof(struct virtio_blk_config)))
return false;

/* Read configuration from the corresponding register */
uint32_t *config = (uint32_t *) &vblk->config;
*value = config[addr - _(Config)];

return true;
}
#undef _
}

static bool virtio_blk_reg_write(virtio_blk_state_t *vblk,
uint32_t addr,
uint32_t value)
{
/* TODO: replace the register address with enum.
* For the address after the configuration space, it should be
* handled by structure pointer depend on the device.
*/
#define _(reg) VIRTIO_##reg
switch (addr) {
case 5: /* DeviceFeaturesSel (W) */
case _(DeviceFeaturesSel):
vblk->DeviceFeaturesSel = value;
return true;
case 8: /* DriverFeatures (W) */
case _(DriverFeatures):
vblk->DriverFeaturesSel == 0 ? (vblk->DriverFeatures = value) : 0;
return true;
case 9: /* DriverFeaturesSel (W) */
case _(DriverFeaturesSel):
vblk->DriverFeaturesSel = value;
return true;
case 12: /* QueueSel (W) */
case _(QueueSel):
if (value < ARRAY_SIZE(vblk->queues))
vblk->QueueSel = value;
else
virtio_blk_set_fail(vblk);
return true;
case 14: /* QueueNum (W) */
case _(QueueNum):
if (value > 0 && value <= VBLK_QUEUE_NUM_MAX)
VBLK_QUEUE.QueueNum = value;
else
virtio_blk_set_fail(vblk);
return true;
case 17: /* QueueReady (RW) */
case _(QueueReady):
VBLK_QUEUE.ready = value & 1;
if (value & 1)
VBLK_QUEUE.last_avail = vblk->ram[VBLK_QUEUE.QueueAvail] >> 16;
return true;
case 32: /* QueueDescLow (W) */
case _(QueueDescLow):
VBLK_QUEUE.QueueDesc = vblk_preprocess(vblk, value);
return true;
case 33: /* QueueDescHigh (W) */
case _(QueueDescHigh):
if (value)
virtio_blk_set_fail(vblk);
return true;
case 36: /* QueueAvailLow (W) */
case _(QueueDriverLow):
VBLK_QUEUE.QueueAvail = vblk_preprocess(vblk, value);
return true;
case 37: /* QueueAvailHigh (W) */
case _(QueueDriverHigh):
if (value)
virtio_blk_set_fail(vblk);
return true;
case 40: /* QueueUsedLow (W) */
case _(QueueDeviceLow):
VBLK_QUEUE.QueueUsed = vblk_preprocess(vblk, value);
return true;
case 41: /* QueueUsedHigh (W) */
case _(QueueDeviceHigh):
if (value)
virtio_blk_set_fail(vblk);
return true;
case 20: /* QueueNotify (W) */
case _(QueueNotify):
if (value < ARRAY_SIZE(vblk->queues))
virtio_queue_notify_handler(vblk, value);
else
virtio_blk_set_fail(vblk);
return true;
case 25: /* InterruptACK (W) */
case _(InterruptACK):
vblk->InterruptStatus &= ~value;
return true;
case 28: /* Status (RW) */
case _(Status):
virtio_blk_update_status(vblk, value);
return true;
default:
return false;
/* Undefined register before the configuration space */
if (addr < _(Config))
return false;

/* Invalid address which exceeded the max range */
if (addr > (_(Config) + sizeof(struct virtio_blk_config)))
return false;

/* Write configuration to the corresponding register */
uint32_t *config = (uint32_t *) &vblk->config;
config[addr - _(Config)] = value;

return true;
}
#undef _
}

void virtio_blk_read(vm_t *vm,
Expand Down Expand Up @@ -390,7 +397,7 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file)
if (!disk_file) {
/* By setting the block capacity to zero, the kernel will
* then not to touch the device after booting */
vblk->capacity = 0;
vblk->config.capacity = 0;
return NULL;
}

Expand All @@ -417,7 +424,7 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file)
close(disk_fd);

vblk->disk = disk_mem;
vblk->capacity = (disk_size - 1) / DISK_BLK_SIZE + 1;
vblk->config.capacity = (disk_size - 1) / DISK_BLK_SIZE + 1;

return disk_mem;
}
Loading

0 comments on commit 8d69110

Please sign in to comment.