Skip to content

Commit e2e87ee

Browse files
committed
CI: Refine Linux boot script for new feature verification
The Linux boot script previously tested both booting and VirtIO block access simultaneously. This commit refines the boot script to test each guest Linux feature independently. In addition, a new color (yellow) is introduced to clearly indicate which test is currently running in the CI, improving debugging capabilities. For future VirtIO device tests or other new features, TEST_OPTIONS and EXPECT_CMDS can be easily updated to extend the tests, enhancing the overall flexibility of the script. The VirtIO block device image or loop device is prepared by the .ci/boot-linux-prepare.sh script which decouples data and control for system emulation validation in .ci/boot-linux.sh. This script can be extended in the future to support additional VirtIO devices by preparing their associated data prior to Linux boot validation. The system validation flow is abstracted into a variable called BOOT_LINUX_TEST which uses sudo only for data preparation and does not use sudo to run .ci/boot-linux.sh, enhancing security when launching the system VM.
1 parent 4303138 commit e2e87ee

File tree

3 files changed

+158
-46
lines changed

3 files changed

+158
-46
lines changed

.ci/boot-linux-prepare.sh

+67
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

+71-40
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

+20-6
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

0 commit comments

Comments
 (0)