From b6bd4b267d063d0eea390d4523a046cccdc4f4f7 Mon Sep 17 00:00:00 2001 From: Yu-Cheng Chen Date: Wed, 22 Jan 2025 19:11:06 +0800 Subject: [PATCH] Migrate virtio-blk from semu This commit migrates @shengwen-tw's brilliant virtio implementation from semu, with the following modification: 1. Rename virtio_blk_reg_read/write to virtio_blk_read/write The original virtio_blk_read verified whether the virtio MMIO was aligned to 4. Since rv32emu does not enforce alignment checks for UART and PLIC devices or raise misalignment exceptions, this verification has been omitted. 2. Implement MMIO_VIRTIOBLK 3. Implement vblk_new() and vblk_delete() These functions align with the conventions used for UART and PLIC devices. 4. Introduce new argument '-v' for virtio-blk disk image --- README.md | 2 +- src/devices/minimal.dts | 6 + src/devices/virtio-blk.c | 437 +++++++++++++++++++++++++++++++++++++++ src/devices/virtio.h | 105 ++++++++++ src/main.c | 9 +- src/riscv.c | 6 + src/riscv.h | 6 + src/system.c | 28 +++ 8 files changed, 597 insertions(+), 2 deletions(-) create mode 100644 src/devices/virtio-blk.c create mode 100644 src/devices/virtio.h diff --git a/README.md b/README.md index 1eb08b28..b326cd5b 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ $ make ENABLE_SYSTEM=1 system Build using run using specified images: ```shell $ make ENABLE_SYSTEM=1 -$ build/rv32emu -k -i +$ build/rv32emu -k -i -v ``` #### Build Linux image diff --git a/src/devices/minimal.dts b/src/devices/minimal.dts index cb4951de..315be209 100644 --- a/src/devices/minimal.dts +++ b/src/devices/minimal.dts @@ -65,5 +65,11 @@ no-loopback-test; clock-frequency = <5000000>; /* the baudrate divisor is ignored */ }; + + blk0: virtio@4200000 { + compatible = "virtio,mmio"; + reg = <0x4200000 0x200>; + interrupts = <3>; + }; }; }; diff --git a/src/devices/virtio-blk.c b/src/devices/virtio-blk.c new file mode 100644 index 00000000..c60f614d --- /dev/null +++ b/src/devices/virtio-blk.c @@ -0,0 +1,437 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "riscv.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) + +PACKED(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]; + uint64_t disk_size; +}); + +PACKED(struct vblk_req_header { + uint32_t type; + uint32_t reserved; + uint64_t sector; + uint8_t status; +}); + +static struct virtio_blk_config vblk_configs[VBLK_DEV_CNT_MAX]; +static int vblk_dev_cnt = 0; + +static void virtio_blk_set_fail(virtio_blk_state_t *vblk) +{ + vblk->Status |= VIRTIO_STATUS__DEVICE_NEEDS_RESET; + if (vblk->Status & VIRTIO_STATUS__DRIVER_OK) + vblk->InterruptStatus |= VIRTIO_INT__CONF_CHANGE; +} + +static inline uint32_t vblk_preprocess(virtio_blk_state_t *vblk, uint32_t addr) +{ + if ((addr >= MEM_SIZE) || (addr & 0b11)) + return virtio_blk_set_fail(vblk), 0; + + return addr >> 2; +} + +static void virtio_blk_update_status(virtio_blk_state_t *vblk, uint32_t status) +{ + vblk->Status |= status; + if (status) + return; + + /* Reset */ + uint32_t *ram = vblk->ram; + uint32_t *disk = vblk->disk; + void *priv = vblk->priv; + uint32_t capacity = PRIV(vblk)->capacity; + memset(vblk, 0, sizeof(*vblk)); + vblk->ram = ram; + vblk->disk = disk; + vblk->priv = priv; + PRIV(vblk)->capacity = capacity; +} + +static void virtio_blk_write_handler(virtio_blk_state_t *vblk, + uint64_t sector, + uint32_t desc_addr, + uint32_t len) +{ + void *dest = (void *) ((uintptr_t) vblk->disk + sector * DISK_BLK_SIZE); + const void *src = (void *) ((uintptr_t) vblk->ram + desc_addr); + memcpy(dest, src, len); +} + +static void virtio_blk_read_handler(virtio_blk_state_t *vblk, + uint64_t sector, + uint32_t desc_addr, + uint32_t len) +{ + void *dest = (void *) ((uintptr_t) vblk->ram + desc_addr); + const void *src = + (void *) ((uintptr_t) vblk->disk + sector * DISK_BLK_SIZE); + memcpy(dest, src, len); +} + +static int virtio_blk_desc_handler(virtio_blk_state_t *vblk, + const virtio_blk_queue_t *queue, + uint32_t desc_idx, + uint32_t *plen) +{ + /* A full virtio_blk_req is represented by 3 descriptors, where + * the first descriptor contains: + * le32 type + * le32 reserved + * le64 sector + * the second descriptor contains: + * u8 data[][512] + * the third descriptor contains: + * u8 status + */ + struct virtq_desc vq_desc[3]; + + /* Collect the descriptors */ + for (int i = 0; i < 3; i++) { + /* The size of the `struct virtq_desc` is 4 words */ + const uint32_t *desc = &vblk->ram[queue->QueueDesc + desc_idx * 4]; + + /* Retrieve the fields of current descriptor */ + vq_desc[i].addr = desc[0]; + vq_desc[i].len = desc[2]; + vq_desc[i].flags = desc[3]; + desc_idx = desc[3] >> 16; /* vq_desc[desc_cnt].next */ + } + + /* The next flag for the first and second descriptors should be set, + * whereas for the third descriptor is should not be set + */ + if (!(vq_desc[0].flags & VIRTIO_DESC_F_NEXT) || + !(vq_desc[1].flags & VIRTIO_DESC_F_NEXT) || + (vq_desc[2].flags & VIRTIO_DESC_F_NEXT)) { + /* since the descriptor list is abnormal, we don't write the status + * back here */ + virtio_blk_set_fail(vblk); + return -1; + } + + /* Process the header */ + const struct vblk_req_header *header = + (struct vblk_req_header *) ((uintptr_t) vblk->ram + vq_desc[0].addr); + uint32_t type = header->type; + uint64_t sector = header->sector; + uint8_t *status = (uint8_t *) ((uintptr_t) vblk->ram + vq_desc[2].addr); + + /* Check sector index is valid */ + if (sector > (PRIV(vblk)->capacity - 1)) { + *status = VIRTIO_BLK_S_IOERR; + return -1; + } + + /* Process the data */ + switch (type) { + case VIRTIO_BLK_T_IN: + virtio_blk_read_handler(vblk, sector, vq_desc[1].addr, vq_desc[1].len); + break; + case VIRTIO_BLK_T_OUT: + virtio_blk_write_handler(vblk, sector, vq_desc[1].addr, vq_desc[1].len); + break; + default: + fprintf(stderr, "unsupported virtio-blk operation!\n"); + *status = VIRTIO_BLK_S_UNSUPP; + return -1; + } + + /* Return the device status */ + *status = VIRTIO_BLK_S_OK; + *plen = vq_desc[1].len; + + return 0; +} + +static void virtio_queue_notify_handler(virtio_blk_state_t *vblk, int index) +{ + uint32_t *ram = vblk->ram; + virtio_blk_queue_t *queue = &vblk->queues[index]; + if (vblk->Status & VIRTIO_STATUS__DEVICE_NEEDS_RESET) + return; + + if (!((vblk->Status & VIRTIO_STATUS__DRIVER_OK) && queue->ready)) + return virtio_blk_set_fail(vblk); + + /* Check for new buffers */ + uint16_t new_avail = ram[queue->QueueAvail] >> 16; + if (new_avail - queue->last_avail > (uint16_t) queue->QueueNum) + return (fprintf(stderr, "size check fail\n"), + virtio_blk_set_fail(vblk)); + + if (queue->last_avail == new_avail) + return; + + /* Process them */ + uint16_t new_used = ram[queue->QueueUsed] >> 16; /* virtq_used.idx (le16) */ + while (queue->last_avail != new_avail) { + /* Obtain the index in the ring buffer */ + uint16_t queue_idx = queue->last_avail % queue->QueueNum; + + /* Since each buffer index occupies 2 bytes but the memory is aligned + * with 4 bytes, and the first element of the available queue is stored + * at ram[queue->QueueAvail + 1], to acquire the buffer index, it + * requires the following array index calculation and bit shifting. + * Check also the `struct virtq_avail` on the spec. + */ + uint16_t buffer_idx = ram[queue->QueueAvail + 1 + queue_idx / 2] >> + (16 * (queue_idx % 2)); + + /* Consume request from the available queue and process the data in the + * descriptor list. + */ + uint32_t len = 0; + int result = virtio_blk_desc_handler(vblk, queue, buffer_idx, &len); + if (result != 0) + return virtio_blk_set_fail(vblk); + + /* Write used element information (`struct virtq_used_elem`) to the used + * queue */ + uint32_t vq_used_addr = + queue->QueueUsed + 1 + (new_used % queue->QueueNum) * 2; + ram[vq_used_addr] = buffer_idx; /* virtq_used_elem.id (le32) */ + ram[vq_used_addr + 1] = len; /* virtq_used_elem.len (le32) */ + queue->last_avail++; + new_used++; + } + + /* Check le32 len field of `struct virtq_used_elem` on the spec */ + vblk->ram[queue->QueueUsed] &= MASK(16); /* Reset low 16 bits to zero */ + vblk->ram[queue->QueueUsed] |= ((uint32_t) new_used) << 16; /* len */ + + /* Send interrupt, unless VIRTQ_AVAIL_F_NO_INTERRUPT is set */ + if (!(ram[queue->QueueAvail] & 1)) + vblk->InterruptStatus |= VIRTIO_INT__USED_RING; +} + +uint32_t virtio_blk_read(virtio_blk_state_t *vblk, uint32_t addr) +{ + addr = addr >> 2; +#define _(reg) VIRTIO_##reg + switch (addr) { + case _(MagicValue): + return 0x74726976; + case _(Version): + return 2; + case _(DeviceID): + return 2; + case _(VendorID): + return VIRTIO_VENDOR_ID; + case _(DeviceFeatures): + return vblk->DeviceFeaturesSel == 0 + ? VBLK_FEATURES_0 + : (vblk->DeviceFeaturesSel == 1 ? VBLK_FEATURES_1 : 0); + case _(QueueNumMax): + return VBLK_QUEUE_NUM_MAX; + case _(QueueReady): + return VBLK_QUEUE.ready ? 1 : 0; + case _(InterruptStatus): + return vblk->InterruptStatus; + case _(Status): + return vblk->Status; + case _(ConfigGeneration): + return 0; + default: + /* Read configuration from the corresponding register */ + return ((uint32_t *) PRIV(vblk))[addr - _(Config)]; + } +#undef _ +} + +void virtio_blk_write(virtio_blk_state_t *vblk, uint32_t addr, uint32_t value) +{ + addr = addr >> 2; +#define _(reg) VIRTIO_##reg + switch (addr) { + case _(DeviceFeaturesSel): + vblk->DeviceFeaturesSel = value; + break; + case _(DriverFeatures): + vblk->DriverFeaturesSel == 0 ? (vblk->DriverFeatures = value) : 0; + break; + case _(DriverFeaturesSel): + vblk->DriverFeaturesSel = value; + break; + case _(QueueSel): + if (value < ARRAY_SIZE(vblk->queues)) + vblk->QueueSel = value; + else + virtio_blk_set_fail(vblk); + break; + case _(QueueNum): + if (value > 0 && value <= VBLK_QUEUE_NUM_MAX) + VBLK_QUEUE.QueueNum = value; + else + virtio_blk_set_fail(vblk); + break; + case _(QueueReady): + VBLK_QUEUE.ready = value & 1; + if (value & 1) + VBLK_QUEUE.last_avail = vblk->ram[VBLK_QUEUE.QueueAvail] >> 16; + break; + case _(QueueDescLow): + VBLK_QUEUE.QueueDesc = vblk_preprocess(vblk, value); + break; + case _(QueueDescHigh): + if (value) + virtio_blk_set_fail(vblk); + break; + case _(QueueDriverLow): + VBLK_QUEUE.QueueAvail = vblk_preprocess(vblk, value); + break; + case _(QueueDriverHigh): + if (value) + virtio_blk_set_fail(vblk); + break; + case _(QueueDeviceLow): + VBLK_QUEUE.QueueUsed = vblk_preprocess(vblk, value); + break; + case _(QueueDeviceHigh): + if (value) + virtio_blk_set_fail(vblk); + break; + case _(QueueNotify): + if (value < ARRAY_SIZE(vblk->queues)) + virtio_queue_notify_handler(vblk, value); + else + virtio_blk_set_fail(vblk); + break; + case _(InterruptACK): + vblk->InterruptStatus &= ~value; + break; + case _(Status): + virtio_blk_update_status(vblk, value); + break; + default: + /* Write configuration to the corresponding register */ + ((uint32_t *) PRIV(vblk))[addr - _(Config)] = value; + break; + } +#undef _ +} + +uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file) +{ + if (vblk_dev_cnt >= VBLK_DEV_CNT_MAX) { + fprintf(stderr, + "Exceeded the number of virtio-blk devices that 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 */ + PRIV(vblk)->capacity = 0; + return NULL; + } + + /* Open disk file */ + int disk_fd = open(disk_file, O_RDWR); + if (disk_fd < 0) { + fprintf(stderr, "could not open %s\n", disk_file); + exit(2); + } + + /* Get the disk image size */ + struct stat st; + fstat(disk_fd, &st); + PRIV(vblk)->disk_size = st.st_size; + + /* Set up the disk memory */ + uint32_t *disk_mem; +#if HAVE_MMAP + disk_mem = mmap(NULL, PRIV(vblk)->disk_size, PROT_READ | PROT_WRITE, + MAP_SHARED, disk_fd, 0); + if (disk_mem == MAP_FAILED) { + fprintf(stderr, "Could not map disk\n"); + return NULL; + } +#else + disk_mem = malloc(PRIV(vblk)->disk_size); + if (!disk_mem) { + fprintf(stderr, "Could not map disk\n"); + return NULL; + } +#endif + assert(!(((uintptr_t) disk_mem) & 0b11)); + close(disk_fd); + + vblk->disk = disk_mem; + PRIV(vblk)->capacity = (PRIV(vblk)->disk_size - 1) / DISK_BLK_SIZE + 1; + + return disk_mem; +} + +virtio_blk_state_t *vblk_new() +{ + virtio_blk_state_t *vblk = calloc(1, sizeof(virtio_blk_state_t)); + assert(vblk); + return vblk; +} + +void vblk_delete(virtio_blk_state_t *vblk) +{ +#if HAVE_MMAP + munmap(vblk->disk, PRIV(vblk)->disk_size); +#else + free(vblk->disk); +#endif + free(vblk); +} +#undef PRIV diff --git a/src/devices/virtio.h b/src/devices/virtio.h new file mode 100644 index 00000000..4220f2dd --- /dev/null +++ b/src/devices/virtio.h @@ -0,0 +1,105 @@ +#pragma once + +#define VIRTIO_VENDOR_ID 0x12345678 + +#define VIRTIO_STATUS__DRIVER_OK 4 +#define VIRTIO_STATUS__DEVICE_NEEDS_RESET 64 + +#define VIRTIO_INT__USED_RING 1 +#define VIRTIO_INT__CONF_CHANGE 2 + +#define VIRTIO_DESC_F_NEXT 1 +#define VIRTIO_DESC_F_WRITE 2 + +#define VIRTIO_BLK_T_IN 0 +#define VIRTIO_BLK_T_OUT 1 +#define VIRTIO_BLK_T_FLUSH 4 +#define VIRTIO_BLK_T_GET_ID 8 +#define VIRTIO_BLK_T_GET_LIFETIME 10 +#define VIRTIO_BLK_T_DISCARD 11 +#define VIRTIO_BLK_T_WRITE_ZEROES 13 +#define VIRTIO_BLK_T_SECURE_ERASE 14 + +#define VIRTIO_BLK_S_OK 0 +#define VIRTIO_BLK_S_IOERR 1 +#define VIRTIO_BLK_S_UNSUPP 2 + +/* VirtIO MMIO registers */ +#define VIRTIO_REG_LIST \ + _(MagicValue, 0x000) /* R */ \ + _(Version, 0x004) /* R */ \ + _(DeviceID, 0x008) /* R */ \ + _(VendorID, 0x00c) /* R */ \ + _(DeviceFeatures, 0x010) /* R */ \ + _(DeviceFeaturesSel, 0x014) /* W */ \ + _(DriverFeatures, 0x020) /* W */ \ + _(DriverFeaturesSel, 0x024) /* W */ \ + _(QueueSel, 0x030) /* W */ \ + _(QueueNumMax, 0x034) /* R */ \ + _(QueueNum, 0x038) /* W */ \ + _(QueueReady, 0x044) /* RW */ \ + _(QueueNotify, 0x050) /* W */ \ + _(InterruptStatus, 0x60) /* R */ \ + _(InterruptACK, 0x064) /* W */ \ + _(Status, 0x070) /* RW */ \ + _(QueueDescLow, 0x080) /* W */ \ + _(QueueDescHigh, 0x084) /* W */ \ + _(QueueDriverLow, 0x090) /* W */ \ + _(QueueDriverHigh, 0x094) /* W */ \ + _(QueueDeviceLow, 0x0a0) /* W */ \ + _(QueueDeviceHigh, 0x0a4) /* W */ \ + _(ConfigGeneration, 0x0fc) /* R */ \ + _(Config, 0x100) /* RW */ + +enum { +#define _(reg, addr) VIRTIO_##reg = addr >> 2, + VIRTIO_REG_LIST +#undef _ +}; + +struct virtq_desc { + uint32_t addr; + uint32_t len; + uint16_t flags; + uint16_t next; +}; + +#define IRQ_VBLK 3 +#define IRQ_VBLK_BIT (1 << IRQ_VBLK) + +typedef struct { + uint32_t QueueNum; + uint32_t QueueDesc; + uint32_t QueueAvail; + uint32_t QueueUsed; + uint16_t last_avail; + bool ready; +} virtio_blk_queue_t; + +typedef struct { + /* feature negotiation */ + uint32_t DeviceFeaturesSel; + uint32_t DriverFeatures; + uint32_t DriverFeaturesSel; + /* queue config */ + uint32_t QueueSel; + virtio_blk_queue_t queues[2]; + /* status */ + uint32_t Status; + uint32_t InterruptStatus; + /* supplied by environment */ + uint32_t *ram; + uint32_t *disk; + /* implementation-specific */ + void *priv; +} virtio_blk_state_t; + +uint32_t virtio_blk_read(virtio_blk_state_t *vblk, uint32_t addr); + +void virtio_blk_write(virtio_blk_state_t *vblk, uint32_t addr, uint32_t value); + +uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file); + +virtio_blk_state_t *vblk_new(); + +void vblk_delete(virtio_blk_state_t *vblk); diff --git a/src/main.c b/src/main.c index 861a3bb1..2c609a24 100644 --- a/src/main.c +++ b/src/main.c @@ -42,7 +42,7 @@ static char *opt_prog_name; /* target argc and argv */ static int prog_argc; static char **prog_args; -static const char *optstr = "tgqmhpd:a:k:i:b:"; +static const char *optstr = "tgqmhpd:a:k:i:b:v:"; /* enable misaligned memory access */ static bool opt_misaligned = false; @@ -56,6 +56,7 @@ static char *prof_out_file; static char *opt_kernel_img; static char *opt_rootfs_img; static char *opt_bootargs; +static char *opt_virtio_img; #endif static void print_usage(const char *filename) @@ -73,6 +74,7 @@ static void print_usage(const char *filename) #if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) " -k : use as kernel image\n" " -i : use as rootfs\n" + " -v : use as virtio-blk disk image\n" " -b : use customized for the kernel\n" #endif " -d [filename]: dump registers as JSON to the " @@ -118,6 +120,10 @@ static bool parse_args(int argc, char **args) opt_bootargs = optarg; emu_argc++; break; + case 'v': + opt_virtio_img = optarg; + emu_argc++; + break; #endif case 'q': opt_quiet_outputs = true; @@ -258,6 +264,7 @@ int main(int argc, char **args) attr.data.system.kernel = opt_kernel_img; attr.data.system.initrd = opt_rootfs_img; attr.data.system.bootargs = opt_bootargs; + attr.data.system.vblk_device = opt_virtio_img; #else attr.data.user.elf_program = opt_prog_name; #endif diff --git a/src/riscv.c b/src/riscv.c index 60fb1cac..6a3c3eff 100644 --- a/src/riscv.c +++ b/src/riscv.c @@ -502,6 +502,11 @@ riscv_t *rv_create(riscv_user_t rv_attr) attr->uart->in_fd = attr->fd_stdin; attr->uart->out_fd = attr->fd_stdout; + /* setup virtio-blk */ + attr->vblk = vblk_new(); + attr->vblk->ram = (uint32_t *) attr->mem->mem_base; + attr->disk = virtio_blk_init(attr->vblk, attr->data.system.vblk_device); + capture_keyboard_input(); #endif /* !RV32_HAS(SYSTEM) || (RV32_HAS(SYSTEM) && RV32_HAS(ELF_LOADER)) */ @@ -642,6 +647,7 @@ void rv_delete(riscv_t *rv) #if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) u8250_delete(attr->uart); plic_delete(attr->plic); + vblk_delete(attr->vblk); #endif free(rv); } diff --git a/src/riscv.h b/src/riscv.h index 1ed7ba65..e978c04f 100644 --- a/src/riscv.h +++ b/src/riscv.h @@ -15,6 +15,7 @@ #if RV32_HAS(SYSTEM) #include "devices/plic.h" #include "devices/uart.h" +#include "devices/virtio.h" #endif /* RV32_HAS(SYSTEM) */ #if RV32_HAS(EXT_F) @@ -552,6 +553,7 @@ typedef struct { char *kernel; char *initrd; char *bootargs; + char *vblk_device; } vm_system_t; #endif /* RV32_HAS(SYSTEM) */ @@ -571,6 +573,10 @@ typedef struct { /* plic object */ plic_t *plic; + + /* virtio-blk device */ + uint32_t *disk; + virtio_blk_state_t *vblk; #endif /* RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) */ /* vm memory object */ diff --git a/src/system.c b/src/system.c index 4a5aac97..38cfc1d6 100644 --- a/src/system.c +++ b/src/system.c @@ -11,6 +11,7 @@ #include "devices/plic.h" #include "devices/uart.h" +#include "devices/virtio.h" #include "riscv_private.h" #define R 1 @@ -28,6 +29,15 @@ void emu_update_uart_interrupts(riscv_t *rv) plic_update_interrupts(attr->plic); } +void emu_update_vblk_interrupts(riscv_t *rv) +{ + vm_attr_t *attr = PRIV(rv); + if (attr->vblk->InterruptStatus) + attr->plic->active |= IRQ_VBLK_BIT; + else + attr->plic->active &= ~IRQ_VBLK_BIT; + plic_update_interrupts(attr->plic); +} /* * Linux kernel might create signal frame when returning from trap * handling, which modifies the SEPC CSR. Thus, the fault instruction @@ -45,6 +55,7 @@ extern bool need_handle_signal; enum SUPPORTED_MMIO { MMIO_PLIC, MMIO_UART, + MMIO_VIRTIOBLK, }; /* clang-format off */ @@ -72,6 +83,17 @@ enum SUPPORTED_MMIO { return; \ ) \ break; \ + case MMIO_VIRTIOBLK: \ + IIF(rw)( /* read */ \ + mmio_read_val = virtio_blk_read(PRIV(rv)->vblk, addr & 0xFFFFF); \ + emu_update_vblk_interrupts(rv); \ + return mmio_read_val; \ + , /* write */ \ + virtio_blk_write(PRIV(rv)->vblk, addr & 0xFFFFF, val); \ + emu_update_vblk_interrupts(rv); \ + return; \ + ) \ + break; \ default: \ fprintf(stderr, "unknown MMIO type %d\n", io); \ break; \ @@ -91,6 +113,9 @@ enum SUPPORTED_MMIO { case 0x40: /* UART */ \ MMIO_OP(MMIO_UART, MMIO_R); \ break; \ + case 0x42: /* Virtio-blk */ \ + MMIO_OP(MMIO_VIRTIOBLK, MMIO_R); \ + break; \ default: \ __UNREACHABLE; \ break; \ @@ -110,6 +135,9 @@ enum SUPPORTED_MMIO { case 0x40: /* UART */ \ MMIO_OP(MMIO_UART, MMIO_W); \ break; \ + case 0x42: /* Virtio-blk */ \ + MMIO_OP(MMIO_VIRTIOBLK, MMIO_W); \ + break; \ default: \ __UNREACHABLE; \ break; \