Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor VirtIO MMIO register map handling #23

Merged
merged 1 commit into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions common.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,12 @@ static inline int ilog2(int x)
{
return 31 - __builtin_clz(x | 1);
}

/* Range check
* For any variable range checking:
* if (x >= minx && x <= maxx) ...
* it is faster to use bit operation:
* if ((signed)((x - minx) | (maxx - x)) >= 0) ...
*/
#define RANGE_CHECK(x, minx, size) \
((int32_t) ((x - minx) | (minx + size - 1 - x)) >= 0)
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 @@ -100,6 +101,8 @@ typedef struct {
/* supplied by environment */
int tap_fd;
uint32_t *ram;
/* implementation-specific */
void *priv;
} virtio_net_state_t;

void virtio_net_read(vm_t *core,
Expand Down Expand Up @@ -147,7 +150,8 @@ typedef struct {
/* supplied by environment */
uint32_t *ram;
uint32_t *disk;
uint64_t capacity;
/* implementation-specific */
void *priv;
} virtio_blk_state_t;

void virtio_blk_read(vm_t *vm,
Expand Down
146 changes: 100 additions & 46 deletions virtio-blk.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,64 @@
#include <sys/stat.h>
#include <unistd.h>

#include "common.h"
#include "device.h"
#include "riscv.h"
#include "riscv_private.h"
#include "virtio.h"

#define DISK_BLK_SIZE 512

#define VBLK_DEV_CNT_MAX 1

#define VBLK_FEATURES_0 0
#define VBLK_FEATURES_1 1 /* VIRTIO_F_VERSION_1 */
#define VBLK_QUEUE_NUM_MAX 1024
#define VBLK_QUEUE (vblk->queues[vblk->QueueSel])

#define PRIV(x) ((struct virtio_blk_config *) x->priv)

struct virtio_blk_config {
uint64_t capacity;
uint32_t size_max;
uint32_t seg_max;

struct virtio_blk_geometry {
uint16_t cylinders;
uint8_t heads;
uint8_t sectors;
} geometry;

uint32_t blk_size;

struct virtio_blk_topology {
uint8_t physical_block_exp;
uint8_t alignment_offset;
uint16_t min_io_size;
uint32_t opt_io_size;
} topology;

uint8_t writeback;
uint8_t unused0[3];
uint32_t max_discard_sectors;
uint32_t max_discard_seg;
uint32_t discard_sector_alignment;
uint32_t max_write_zeroes_sectors;
uint32_t max_write_zeroes_seg;
uint8_t write_zeroes_may_unmap;
uint8_t unused1[3];
} __attribute__((packed));

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

struct virtio_blk_config vblk_configs[VBLK_DEV_CNT_MAX];
int vblk_dev_cnt = 0;

static void virtio_blk_set_fail(virtio_blk_state_t *vblk)
{
vblk->Status |= VIRTIO_STATUS__DEVICE_NEEDS_RESET;
Expand All @@ -52,11 +91,13 @@ 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;
void *priv = vblk->priv;
uint32_t capacity = PRIV(vblk)->capacity;
memset(vblk, 0, sizeof(*vblk));
vblk->ram = ram;
vblk->disk = disk;
vblk->capacity = capacity;
vblk->priv = priv;
PRIV(vblk)->capacity = capacity;
}

static void virtio_blk_write_handler(virtio_blk_state_t *vblk,
Expand Down Expand Up @@ -128,7 +169,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 > (PRIV(vblk)->capacity - 1)) {
*status = VIRTIO_BLK_S_IOERR;
return -1;
}
Expand Down Expand Up @@ -219,125 +260,129 @@ 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;
/* Invalid address which exceeded the range */
if (!RANGE_CHECK(addr, _(Config), sizeof(struct virtio_blk_config)))
return false;

/* Read configuration from the corresponding register */
*value = ((uint32_t *) PRIV(vblk))[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;
/* Invalid address which exceeded the range */
if (!RANGE_CHECK(addr, _(Config), sizeof(struct virtio_blk_config)))
return false;

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

return true;
}
#undef _
}

void virtio_blk_read(vm_t *vm,
Expand Down Expand Up @@ -386,11 +431,20 @@ void virtio_blk_write(vm_t *vm,

uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file)
{
if (vblk_dev_cnt >= VBLK_DEV_CNT_MAX) {
fprintf(stderr,
"Excedded the number of virtio-blk device can be allocated.\n");
exit(2);
}

/* Allocate memory for the private member */
vblk->priv = &vblk_configs[vblk_dev_cnt++];

/* No disk image is provided */
if (!disk_file) {
/* By setting the block capacity to zero, the kernel will
* then not to touch the device after booting */
vblk->capacity = 0;
PRIV(vblk)->capacity = 0;
return NULL;
}

Expand All @@ -417,7 +471,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;
PRIV(vblk)->capacity = (disk_size - 1) / DISK_BLK_SIZE + 1;

return disk_mem;
}
Loading