Skip to content

Implement virtio-blk device #21

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

Merged
merged 6 commits into from
Jul 24, 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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
*.o
*.o.d
*.sw*
semu

# Linux-specific
*.dtb
Image
ext4.img
11 changes: 9 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ OBJS_EXTRA :=

ifeq ($(UNAME_S),Linux)
CFLAGS += -D ENABLE_VIRTIONET
CFLAGS += -D ENABLE_VIRTIOBLK
OBJS_EXTRA += virtio-net.o
OBJS_EXTRA += virtio-blk.o
MINIMAL_DTS = minimal-virtio.dts
else
MINIMAL_DTS = minimal.dts
Expand Down Expand Up @@ -44,9 +46,13 @@ minimal.dtb: $(MINIMAL_DTS)
# Rules for downloading prebuilt Linux kernel image
include mk/external.mk

check: $(BIN) minimal.dtb $(KERNEL_DATA)
ext4.img:
$(Q)dd if=/dev/zero of=$@ bs=4k count=600
$(Q)mkfs.ext4 -F $@

check: $(BIN) minimal.dtb $(KERNEL_DATA) ext4.img
@$(call notice, Ready to launch Linux kernel. Please be patient.)
$(Q)./$(BIN) $(KERNEL_DATA)
$(Q)./$(BIN) -k $(KERNEL_DATA) -b minimal.dtb -d ext4.img

build-image:
scripts/build-image.sh
Expand All @@ -57,5 +63,6 @@ clean:
distclean: clean
$(Q)$(RM) minimal.dtb
$(Q)$(RM) Image
$(Q)$(RM) ext4.img

-include $(deps)
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ A minimalist RISC-V system emulator capable of running Linux the kernel and corr
- UART: 8250/16550
- PLIC (platform-level interrupt controller): 32 interrupts, no priority
- Standard SBI, with the timer extension
- VirtIO: virtio-net, mapped as TAP interface
- VirtIO: virtio-blk acquires disk image from the host, and virtio-net is mapped as TAP interface

## Prerequisites

Expand Down Expand Up @@ -62,6 +62,16 @@ Please note that it only supports the Linux host environment.
$ make build-image
```

## Usage

```
./semu -k linux-image [-b dtb-file] [-d disk-image]
```

* `linux-image` is the path to the Linux kernel `Image`.
* `dtb-file` is optional, as it specifies the user-specified device tree blob.
* `disk-image` is optional, as it specifies the path of a disk image in ext4 file system for the virtio-blk device.

## License

`semu` is released under the MIT License.
Expand Down
3 changes: 2 additions & 1 deletion configs/linux.config
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ CONFIG_BLK_DEV=y
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_RAM is not set
# CONFIG_ATA_OVER_ETH is not set
# CONFIG_VIRTIO_BLK is not set
CONFIG_VIRTIO_BLK=y
# CONFIG_BLK_DEV_RBD is not set
# CONFIG_BLK_DEV_UBLK is not set

Expand Down Expand Up @@ -1594,6 +1594,7 @@ CONFIG_NLATTR=y
CONFIG_GENERIC_ATOMIC64=y
# CONFIG_IRQ_POLL is not set
CONFIG_LIBFDT=y
CONFIG_SG_POOL=y
CONFIG_ARCH_STACKWALK=y
CONFIG_SBITMAP=y
# end of Library routines
Expand Down
51 changes: 51 additions & 0 deletions device.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,66 @@ void virtio_net_refresh_queue(virtio_net_state_t *vnet);
bool virtio_net_init(virtio_net_state_t *vnet);
#endif /* ENABLE_VIRTIONET */

/* VirtIO-Block */
#if defined(ENABLE_VIRTIOBLK)

#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;
uint64_t capacity;
} virtio_blk_state_t;

void virtio_blk_read(vm_t *vm,
virtio_blk_state_t *vblk,
uint32_t addr,
uint8_t width,
uint32_t *value);

void virtio_blk_write(vm_t *vm,
virtio_blk_state_t *vblk,
uint32_t addr,
uint8_t width,
uint32_t value);

uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file);
#endif /* ENABLE_VIRTIOBLK */

/* memory mapping */

typedef struct {
bool stopped;
uint32_t *ram;
uint32_t *disk;
plic_state_t plic;
u8250_state_t uart;
#if defined(ENABLE_VIRTIONET)
virtio_net_state_t vnet;
#endif
#if defined(ENABLE_VIRTIOBLK)
virtio_blk_state_t vblk;
#endif
uint32_t timer_lo, timer_hi;
} emu_state_t;
101 changes: 95 additions & 6 deletions main.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -57,6 +58,18 @@ static void emu_update_vnet_interrupts(vm_t *vm)
}
#endif

#if defined(ENABLE_VIRTIOBLK)
static void emu_update_vblk_interrupts(vm_t *vm)
{
emu_state_t *data = (emu_state_t *) vm->priv;
if (data->vblk.InterruptStatus)
data->plic.active |= IRQ_VBLK_BIT;
else
data->plic.active &= ~IRQ_VBLK_BIT;
plic_update_interrupts(vm, &data->plic);
}
#endif

static void mem_load(vm_t *vm, uint32_t addr, uint8_t width, uint32_t *value)
{
emu_state_t *data = (emu_state_t *) vm->priv;
Expand Down Expand Up @@ -85,8 +98,14 @@ static void mem_load(vm_t *vm, uint32_t addr, uint8_t width, uint32_t *value)
emu_update_vnet_interrupts(vm);
return;
#endif
#if defined(ENABLE_VIRTIOBLK)
case 0x42: /* virtio-blk */
virtio_blk_read(vm, &data->vblk, addr & 0xFFFFF, width, value);
emu_update_vblk_interrupts(vm);
return;
}
}
#endif
vm_set_exception(vm, RV_EXC_LOAD_FAULT, vm->exc_val);
}

Expand Down Expand Up @@ -118,7 +137,13 @@ static void mem_store(vm_t *vm, uint32_t addr, uint8_t width, uint32_t value)
emu_update_vnet_interrupts(vm);
return;
#endif
#if defined(ENABLE_VIRTIOBLK)
case 0x42: /* virtio-blk */
virtio_blk_write(vm, &data->vblk, addr & 0xFFFFF, width, value);
emu_update_vblk_interrupts(vm);
return;
}
#endif
}
vm_set_exception(vm, RV_EXC_STORE_FAULT, vm->exc_val);
}
Expand Down Expand Up @@ -264,8 +289,67 @@ static void map_file(char **ram_loc, const char *name)
close(fd);
}

static void usage(const char *execpath)
{
fprintf(stderr, "Usage: %s -k linux-image [-b dtb] [-d disk-image]\n",
execpath);
}

static void handle_options(int argc,
char **argv,
char **kernel_file,
char **dtb_file,
char **disk_file)
{
*kernel_file = *dtb_file = *disk_file = NULL;

int optidx = 0;
struct option opts[] = {
{"kernel", 1, NULL, 'k'},
{"dtb", 1, NULL, 'b'},
{"disk", 1, NULL, 'd'},
{"help", 0, NULL, 'h'},
};

int c;
while ((c = getopt_long(argc, argv, "k:b:d:h", opts, &optidx)) != -1) {
switch (c) {
case 'k':
*kernel_file = optarg;
break;
case 'b':
*dtb_file = optarg;
break;
case 'd':
*disk_file = optarg;
break;
case 'h':
usage(argv[0]);
exit(0);
default:
break;
}
}

if (!*kernel_file) {
fprintf(stderr,
"Linux kernel image file must "
"be provided via -k option.\n");
usage(argv[0]);
exit(2);
}

if (!*dtb_file)
*dtb_file = "minimal.dtb";
}

static int semu_start(int argc, char **argv)
{
char *kernel_file;
char *dtb_file;
char *disk_file;
handle_options(argc, argv, &kernel_file, &dtb_file, &disk_file);

/* Initialize the emulator */
emu_state_t emu;
memset(&emu, 0, sizeof(emu));
Expand All @@ -289,11 +373,11 @@ static int semu_start(int argc, char **argv)

char *ram_loc = (char *) emu.ram;
/* Load Linux kernel image */
map_file(&ram_loc, argv[1]);
map_file(&ram_loc, kernel_file);
/* Load at last 1 MiB to prevent kernel / initrd from overwriting it */
uint32_t dtb_addr = RAM_SIZE - 1024 * 1024; /* Device tree */
ram_loc = ((char *) emu.ram) + dtb_addr;
map_file(&ram_loc, (argc == 3) ? argv[2] : "minimal.dtb");
map_file(&ram_loc, dtb_file);
/* TODO: load disk image via virtio_blk */
/* Hook for unmapping files */
atexit(unmap_files);
Expand All @@ -312,6 +396,10 @@ static int semu_start(int argc, char **argv)
fprintf(stderr, "No virtio-net functioned\n");
emu.vnet.ram = emu.ram;
#endif
#if defined(ENABLE_VIRTIOBLK)
emu.vblk.ram = emu.ram;
emu.disk = virtio_blk_init(&(emu.vblk), disk_file);
#endif

/* Emulate */
uint32_t peripheral_update_ctr = 0;
Expand All @@ -328,6 +416,11 @@ static int semu_start(int argc, char **argv)
if (emu.vnet.InterruptStatus)
emu_update_vnet_interrupts(&vm);
#endif

#if defined(ENABLE_VIRTIOBLK)
if (emu.vblk.InterruptStatus)
emu_update_vblk_interrupts(&vm);
#endif
}

if (vm.insn_count_hi > emu.timer_hi ||
Expand Down Expand Up @@ -360,9 +453,5 @@ static int semu_start(int argc, char **argv)

int main(int argc, char **argv)
{
if (argc < 2) {
printf("Usage: %s <linux-image> [<dtb>]\n", argv[0]);
return 2;
}
return semu_start(argc, argv);
}
6 changes: 6 additions & 0 deletions minimal-virtio.dts
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,11 @@
reg = <0x4100000 0x100000>;
interrupts = <2>;
};

blk0: virtio@4200000 {
compatible = "virtio,mmio";
reg = <0x4200000 0x200>;
interrupts = <3>;
};
};
};
Loading