diff --git a/.ci/boot-linux.sh b/.ci/boot-linux.sh index b1f5868f..9a91abd9 100755 --- a/.ci/boot-linux.sh +++ b/.ci/boot-linux.sh @@ -61,6 +61,20 @@ if [ "${ENABLE_VBLK}" -eq "1" ]; then expect "# " { send "\x01"; send "x" } timeout { exit 3 } ') + # multiple blocks, Read-only, one disk image, one loop device (/dev/loopx(Linux) or /dev/diskx(Darwin)) + TEST_OPTIONS+=("${OPTS_BASE} -x vblk:${VBLK_IMG},readonly -x vblk:${BLK_DEV},readonly") + EXPECT_CMDS+=(' + expect "buildroot login:" { send "root\n" } timeout { exit 1 } + expect "# " { send "uname -a\n" } timeout { exit 2 } + expect "riscv32 GNU/Linux" { send "mkdir mnt && mount /dev/vda mnt\n" } timeout { exit 3 } + expect "# " { send "echo rv32emu > mnt/emu.txt\n" } timeout { exit 3 } + expect -ex "-sh: can'\''t create mnt/emu.txt: Read-only file system" {} timeout { exit 3 } + expect "# " { send "mkdir mnt2 && mount /dev/vdb mnt2\n" } timeout { exit 3 } + expect "# " { send "echo rv32emu > mnt2/emu.txt\n" } timeout { exit 3 } + expect -ex "-sh: can'\''t create mnt2/emu.txt: Read-only file system" {} timeout { exit 3 } + expect "# " { send "\x01"; send "x" } timeout { exit 3 } + ') + # Read-write using disk image TEST_OPTIONS+=("${OPTS_BASE} -x vblk:${VBLK_IMG}") VBLK_EXPECT_CMDS=' @@ -77,6 +91,23 @@ if [ "${ENABLE_VBLK}" -eq "1" ]; then # Read-write using /dev/loopx(Linux) or /dev/diskx(Darwin) block device TEST_OPTIONS+=("${OPTS_BASE} -x vblk:${BLK_DEV}") EXPECT_CMDS+=("${VBLK_EXPECT_CMDS}") + + # multiple blocks, Read-write, one disk image and one loop device (/dev/loopx(Linux) or /dev/diskx(Darwin)) + TEST_OPTIONS+=("${OPTS_BASE} -x vblk:${VBLK_IMG} -x vblk:${BLK_DEV}") + VBLK_EXPECT_CMDS=' + expect "buildroot login:" { send "root\n" } timeout { exit 1 } + expect "# " { send "uname -a\n" } timeout { exit 2 } + expect "riscv32 GNU/Linux" { send "mkdir mnt && mount /dev/vda mnt\n" } timeout { exit 3 } + expect "# " { send "echo rv32emu > mnt/emu.txt\n" } timeout { exit 3 } + expect "# " { send "sync\n" } timeout { exit 3 } + expect "# " { send "umount mnt\n" } timeout { exit 3 } + expect "# " { send "mkdir mnt2 && mount /dev/vdb mnt2\n" } timeout { exit 3 } + expect "# " { send "echo rv32emu > mnt2/emu.txt\n" } timeout { exit 3 } + expect "# " { send "sync\n" } timeout { exit 3 } + expect "# " { send "umount mnt2\n" } timeout { exit 3 } + expect "# " { send "\x01"; send "x" } timeout { exit 3 } + ' + EXPECT_CMDS+=("${VBLK_EXPECT_CMDS}") fi for i in "${!TEST_OPTIONS[@]}"; do diff --git a/README.md b/README.md index 3efc805d..8c86a40f 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,12 @@ Mount the virtual block device and create a test file after booting, note that r ``` Reboot and re-mount the virtual block device, the written file should remain existing. +To specify multiple virtual block devices, pass multiple `-x vblk` options when launching the emulator. Each option can point to either a disk image or a hostOS block device, with optional read-only mode. For example: +```shell +$ build/rv32emu -k -i -x vblk:disk.img -x vblk:/dev/loop22,readonly +``` +Note that the /dev/vdx device order in guestOS is assigned in reverse: the first `-x vblk` argument corresponds to the device with the highest letter, while subsequent arguments receive lower-lettered device names. + #### Customize bootargs Build and run with customized bootargs to boot the guestOS. Otherwise, the default bootargs defined in `src/devices/minimal.dts` will be used. ```shell diff --git a/src/devices/minimal.dts b/src/devices/minimal.dts index 315be209..1b0b0957 100644 --- a/src/devices/minimal.dts +++ b/src/devices/minimal.dts @@ -66,10 +66,14 @@ clock-frequency = <5000000>; /* the baudrate divisor is ignored */ }; - blk0: virtio@4200000 { + /* + * Virtio block example subnode + * The actual subnode are generated dynamically depends on the CLI -x vblk option + */ + /*blk0: virtio@4100000 { compatible = "virtio,mmio"; - reg = <0x4200000 0x200>; - interrupts = <3>; - }; + reg = <0x4100000 0x200>; + interrupts = <2>; + };*/ }; }; diff --git a/src/devices/virtio-blk.c b/src/devices/virtio-blk.c index 5ee34085..47841209 100644 --- a/src/devices/virtio-blk.c +++ b/src/devices/virtio-blk.c @@ -32,9 +32,6 @@ #define DISK_BLK_SIZE 512 -/* TODO: Enable mutiple virtio-blk devices. */ -#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 @@ -81,9 +78,6 @@ PACKED(struct vblk_req_header { 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; @@ -401,20 +395,16 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file, bool readonly) { - if (vblk_dev_cnt >= VBLK_DEV_CNT_MAX) { - rv_log_error( - "Exceeded the number of virtio-blk devices that can be allocated"); - exit(EXIT_FAILURE); - } - /* * For mmap_fallback, if vblk is not specified, disk_fd should remain -1 and * no fsync should be performed on exit. */ + vblk->disk_fd = -1; /* Allocate memory for the private member */ - vblk->priv = &vblk_configs[vblk_dev_cnt++]; + vblk->priv = calloc(1, sizeof(struct virtio_blk_config)); + assert(vblk->priv); /* No disk image is provided */ if (!disk_file) { @@ -549,5 +539,6 @@ void vblk_delete(virtio_blk_state_t *vblk) else munmap(vblk->disk, VBLK_PRIV(vblk)->disk_size); #endif + free(vblk->priv); free(vblk); } diff --git a/src/devices/virtio.h b/src/devices/virtio.h index 28da79f7..a856627b 100644 --- a/src/devices/virtio.h +++ b/src/devices/virtio.h @@ -76,8 +76,7 @@ struct virtq_desc { uint16_t next; }; -#define IRQ_VBLK_SHIFT 3 -#define IRQ_VBLK_BIT (1 << IRQ_VBLK_SHIFT) +#define IRQ_VBLK_BIT(base, i) (1 << (base + i)) typedef struct { uint32_t queue_num; diff --git a/src/main.c b/src/main.c index 08df7754..d5729853 100644 --- a/src/main.c +++ b/src/main.c @@ -60,7 +60,10 @@ static char *prof_out_file; static char *opt_kernel_img; static char *opt_rootfs_img; static char *opt_bootargs; -static char *opt_virtio_blk_img; +/* FIXME: handle overflow */ +#define VBLK_DEV_MAX 100 +static char *opt_virtio_blk_img[VBLK_DEV_MAX]; +static int opt_virtio_blk_idx = 0; #endif static void print_usage(const char *filename) @@ -78,8 +81,10 @@ 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" - " -x vblk:[,readonly] : use as virtio-blk disk image " - "(default read and write)\n" + " -x vblk:[,readonly]: use " + " as virtio-blk disk image " + "(default read and write). This option may be specified " + "multiple times for multiple block devices\n" " -b : use customized for the kernel\n" #endif " -d [filename]: dump registers as JSON to the " @@ -127,7 +132,8 @@ static bool parse_args(int argc, char **args) break; case 'x': if (!strncmp("vblk:", optarg, 5)) - opt_virtio_blk_img = optarg + 5; /* strlen("vblk:") */ + opt_virtio_blk_img[opt_virtio_blk_idx++] = + optarg + 5; /* strlen("vblk:") */ else return false; emu_argc++; @@ -273,7 +279,12 @@ 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_blk_img; + if (opt_virtio_blk_idx) { + attr.data.system.vblk_device = opt_virtio_blk_img; + attr.data.system.vblk_device_cnt = opt_virtio_blk_idx; + } else { + attr.data.system.vblk_device = NULL; + } #else attr.data.user.elf_program = opt_prog_name; #endif diff --git a/src/riscv.c b/src/riscv.c index 1f84f89a..5b43114d 100644 --- a/src/riscv.c +++ b/src/riscv.c @@ -293,45 +293,110 @@ static void load_dtb(char **ram_loc, vm_attr_t *attr) { #include "minimal_dtb.h" char *bootargs = attr->data.system.bootargs; - char *vblk = attr->data.system.vblk_device; + char **vblk = attr->data.system.vblk_device; char *blob = *ram_loc; char *buf; size_t len; int node, err; int totalsize; - memcpy(blob, minimal, sizeof(minimal)); +#define DTB_EXPAND_SIZE 1024 /* or more if needed */ + + /* Allocate enough memory for DTB + extra room */ + size_t minimal_len = ARRAY_SIZE(minimal); + void *dtb_buf = calloc(minimal_len + DTB_EXPAND_SIZE, sizeof(uint8_t)); + assert(dtb_buf); + + /* Expand it to a usable DTB blob */ + err = fdt_open_into(minimal, dtb_buf, minimal_len + DTB_EXPAND_SIZE); + if (err < 0) { + rv_log_error("fdt_open_into fails\n"); + exit(EXIT_FAILURE); + } if (bootargs) { - node = fdt_path_offset(blob, "/chosen"); + node = fdt_path_offset(dtb_buf, "/chosen"); assert(node > 0); - len = strlen(bootargs) + 1; - buf = malloc(len); + len = strlen(bootargs); + buf = malloc(len + 1); assert(buf); - memcpy(buf, bootargs, len - 1); + memcpy(buf, bootargs, len); buf[len] = 0; - err = fdt_setprop(blob, node, "bootargs", buf, len + 1); + err = fdt_setprop(dtb_buf, node, "bootargs", buf, len + 1); if (err == -FDT_ERR_NOSPACE) { - blob = realloc_property(blob, node, "bootargs", len); - err = fdt_setprop(blob, node, "bootargs", buf, len); + dtb_buf = realloc_property(dtb_buf, node, "bootargs", len); + err = fdt_setprop(dtb_buf, node, "bootargs", buf, len); } free(buf); assert(!err); } - /* remove the vblk node from soc if it is not specified */ - if (!vblk) { - int subnode; - node = fdt_path_offset(blob, "/soc@F0000000"); + if (vblk) { + int node = fdt_path_offset(dtb_buf, "/soc@F0000000"); assert(node >= 0); - subnode = fdt_subnode_offset(blob, node, "virtio@4200000"); - assert(subnode >= 0); + uint32_t base_addr = 0x4000000; + uint32_t addr_offset = 0x100000; + uint32_t size = 0x200; + + uint32_t next_addr = base_addr; + uint32_t next_irq = 1; + + /* scan existing nodes to get next addr and irq */ + int subnode; + fdt_for_each_subnode(subnode, dtb_buf, node) + { + const char *name = fdt_get_name(dtb_buf, subnode, NULL); + assert(name); + + uint32_t addr = strtoul(name + 7, NULL, 16); + if (addr == next_addr) + next_addr = addr + addr_offset; + + const fdt32_t *irq_prop = + fdt_getprop(dtb_buf, subnode, "interrupts", NULL); + if (irq_prop) { + uint32_t irq = fdt32_to_cpu(*irq_prop); + if (irq == next_irq) + next_irq = irq + 1; + } + } + /* set IRQ for virtio block, see devices/virtio.h */ + attr->vblk_irq_base = next_irq; + + /* adding new virtio block nodes */ + for (int i = 0; i < attr->vblk_cnt; i++) { + uint32_t new_addr = next_addr + i * addr_offset; + uint32_t new_irq = next_irq + i; + + char node_name[32]; + snprintf(node_name, sizeof(node_name), "virtio@%x", new_addr); + + int subnode = fdt_add_subnode(dtb_buf, node, node_name); + if (subnode == -FDT_ERR_NOSPACE) { + rv_log_warn("add subnode no space!\n"); + } + assert(subnode >= 0); - assert(fdt_del_node(blob, subnode) == 0); + /* compatible = "virtio,mmio" */ + assert(fdt_setprop_string(dtb_buf, subnode, "compatible", + "virtio,mmio") == 0); + + /* reg = */ + uint32_t reg[2] = {cpu_to_fdt32(new_addr), cpu_to_fdt32(size)}; + assert(fdt_setprop(dtb_buf, subnode, "reg", reg, sizeof(reg)) == 0); + + /* interrupts = */ + uint32_t irq = cpu_to_fdt32(new_irq); + assert(fdt_setprop(dtb_buf, subnode, "interrupts", &irq, + sizeof(irq)) == 0); + } } + memcpy(blob, dtb_buf, minimal_len + DTB_EXPAND_SIZE); + free(dtb_buf); + totalsize = fdt_totalsize(blob); *ram_loc += totalsize; return; @@ -406,28 +471,36 @@ static void rv_fsync_device() * * vblk is optional, so it could be NULL */ - if (attr->vblk) { - if (attr->vblk->disk_fd >= 3) { - if (attr->vblk->device_features & VIRTIO_BLK_F_RO) /* readonly */ - goto end; - - if (pwrite(attr->vblk->disk_fd, attr->vblk->disk, - attr->vblk->disk_size, 0) == -1) { - rv_log_error("pwrite block device failed: %s", strerror(errno)); - return; + if (attr->vblk_cnt) { + for (int i = 0; i < attr->vblk_cnt; i++) { + virtio_blk_state_t *vblk = attr->vblk[i]; + if (vblk->disk_fd >= 3) { + if (vblk->device_features & VIRTIO_BLK_F_RO) /* readonly */ + goto end; + + if (pwrite(vblk->disk_fd, vblk->disk, vblk->disk_size, 0) == + -1) { + rv_log_error("pwrite block device failed: %s", + strerror(errno)); + return; + } + + if (fsync(vblk->disk_fd) == -1) { + rv_log_error("fsync block device failed: %s", + strerror(errno)); + return; + } + rv_log_info("Sync block device OK"); + + end: + close(vblk->disk_fd); } - if (fsync(attr->vblk->disk_fd) == -1) { - rv_log_error("fsync block device failed: %s", strerror(errno)); - return; - } - rv_log_info("Sync block device OK"); - - end: - close(attr->vblk->disk_fd); + vblk_delete(vblk); } - vblk_delete(attr->vblk); + free(attr->vblk); + free(attr->disk); } } #endif /* RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) */ @@ -548,6 +621,9 @@ riscv_t *rv_create(riscv_user_t rv_attr) * *----------------*----------------*-------* */ + /* load_dtb needs the count to add the virtio block subnode dynamically */ + attr->vblk_cnt = attr->data.system.vblk_device_cnt; + char *ram_loc = (char *) attr->mem->mem_base; map_file(&ram_loc, attr->data.system.kernel); rv_log_info("Kernel loaded"); @@ -590,36 +666,47 @@ riscv_t *rv_create(riscv_user_t rv_attr) attr->uart->out_fd = attr->fd_stdout; /* setup virtio-blk */ - attr->vblk = NULL; - if (attr->data.system.vblk_device) { + attr->vblk_mmio_base_hi = 0x41; + attr->vblk_mmio_max_hi = attr->vblk_mmio_base_hi + attr->vblk_cnt; + + attr->vblk = malloc(sizeof(virtio_blk_state_t *) * attr->vblk_cnt); + assert(attr->vblk); + attr->disk = malloc(sizeof(uint32_t *) * attr->vblk_cnt); + assert(attr->disk); + + if (attr->vblk_cnt) { + for (int i = 0; i < attr->vblk_cnt; i++) { /* Currently, only used for block image path and permission */ #define MAX_OPTS 2 - char *vblk_opts[MAX_OPTS] = {NULL}; - int vblk_opt_idx = 0; - char *opt = strtok(attr->data.system.vblk_device, ","); - while (opt) { - if (vblk_opt_idx == MAX_OPTS) { - rv_log_error("Too many arguments for vblk"); - break; + char *vblk_device_str = attr->data.system.vblk_device[i]; + char *vblk_opts[MAX_OPTS] = {NULL}; + int vblk_opt_idx = 0; + char *opt = strtok(vblk_device_str, ","); + while (opt) { + if (vblk_opt_idx == MAX_OPTS) { + rv_log_error("Too many arguments for vblk"); + break; + } + vblk_opts[vblk_opt_idx++] = opt; + opt = strtok(NULL, ","); } - vblk_opts[vblk_opt_idx++] = opt; - opt = strtok(NULL, ","); - } - char *vblk_device = vblk_opts[0]; - char *vblk_readonly = vblk_opts[1]; - - bool readonly = false; - if (vblk_readonly) { - if (strcmp(vblk_readonly, "readonly") != 0) { - rv_log_error("Unknown vblk option: %s", vblk_readonly); - exit(EXIT_FAILURE); + char *vblk_device = vblk_opts[0]; + char *vblk_readonly = vblk_opts[1]; + + bool readonly = false; + if (vblk_readonly) { + if (strcmp(vblk_readonly, "readonly") != 0) { + rv_log_error("Unknown vblk option: %s", vblk_readonly); + exit(EXIT_FAILURE); + } + readonly = true; } - readonly = true; - } - attr->vblk = vblk_new(); - attr->vblk->ram = (uint32_t *) attr->mem->mem_base; - attr->disk = virtio_blk_init(attr->vblk, vblk_device, readonly); + attr->vblk[i] = vblk_new(); + attr->vblk[i]->ram = (uint32_t *) attr->mem->mem_base; + attr->disk[i] = + virtio_blk_init(attr->vblk[i], vblk_device, readonly); + } } capture_keyboard_input(); diff --git a/src/riscv.h b/src/riscv.h index 88eceef5..63f375be 100644 --- a/src/riscv.h +++ b/src/riscv.h @@ -461,7 +461,8 @@ typedef struct { char *kernel; char *initrd; char *bootargs; - char *vblk_device; + char **vblk_device; + int vblk_device_cnt; } vm_system_t; #endif /* RV32_HAS(SYSTEM) */ @@ -483,8 +484,13 @@ typedef struct { plic_t *plic; /* virtio-blk device */ - uint32_t *disk; - virtio_blk_state_t *vblk; + uint32_t **disk; + virtio_blk_state_t **vblk; + virtio_blk_state_t *vblk_curr; + uint32_t vblk_mmio_base_hi; + uint32_t vblk_mmio_max_hi; + int vblk_irq_base; + int vblk_cnt; #endif /* RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) */ /* vm memory object */ diff --git a/src/system.c b/src/system.c index a87b5548..4dd66f22 100644 --- a/src/system.c +++ b/src/system.c @@ -22,11 +22,13 @@ void emu_update_uart_interrupts(riscv_t *rv) void emu_update_vblk_interrupts(riscv_t *rv) { vm_attr_t *attr = PRIV(rv); - if (attr->vblk->interrupt_status) - attr->plic->active |= IRQ_VBLK_BIT; - else - attr->plic->active &= ~IRQ_VBLK_BIT; - plic_update_interrupts(attr->plic); + for (int i = 0; i < attr->vblk_cnt; i++) { + if (attr->vblk[i]->interrupt_status) + attr->plic->active |= IRQ_VBLK_BIT(attr->vblk_irq_base, i); + else + attr->plic->active &= ~IRQ_VBLK_BIT(attr->vblk_irq_base, i); + plic_update_interrupts(attr->plic); + } } #endif diff --git a/src/system.h b/src/system.h index af94f612..ace50359 100644 --- a/src/system.h +++ b/src/system.h @@ -54,11 +54,11 @@ enum SUPPORTED_MMIO { break; \ case MMIO_VIRTIOBLK: \ IIF(rw)( /* read */ \ - mmio_read_val = virtio_blk_read(PRIV(rv)->vblk, addr & 0xFFFFF); \ + mmio_read_val = virtio_blk_read(PRIV(rv)->vblk_curr, addr & 0xFFFFF); \ emu_update_vblk_interrupts(rv); \ return mmio_read_val; \ , /* write */ \ - virtio_blk_write(PRIV(rv)->vblk, addr & 0xFFFFF, val); \ + virtio_blk_write(PRIV(rv)->vblk_curr, addr & 0xFFFFF, val); \ emu_update_vblk_interrupts(rv); \ return; \ ) \ @@ -69,49 +69,59 @@ enum SUPPORTED_MMIO { } /* clang-format on */ -#define MMIO_READ() \ - do { \ - uint32_t mmio_read_val; \ - if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \ - /* 256 regions of 1MiB */ \ - switch ((addr >> 20) & MASK(8)) { \ - case 0x0: \ - case 0x2: /* PLIC (0 - 0x3F) */ \ - MMIO_OP(MMIO_PLIC, MMIO_R); \ - break; \ - case 0x40: /* UART */ \ - MMIO_OP(MMIO_UART, MMIO_R); \ - break; \ - case 0x42: /* Virtio-blk */ \ - MMIO_OP(MMIO_VIRTIOBLK, MMIO_R); \ - break; \ - default: \ - __UNREACHABLE; \ - break; \ - } \ - } \ +#define MMIO_READ() \ + do { \ + uint32_t mmio_read_val; \ + if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \ + /* 256 regions of 1MiB */ \ + uint32_t hi = (addr >> 20) & MASK(8); \ + if (hi >= PRIV(rv)->vblk_mmio_base_hi && \ + hi <= PRIV(rv)->vblk_mmio_max_hi) { \ + PRIV(rv)->vblk_curr = \ + PRIV(rv)->vblk[hi - PRIV(rv)->vblk_mmio_base_hi]; \ + MMIO_OP(MMIO_VIRTIOBLK, MMIO_R); \ + } else { \ + switch (hi) { \ + case 0x0: \ + case 0x2: /* PLIC (0 - 0x3F) */ \ + MMIO_OP(MMIO_PLIC, MMIO_R); \ + break; \ + case 0x40: /* UART */ \ + MMIO_OP(MMIO_UART, MMIO_R); \ + break; \ + default: \ + __UNREACHABLE; \ + break; \ + } \ + } \ + } \ } while (0) -#define MMIO_WRITE() \ - do { \ - if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \ - /* 256 regions of 1MiB */ \ - switch ((addr >> 20) & MASK(8)) { \ - case 0x0: \ - case 0x2: /* PLIC (0 - 0x3F) */ \ - MMIO_OP(MMIO_PLIC, MMIO_W); \ - break; \ - case 0x40: /* UART */ \ - MMIO_OP(MMIO_UART, MMIO_W); \ - break; \ - case 0x42: /* Virtio-blk */ \ - MMIO_OP(MMIO_VIRTIOBLK, MMIO_W); \ - break; \ - default: \ - __UNREACHABLE; \ - break; \ - } \ - } \ +#define MMIO_WRITE() \ + do { \ + if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \ + /* 256 regions of 1MiB */ \ + uint32_t hi = (addr >> 20) & MASK(8); \ + if (hi >= PRIV(rv)->vblk_mmio_base_hi && \ + hi <= PRIV(rv)->vblk_mmio_max_hi) { \ + PRIV(rv)->vblk_curr = \ + PRIV(rv)->vblk[hi - PRIV(rv)->vblk_mmio_base_hi]; \ + MMIO_OP(MMIO_VIRTIOBLK, MMIO_W); \ + } else { \ + switch (hi) { \ + case 0x0: \ + case 0x2: /* PLIC (0 - 0x3F) */ \ + MMIO_OP(MMIO_PLIC, MMIO_W); \ + break; \ + case 0x40: /* UART */ \ + MMIO_OP(MMIO_UART, MMIO_W); \ + break; \ + default: \ + __UNREACHABLE; \ + break; \ + } \ + } \ + } \ } while (0) void emu_update_uart_interrupts(riscv_t *rv);