Skip to content

Commit da0a2b9

Browse files
committed
Update headless mode and CI
Currently, on Linux we use 'SDL_VIDEODRIVER=offscreen' to help validate the correctness of the vGPU functionality. However, since this flag does not work properly on macOS, this commit makes minor changes to the codebase to introduce a headless mode. With headless mode enabled, we can still verify the following: - The Virtio-GPU driver loads in the guest kernel. - The '/dev/dri/card0' DRM device node appears. - The 'virtio_gpu' driver binds correctly. - GPU commands work (create resource, attach backing, transfer data). - DirectFB/graphics libraries can initialize via DRM/KMS. - The protocol implementation is correct.
1 parent b1f547f commit da0a2b9

File tree

6 files changed

+283
-15
lines changed

6 files changed

+283
-15
lines changed

.ci/test-vgpu.sh

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6+
. "${SCRIPT_DIR}/common.sh"
7+
8+
# Override timeout and sleep duration for macOS - emulation is significantly slower
9+
case "${OS_TYPE}" in
10+
Darwin)
11+
TIMEOUT=10800
12+
DFB_SLEEP=180
13+
;;
14+
*)
15+
DFB_SLEEP=5
16+
;;
17+
esac
18+
export DFB_SLEEP
19+
20+
cleanup
21+
trap cleanup EXIT
22+
23+
# Download pre-built ext4.img with DirectFB if not present
24+
EXT4_URL="https://github.com/Mes0903/semu/raw/blob/ext4.img.bz2"
25+
EXT4_SHA1="83ed49c16d341bdf3210141d5f6d5842b77a6adc"
26+
27+
if [ ! -f "ext4.img.bz2" ]; then
28+
echo "Downloading ext4.img.bz2 for DirectFB testing..."
29+
curl --progress-bar -O -L "${EXT4_URL}"
30+
echo "${EXT4_SHA1} ext4.img.bz2" | sha1sum -c -
31+
fi
32+
33+
echo "Decompressing ext4.img.bz2 for DirectFB testing..."
34+
rm -f ext4.img
35+
bunzip2 -kf ext4.img.bz2
36+
37+
# NOTE: We want to capture the expect exit code and map
38+
# it to our MESSAGES array for meaningful error output.
39+
# Temporarily disable errexit for the expect call.
40+
set +e
41+
expect <<'DONE'
42+
set timeout $env(TIMEOUT)
43+
spawn make check
44+
45+
# Boot and login
46+
expect "buildroot login:" { send "root\r" } timeout { exit 1 }
47+
expect "# " { send "uname -a\r" } timeout { exit 2 }
48+
expect "riscv32 GNU/Linux" {}
49+
50+
# ---------------- vgpu basic checks ----------------
51+
expect "# " { send "ls -la /dev/dri/ 2>/dev/null || true\r" }
52+
expect "# " { send "test -c /dev/dri/card0 && echo __VGPU_DRM_OK__ || echo __VGPU_DRM_MISSING__\r" } timeout { exit 3 }
53+
expect {
54+
-re "\r?\n__VGPU_DRM_OK__" {}
55+
-re "\r?\n__VGPU_DRM_MISSING__" { exit 3 }
56+
timeout { exit 3 }
57+
}
58+
59+
# virtio transport may be virtio-mmio, binding check should look at virtio_gpu driver directory
60+
expect "# " {
61+
send "sh -lc 'ls /sys/bus/virtio/drivers/virtio_gpu/virtio* >/dev/null 2>&1 && echo __VGPU_BIND_OK__ || echo __VGPU_BIND_BAD__'\r"
62+
} timeout { exit 3 }
63+
expect {
64+
-re "\r?\n__VGPU_BIND_OK__" {}
65+
-re "\r?\n__VGPU_BIND_BAD__" {
66+
send "ls -l /sys/bus/virtio/drivers/virtio_gpu/ 2>/dev/null || true\r"
67+
# emit literal $d via \u0024 to avoid Tcl variable substitution
68+
send "sh -lc 'for d in /sys/bus/virtio/devices/virtio*; do echo \u0024d; ls -l \u0024d/driver 2>/dev/null || true; done'\r"
69+
exit 3
70+
}
71+
timeout { exit 3 }
72+
}
73+
74+
# Useful logs (non-fatal)
75+
expect "# " { send "dmesg | grep -Ei 'virtio.*gpu|drm.*virtio|scanout|number of scanouts' | tail -n 80 || true\r" }
76+
77+
# ---------------- DirectFB2 ----------------
78+
# Strategy:
79+
# 1) Stop X11 if running (it holds the DRM device)
80+
# 2) Check run.sh exists at /root/run.sh
81+
# 3) cd to /root and source run.sh to set PATH/LD_LIBRARY_PATH
82+
# 4) Verify df_drivertest is in PATH
83+
# 5) Run df_drivertest and check for DirectFB init messages
84+
#
85+
# NOTE: df_drivertest may segfault when killed due to a race condition in DirectFB2's
86+
# fusion module (libfusion) during signal handling. When SIGTERM is sent, the signal
87+
# handler starts cleanup while the "Fusion Dispatch" thread may still be accessing
88+
# shared state, leading to a use-after-free crash. The test passes if DirectFB init
89+
# messages appear, even if the program crashes afterward during cleanup.
90+
91+
# Step 0: Stop X11 to release DRM device (it holds /dev/dri/card0)
92+
# Use pidof with fallback to ps/grep if pidof unavailable
93+
expect "# " {
94+
send "sh -lc '\
95+
if command -v pidof >/dev/null 2>&1; then \
96+
pidof Xorg >/dev/null 2>&1 && kill \u0024(pidof Xorg) 2>/dev/null || true; \
97+
else \
98+
ps | grep Xorg | grep -v grep | awk \"{print \u00241}\" | xargs kill 2>/dev/null || true; \
99+
fi; \
100+
sleep 1; echo __X11_STOPPED__'\r"
101+
}
102+
expect "__X11_STOPPED__" {}
103+
104+
# Step 1: Check run.sh exists
105+
expect "# " { send "test -f /root/run.sh && echo __RUNSH_OK__ || echo __DFB_RUNSH_MISSING__\r" }
106+
expect {
107+
-re "\r?\n__RUNSH_OK__" {}
108+
-re "\r?\n__DFB_RUNSH_MISSING__" { exit 4 }
109+
timeout { exit 4 }
110+
}
111+
112+
# Step 2: cd to /root and source run.sh
113+
expect "# " { send "cd /root && . ./run.sh >/dev/null 2>&1; echo __SRC_DONE__\r" }
114+
expect "__SRC_DONE__" {}
115+
116+
# Step 3: Verify df_drivertest is available
117+
expect "# " { send "command -v df_drivertest && echo __APP_OK__ || echo __APP_MISS__\r" }
118+
expect {
119+
-re "\r?\n__APP_OK__" {}
120+
-re "\r?\n__APP_MISS__" { exit 4 }
121+
timeout { exit 4 }
122+
}
123+
124+
# Step 4: Run df_drivertest and check output (run in background, kill after delay)
125+
expect "# " { send "df_drivertest >/tmp/dfb.log 2>&1 & sleep $env(DFB_SLEEP); kill \u0024! 2>/dev/null; head -30 /tmp/dfb.log\r" }
126+
# Check for DRMKMS init message
127+
expect "# " { send "grep -qi 'DRMKMS/System' /tmp/dfb.log && echo __DFB_OK__ || echo __DFB_FAIL__\r" }
128+
expect {
129+
-re "\r?\n__DFB_OK__" {}
130+
-re "\r?\n__DFB_FAIL__" { exit 4 }
131+
timeout { exit 4 }
132+
}
133+
DONE
134+
135+
ret="$?"
136+
set -e # Re-enable errexit after capturing expect's return code
137+
138+
MESSAGES=(
139+
"PASS: headless vgpu + DirectFB2 checks"
140+
"FAIL: boot/login prompt not found"
141+
"FAIL: shell prompt not found"
142+
"FAIL: virtio-gpu basic checks failed (/dev/dri/card0 or virtio_gpu binding)"
143+
"FAIL: DirectFB2 check failed (run.sh/df_drivertest missing or no DRMKMS init messages)"
144+
)
145+
146+
# Clean up pre-built ext4.img so other tests can use their own
147+
if [ -f "ext4.img.bz2" ]; then
148+
rm -f ext4.img
149+
fi
150+
151+
if [[ "${ret}" -eq 0 ]]; then
152+
print_success "${MESSAGES[0]}"
153+
exit 0
154+
fi
155+
156+
print_error "${MESSAGES[${ret}]:-FAIL: unknown error (exit code ${ret})}"
157+
exit "${ret}"

.ci/test-vinput.sh

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6+
. "${SCRIPT_DIR}/common.sh"
7+
8+
# Override timeout for macOS - emulation is significantly slower
9+
case "${OS_TYPE}" in
10+
Darwin)
11+
TIMEOUT=10800
12+
;;
13+
esac
14+
15+
cleanup
16+
trap cleanup EXIT
17+
18+
# NOTE: We want to capture the expect exit code and map
19+
# it to our MESSAGES array for meaningful error output.
20+
# Temporarily disable errexit for the expect call.
21+
set +e
22+
expect <<'DONE'
23+
set timeout $env(TIMEOUT)
24+
spawn make check
25+
26+
# Boot and login
27+
expect "buildroot login:" { send "root\r" } timeout { exit 1 }
28+
expect "# " { send "uname -a\r" } timeout { exit 2 }
29+
expect "riscv32 GNU/Linux" {}
30+
31+
# ---------------- virtio-input ----------------
32+
# Require actual event* nodes, not just /dev/input directory existence
33+
expect "# " { send "ls /dev/input/event* >/dev/null 2>&1 && echo __EVT_OK__ || echo __EVT_BAD__\r" }
34+
expect {
35+
-re "\r?\n__EVT_OK__" {}
36+
-re "\r?\n__EVT_BAD__" { exit 3 }
37+
timeout { exit 3 }
38+
}
39+
40+
expect "# " { send "cat /proc/bus/input/devices | head -20\r" }
41+
expect "# " { send "grep -qi virtio /proc/bus/input/devices && echo __VPROC_OK__ || echo __VPROC_WARN__\r" }
42+
expect -re "__VPROC_(OK|WARN)__" {} timeout { exit 3 }
43+
DONE
44+
45+
ret="$?"
46+
set -e # Re-enable errexit after capturing expect's return code
47+
48+
MESSAGES=(
49+
"PASS: headless virtio-input checks"
50+
"FAIL: boot/login prompt not found"
51+
"FAIL: shell prompt not found"
52+
"FAIL: virtio-input basic checks failed (/dev/input/event* or /proc/bus/input/devices)"
53+
"FAIL: virtio-input event stream did not produce bytes (needs host->virtio-input injection path)"
54+
)
55+
56+
if [[ "${ret}" -eq 0 ]]; then
57+
print_success "${MESSAGES[0]}"
58+
exit 0
59+
fi
60+
61+
print_error "${MESSAGES[${ret}]:-FAIL: unknown error (exit code ${ret})}"
62+
exit "${ret}"

.github/actions/setup-semu/action.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ runs:
1414
device-tree-compiler \
1515
expect \
1616
libasound2-dev \
17-
libudev-dev
17+
libudev-dev \
18+
libsdl2-dev \
1819
1920
- name: Install dependencies (macOS)
2021
if: runner.os == 'macOS'
@@ -23,4 +24,4 @@ runs:
2324
HOMEBREW_NO_AUTO_UPDATE: 1
2425
HOMEBREW_NO_ANALYTICS: 1
2526
run: |
26-
brew install make dtc expect e2fsprogs
27+
brew install make dtc expect e2fsprogs sdl2

.github/workflows/main.yml

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,18 @@ jobs:
7373
run: .ci/test-sound.sh
7474
shell: bash
7575
timeout-minutes: 5
76+
- name: virtio-gpu test
77+
run: .ci/test-vgpu.sh
78+
shell: bash
79+
timeout-minutes: 5
80+
env:
81+
SDL_VIDEODRIVER: offscreen
82+
- name: virtio-input test
83+
run: .ci/test-vinput.sh
84+
shell: bash
85+
timeout-minutes: 5
86+
env:
87+
SDL_VIDEODRIVER: offscreen
7688

7789
semu-macOS:
7890
runs-on: macos-latest
@@ -101,7 +113,7 @@ jobs:
101113
- name: install-dependencies
102114
uses: ./.github/actions/setup-semu
103115
- name: default build
104-
run: make ENABLE_SDL=0
116+
run: make
105117
shell: bash
106118
timeout-minutes: 20
107119
- name: automated test
@@ -126,6 +138,18 @@ jobs:
126138
run: .ci/test-sound.sh
127139
shell: bash
128140
timeout-minutes: 20
141+
- name: virtio-gpu test
142+
run: .ci/test-vgpu.sh
143+
shell: bash
144+
timeout-minutes: 60
145+
env:
146+
SDL_VIDEODRIVER: offscreen
147+
- name: virtio-input test
148+
run: .ci/test-vinput.sh
149+
shell: bash
150+
timeout-minutes: 20
151+
env:
152+
SDL_VIDEODRIVER: offscreen
129153

130154
coding_style:
131155
runs-on: ubuntu-24.04

Makefile

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,6 @@ else
176176
override ENABLE_VIRTIOGPU := 0
177177
endif
178178

179-
# virtio-gpu
180-
ifneq ($(UNAME_S),Linux)
181-
ENABLE_VIRTIOGPU := 0
182-
endif
183179
ifeq ($(ENABLE_VIRTIOGPU),1)
184180
OBJS_EXTRA += window-events.o
185181
OBJS_EXTRA += virtio-gpu.o
@@ -191,9 +187,6 @@ $(call set-feature, VIRTIOGPU)
191187

192188
# virtio-input
193189
ENABLE_VIRTIOINPUT ?= 1
194-
ifneq ($(UNAME_S),Linux)
195-
ENABLE_VIRTIOINPUT := 0
196-
endif
197190
$(call set-feature, VIRTIOINPUT)
198191
ifeq ($(call has, VIRTIOINPUT), 1)
199192
OBJS_EXTRA += virtio-input.o

0 commit comments

Comments
 (0)