summaryrefslogtreecommitdiffstats
path: root/src/ci/docker/host-x86_64/test-various
diff options
context:
space:
mode:
Diffstat (limited to 'src/ci/docker/host-x86_64/test-various')
-rw-r--r--src/ci/docker/host-x86_64/test-various/Dockerfile26
-rw-r--r--src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml9
-rw-r--r--src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py142
-rw-r--r--src/ci/docker/host-x86_64/test-various/uefi_qemu_test/src/main.rs45
4 files changed, 220 insertions, 2 deletions
diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile
index b75e2f085..cf4451f8b 100644
--- a/src/ci/docker/host-x86_64/test-various/Dockerfile
+++ b/src/ci/docker/host-x86_64/test-various/Dockerfile
@@ -1,6 +1,7 @@
FROM ubuntu:20.04
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ clang-11 \
g++ \
make \
ninja-build \
@@ -16,11 +17,21 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins
pkg-config \
xz-utils \
wget \
- patch
+ patch \
+ ovmf \
+ qemu-efi-aarch64 \
+ qemu-system-arm \
+ qemu-system-x86 \
+ && rm -rf /var/lib/apt/lists/*
RUN curl -sL https://nodejs.org/dist/v15.14.0/node-v15.14.0-linux-x64.tar.xz | \
tar -xJ
+# Install 32-bit OVMF files for the i686-unknown-uefi test. This package
+# is not available in ubuntu 20.04, so download a 22.04 package.
+RUN curl -sL --output ovmf-ia32.deb http://mirrors.kernel.org/ubuntu/pool/universe/e/edk2/ovmf-ia32_2022.02-3_all.deb
+RUN dpkg -i ovmf-ia32.deb && rm ovmf-ia32.deb
+
WORKDIR /build/
COPY scripts/musl-patch-configure.diff /build/
COPY scripts/musl-toolchain.sh /build/
@@ -64,4 +75,15 @@ ENV MUSL_TARGETS=x86_64-unknown-linux-musl \
CXX_x86_64_unknown_linux_musl=x86_64-linux-musl-g++
ENV MUSL_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $MUSL_TARGETS
-ENV SCRIPT $WASM_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT
+COPY host-x86_64/test-various/uefi_qemu_test /uefi_qemu_test
+ENV UEFI_TARGETS=aarch64-unknown-uefi,i686-unknown-uefi,x86_64-unknown-uefi \
+ CC_aarch64_unknown_uefi=clang-11 \
+ CXX_aarch64_unknown_uefi=clang++-11 \
+ CC_i686_unknown_uefi=clang-11 \
+ CXX_i686_unknown_uefi=clang++-11 \
+ CC_x86_64_unknown_uefi=clang-11 \
+ CXX_x86_64_unknown_uefi=clang++-11
+ENV UEFI_SCRIPT python3 /checkout/x.py --stage 2 build --host='' --target $UEFI_TARGETS && \
+ python3 -u /uefi_qemu_test/run.py
+
+ENV SCRIPT $WASM_SCRIPT && $NVPTX_SCRIPT && $MUSL_SCRIPT && $UEFI_SCRIPT
diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml
new file mode 100644
index 000000000..fa8e5b3d0
--- /dev/null
+++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "uefi_qemu_test"
+version = "0.0.0"
+edition = "2021"
+
+[workspace]
+
+[dependencies]
+r-efi = "4.1.0"
diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py
new file mode 100644
index 000000000..ffae7b0d4
--- /dev/null
+++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python3
+
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+from pathlib import Path
+
+TARGET_AARCH64 = 'aarch64-unknown-uefi'
+TARGET_I686 = 'i686-unknown-uefi'
+TARGET_X86_64 = 'x86_64-unknown-uefi'
+
+def run(*cmd, capture=False, check=True, env=None, timeout=None):
+ """Print and run a command, optionally capturing the output."""
+ cmd = [str(p) for p in cmd]
+ print(' '.join(cmd))
+ return subprocess.run(cmd,
+ capture_output=capture,
+ check=check,
+ env=env,
+ text=True,
+ timeout=timeout)
+
+def build_and_run(tmp_dir, target):
+ if target == TARGET_AARCH64:
+ boot_file_name = 'bootaa64.efi'
+ ovmf_dir = Path('/usr/share/AAVMF')
+ ovmf_code = 'AAVMF_CODE.fd'
+ ovmf_vars = 'AAVMF_VARS.fd'
+ qemu = 'qemu-system-aarch64'
+ machine = 'virt'
+ cpu = 'cortex-a72'
+ elif target == TARGET_I686:
+ boot_file_name = 'bootia32.efi'
+ ovmf_dir = Path('/usr/share/OVMF')
+ ovmf_code = 'OVMF32_CODE_4M.secboot.fd'
+ ovmf_vars = 'OVMF32_VARS_4M.fd'
+ # The i686 target intentionally uses 64-bit qemu; the important
+ # difference is that the OVMF code provides a 32-bit environment.
+ qemu = 'qemu-system-x86_64'
+ machine = 'q35'
+ cpu = 'qemu64'
+ elif target == TARGET_X86_64:
+ boot_file_name = 'bootx64.efi'
+ ovmf_dir = Path('/usr/share/OVMF')
+ ovmf_code = 'OVMF_CODE.fd'
+ ovmf_vars = 'OVMF_VARS.fd'
+ qemu = 'qemu-system-x86_64'
+ machine = 'q35'
+ cpu = 'qemu64'
+ else:
+ raise KeyError('invalid target')
+
+ host_artifacts = Path('/checkout/obj/build/x86_64-unknown-linux-gnu')
+ stage0 = host_artifacts / 'stage0/bin'
+ stage2 = host_artifacts / 'stage2/bin'
+
+ env = dict(os.environ)
+ env['PATH'] = '{}:{}:{}'.format(stage2, stage0, env['PATH'])
+
+ # Copy the test create into `tmp_dir`.
+ test_crate = Path(tmp_dir) / 'uefi_qemu_test'
+ shutil.copytree('/uefi_qemu_test', test_crate)
+
+ # Build the UEFI executable.
+ run('cargo',
+ 'build',
+ '--manifest-path',
+ test_crate / 'Cargo.toml',
+ '--target',
+ target,
+ env=env)
+
+ # Create a mock EFI System Partition in a subdirectory.
+ esp = test_crate / 'esp'
+ boot = esp / 'efi/boot'
+ os.makedirs(boot, exist_ok=True)
+
+ # Copy the executable into the ESP.
+ src_exe_path = test_crate / 'target' / target / 'debug/uefi_qemu_test.efi'
+ shutil.copy(src_exe_path, boot / boot_file_name)
+ print(src_exe_path, boot / boot_file_name)
+
+ # Select the appropriate EDK2 build.
+ ovmf_code = ovmf_dir / ovmf_code
+ ovmf_vars = ovmf_dir / ovmf_vars
+
+ # Make a writable copy of the vars file. aarch64 doesn't boot
+ # correctly with read-only vars.
+ ovmf_rw_vars = Path(tmp_dir) / 'vars.fd'
+ shutil.copy(ovmf_vars, ovmf_rw_vars)
+
+ # Run the executable in QEMU and capture the output.
+ output = run(qemu,
+ '-machine',
+ machine,
+ '-cpu',
+ cpu,
+ '-display',
+ 'none',
+ '-serial',
+ 'stdio',
+ '-drive',
+ f'if=pflash,format=raw,readonly=on,file={ovmf_code}',
+ '-drive',
+ f'if=pflash,format=raw,readonly=off,file={ovmf_rw_vars}',
+ '-drive',
+ f'format=raw,file=fat:rw:{esp}',
+ capture=True,
+ # Ubuntu 20.04 (which is what the Dockerfile currently
+ # uses) provides QEMU 4.2.1, which segfaults on
+ # shutdown under some circumstances. That has been
+ # fixed in newer versions of QEMU, but for now just
+ # don't check the exit status.
+ check=False,
+ # Set a timeout to kill the VM in case something goes wrong.
+ timeout=60).stdout
+
+ if 'Hello World!' in output:
+ print('VM produced expected output')
+ else:
+ print('unexpected VM output:')
+ print('---start---')
+ print(output)
+ print('---end---')
+ sys.exit(1)
+
+
+def main():
+ targets = [TARGET_AARCH64, TARGET_I686, TARGET_X86_64]
+
+ for target in targets:
+ # Create a temporary directory so that we have a writeable
+ # workspace.
+ with tempfile.TemporaryDirectory() as tmp_dir:
+ build_and_run(tmp_dir, target)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/src/main.rs b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/src/main.rs
new file mode 100644
index 000000000..2ec554c14
--- /dev/null
+++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/src/main.rs
@@ -0,0 +1,45 @@
+// Code is adapted from this hello world example:
+// https://doc.rust-lang.org/nightly/rustc/platform-support/unknown-uefi.html
+
+#![no_main]
+#![no_std]
+
+use core::{panic, ptr};
+use r_efi::efi::{Char16, Handle, Status, SystemTable, RESET_SHUTDOWN};
+
+#[panic_handler]
+fn panic_handler(_info: &panic::PanicInfo) -> ! {
+ loop {}
+}
+
+#[export_name = "efi_main"]
+pub extern "C" fn main(_h: Handle, st: *mut SystemTable) -> Status {
+ let s = [
+ 0x0048u16, 0x0065u16, 0x006cu16, 0x006cu16, 0x006fu16, // "Hello"
+ 0x0020u16, // " "
+ 0x0057u16, 0x006fu16, 0x0072u16, 0x006cu16, 0x0064u16, // "World"
+ 0x0021u16, // "!"
+ 0x000au16, // "\n"
+ 0x0000u16, // NUL
+ ];
+
+ // Print "Hello World!".
+ let r = unsafe { ((*(*st).con_out).output_string)((*st).con_out, s.as_ptr() as *mut Char16) };
+ if r.is_error() {
+ return r;
+ }
+
+ // Shut down.
+ unsafe {
+ ((*((*st).runtime_services)).reset_system)(
+ RESET_SHUTDOWN,
+ Status::SUCCESS,
+ 0,
+ ptr::null_mut(),
+ );
+ }
+
+ // This should never be reached because `reset_system` should never
+ // return, so fail with an error if we get here.
+ Status::UNSUPPORTED
+}