Skip to content

Commit ee4cb91

Browse files
committed
Support multiple virtio block devices
Previously, sysprog21#539 set VBLK_DEV_CNT_MAX to 1, limiting the virtual machine to a single block device. This commit introduces support for multiple block devices by switching from statically allocated storage to dynamic allocation based on CLI input. Key changes: 1. Dynamic Allocation Virtio block related fields are now dynamically allocated based on the number of -x vblk options provided. 2. Device Tree Generation The prewritten virtio@ subnode for block device in device/minimal.dts is commented and it can be an example subnode for future reference. load_dtb() dynamically appends virtio@ subnodes under the SoC node based on the number of block devices. 3. MMIO Handling Instead of hardcoding switch cases for each MMIO region of block device, the code now checks whether the accessed MMIO address falls within the valid range of any block device. Note: The /dev/vdx device order 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.
1 parent 6fd635b commit ee4cb91

File tree

9 files changed

+242
-126
lines changed

9 files changed

+242
-126
lines changed

mk/system.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ $(BUILD_DTB2C): $(BIN_TO_C) $(BUILD_DTB)
2929
$(VECHO) " BIN2C\t$@\n"
3030
$(Q)$(BIN_TO_C) $(BUILD_DTB) > $@
3131

32+
MMIO_TMPL := src/devices/mmio_template.h
33+
3234
$(DEV_OUT)/%.o: $(DEV_SRC)/%.c $(deps_emcc)
3335
$(Q)mkdir -p $(DEV_OUT)
3436
$(VECHO) " CC\t$@\n"

src/devices/minimal.dts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,14 @@
6666
clock-frequency = <5000000>; /* the baudrate divisor is ignored */
6767
};
6868

69-
blk0: virtio@4200000 {
69+
/*
70+
* Virtio block example subnode
71+
* The actual subnode are generated dynamically depends on the CLI -x vblk option
72+
*/
73+
/*blk0: virtio@4100000 {
7074
compatible = "virtio,mmio";
71-
reg = <0x4200000 0x200>;
72-
interrupts = <3>;
73-
};
75+
reg = <0x4100000 0x200>;
76+
interrupts = <2>;
77+
};*/
7478
};
7579
};

src/devices/virtio-blk.c

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
#define DISK_BLK_SIZE 512
3434

3535
/* TODO: Enable mutiple virtio-blk devices. */
36-
#define VBLK_DEV_CNT_MAX 1
36+
#define VBLK_DEV_CNT_MAX 3
3737

3838
#define VBLK_FEATURES_0 0
3939
#define VBLK_FEATURES_1 1 /* VIRTIO_F_VERSION_1 */
@@ -81,9 +81,6 @@ PACKED(struct vblk_req_header {
8181
uint8_t status;
8282
});
8383

84-
static struct virtio_blk_config vblk_configs[VBLK_DEV_CNT_MAX];
85-
static int vblk_dev_cnt = 0;
86-
8784
static void virtio_blk_set_fail(virtio_blk_state_t *vblk)
8885
{
8986
vblk->status |= VIRTIO_STATUS_DEVICE_NEEDS_RESET;
@@ -401,20 +398,16 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk,
401398
char *disk_file,
402399
bool readonly)
403400
{
404-
if (vblk_dev_cnt >= VBLK_DEV_CNT_MAX) {
405-
rv_log_error(
406-
"Exceeded the number of virtio-blk devices that can be allocated");
407-
exit(EXIT_FAILURE);
408-
}
409-
410401
/*
411402
* For mmap_fallback, if vblk is not specified, disk_fd should remain -1 and
412403
* no fsync should be performed on exit.
413404
*/
405+
414406
vblk->disk_fd = -1;
415407

416408
/* Allocate memory for the private member */
417-
vblk->priv = &vblk_configs[vblk_dev_cnt++];
409+
vblk->priv = calloc(1, sizeof(struct virtio_blk_config));
410+
assert(vblk->priv);
418411

419412
/* No disk image is provided */
420413
if (!disk_file) {
@@ -549,5 +542,6 @@ void vblk_delete(virtio_blk_state_t *vblk)
549542
else
550543
munmap(vblk->disk, VBLK_PRIV(vblk)->disk_size);
551544
#endif
545+
free(vblk->priv);
552546
free(vblk);
553547
}

src/devices/virtio.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ struct virtq_desc {
7676
uint16_t next;
7777
};
7878

79-
#define IRQ_VBLK_SHIFT 3
80-
#define IRQ_VBLK_BIT (1 << IRQ_VBLK_SHIFT)
79+
#define IRQ_VBLK_BIT(base, i) (1 << (base + i))
8180

8281
typedef struct {
8382
uint32_t queue_num;

src/main.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ static char *prof_out_file;
6060
static char *opt_kernel_img;
6161
static char *opt_rootfs_img;
6262
static char *opt_bootargs;
63-
static char *opt_virtio_blk_img;
63+
/* FIXME: handle overflow */
64+
#define VBLK_DEV_MAX 100
65+
static char *opt_virtio_blk_img[VBLK_DEV_MAX];
66+
static int opt_virtio_blk_idx = 0;
6467
#endif
6568

6669
static void print_usage(const char *filename)
@@ -78,7 +81,8 @@ static void print_usage(const char *filename)
7881
#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER)
7982
" -k <image> : use <image> as kernel image\n"
8083
" -i <image> : use <image> as rootfs\n"
81-
" -x vblk:<image>[,readonly] : use <image> as virtio-blk disk image "
84+
" -x vblk:<image>[,readonly] [-x vblk:<image>[,readonly]]: use "
85+
"<image> as virtio-blk disk image "
8286
"(default read and write)\n"
8387
" -b <bootargs> : use customized <bootargs> for the kernel\n"
8488
#endif
@@ -127,7 +131,8 @@ static bool parse_args(int argc, char **args)
127131
break;
128132
case 'x':
129133
if (!strncmp("vblk:", optarg, 5))
130-
opt_virtio_blk_img = optarg + 5; /* strlen("vblk:") */
134+
opt_virtio_blk_img[opt_virtio_blk_idx++] =
135+
optarg + 5; /* strlen("vblk:") */
131136
else
132137
return false;
133138
emu_argc++;
@@ -273,7 +278,12 @@ int main(int argc, char **args)
273278
attr.data.system.kernel = opt_kernel_img;
274279
attr.data.system.initrd = opt_rootfs_img;
275280
attr.data.system.bootargs = opt_bootargs;
276-
attr.data.system.vblk_device = opt_virtio_blk_img;
281+
if (opt_virtio_blk_idx) {
282+
attr.data.system.vblk_device = opt_virtio_blk_img;
283+
attr.data.system.vblk_device_cnt = opt_virtio_blk_idx;
284+
} else {
285+
attr.data.system.vblk_device = NULL;
286+
}
277287
#else
278288
attr.data.user.elf_program = opt_prog_name;
279289
#endif

src/riscv.c

Lines changed: 141 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -293,14 +293,26 @@ static void load_dtb(char **ram_loc, vm_attr_t *attr)
293293
{
294294
#include "minimal_dtb.h"
295295
char *bootargs = attr->data.system.bootargs;
296-
char *vblk = attr->data.system.vblk_device;
296+
char **vblk = attr->data.system.vblk_device;
297297
char *blob = *ram_loc;
298298
char *buf;
299299
size_t len;
300300
int node, err;
301301
int totalsize;
302302

303-
memcpy(blob, minimal, sizeof(minimal));
303+
#define DTB_EXPAND_SIZE 1024 // or more if needed
304+
305+
/* Allocate enough memory for DTB + extra room */
306+
int minimal_len = ARRAY_SIZE(minimal);
307+
void *dtb_buf = calloc(minimal_len + DTB_EXPAND_SIZE, sizeof(uint8_t));
308+
assert(dtb_buf);
309+
310+
/* Expand it to a usable DTB blob */
311+
err = fdt_open_into(minimal, dtb_buf, minimal_len + DTB_EXPAND_SIZE);
312+
if (err < 0) {
313+
rv_log_error("fdt_open_into fails\n");
314+
exit(EXIT_FAILURE);
315+
}
304316

305317
if (bootargs) {
306318
node = fdt_path_offset(blob, "/chosen");
@@ -320,18 +332,71 @@ static void load_dtb(char **ram_loc, vm_attr_t *attr)
320332
assert(!err);
321333
}
322334

323-
/* remove the vblk node from soc if it is not specified */
324-
if (!vblk) {
325-
int subnode;
326-
node = fdt_path_offset(blob, "/soc@F0000000");
335+
if (vblk) {
336+
int node = fdt_path_offset(dtb_buf, "/soc@F0000000");
327337
assert(node >= 0);
328338

329-
subnode = fdt_subnode_offset(blob, node, "virtio@4200000");
330-
assert(subnode >= 0);
339+
uint32_t base_addr = 0x4000000;
340+
uint32_t addr_offset = 0x100000;
341+
uint32_t size = 0x200;
342+
343+
uint32_t next_addr = base_addr;
344+
uint32_t next_irq = 1;
345+
346+
// scan existing nodes to get next addr and irq
347+
int subnode;
348+
fdt_for_each_subnode(subnode, dtb_buf, node)
349+
{
350+
const char *name = fdt_get_name(dtb_buf, subnode, NULL);
351+
assert(name);
352+
353+
uint32_t addr = strtoul(name + 7, NULL, 16);
354+
if (addr == next_addr)
355+
next_addr = addr + addr_offset;
356+
357+
const fdt32_t *irq_prop =
358+
fdt_getprop(dtb_buf, subnode, "interrupts", NULL);
359+
if (irq_prop) {
360+
uint32_t irq = fdt32_to_cpu(*irq_prop);
361+
if (irq == next_irq)
362+
next_irq = irq + 1;
363+
}
364+
}
365+
/* set IRQ for virtio block, see devices/virtio.h */
366+
attr->vblk_irq_base = next_irq;
367+
368+
/* adding new virtio block nodes */
369+
for (int i = 0; i < attr->vblk_cnt; i++) {
370+
uint32_t new_addr = next_addr + i * addr_offset;
371+
uint32_t new_irq = next_irq + i;
372+
373+
char node_name[32];
374+
snprintf(node_name, sizeof(node_name), "virtio@%x", new_addr);
375+
376+
int subnode = fdt_add_subnode(dtb_buf, node, node_name);
377+
if (subnode == -FDT_ERR_NOSPACE) {
378+
rv_log_warn("add subnode no space!\n");
379+
}
380+
assert(subnode >= 0);
381+
382+
/* compatible = "virtio,mmio" */
383+
assert(fdt_setprop_string(dtb_buf, subnode, "compatible",
384+
"virtio,mmio") == 0);
385+
386+
/* reg = <new_addr size> */
387+
uint32_t reg[2] = {cpu_to_fdt32(new_addr), cpu_to_fdt32(size)};
388+
assert(fdt_setprop(dtb_buf, subnode, "reg", reg, sizeof(reg)) == 0);
331389

332-
assert(fdt_del_node(blob, subnode) == 0);
390+
/* interrupts = <new_irq> */
391+
uint32_t irq = cpu_to_fdt32(new_irq);
392+
assert(fdt_setprop(dtb_buf, subnode, "interrupts", &irq,
393+
sizeof(irq)) == 0);
394+
}
333395
}
334396

397+
memcpy(blob, dtb_buf, minimal_len + DTB_EXPAND_SIZE);
398+
free(dtb_buf);
399+
335400
totalsize = fdt_totalsize(blob);
336401
*ram_loc += totalsize;
337402
return;
@@ -406,28 +471,36 @@ static void rv_fsync_device()
406471
*
407472
* vblk is optional, so it could be NULL
408473
*/
409-
if (attr->vblk) {
410-
if (attr->vblk->disk_fd >= 3) {
411-
if (attr->vblk->device_features & VIRTIO_BLK_F_RO) /* readonly */
412-
goto end;
413-
414-
if (pwrite(attr->vblk->disk_fd, attr->vblk->disk,
415-
attr->vblk->disk_size, 0) == -1) {
416-
rv_log_error("pwrite block device failed: %s", strerror(errno));
417-
return;
474+
if (attr->vblk_cnt) {
475+
for (int i = 0; i < attr->vblk_cnt; i++) {
476+
virtio_blk_state_t *vblk = attr->vblk[i];
477+
if (vblk->disk_fd >= 3) {
478+
if (vblk->device_features & VIRTIO_BLK_F_RO) /* readonly */
479+
goto end;
480+
481+
if (pwrite(vblk->disk_fd, vblk->disk, vblk->disk_size, 0) ==
482+
-1) {
483+
rv_log_error("pwrite block device failed: %s",
484+
strerror(errno));
485+
return;
486+
}
487+
488+
if (fsync(vblk->disk_fd) == -1) {
489+
rv_log_error("fsync block device failed: %s",
490+
strerror(errno));
491+
return;
492+
}
493+
rv_log_info("Sync block device OK");
494+
495+
end:
496+
close(vblk->disk_fd);
418497
}
419498

420-
if (fsync(attr->vblk->disk_fd) == -1) {
421-
rv_log_error("fsync block device failed: %s", strerror(errno));
422-
return;
423-
}
424-
rv_log_info("Sync block device OK");
425-
426-
end:
427-
close(attr->vblk->disk_fd);
499+
vblk_delete(vblk);
428500
}
429501

430-
vblk_delete(attr->vblk);
502+
free(attr->vblk);
503+
free(attr->disk);
431504
}
432505
}
433506
#endif /* RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) */
@@ -548,6 +621,9 @@ riscv_t *rv_create(riscv_user_t rv_attr)
548621
* *----------------*----------------*-------*
549622
*/
550623

624+
/* load_dtb needs the count to add the virtio block subnode dynamically */
625+
attr->vblk_cnt = attr->data.system.vblk_device_cnt;
626+
551627
char *ram_loc = (char *) attr->mem->mem_base;
552628
map_file(&ram_loc, attr->data.system.kernel);
553629
rv_log_info("Kernel loaded");
@@ -590,36 +666,49 @@ riscv_t *rv_create(riscv_user_t rv_attr)
590666
attr->uart->out_fd = attr->fd_stdout;
591667

592668
/* setup virtio-blk */
593-
attr->vblk = NULL;
594-
if (attr->data.system.vblk_device) {
669+
attr->vblk_mmio_base_hi = 0x41;
670+
attr->vblk_mmio_max_hi = attr->vblk_mmio_base_hi + attr->vblk_cnt;
671+
672+
rv_log_warn("0x%x 0x%x", attr->vblk_mmio_base_hi, attr->vblk_mmio_max_hi);
673+
674+
attr->vblk = malloc(sizeof(virtio_blk_state_t *) * attr->vblk_cnt);
675+
assert(attr->vblk);
676+
attr->disk = malloc(sizeof(uint32_t *) * attr->vblk_cnt);
677+
assert(attr->disk);
678+
679+
if (attr->vblk_cnt) {
680+
for (int i = 0; i < attr->vblk_cnt; i++) {
595681
/* Currently, only used for block image path and permission */
596682
#define MAX_OPTS 2
597-
char *vblk_opts[MAX_OPTS] = {NULL};
598-
int vblk_opt_idx = 0;
599-
char *opt = strtok(attr->data.system.vblk_device, ",");
600-
while (opt) {
601-
if (vblk_opt_idx == MAX_OPTS) {
602-
rv_log_error("Too many arguments for vblk");
603-
break;
683+
char *vblk_device_str = attr->data.system.vblk_device[i];
684+
char *vblk_opts[MAX_OPTS] = {NULL};
685+
int vblk_opt_idx = 0;
686+
char *opt = strtok(vblk_device_str, ",");
687+
while (opt) {
688+
if (vblk_opt_idx == MAX_OPTS) {
689+
rv_log_error("Too many arguments for vblk");
690+
break;
691+
}
692+
vblk_opts[vblk_opt_idx++] = opt;
693+
opt = strtok(NULL, ",");
604694
}
605-
vblk_opts[vblk_opt_idx++] = opt;
606-
opt = strtok(NULL, ",");
607-
}
608-
char *vblk_device = vblk_opts[0];
609-
char *vblk_readonly = vblk_opts[1];
610-
611-
bool readonly = false;
612-
if (vblk_readonly) {
613-
if (strcmp(vblk_readonly, "readonly") != 0) {
614-
rv_log_error("Unknown vblk option: %s", vblk_readonly);
615-
exit(EXIT_FAILURE);
695+
char *vblk_device = vblk_opts[0];
696+
char *vblk_readonly = vblk_opts[1];
697+
698+
bool readonly = false;
699+
if (vblk_readonly) {
700+
if (strcmp(vblk_readonly, "readonly") != 0) {
701+
rv_log_error("Unknown vblk option: %s", vblk_readonly);
702+
exit(EXIT_FAILURE);
703+
}
704+
readonly = true;
616705
}
617-
readonly = true;
618-
}
619706

620-
attr->vblk = vblk_new();
621-
attr->vblk->ram = (uint32_t *) attr->mem->mem_base;
622-
attr->disk = virtio_blk_init(attr->vblk, vblk_device, readonly);
707+
attr->vblk[i] = vblk_new();
708+
attr->vblk[i]->ram = (uint32_t *) attr->mem->mem_base;
709+
attr->disk[i] =
710+
virtio_blk_init(attr->vblk[i], vblk_device, readonly);
711+
}
623712
}
624713

625714
capture_keyboard_input();

0 commit comments

Comments
 (0)