Skip to content

Commit cb2ecc5

Browse files
committed
Support readonly feature of VirtIO block device
Add support for comma-separated options for VirtIO devices. For the readonly option, if the string readonly is specified, subsequent VirtIO block device access will be read-only; otherwise, the default behavior is read and write. To implement this, a device_features field is introduced to store device features during initialization. VIRTIO_BLK_F_RO is the feature bit to enable readonly. This feature enhances the security of the VirtIO block device by preventing unintended data corruption in the guest OS when accessing critical data. Additionally, after implementing the VirtIO block device in sysprog21#539, some register access errors were shown and they are fixed in this commit: - reading from the VirtIO DeviceFeatures register now correctly checks the device_features_sel instead of driver_features_sel. - writing to the VirtIO DeviceFeaturesSel register now correctly updates device_features_sel instead of driver_features_sel.
1 parent c3163e5 commit cb2ecc5

File tree

4 files changed

+50
-13
lines changed

4 files changed

+50
-13
lines changed

src/devices/virtio-blk.c

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#define DISK_BLK_SIZE 512
1919

20+
2021
/* TODO: Enable mutiple virtio-blk devices. */
2122
#define VBLK_DEV_CNT_MAX 1
2223

@@ -94,11 +95,13 @@ static void virtio_blk_update_status(virtio_blk_state_t *vblk, uint32_t status)
9495
return;
9596

9697
/* Reset */
98+
uint32_t device_features = vblk->device_features;
9799
uint32_t *ram = vblk->ram;
98100
uint32_t *disk = vblk->disk;
99101
void *priv = vblk->priv;
100102
uint32_t capacity = VBLK_PRIV(vblk)->capacity;
101103
memset(vblk, 0, sizeof(*vblk));
104+
vblk->device_features = device_features;
102105
vblk->ram = ram;
103106
vblk->disk = disk;
104107
vblk->priv = priv;
@@ -187,6 +190,11 @@ static int virtio_blk_desc_handler(virtio_blk_state_t *vblk,
187190
virtio_blk_read_handler(vblk, sector, vq_desc[1].addr, vq_desc[1].len);
188191
break;
189192
case VIRTIO_BLK_T_OUT:
193+
if (vblk->device_features & VIRTIO_BLK_F_RO) { /* readonly */
194+
rv_log_error("Fail to write on a read only block device");
195+
*status = VIRTIO_BLK_S_IOERR;
196+
return -1;
197+
}
190198
virtio_blk_write_handler(vblk, sector, vq_desc[1].addr, vq_desc[1].len);
191199
break;
192200
default:
@@ -279,9 +287,9 @@ uint32_t virtio_blk_read(virtio_blk_state_t *vblk, uint32_t addr)
279287
case _(VendorID):
280288
return VIRTIO_VENDOR_ID;
281289
case _(DeviceFeatures):
282-
return vblk->driver_features_sel == 0
283-
? VBLK_FEATURES_0
284-
: (vblk->driver_features_sel == 1 ? VBLK_FEATURES_1 : 0);
290+
return vblk->device_features_sel == 0
291+
? VBLK_FEATURES_0 | vblk->device_features
292+
: (vblk->device_features_sel == 1 ? VBLK_FEATURES_1 : 0);
285293
case _(QueueNumMax):
286294
return VBLK_QUEUE_NUM_MAX;
287295
case _(QueueReady):
@@ -305,7 +313,7 @@ void virtio_blk_write(virtio_blk_state_t *vblk, uint32_t addr, uint32_t value)
305313
#define _(reg) VIRTIO_##reg
306314
switch (addr) {
307315
case _(DeviceFeaturesSel):
308-
vblk->driver_features_sel = value;
316+
vblk->device_features_sel = value;
309317
break;
310318
case _(DriverFeatures):
311319
vblk->driver_features_sel == 0 ? (vblk->driver_features = value) : 0;
@@ -371,7 +379,9 @@ void virtio_blk_write(virtio_blk_state_t *vblk, uint32_t addr, uint32_t value)
371379
#undef _
372380
}
373381

374-
uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file)
382+
uint32_t *virtio_blk_init(virtio_blk_state_t *vblk,
383+
char *disk_file,
384+
bool readonly)
375385
{
376386
if (vblk_dev_cnt >= VBLK_DEV_CNT_MAX) {
377387
rv_log_error(
@@ -391,7 +401,7 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file)
391401
}
392402

393403
/* Open disk file */
394-
int disk_fd = open(disk_file, O_RDWR);
404+
int disk_fd = open(disk_file, readonly ? O_RDONLY : O_RDWR);
395405
if (disk_fd < 0) {
396406
rv_log_error("Could not open %s", disk_file);
397407
exit(EXIT_FAILURE);
@@ -405,8 +415,9 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file)
405415
/* Set up the disk memory */
406416
uint32_t *disk_mem;
407417
#if HAVE_MMAP
408-
disk_mem = mmap(NULL, VBLK_PRIV(vblk)->disk_size, PROT_READ | PROT_WRITE,
409-
MAP_SHARED, disk_fd, 0);
418+
disk_mem = mmap(NULL, VBLK_PRIV(vblk)->disk_size,
419+
readonly ? PROT_READ : (PROT_READ | PROT_WRITE), MAP_SHARED,
420+
disk_fd, 0);
410421
if (disk_mem == MAP_FAILED)
411422
goto err;
412423
#else
@@ -421,6 +432,9 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file)
421432
VBLK_PRIV(vblk)->capacity =
422433
(VBLK_PRIV(vblk)->disk_size - 1) / DISK_BLK_SIZE + 1;
423434

435+
if (readonly)
436+
vblk->device_features = VIRTIO_BLK_F_RO;
437+
424438
return disk_mem;
425439

426440
err:

src/devices/virtio.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
#define VIRTIO_BLK_S_IOERR 1
3434
#define VIRTIO_BLK_S_UNSUPP 2
3535

36+
/* TODO: support more features */
37+
#define VIRTIO_BLK_F_RO (1 << 5)
38+
3639
/* VirtIO MMIO registers */
3740
#define VIRTIO_REG_LIST \
3841
_(MagicValue, 0x000) /* R */ \
@@ -87,6 +90,7 @@ typedef struct {
8790

8891
typedef struct {
8992
/* feature negotiation */
93+
uint32_t device_features;
9094
uint32_t device_features_sel;
9195
uint32_t driver_features;
9296
uint32_t driver_features_sel;
@@ -107,7 +111,9 @@ uint32_t virtio_blk_read(virtio_blk_state_t *vblk, uint32_t addr);
107111

108112
void virtio_blk_write(virtio_blk_state_t *vblk, uint32_t addr, uint32_t value);
109113

110-
uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file);
114+
uint32_t *virtio_blk_init(virtio_blk_state_t *vblk,
115+
char *disk_file,
116+
bool readonly);
111117

112118
virtio_blk_state_t *vblk_new();
113119

src/main.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ static void print_usage(const char *filename)
7474
#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER)
7575
" -k <image> : use <image> as kernel image\n"
7676
" -i <image> : use <image> as rootfs\n"
77-
" -x vblk:<image> : use <image> as virtio-blk disk image\n"
77+
" -x vblk:<image>[,readonly] : use <image> as virtio-blk disk image "
78+
"(default read and write)\n"
7879
" -b <bootargs> : use customized <bootargs> for the kernel\n"
7980
#endif
8081
" -d [filename]: dump registers as JSON to the "

src/riscv.c

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -535,9 +535,25 @@ riscv_t *rv_create(riscv_user_t rv_attr)
535535
attr->uart->out_fd = attr->fd_stdout;
536536

537537
/* setup virtio-blk */
538-
attr->vblk = vblk_new();
539-
attr->vblk->ram = (uint32_t *) attr->mem->mem_base;
540-
attr->disk = virtio_blk_init(attr->vblk, attr->data.system.vblk_device);
538+
if (attr->data.system.vblk_device) {
539+
/* Currently, only used for block image path and permission */
540+
#define MAX_OPTS 2
541+
char *vblk_opts[MAX_OPTS];
542+
memset(vblk_opts, 0, sizeof(char *[MAX_OPTS]));
543+
int vblk_opt_idx = 0;
544+
char *opt = strtok(attr->data.system.vblk_device, ",");
545+
while (opt) {
546+
vblk_opts[vblk_opt_idx++] = opt;
547+
opt = strtok(NULL, ",");
548+
}
549+
char *vblk_device = vblk_opts[0];
550+
char *vblk_readonly = vblk_opts[1];
551+
552+
attr->vblk = vblk_new();
553+
attr->vblk->ram = (uint32_t *) attr->mem->mem_base;
554+
attr->disk = virtio_blk_init(attr->vblk, vblk_device,
555+
vblk_readonly ? true : false);
556+
}
541557

542558
capture_keyboard_input();
543559
#endif /* !RV32_HAS(SYSTEM) || (RV32_HAS(SYSTEM) && RV32_HAS(ELF_LOADER)) */

0 commit comments

Comments
 (0)