Skip to content

Commit fac64d7

Browse files
committed
integration-test: use Limine as Multiboot(2) bootloader
Using Limine as bootloader has the main benefit that we easily can create a hybrid-bootable ISO that can boot on UEFI and BIOS environments, but we still have a Multiboot2 32-bit hand-off. We only tested BIOS so far but now, we can also extend our testing to UEFI environments.
1 parent a60993e commit fac64d7

File tree

15 files changed

+187
-158
lines changed

15 files changed

+187
-158
lines changed

README.md

+9-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ at your option.
2222

2323
### Contribution
2424

25-
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the
26-
work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any
25+
Unless you explicitly state otherwise, any contribution intentionally submitted
26+
for inclusion in the
27+
work by you, as defined in the Apache-2.0 license, shall be dual licensed as
28+
above, without any
2729
additional terms or conditions.
30+
31+
## Developer Guide
32+
33+
This is a pretty normal cargo workspace with two crates, please read the
34+
[instructions](./integration-test/README.md).

integration-test/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
serial.txt

integration-test/README.md

+14-6
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,29 @@
33
This directory contains integration tests for the `multiboot2` and the
44
`multiboot2-header` crate. The integration tests start a QEMU VM and do certain
55
checks at runtime. If something fails, they instruct QEMU to exit with an error
6-
code. All output of the VM is printed to the screen. If
6+
code. All output of the VM is printed to the screen.
77

8-
The `bins` directory contains binaries that **are** the tests. The `tests`
8+
The `bins` directory contains Rust binaries that **are** the tests. The `tests`
99
directory contains test definitions, run scripts, and other relevant files. The
1010
main entry to run all tests is `./run.sh` in this directory.
1111

12-
## TL;DR:
13-
- `$ nix-shell --run ./run.sh` to execute the integration tests with Nix (recommended)
14-
- `$ ./run.sh` to execute the integration tests (you have to install dependencies manually)
12+
## TL;DR
13+
14+
- `$ nix-shell --run ./run.sh` to execute the integration tests with Nix
15+
(recommended)
16+
- `$ nix-shell --run "integration-test/run.sh"` to run the test from the
17+
project root
18+
- `$ ./run.sh` to execute the integration tests (you have to get the
19+
dependencies manually)
1520

1621
## Prerequisites
22+
1723
The tests are executed best when using [`nix`](https://nixos.org/)/`nix-shell`
1824
to get the relevant tools. Otherwise, please make sure the following packages
1925
are available:
20-
- grub helper tools
26+
27+
- grub helper tools (grub-file)
2128
- rustup
29+
- OVMF
2230
- QEMU
2331
- xorriso

integration-test/run.sh

+137-12
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,155 @@ IFS=$'\n\t'
77
DIR=$(dirname "$(realpath "$0")")
88
cd "$DIR" || exit
99

10+
BINS_DIR=bins/target/x86-unknown-none/release
11+
ANSI_HIGHLIGHT="\e[1;32m" # green + bold
12+
ANSI_HIGHLIGHT_ERROR="\e[1;31m" # red + bold
13+
ANSI_RESET="\e[0m"
14+
QEMU_ARGS_BASE=(
15+
-machine q35,accel=kvm
16+
-m 128m # for OVMF, we need more than just 24
17+
-debugcon stdio
18+
-serial file:serial.txt
19+
-no-reboot
20+
-device isa-debug-exit,iobase=0xf4,iosize=0x04
21+
-display none `# relevant for the CI`
22+
)
23+
1024
function fn_main() {
25+
git submodule update --init
26+
fn_build_limine_hosttool
1127
fn_build_rust_bins
12-
fn_multiboot2_integrationtest
13-
fn_multiboot2_header_integrationtest
28+
29+
fn_test_payload
30+
fn_test_loader
31+
}
32+
33+
function fn_build_limine_hosttool() {
34+
cd limine-bootloader
35+
make
36+
test -f ./limine
37+
file --brief ./limine | grep -q "ELF 64-bit LSB executable"
38+
cd -
1439
}
1540

1641
function fn_build_rust_bins() {
1742
cd "bins"
1843
cargo --version
1944
cargo build --release --verbose
20-
cd "$DIR"
45+
cd -
46+
47+
test -f $BINS_DIR/multiboot2_chainloader
48+
file --brief $BINS_DIR/multiboot2_chainloader | grep -q "ELF 32-bit LSB executable"
49+
# For simplicity, the chainloader itself boots via Multiboot 1. Sufficient.
50+
grub-file --is-x86-multiboot $BINS_DIR/multiboot2_chainloader
51+
52+
test -f $BINS_DIR/multiboot2_payload
53+
file --brief $BINS_DIR/multiboot2_payload | grep -q "ELF 32-bit LSB executable"
54+
grub-file --is-x86-multiboot2 $BINS_DIR/multiboot2_payload
55+
}
56+
57+
function fn_prepare_test_vol() {
58+
TEST_VOL="$TEST_DIR/.vol"
59+
rm -rf $TEST_VOL
60+
mkdir -p $TEST_VOL
61+
cp $TEST_DIR/limine.cfg $TEST_VOL
62+
63+
# copy limine artifacts
64+
mkdir -p $TEST_VOL/limine
65+
cp limine-bootloader/limine-bios-cd.bin $TEST_VOL/limine
66+
cp limine-bootloader/limine-bios.sys $TEST_VOL/limine
67+
cp limine-bootloader/limine-uefi-cd.bin $TEST_VOL/limine
68+
69+
mkdir -p $TEST_VOL/EFI/BOOT
70+
cp limine-bootloader/BOOTX64.EFI $TEST_VOL/EFI_BOOT
2171
}
2272

23-
function fn_multiboot2_integrationtest() {
24-
cd tests/multiboot2
25-
./build_img.sh
26-
./run_qemu.sh
27-
cd "$DIR"
73+
74+
75+
# Builds a hybrid-bootable image using Limine as bootloader. Expects that
76+
# all relevant files are in the directory describing the root volume.
77+
function fn_build_limine_iso() {
78+
xorriso -as mkisofs -b limine/limine-bios-cd.bin \
79+
-no-emul-boot -boot-load-size 4 -boot-info-table \
80+
--efi-boot limine/limine-uefi-cd.bin \
81+
-efi-boot-part --efi-boot-image --protective-msdos-label \
82+
$TEST_VOL -o $TEST_DIR/image.iso 2>/dev/null
83+
84+
./limine-bootloader/limine bios-install $TEST_DIR/image.iso 2>/dev/null
2885
}
2986

30-
function fn_multiboot2_header_integrationtest() {
31-
cd tests/multiboot2-header
32-
./run_qemu.sh
33-
cd "$DIR"
87+
function fn_run_qemu() {
88+
set +e
89+
90+
# As QEMU can't print serial and debugcon to stdout simultaneously, I
91+
# add a background task watching serial.txt
92+
rm serial.txt
93+
touch serial.txt
94+
tail -f serial.txt &
95+
96+
qemu-system-x86_64 "${QEMU_ARGS[@]}"
97+
EXIT_CODE=$?
98+
# Custom exit code used by the integration test to report success.
99+
QEMU_EXIT_SUCCESS=73
100+
101+
set -e
102+
103+
echo "#######################################"
104+
if [[ $EXIT_CODE -eq $QEMU_EXIT_SUCCESS ]]; then
105+
echo -e "${ANSI_HIGHLIGHT}SUCCESS${ANSI_RESET}"
106+
echo # newline
107+
else
108+
echo -e "${ANSI_HIGHLIGHT_ERROR}FAILED - Integration Test 'multiboot2-header'${ANSI_RESET}"
109+
exit "$EXIT_CODE"
110+
fi
111+
}
112+
113+
function fn_run_test_bios() {
114+
local ISO=$1
115+
local QEMU_ARGS=("${QEMU_ARGS_BASE[@]}") # copy array
116+
local QEMU_ARGS+=(
117+
-cdrom "$ISO"
118+
)
119+
echo -e "Running '${ANSI_HIGHLIGHT}$ISO${ANSI_RESET}' in QEMU (with legacy BIOS firmware)"
120+
fn_run_qemu
121+
}
122+
123+
function fn_run_test_uefi() {
124+
local ISO=$1
125+
local QEMU_ARGS=("${QEMU_ARGS_BASE[@]}") # copy array
126+
local QEMU_ARGS+=(
127+
# Usually, this comes from the Nix shell.
128+
-bios $OVMF
129+
-cdrom "$ISO"
130+
)
131+
echo -e "Running '${ANSI_HIGHLIGHT}$ISO${ANSI_RESET}' in QEMU (with UEFI/OVMF firmware)"
132+
fn_run_qemu $QEMU_ARGS
133+
}
134+
135+
function fn_test_payload() {
136+
local TEST_DIR=tests/01-boot-payload
137+
fn_prepare_test_vol
138+
139+
cp $BINS_DIR/multiboot2_payload $TEST_VOL/kernel
140+
141+
fn_build_limine_iso
142+
143+
fn_run_test_bios $TEST_DIR/image.iso
144+
fn_run_test_uefi $TEST_DIR/image.iso
145+
}
146+
147+
# Tests the loader by chainloading the Multiboot2 payload.
148+
function fn_test_loader() {
149+
local TEST_DIR=tests/02-boot-loader-and-chainload
150+
fn_prepare_test_vol
151+
152+
cp $BINS_DIR/multiboot2_chainloader $TEST_VOL/kernel
153+
cp $BINS_DIR/multiboot2_payload $TEST_VOL/payload
154+
155+
fn_build_limine_iso
156+
157+
fn_run_test_bios $TEST_DIR/image.iso
158+
fn_run_test_uefi $TEST_DIR/image.iso
34159
}
35160

36161
fn_main

integration-test/tests/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.vol
2+
image.iso
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
TIMEOUT=0
2+
SERIAL=yes
3+
VERBOSE=yes
4+
INTERFACE_BRANDING=integration-test
5+
6+
:integration-test
7+
PROTOCOL=multiboot2
8+
KERNEL_PATH=boot:///kernel
9+
KERNEL_CMDLINE=some kernel cmdline
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
TIMEOUT=0
2+
SERIAL=yes
3+
VERBOSE=yes
4+
INTERFACE_BRANDING=integration-test
5+
6+
:integration-test
7+
# For legacy reasons, the loader itself boots via Multiboot 1. Sufficient.
8+
PROTOCOL=multiboot
9+
KERNEL_PATH=boot:///kernel
10+
KERNEL_CMDLINE=some kernel cmdline
11+
MODULE_PATH=boot:///payload
12+
KERNEL_CMDLINE=some payload cmdline

integration-test/tests/multiboot2-header/README.md

-7
This file was deleted.

integration-test/tests/multiboot2-header/run_qemu.sh

-41
This file was deleted.

integration-test/tests/multiboot2/.gitignore

-2
This file was deleted.

integration-test/tests/multiboot2/README.md

-5
This file was deleted.

integration-test/tests/multiboot2/build_img.sh

-29
This file was deleted.

integration-test/tests/multiboot2/grub.cfg

-13
This file was deleted.

integration-test/tests/multiboot2/run_qemu.sh

-40
This file was deleted.

0 commit comments

Comments
 (0)