Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 92fd789

Browse files
authoredMar 20, 2025··
Merge pull request #572 from ChinYikMing/virtio-blk-ioctl
Enable VirtIO block to access hostOS /dev/ block devices
2 parents ce0fe74 + e2e87ee commit 92fd789

File tree

8 files changed

+320
-65
lines changed

8 files changed

+320
-65
lines changed
 

‎.ci/boot-linux-prepare.sh

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/usr/bin/env bash
2+
3+
. .ci/common.sh
4+
5+
check_platform
6+
7+
VBLK_IMG=build/disk.img
8+
which dd >/dev/null 2>&1 || { echo "Error: dd not found"; exit 1; }
9+
which mkfs.ext4 >/dev/null 2>&1 || which $(brew --prefix e2fsprogs)/sbin/mkfs.ext4 >/dev/null 2>&1 || \
10+
{ echo "Error: mkfs.ext4 not found"; exit 1; }
11+
which 7z >/dev/null 2>&1 || { echo "Error: 7z not found"; exit 1; }
12+
13+
ACTION=$1
14+
15+
case "$ACTION" in
16+
setup)
17+
# Setup a disk image
18+
dd if=/dev/zero of=${VBLK_IMG} bs=4M count=32
19+
20+
# Setup a /dev/ block device with ${VBLK_IMG} to test guestOS access to hostOS /dev/ block device
21+
case "${OS_TYPE}" in
22+
Linux)
23+
mkfs.ext4 ${VBLK_IMG}
24+
BLK_DEV=$(losetup -f)
25+
losetup ${BLK_DEV} ${VBLK_IMG}
26+
;;
27+
Darwin)
28+
$(brew --prefix e2fsprogs)/sbin/mkfs.ext4 ${VBLK_IMG}
29+
BLK_DEV=$(hdiutil attach -nomount ${VBLK_IMG})
30+
;;
31+
esac
32+
33+
# On Linux, ${VBLK_IMG} will be created by root and owned by root:root.
34+
# Even if "others" have read and write (rw) permissions, accessing the file for certain operations may
35+
# still require elevated privileges (e.g., setuid).
36+
# To simplify this, we change the ownership to a non-root user.
37+
# Use this with caution—changing ownership to runner:runner is specific to the GitHub CI environment.
38+
chown runner: ${VBLK_IMG}
39+
# Add other's rw permission to the disk image and device, so non-superuser can rw them
40+
chmod o+r,o+w ${VBLK_IMG}
41+
chmod o+r,o+w ${BLK_DEV}
42+
43+
# Export ${BLK_DEV} to a tmp file. Then, source to "$GITHUB_ENV" in job step.
44+
echo "export BLK_DEV=${BLK_DEV}" > "${TMP_FILE}"
45+
;;
46+
cleanup)
47+
# Detach the /dev/loopx(Linux) or /dev/diskx(Darwin)
48+
case "${OS_TYPE}" in
49+
Linux)
50+
losetup -d ${BLK_DEV}
51+
;;
52+
Darwin)
53+
hdiutil detach ${BLK_DEV}
54+
;;
55+
esac
56+
57+
# delete disk image
58+
rm -f ${VBLK_IMG}
59+
60+
# delete tmp file
61+
rm "${TMP_FILE}"
62+
;;
63+
*)
64+
printf "Usage: %s {setup|cleanup}\n" "$0"
65+
exit 1
66+
;;
67+
esac

‎.ci/boot-linux.sh

Lines changed: 71 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
#!/usr/bin/env bash
22

3+
. .ci/common.sh
4+
5+
check_platform
6+
37
function cleanup {
48
sleep 1
59
pkill -9 rv32emu
@@ -8,71 +12,98 @@ function cleanup {
812
function ASSERT {
913
$*
1014
local RES=$?
11-
if [ $RES -ne 0 ]; then
15+
if [ ${RES} -ne 0 ]; then
1216
echo 'Assert failed: "' $* '"'
13-
exit $RES
17+
exit ${RES}
1418
fi
1519
}
1620

1721
cleanup
1822

1923
ENABLE_VBLK=1
2024
VBLK_IMG=build/disk.img
21-
which dd >/dev/null 2>&1 || ENABLE_VBLK=0
22-
which mkfs.ext4 >/dev/null 2>&1 || which $(brew --prefix e2fsprogs)/sbin/mkfs.ext4 >/dev/null 2>&1 || ENABLE_VBLK=0
23-
which 7z >/dev/null 2>&1 || ENABLE_VBLK=0
25+
[ -f "${VBLK_IMG}" ] || ENABLE_VBLK=0
2426

2527
TIMEOUT=50
26-
OPTS=" -k build/linux-image/Image "
27-
OPTS+=" -i build/linux-image/rootfs.cpio "
28-
if [ "$ENABLE_VBLK" -eq "1" ]; then
29-
dd if=/dev/zero of=$VBLK_IMG bs=4M count=32
30-
mkfs.ext4 $VBLK_IMG || $(brew --prefix e2fsprogs)/sbin/mkfs.ext4 $VBLK_IMG
31-
OPTS+=" -x vblk:$VBLK_IMG "
32-
else
33-
printf "Virtio-blk Test...Passed\n"
34-
fi
35-
RUN_LINUX="build/rv32emu ${OPTS}"
28+
OPTS_BASE=" -k build/linux-image/Image"
29+
OPTS_BASE+=" -i build/linux-image/rootfs.cpio"
3630

37-
if [ "$ENABLE_VBLK" -eq "1" ]; then
38-
ASSERT expect <<DONE
39-
set timeout ${TIMEOUT}
40-
spawn ${RUN_LINUX}
41-
expect "buildroot login:" { send "root\n" } timeout { exit 1 }
42-
expect "# " { send "uname -a\n" } timeout { exit 2 }
43-
expect "riscv32 GNU/Linux" { send "mkdir mnt && mount /dev/vda mnt\n" } timeout { exit 3 }
44-
expect "# " { send "echo rv32emu > mnt/emu.txt\n" } timeout { exit 3 }
45-
expect "# " { send "sync\n" } timeout { exit 3 }
46-
expect "# " { send "umount mnt\n" } timeout { exit 3 }
47-
expect "# " { send "\x01"; send "x" } timeout { exit 3 }
48-
DONE
49-
else
50-
ASSERT expect <<DONE
51-
set timeout ${TIMEOUT}
52-
spawn ${RUN_LINUX}
31+
TEST_OPTIONS=("base (${OPTS_BASE})")
32+
EXPECT_CMDS=('
5333
expect "buildroot login:" { send "root\n" } timeout { exit 1 }
5434
expect "# " { send "uname -a\n" } timeout { exit 2 }
5535
expect "riscv32 GNU/Linux" { send "\x01"; send "x" } timeout { exit 3 }
56-
DONE
57-
fi
58-
ret=$?
59-
cleanup
36+
')
6037

6138
COLOR_G='\e[32;01m' # Green
6239
COLOR_R='\e[31;01m' # Red
40+
COLOR_Y='\e[33;01m' # Yellow
6341
COLOR_N='\e[0m' # No color
6442

6543
MESSAGES=("${COLOR_G}OK!" \
6644
"${COLOR_R}Fail to boot" \
6745
"${COLOR_R}Fail to login" \
6846
"${COLOR_R}Fail to run commands" \
69-
"${COLOR_R}Fail to find emu.txt in $VBLK_IMG"\
47+
"${COLOR_R}Fail to find emu.txt in ${VBLK_IMG}"\
7048
)
7149

72-
printf "\nBoot Linux Test: [ ${MESSAGES[$ret]}${COLOR_N} ]\n"
73-
if [ "$ENABLE_VBLK" -eq "1" ]; then
74-
7z l $VBLK_IMG | grep emu.txt >/dev/null 2>&1 || ret=4
75-
printf "Virtio-blk Test: [ ${MESSAGES[$ret]}${COLOR_N} ]\n"
50+
if [ "${ENABLE_VBLK}" -eq "1" ]; then
51+
# Read-only
52+
TEST_OPTIONS+=("${OPTS_BASE} -x vblk:${VBLK_IMG},readonly")
53+
EXPECT_CMDS+=('
54+
expect "buildroot login:" { send "root\n" } timeout { exit 1 }
55+
expect "# " { send "uname -a\n" } timeout { exit 2 }
56+
expect "riscv32 GNU/Linux" { send "mkdir mnt && mount /dev/vda mnt\n" } timeout { exit 3 }
57+
expect "# " { send "echo rv32emu > mnt/emu.txt\n" } timeout { exit 3 }
58+
expect -ex "-sh: can'\''t create mnt/emu.txt: Read-only file system" {} timeout { exit 3 }
59+
expect "# " { send "\x01"; send "x" } timeout { exit 3 }
60+
')
61+
62+
# Read-write using disk image
63+
TEST_OPTIONS+=("${OPTS_BASE} -x vblk:${VBLK_IMG}")
64+
VBLK_EXPECT_CMDS='
65+
expect "buildroot login:" { send "root\n" } timeout { exit 1 }
66+
expect "# " { send "uname -a\n" } timeout { exit 2 }
67+
expect "riscv32 GNU/Linux" { send "mkdir mnt && mount /dev/vda mnt\n" } timeout { exit 3 }
68+
expect "# " { send "echo rv32emu > mnt/emu.txt\n" } timeout { exit 3 }
69+
expect "# " { send "sync\n" } timeout { exit 3 }
70+
expect "# " { send "umount mnt\n" } timeout { exit 3 }
71+
expect "# " { send "\x01"; send "x" } timeout { exit 3 }
72+
'
73+
EXPECT_CMDS+=("${VBLK_EXPECT_CMDS}")
74+
75+
# Read-write using /dev/loopx(Linux) or /dev/diskx(Darwin) block device
76+
TEST_OPTIONS+=("${OPTS_BASE} -x vblk:${BLK_DEV}")
77+
EXPECT_CMDS+=("${VBLK_EXPECT_CMDS}")
7678
fi
7779

80+
for i in "${!TEST_OPTIONS[@]}"; do
81+
printf "${COLOR_Y}===== Test option: ${TEST_OPTIONS[$i]} =====${COLOR_N}\n"
82+
83+
OPTS="${OPTS_BASE}"
84+
# No need to add option when running base test
85+
if [[ ! "${TEST_OPTIONS[$i]}" =~ "base" ]]; then
86+
OPTS+="${TEST_OPTIONS[$i]}"
87+
fi
88+
RUN_LINUX="build/rv32emu ${OPTS}"
89+
90+
ASSERT expect <<-DONE
91+
set timeout ${TIMEOUT}
92+
spawn ${RUN_LINUX}
93+
${EXPECT_CMDS[$i]}
94+
DONE
95+
96+
ret=$?
97+
cleanup
98+
99+
printf "\nBoot Linux Test: [ ${MESSAGES[$ret]}${COLOR_N} ]\n"
100+
if [[ "${TEST_OPTIONS[$i]}" =~ vblk ]]; then
101+
# read-only test first, so the emu.txt definitely does not exist, skipping the check
102+
if [[ ! "${TEST_OPTIONS[$i]}" =~ readonly ]]; then
103+
7z l ${VBLK_IMG} | grep emu.txt >/dev/null 2>&1 || ret=4
104+
fi
105+
printf "Virtio-blk Test: [ ${MESSAGES[$ret]}${COLOR_N} ]\n"
106+
fi
107+
done
108+
78109
exit ${ret}

‎.github/workflows/main.yml

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,14 @@ jobs:
6565
actions-cache-folder: 'emsdk-cache'
6666
- name: Set parallel jobs variable
6767
run: |
68-
echo "PARALLEL=-j$(nproc)" >> $GITHUB_ENV
68+
echo "PARALLEL=-j$(nproc)" >> "$GITHUB_ENV"
69+
echo "BOOT_LINUX_TEST=TMP_FILE=\$(mktemp "$RUNNER_TEMP/tmpfile.XXXXXX"); \
70+
sudo env TMP_FILE=\${TMP_FILE} .ci/boot-linux-prepare.sh setup; \
71+
. \${TMP_FILE}; \
72+
.ci/boot-linux.sh; \
73+
EXIT_CODE=\$?; \
74+
sudo env TMP_FILE=\${TMP_FILE} BLK_DEV=\${BLK_DEV} .ci/boot-linux-prepare.sh cleanup; \
75+
exit \${EXIT_CODE};" >> "$GITHUB_ENV"
6976
- name: fetch artifact first to reduce HTTP requests
7077
env:
7178
CC: ${{ steps.install_cc.outputs.cc }}
@@ -265,15 +272,15 @@ jobs:
265272
CC: ${{ steps.install_cc.outputs.cc }}
266273
run: |
267274
make distclean && make INITRD_SIZE=32 ENABLE_SYSTEM=1 $PARALLEL && make ENABLE_SYSTEM=1 artifact $PARALLEL
268-
.ci/boot-linux.sh
275+
bash -c "${BOOT_LINUX_TEST}"
269276
make ENABLE_SYSTEM=1 clean
270277
if: ${{ always() }}
271278
- name: boot Linux kernel test (JIT)
272279
env:
273280
CC: ${{ steps.install_cc.outputs.cc }}
274281
run: |
275282
make distclean && make INITRD_SIZE=32 ENABLE_SYSTEM=1 ENABLE_JIT=1 ENABLE_T2C=0 ENABLE_MOP_FUSION=0 $PARALLEL && make ENABLE_SYSTEM=1 artifact $PARALLEL
276-
.ci/boot-linux.sh
283+
bash -c "${BOOT_LINUX_TEST}"
277284
make ENABLE_SYSTEM=1 ENABLE_JIT=1 ENABLE_T2C=0 ENABLE_MOP_FUSION=0 clean
278285
if: ${{ always() }}
279286
- name: Architecture test
@@ -292,7 +299,7 @@ jobs:
292299
uses: actions/checkout@v4
293300
- name: Set parallel jobs variable
294301
run: |
295-
echo "PARALLEL=-j$(nproc)" >> $GITHUB_ENV
302+
echo "PARALLEL=-j$(nproc)" >> "$GITHUB_ENV"
296303
- name: build artifact
297304
# The GitHub Action for non-x86 CPU
298305
uses: allinurl/run-on-arch-action@master
@@ -351,7 +358,14 @@ jobs:
351358
actions-cache-folder: 'emsdk-cache'
352359
- name: Set parallel jobs variable
353360
run: |
354-
echo "PARALLEL=-j$(sysctl -n hw.logicalcpu)" >> $GITHUB_ENV
361+
echo "PARALLEL=-j$(sysctl -n hw.logicalcpu)" >> "$GITHUB_ENV"
362+
echo "BOOT_LINUX_TEST=TMP_FILE=\$(mktemp "$RUNNER_TEMP/tmpfile.XXXXXX"); \
363+
sudo env TMP_FILE=\${TMP_FILE} .ci/boot-linux-prepare.sh setup; \
364+
. \${TMP_FILE}; \
365+
.ci/boot-linux.sh; \
366+
EXIT_CODE=\$?; \
367+
sudo env TMP_FILE=\${TMP_FILE} BLK_DEV=\${BLK_DEV} .ci/boot-linux-prepare.sh cleanup; \
368+
exit \${EXIT_CODE};" >> "$GITHUB_ENV"
355369
- name: Symlink gcc-14 due to the default /usr/local/bin/gcc links to system's clang
356370
run: |
357371
ln -s /opt/homebrew/opt/gcc/bin/gcc-14 /usr/local/bin/gcc-14
@@ -463,7 +477,7 @@ jobs:
463477
run: |
464478
make distclean && make INITRD_SIZE=32 ENABLE_SYSTEM=1 $PARALLEL && \
465479
make ENABLE_SYSTEM=1 artifact $PARALLEL
466-
.ci/boot-linux.sh
480+
bash -c "${BOOT_LINUX_TEST}"
467481
make ENABLE_SYSTEM=1 clean
468482
if: ${{ always() }}
469483
- name: Architecture test

‎README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ Generate ext4 image file for virtio block device in Unix-like system:
9393
$ dd if=/dev/zero of=disk.img bs=4M count=32
9494
$ mkfs.ext4 disk.img
9595
```
96+
Instead of creating a new block device image, you can share the hostOS's existing block devices. For example, on macOS host, specify the block device path as `-x vblk:/dev/disk3`, or on Linux host as `-x vblk:/dev/loop3`, assuming these paths point to valid block devices.
97+
9698
Mount the virtual block device and create a test file after booting, note that root privilege is required to mount and unmount a disk:
9799
```shell
98100
# mkdir mnt

‎src/devices/virtio-blk.c

Lines changed: 111 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,30 @@
44
*/
55

66
#include <assert.h>
7+
#include <errno.h>
78
#include <fcntl.h>
9+
#include <libgen.h>
810
#include <stdbool.h>
911
#include <stdio.h>
1012
#include <stdlib.h>
1113
#include <string.h>
14+
#include <sys/ioctl.h>
1215
#include <sys/mman.h>
1316
#include <sys/stat.h>
1417
#include <unistd.h>
1518

19+
/*
20+
* The /dev/ block devices cannot be embedded to the part of the wasm.
21+
* Thus, accessing /dev/ block devices is not supported for wasm.
22+
*/
23+
#if !defined(__EMSCRIPTEN__)
24+
#if defined(__APPLE__)
25+
#include <sys/disk.h> /* DKIOCGETBLOCKCOUNT and DKIOCGETBLOCKSIZE */
26+
#else
27+
#include <linux/fs.h> /* BLKGETSIZE64 */
28+
#endif
29+
#endif /* !defined(__EMSCRIPTEN__) */
30+
1631
#include "virtio.h"
1732

1833
#define DISK_BLK_SIZE 512
@@ -97,12 +112,16 @@ static void virtio_blk_update_status(virtio_blk_state_t *vblk, uint32_t status)
97112
uint32_t device_features = vblk->device_features;
98113
uint32_t *ram = vblk->ram;
99114
uint32_t *disk = vblk->disk;
115+
uint64_t disk_size = vblk->disk_size;
116+
int disk_fd = vblk->disk_fd;
100117
void *priv = vblk->priv;
101118
uint32_t capacity = VBLK_PRIV(vblk)->capacity;
102119
memset(vblk, 0, sizeof(*vblk));
103120
vblk->device_features = device_features;
104121
vblk->ram = ram;
105122
vblk->disk = disk;
123+
vblk->disk_size = disk_size;
124+
vblk->disk_fd = disk_fd;
106125
vblk->priv = priv;
107126
VBLK_PRIV(vblk)->capacity = capacity;
108127
}
@@ -388,6 +407,12 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk,
388407
exit(EXIT_FAILURE);
389408
}
390409

410+
/*
411+
* For mmap_fallback, if vblk is not specified, disk_fd should remain -1 and
412+
* no fsync should be performed on exit.
413+
*/
414+
vblk->disk_fd = -1;
415+
391416
/* Allocate memory for the private member */
392417
vblk->priv = &vblk_configs[vblk_dev_cnt++];
393418

@@ -402,30 +427,92 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk,
402427
/* Open disk file */
403428
int disk_fd = open(disk_file, readonly ? O_RDONLY : O_RDWR);
404429
if (disk_fd < 0) {
405-
rv_log_error("Could not open %s", disk_file);
406-
exit(EXIT_FAILURE);
430+
rv_log_error("Could not open %s: %s", disk_file, strerror(errno));
431+
goto fail;
407432
}
408433

409-
/* Get the disk image size */
410434
struct stat st;
411-
fstat(disk_fd, &st);
412-
VBLK_PRIV(vblk)->disk_size = st.st_size;
435+
if (fstat(disk_fd, &st) == -1) {
436+
rv_log_error("fstat failed: %s", strerror(errno));
437+
goto disk_size_fail;
438+
}
439+
440+
const char *disk_file_dirname = dirname(disk_file);
441+
if (!disk_file_dirname) {
442+
rv_log_error("Fail dirname disk_file: %s: %s", disk_file,
443+
strerror(errno));
444+
goto disk_size_fail;
445+
}
446+
/* Get the disk size */
447+
uint64_t disk_size;
448+
if (!strcmp(disk_file_dirname, "/dev")) { /* from /dev/, leverage ioctl */
449+
if ((st.st_mode & S_IFMT) != S_IFBLK) {
450+
rv_log_error("%s is not block device", disk_file);
451+
goto fail;
452+
}
453+
#if !defined(__EMSCRIPTEN__)
454+
#if defined(__APPLE__)
455+
uint32_t block_size;
456+
uint64_t block_count;
457+
if (ioctl(disk_fd, DKIOCGETBLOCKCOUNT, &block_count) == -1) {
458+
rv_log_error("DKIOCGETBLOCKCOUNT failed: %s", strerror(errno));
459+
goto disk_size_fail;
460+
}
461+
if (ioctl(disk_fd, DKIOCGETBLOCKSIZE, &block_size) == -1) {
462+
rv_log_error("DKIOCGETBLOCKSIZE failed: %s", strerror(errno));
463+
goto disk_size_fail;
464+
}
465+
disk_size = block_count * block_size;
466+
#else /* Linux */
467+
if (ioctl(disk_fd, BLKGETSIZE64, &disk_size) == -1) {
468+
rv_log_error("BLKGETSIZE64 failed: %s", strerror(errno));
469+
goto disk_size_fail;
470+
}
471+
#endif
472+
#endif /* !defined(__EMSCRIPTEN__) */
473+
} else { /* other path, get the size of block device via stat buffer */
474+
disk_size = st.st_size;
475+
}
476+
VBLK_PRIV(vblk)->disk_size = disk_size;
413477

414478
/* Set up the disk memory */
415479
uint32_t *disk_mem;
416480
#if HAVE_MMAP
417481
disk_mem = mmap(NULL, VBLK_PRIV(vblk)->disk_size,
418482
readonly ? PROT_READ : (PROT_READ | PROT_WRITE), MAP_SHARED,
419483
disk_fd, 0);
420-
if (disk_mem == MAP_FAILED)
421-
goto err;
422-
#else
484+
if (disk_mem == MAP_FAILED) {
485+
if (errno != EINVAL)
486+
goto disk_mem_err;
487+
/*
488+
* On Apple platforms, mmap() on block devices appears to be unsupported
489+
* and EINVAL is set to errno.
490+
*/
491+
rv_log_trace(
492+
"Fallback to malloc-based block device due to mmap() failure");
493+
goto mmap_fallback;
494+
}
495+
/*
496+
* disk_fd should be closed on exit after flushing heap data back to the
497+
* device when using mmap_fallback.
498+
*/
499+
close(disk_fd);
500+
goto disk_mem_ok;
501+
#endif
502+
503+
mmap_fallback:
423504
disk_mem = malloc(VBLK_PRIV(vblk)->disk_size);
424505
if (!disk_mem)
425-
goto err;
426-
#endif
506+
goto disk_mem_err;
507+
vblk->disk_fd = disk_fd;
508+
vblk->disk_size = disk_size;
509+
if (pread(disk_fd, disk_mem, disk_size, 0) == -1) {
510+
rv_log_error("pread block device failed: %s", strerror(errno));
511+
goto disk_mem_err;
512+
}
513+
514+
disk_mem_ok:
427515
assert(!(((uintptr_t) disk_mem) & 0b11));
428-
close(disk_fd);
429516

430517
vblk->disk = disk_mem;
431518
VBLK_PRIV(vblk)->capacity =
@@ -436,9 +523,14 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk,
436523

437524
return disk_mem;
438525

439-
err:
440-
rv_log_error("Could not map disk %s", disk_file);
441-
return NULL;
526+
disk_mem_err:
527+
rv_log_error("Could not map disk %s: %s", disk_file, strerror(errno));
528+
529+
disk_size_fail:
530+
close(disk_fd);
531+
532+
fail:
533+
exit(EXIT_FAILURE);
442534
}
443535

444536
virtio_blk_state_t *vblk_new()
@@ -450,10 +542,12 @@ virtio_blk_state_t *vblk_new()
450542

451543
void vblk_delete(virtio_blk_state_t *vblk)
452544
{
545+
/* mmap_fallback is used */
546+
if (vblk->disk_fd != -1)
547+
free(vblk->disk);
453548
#if HAVE_MMAP
454-
munmap(vblk->disk, VBLK_PRIV(vblk)->disk_size);
455-
#else
456-
free(vblk->disk);
549+
else
550+
munmap(vblk->disk, VBLK_PRIV(vblk)->disk_size);
457551
#endif
458552
free(vblk);
459553
}

‎src/devices/virtio.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ typedef struct {
103103
/* supplied by environment */
104104
uint32_t *ram;
105105
uint32_t *disk;
106+
uint64_t disk_size;
107+
int disk_fd;
106108
/* implementation-specific */
107109
void *priv;
108110
} virtio_blk_state_t;

‎src/main.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,11 @@ int main(int argc, char **args)
298298

299299
/* finalize the RISC-V runtime */
300300
rv_delete(rv);
301+
/*
302+
* Other translation units cannot update the pointer, update it here
303+
* to prevent multiple atexit()'s callback be called.
304+
*/
305+
rv = NULL;
301306
rv_log_info("RISC-V emulator is destroyed");
302307

303308
end:

‎src/riscv.c

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,43 @@ static void rv_async_block_clear()
390390
return;
391391
#endif /* !RV32_HAS(JIT) */
392392
}
393-
#endif
393+
394+
static void rv_fsync_device()
395+
{
396+
if (!rv)
397+
return;
398+
399+
vm_attr_t *attr = PRIV(rv);
400+
/*
401+
* mmap_fallback, may need to write and sync the device
402+
*
403+
* vblk is optional, so it could be NULL
404+
*/
405+
if (attr->vblk) {
406+
if (attr->vblk->disk_fd >= 3) {
407+
if (attr->vblk->device_features & VIRTIO_BLK_F_RO) /* readonly */
408+
goto end;
409+
410+
if (pwrite(attr->vblk->disk_fd, attr->vblk->disk,
411+
attr->vblk->disk_size, 0) == -1) {
412+
rv_log_error("pwrite block device failed: %s", strerror(errno));
413+
return;
414+
}
415+
416+
if (fsync(attr->vblk->disk_fd) == -1) {
417+
rv_log_error("fsync block device failed: %s", strerror(errno));
418+
return;
419+
}
420+
rv_log_info("Sync block device OK");
421+
422+
end:
423+
close(attr->vblk->disk_fd);
424+
}
425+
426+
vblk_delete(attr->vblk);
427+
}
428+
}
429+
#endif /* RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER) */
394430

395431
riscv_t *rv_create(riscv_user_t rv_attr)
396432
{
@@ -402,6 +438,8 @@ riscv_t *rv_create(riscv_user_t rv_attr)
402438
#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER)
403439
/* register cleaning callback for CTRL+a+x exit */
404440
atexit(rv_async_block_clear);
441+
/* register device sync callback for CTRL+a+x exit */
442+
atexit(rv_fsync_device);
405443
#endif
406444

407445
/* copy over the attr */
@@ -549,6 +587,7 @@ riscv_t *rv_create(riscv_user_t rv_attr)
549587
attr->uart->out_fd = attr->fd_stdout;
550588

551589
/* setup virtio-blk */
590+
attr->vblk = NULL;
552591
if (attr->data.system.vblk_device) {
553592
/* Currently, only used for block image path and permission */
554593
#define MAX_OPTS 2
@@ -719,7 +758,8 @@ void rv_delete(riscv_t *rv)
719758
#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER)
720759
u8250_delete(attr->uart);
721760
plic_delete(attr->plic);
722-
vblk_delete(attr->vblk);
761+
/* sync device, cleanup inside the callee */
762+
rv_fsync_device();
723763
#endif
724764
free(rv);
725765
}

0 commit comments

Comments
 (0)
Please sign in to comment.