Skip to content

Commit

Permalink
Merge pull request sysprog21#21 from shengwen-tw/virtio-blk
Browse files Browse the repository at this point in the history
Implement virtio-blk device
  • Loading branch information
jserv authored Jul 24, 2023
2 parents ec5824d + efda0c1 commit 21dc47d
Show file tree
Hide file tree
Showing 10 changed files with 637 additions and 21 deletions.
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

0 comments on commit 21dc47d

Please sign in to comment.