-
Notifications
You must be signed in to change notification settings - Fork 264
342 lines (305 loc) · 14 KB
/
Copy pathci.yml
File metadata and controls
342 lines (305 loc) · 14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
name: CI
on:
push:
branches: [ master, main ]
pull_request:
branches: [ master, main ]
jobs:
build:
runs-on: ubuntu-latest # Standard runners: 2 cores, 7GB RAM
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-22.04, ubuntu-24.04]
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: NAND media-layer unit tests
run: |
# Milliseconds, no QEMU build needed, so failures show up before the
# 20-minute build rather than after it.
make -C hw/femu/tests check
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y build-essential pkg-config libglib2.0-dev \
libpixman-1-dev libfdt-dev zlib1g-dev libaio-dev \
libcap-ng-dev libattr1-dev ninja-build python3-pip \
libslirp-dev libseccomp-dev libcurl4-gnutls-dev \
libiscsi-dev libnfs-dev librbd-dev librados-dev \
libssh-dev liblzo2-dev libsnappy-dev libbz2-dev \
liblzma-dev libzstd-dev libgcrypt20-dev libgnutls28-dev \
uuid-dev libcap-dev libxml2-dev libmount-dev \
liburing-dev flex bison
- name: Setup FEMU build environment
run: |
mkdir build-femu
cd build-femu
cp ../femu-scripts/femu-copy-scripts.sh .
./femu-copy-scripts.sh .
- name: Build FEMU using official script
run: |
cd build-femu
echo "GitHub Actions runner info:"
echo " Available CPU cores (nproc): $(nproc)"
echo " Available CPU cores (femu method): $(cat /proc/cpuinfo | grep "vendor_id" | wc -l)"
echo " Memory: $(free -h | grep '^Mem:' | awk '{print $2}')"
echo ""
echo "Starting parallel FEMU build at $(date)"
time ./femu-compile.sh
echo "Build completed at $(date)"
- name: Run the FEMU unit tests through meson
run: |
cd build-femu
# The same tests the standalone step ran, now built with QEMU's own
# flags and headers, so a test that only passes against the stub
# osdep.h is caught here.
./pyvenv/bin/meson test test-femu-nand-media test-femu-pqueue \
--print-errorlogs
- name: Verify FEMU device registration
run: |
cd build-femu
./qemu-system-x86_64 -device help | grep -q "femu" || exit 1
echo "✓ FEMU device successfully registered"
- name: Run basic functionality test
run: |
cd build-femu
# The listing has to list something. Discarding the outcome meant this
# step passed on an empty listing, or on no listing at all.
out=$(timeout 10s ./qemu-system-x86_64 -device femu,help 2>&1 || true)
echo "$out" | head -20
for prop in femu_mode devsz_mb namespaces; do
echo "$out" | grep -q "^ ${prop}=" || {
echo "device help did not list ${prop}"; exit 1; }
done
echo "✓ FEMU device parameters accessible"
- name: Check FEMU modes
run: |
cd build-femu
# Every mode must come up. The process is expected to run until the
# timeout kills it (124); any other exit is a failure. This used to
# accept every outcome, which hid that an unprivileged runner could
# not pin the memory backend and the device aborted in every mode.
for mode in 0 1 2 3 4 5; do
extra=""
# computational storage refuses to start without a data memory size
[ "$mode" -eq 4 ] && extra=",fdm_size=64"
rc=0
timeout 10s ./qemu-system-x86_64 \
-device "femu,devsz_mb=1024,femu_mode=${mode},serial=test${mode}${extra}" \
-nographic -M accel=qtest || rc=$?
if [ "$rc" -ne 124 ] && [ "$rc" -ne 0 ]; then
echo "mode ${mode} exited with ${rc}"; exit "$rc"
fi
echo "✓ FEMU mode ${mode} ran until timeout"
done
- name: Run the FEMU qtests
run: |
cd build-femu
# Drives the controller through the doorbell registers alone and then
# through the shadow doorbell buffer; neither needs KVM or a guest.
QTEST_QEMU_BINARY=./qemu-system-x86_64 ./tests/qtest/qos-test -m quick \
-p /x86_64/pc/i440FX-pcihost/pci-bus-pc/pci-bus/femu/femu-tests
- name: Validate the config-file interface
run: |
chmod +x hw/femu/scripts/ssd-config.sh hw/femu/scripts/ssd-config-test.sh
# Expands every example config and checks FEMU accepts the result, so a
# property that is renamed or removed breaks here rather than in
# somebody's run script. Carries its own negative control, so a check
# that stopped detecting bad arguments fails instead of passing
# everything. Runs here rather than in compatibility-test because that
# job installs no dependencies, so the binary cannot start there.
./hw/femu/scripts/ssd-config-test.sh "$PWD/build-femu/qemu-system-x86_64"
- name: Check the guest device test parses
run: bash -n hw/femu/scripts/femu-test.sh
- name: Archive build artifacts
uses: actions/upload-artifact@v4
if: success()
with:
name: femu-binary-${{ matrix.os }}
path: |
build-femu/qemu-system-x86_64
build-femu/config-host.mak
build-femu/run-*.sh
retention-days: 7
# A second build with the FTL's invariant checks compiled in and the address
# and undefined-behaviour sanitizers on. In the normal build ftl_assert() is
# empty, so a bound violation in the FTL corrupts state silently; here it
# aborts, and so does a heap overrun in the controller.
debug-build:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y build-essential pkg-config libglib2.0-dev \
libpixman-1-dev libfdt-dev zlib1g-dev libaio-dev \
libcap-ng-dev libattr1-dev ninja-build python3-pip \
libslirp-dev libseccomp-dev libcurl4-gnutls-dev \
libssh-dev liblzo2-dev libsnappy-dev libbz2-dev \
liblzma-dev libzstd-dev libgcrypt20-dev libgnutls28-dev \
uuid-dev libcap-dev libxml2-dev libmount-dev \
liburing-dev flex bison
- name: Build with FTL asserts and sanitizers
run: |
mkdir build-debug
cd build-debug
../configure --enable-kvm --target-list=x86_64-softmmu --enable-slirp \
--disable-libnfs --disable-libiscsi --disable-curl \
--enable-asan --enable-ubsan --extra-cflags=-DFEMU_FTL_ASSERT
make -j "$(nproc)"
- name: Run the FEMU unit tests under the sanitizers
run: |
cd build-debug
./pyvenv/bin/meson test test-femu-nand-media test-femu-pqueue \
--print-errorlogs
- name: Instantiate every mode with the checks armed
env:
# QEMU frees little at exit by design; only real errors should fail.
ASAN_OPTIONS: detect_leaks=0
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
run: |
cd build-debug
# Plain `timeout` reports 124 whenever it had to signal the child, so a
# child that dies of SIGABRT while handling SIGTERM -- an armed
# assertion firing on the way out -- is indistinguishable from a clean
# run. --preserve-status passes the child's own status through instead:
# 143 is our SIGTERM, 0 is an early clean exit, and anything else, 134
# for an abort above all, is a failure. The output is checked as well,
# because a sanitizer reports without dying.
for mode in 0 1 2 3 4 5; do
extra=""
# computational storage refuses to start without a data memory size
[ "$mode" -eq 4 ] && extra=",fdm_size=64"
rc=0
timeout --preserve-status 20s ./qemu-system-x86_64 \
-device "femu,devsz_mb=1024,femu_mode=${mode},serial=dbg${mode}${extra}" \
-nographic -M accel=qtest > mode${mode}.log 2>&1 || rc=$?
if [ "$rc" -ne 143 ] && [ "$rc" -ne 0 ]; then
echo "mode ${mode} exited with ${rc}"; cat mode${mode}.log; exit 1
fi
if grep -qE "Sanitizer|runtime error:|Assertion.*failed" mode${mode}.log; then
echo "mode ${mode} reported an error:"; cat mode${mode}.log; exit 1
fi
echo "mode ${mode} came up with the checks armed"
done
- name: Run the FEMU qtests under the sanitizers
env:
# abort_on_error is what makes a report a failure. Without it the
# sanitizer prints the diagnosis and the test still says ok, which is
# how a use-after-free on the queue-delete path went unnoticed.
ASAN_OPTIONS: detect_leaks=0:abort_on_error=1
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
run: |
cd build-debug
# The same tests the release job runs, on a binary that reports a bad
# access instead of continuing past it. This is what makes an
# out-of-bounds index deterministic: without the sanitizers it lands
# somewhere in the allocator and the test still passes.
QTEST_QEMU_BINARY=./qemu-system-x86_64 ./tests/qtest/qos-test -m quick \
-p /x86_64/pc/i440FX-pcihost/pci-bus-pc/pci-bus/femu/femu-tests
- name: Expand every example config with the checks armed
env:
# QEMU builds without -fno-sanitize-recover, so an undefined-behaviour
# report would otherwise print and let the process exit 0. The script
# turns leak detection off itself: QEMU frees little at exit by design.
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
run: |
chmod +x hw/femu/scripts/ssd-config.sh hw/femu/scripts/ssd-config-test.sh
./hw/femu/scripts/ssd-config-test.sh "$PWD/build-debug/qemu-system-x86_64"
compatibility-test:
runs-on: ubuntu-latest # Standard runners
needs: build
if: success()
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: femu-binary-ubuntu-22.04
path: build-femu/
- name: Install the libraries the binary links against
run: |
# This job runs a binary built on another runner image and installed
# nothing for it, so it could not load at all. Every step that ran it
# discarded the outcome, so nothing said so. Install exactly what the
# build job installs: the runtime libraries come with those packages,
# and their own names differ between releases.
sudo apt update
sudo apt install -y build-essential pkg-config libglib2.0-dev \
libpixman-1-dev libfdt-dev zlib1g-dev libaio-dev \
libcap-ng-dev libattr1-dev ninja-build python3-pip \
libslirp-dev libseccomp-dev libcurl4-gnutls-dev \
libiscsi-dev libnfs-dev librbd-dev librados-dev \
libssh-dev liblzo2-dev libsnappy-dev libbz2-dev \
liblzma-dev libzstd-dev libgcrypt20-dev libgnutls28-dev \
uuid-dev libcap-dev libxml2-dev libmount-dev \
liburing-dev flex bison
- name: Make binary executable
run: chmod +x build-femu/qemu-system-x86_64
- name: Check the binary can run at all
run: |
cd build-femu
# Everything below runs this binary. If it cannot load its libraries,
# say so here rather than in the middle of a device test.
./qemu-system-x86_64 --version
- name: Test run scripts syntax
run: |
cd build-femu
# Check syntax of run scripts (already copied by build job)
for script in run-*.sh; do
bash -n "$script"
echo "✓ $script syntax check passed"
done
- name: Validate FEMU configuration parameters
run: |
cd build-femu
# A geometry the device refuses has to fail this step. Backgrounding
# the process, sleeping and killing it reported success whether or not
# the device ever realized. As above, the timeout killing it (124) is
# the expected outcome and anything else is a failure.
# femu_mode=1 so the geometry is the black-box FTL's and actually
# gets checked; the default mode ignores it entirely.
# The artifact is the binary alone. It finds its firmware through a
# link tree in the build directory that is not part of the artifact,
# so name the images in the source this job checks out; without that
# it cannot start a machine at all.
rc=0
timeout 10s ./qemu-system-x86_64 -L "$GITHUB_WORKSPACE/pc-bios" \
-device femu,devsz_mb=2048,femu_mode=1,secsz=512,secs_per_pg=8,pgs_per_blk=256 \
-M accel=qtest -nographic || rc=$?
if [ "$rc" -ne 124 ] && [ "$rc" -ne 0 ]; then
echo "the device did not accept the geometry (exit ${rc})"; exit "$rc"
fi
echo "✓ FEMU SSD parameter validation completed"
code-quality:
runs-on: ubuntu-latest # Lightweight job, standard runner sufficient
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Check code style
run: |
# Check for trailing whitespace in FEMU source files
! find hw/femu -name "*.c" -o -name "*.h" | xargs grep -l '[[:space:]]$'
echo "✓ No trailing whitespace found"
# Check for C++ style comments in C files
! find hw/femu -name "*.c" -o -name "*.h" | xargs grep '//'
echo "✓ No C++ style comments found in C files"
- name: Check build system consistency
run: |
# Verify FEMU is properly integrated in build system
grep -q "config FEMU_PCI" hw/femu/Kconfig
grep -q "source femu/Kconfig" hw/Kconfig
echo "✓ Build system integration verified"
- name: Validate documentation
run: |
# Check that README references are valid
[ -f "README.md" ] && echo "✓ README.md exists"
[ -d "hw/femu" ] && echo "✓ FEMU source directory exists"
[ -f "hw/femu/meson.build" ] && echo "✓ FEMU meson.build exists"