Skip to content

Commit 92fd789

Browse files
authored
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

0 commit comments

Comments
 (0)