Skip to content

Commit 21dc47d

Browse files
authored
Merge pull request #21 from shengwen-tw/virtio-blk
Implement virtio-blk device
2 parents ec5824d + efda0c1 commit 21dc47d

File tree

10 files changed

+637
-21
lines changed

10 files changed

+637
-21
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
*.o
22
*.o.d
3+
*.sw*
34
semu
45

56
# Linux-specific
67
*.dtb
78
Image
9+
ext4.img

Makefile

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ OBJS_EXTRA :=
88

99
ifeq ($(UNAME_S),Linux)
1010
CFLAGS += -D ENABLE_VIRTIONET
11+
CFLAGS += -D ENABLE_VIRTIOBLK
1112
OBJS_EXTRA += virtio-net.o
13+
OBJS_EXTRA += virtio-blk.o
1214
MINIMAL_DTS = minimal-virtio.dts
1315
else
1416
MINIMAL_DTS = minimal.dts
@@ -44,9 +46,13 @@ minimal.dtb: $(MINIMAL_DTS)
4446
# Rules for downloading prebuilt Linux kernel image
4547
include mk/external.mk
4648

47-
check: $(BIN) minimal.dtb $(KERNEL_DATA)
49+
ext4.img:
50+
$(Q)dd if=/dev/zero of=$@ bs=4k count=600
51+
$(Q)mkfs.ext4 -F $@
52+
53+
check: $(BIN) minimal.dtb $(KERNEL_DATA) ext4.img
4854
@$(call notice, Ready to launch Linux kernel. Please be patient.)
49-
$(Q)./$(BIN) $(KERNEL_DATA)
55+
$(Q)./$(BIN) -k $(KERNEL_DATA) -b minimal.dtb -d ext4.img
5056

5157
build-image:
5258
scripts/build-image.sh
@@ -57,5 +63,6 @@ clean:
5763
distclean: clean
5864
$(Q)$(RM) minimal.dtb
5965
$(Q)$(RM) Image
66+
$(Q)$(RM) ext4.img
6067

6168
-include $(deps)

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ A minimalist RISC-V system emulator capable of running Linux the kernel and corr
99
- UART: 8250/16550
1010
- PLIC (platform-level interrupt controller): 32 interrupts, no priority
1111
- Standard SBI, with the timer extension
12-
- VirtIO: virtio-net, mapped as TAP interface
12+
- VirtIO: virtio-blk acquires disk image from the host, and virtio-net is mapped as TAP interface
1313

1414
## Prerequisites
1515

@@ -62,6 +62,16 @@ Please note that it only supports the Linux host environment.
6262
$ make build-image
6363
```
6464

65+
## Usage
66+
67+
```
68+
./semu -k linux-image [-b dtb-file] [-d disk-image]
69+
```
70+
71+
* `linux-image` is the path to the Linux kernel `Image`.
72+
* `dtb-file` is optional, as it specifies the user-specified device tree blob.
73+
* `disk-image` is optional, as it specifies the path of a disk image in ext4 file system for the virtio-blk device.
74+
6575
## License
6676

6777
`semu` is released under the MIT License.

configs/linux.config

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ CONFIG_BLK_DEV=y
644644
# CONFIG_BLK_DEV_NBD is not set
645645
# CONFIG_BLK_DEV_RAM is not set
646646
# CONFIG_ATA_OVER_ETH is not set
647-
# CONFIG_VIRTIO_BLK is not set
647+
CONFIG_VIRTIO_BLK=y
648648
# CONFIG_BLK_DEV_RBD is not set
649649
# CONFIG_BLK_DEV_UBLK is not set
650650

@@ -1594,6 +1594,7 @@ CONFIG_NLATTR=y
15941594
CONFIG_GENERIC_ATOMIC64=y
15951595
# CONFIG_IRQ_POLL is not set
15961596
CONFIG_LIBFDT=y
1597+
CONFIG_SG_POOL=y
15971598
CONFIG_ARCH_STACKWALK=y
15981599
CONFIG_SBITMAP=y
15991600
# end of Library routines

device.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,66 @@ void virtio_net_refresh_queue(virtio_net_state_t *vnet);
117117
bool virtio_net_init(virtio_net_state_t *vnet);
118118
#endif /* ENABLE_VIRTIONET */
119119

120+
/* VirtIO-Block */
121+
#if defined(ENABLE_VIRTIOBLK)
122+
123+
#define IRQ_VBLK 3
124+
#define IRQ_VBLK_BIT (1 << IRQ_VBLK)
125+
126+
typedef struct {
127+
uint32_t QueueNum;
128+
uint32_t QueueDesc;
129+
uint32_t QueueAvail;
130+
uint32_t QueueUsed;
131+
uint16_t last_avail;
132+
bool ready;
133+
} virtio_blk_queue_t;
134+
135+
typedef struct {
136+
/* feature negotiation */
137+
uint32_t DeviceFeaturesSel;
138+
uint32_t DriverFeatures;
139+
uint32_t DriverFeaturesSel;
140+
/* queue config */
141+
uint32_t QueueSel;
142+
virtio_blk_queue_t queues[2];
143+
/* status */
144+
uint32_t Status;
145+
uint32_t InterruptStatus;
146+
/* supplied by environment */
147+
uint32_t *ram;
148+
uint32_t *disk;
149+
uint64_t capacity;
150+
} virtio_blk_state_t;
151+
152+
void virtio_blk_read(vm_t *vm,
153+
virtio_blk_state_t *vblk,
154+
uint32_t addr,
155+
uint8_t width,
156+
uint32_t *value);
157+
158+
void virtio_blk_write(vm_t *vm,
159+
virtio_blk_state_t *vblk,
160+
uint32_t addr,
161+
uint8_t width,
162+
uint32_t value);
163+
164+
uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file);
165+
#endif /* ENABLE_VIRTIOBLK */
166+
120167
/* memory mapping */
121168

122169
typedef struct {
123170
bool stopped;
124171
uint32_t *ram;
172+
uint32_t *disk;
125173
plic_state_t plic;
126174
u8250_state_t uart;
127175
#if defined(ENABLE_VIRTIONET)
128176
virtio_net_state_t vnet;
177+
#endif
178+
#if defined(ENABLE_VIRTIOBLK)
179+
virtio_blk_state_t vblk;
129180
#endif
130181
uint32_t timer_lo, timer_hi;
131182
} emu_state_t;

main.c

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <assert.h>
22
#include <fcntl.h>
3+
#include <getopt.h>
34
#include <stdio.h>
45
#include <stdlib.h>
56
#include <string.h>
@@ -57,6 +58,18 @@ static void emu_update_vnet_interrupts(vm_t *vm)
5758
}
5859
#endif
5960

61+
#if defined(ENABLE_VIRTIOBLK)
62+
static void emu_update_vblk_interrupts(vm_t *vm)
63+
{
64+
emu_state_t *data = (emu_state_t *) vm->priv;
65+
if (data->vblk.InterruptStatus)
66+
data->plic.active |= IRQ_VBLK_BIT;
67+
else
68+
data->plic.active &= ~IRQ_VBLK_BIT;
69+
plic_update_interrupts(vm, &data->plic);
70+
}
71+
#endif
72+
6073
static void mem_load(vm_t *vm, uint32_t addr, uint8_t width, uint32_t *value)
6174
{
6275
emu_state_t *data = (emu_state_t *) vm->priv;
@@ -85,8 +98,14 @@ static void mem_load(vm_t *vm, uint32_t addr, uint8_t width, uint32_t *value)
8598
emu_update_vnet_interrupts(vm);
8699
return;
87100
#endif
101+
#if defined(ENABLE_VIRTIOBLK)
102+
case 0x42: /* virtio-blk */
103+
virtio_blk_read(vm, &data->vblk, addr & 0xFFFFF, width, value);
104+
emu_update_vblk_interrupts(vm);
105+
return;
88106
}
89107
}
108+
#endif
90109
vm_set_exception(vm, RV_EXC_LOAD_FAULT, vm->exc_val);
91110
}
92111

@@ -118,7 +137,13 @@ static void mem_store(vm_t *vm, uint32_t addr, uint8_t width, uint32_t value)
118137
emu_update_vnet_interrupts(vm);
119138
return;
120139
#endif
140+
#if defined(ENABLE_VIRTIOBLK)
141+
case 0x42: /* virtio-blk */
142+
virtio_blk_write(vm, &data->vblk, addr & 0xFFFFF, width, value);
143+
emu_update_vblk_interrupts(vm);
144+
return;
121145
}
146+
#endif
122147
}
123148
vm_set_exception(vm, RV_EXC_STORE_FAULT, vm->exc_val);
124149
}
@@ -264,8 +289,67 @@ static void map_file(char **ram_loc, const char *name)
264289
close(fd);
265290
}
266291

292+
static void usage(const char *execpath)
293+
{
294+
fprintf(stderr, "Usage: %s -k linux-image [-b dtb] [-d disk-image]\n",
295+
execpath);
296+
}
297+
298+
static void handle_options(int argc,
299+
char **argv,
300+
char **kernel_file,
301+
char **dtb_file,
302+
char **disk_file)
303+
{
304+
*kernel_file = *dtb_file = *disk_file = NULL;
305+
306+
int optidx = 0;
307+
struct option opts[] = {
308+
{"kernel", 1, NULL, 'k'},
309+
{"dtb", 1, NULL, 'b'},
310+
{"disk", 1, NULL, 'd'},
311+
{"help", 0, NULL, 'h'},
312+
};
313+
314+
int c;
315+
while ((c = getopt_long(argc, argv, "k:b:d:h", opts, &optidx)) != -1) {
316+
switch (c) {
317+
case 'k':
318+
*kernel_file = optarg;
319+
break;
320+
case 'b':
321+
*dtb_file = optarg;
322+
break;
323+
case 'd':
324+
*disk_file = optarg;
325+
break;
326+
case 'h':
327+
usage(argv[0]);
328+
exit(0);
329+
default:
330+
break;
331+
}
332+
}
333+
334+
if (!*kernel_file) {
335+
fprintf(stderr,
336+
"Linux kernel image file must "
337+
"be provided via -k option.\n");
338+
usage(argv[0]);
339+
exit(2);
340+
}
341+
342+
if (!*dtb_file)
343+
*dtb_file = "minimal.dtb";
344+
}
345+
267346
static int semu_start(int argc, char **argv)
268347
{
348+
char *kernel_file;
349+
char *dtb_file;
350+
char *disk_file;
351+
handle_options(argc, argv, &kernel_file, &dtb_file, &disk_file);
352+
269353
/* Initialize the emulator */
270354
emu_state_t emu;
271355
memset(&emu, 0, sizeof(emu));
@@ -289,11 +373,11 @@ static int semu_start(int argc, char **argv)
289373

290374
char *ram_loc = (char *) emu.ram;
291375
/* Load Linux kernel image */
292-
map_file(&ram_loc, argv[1]);
376+
map_file(&ram_loc, kernel_file);
293377
/* Load at last 1 MiB to prevent kernel / initrd from overwriting it */
294378
uint32_t dtb_addr = RAM_SIZE - 1024 * 1024; /* Device tree */
295379
ram_loc = ((char *) emu.ram) + dtb_addr;
296-
map_file(&ram_loc, (argc == 3) ? argv[2] : "minimal.dtb");
380+
map_file(&ram_loc, dtb_file);
297381
/* TODO: load disk image via virtio_blk */
298382
/* Hook for unmapping files */
299383
atexit(unmap_files);
@@ -312,6 +396,10 @@ static int semu_start(int argc, char **argv)
312396
fprintf(stderr, "No virtio-net functioned\n");
313397
emu.vnet.ram = emu.ram;
314398
#endif
399+
#if defined(ENABLE_VIRTIOBLK)
400+
emu.vblk.ram = emu.ram;
401+
emu.disk = virtio_blk_init(&(emu.vblk), disk_file);
402+
#endif
315403

316404
/* Emulate */
317405
uint32_t peripheral_update_ctr = 0;
@@ -328,6 +416,11 @@ static int semu_start(int argc, char **argv)
328416
if (emu.vnet.InterruptStatus)
329417
emu_update_vnet_interrupts(&vm);
330418
#endif
419+
420+
#if defined(ENABLE_VIRTIOBLK)
421+
if (emu.vblk.InterruptStatus)
422+
emu_update_vblk_interrupts(&vm);
423+
#endif
331424
}
332425

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

361454
int main(int argc, char **argv)
362455
{
363-
if (argc < 2) {
364-
printf("Usage: %s <linux-image> [<dtb>]\n", argv[0]);
365-
return 2;
366-
}
367456
return semu_start(argc, argv);
368457
}

minimal-virtio.dts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,11 @@
6868
reg = <0x4100000 0x100000>;
6969
interrupts = <2>;
7070
};
71+
72+
blk0: virtio@4200000 {
73+
compatible = "virtio,mmio";
74+
reg = <0x4200000 0x200>;
75+
interrupts = <3>;
76+
};
7177
};
7278
};

0 commit comments

Comments
 (0)