Auto merge of #101703 - nicholasbishop:bishop-add-uefi-ci-2, r=jyn514
Add QEMU test for x86_64-unknown-uefi The UEFI targets don't have std support yet, so the normal tests don't work. However, we can compile a simple no-std program and run it under QEMU to at least check that the target compiles, links, and runs. Tested locally with: `src/ci/docker/run.sh x86_64-uefi`
This commit is contained in:
commit
c2a5c3a50f
@ -16,7 +16,9 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins
|
|||||||
pkg-config \
|
pkg-config \
|
||||||
xz-utils \
|
xz-utils \
|
||||||
wget \
|
wget \
|
||||||
patch
|
patch \
|
||||||
|
ovmf \
|
||||||
|
qemu-system-x86
|
||||||
|
|
||||||
RUN curl -sL https://nodejs.org/dist/v15.14.0/node-v15.14.0-linux-x64.tar.xz | \
|
RUN curl -sL https://nodejs.org/dist/v15.14.0/node-v15.14.0-linux-x64.tar.xz | \
|
||||||
tar -xJ
|
tar -xJ
|
||||||
@ -64,4 +66,9 @@ ENV MUSL_TARGETS=x86_64-unknown-linux-musl \
|
|||||||
CXX_x86_64_unknown_linux_musl=x86_64-linux-musl-g++
|
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 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=x86_64-unknown-uefi
|
||||||
|
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
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "uefi_qemu_test"
|
||||||
|
version = "0.0.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
r-efi = "4.1.0"
|
96
src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py
Normal file
96
src/ci/docker/host-x86_64/test-various/uefi_qemu_test/run.py
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def run(*cmd, capture=False, check=True, env=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)
|
||||||
|
|
||||||
|
|
||||||
|
def build_and_run(tmp_dir):
|
||||||
|
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.
|
||||||
|
target = 'x86_64-unknown-uefi'
|
||||||
|
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 / 'bootx64.efi')
|
||||||
|
|
||||||
|
# Run the executable in QEMU and capture the output.
|
||||||
|
qemu = 'qemu-system-x86_64'
|
||||||
|
ovmf_dir = Path('/usr/share/OVMF')
|
||||||
|
ovmf_code = ovmf_dir / 'OVMF_CODE.fd'
|
||||||
|
ovmf_vars = ovmf_dir / 'OVMF_VARS.fd'
|
||||||
|
output = run(qemu,
|
||||||
|
'-display',
|
||||||
|
'none',
|
||||||
|
'-serial',
|
||||||
|
'stdio',
|
||||||
|
'-drive',
|
||||||
|
f'if=pflash,format=raw,readonly=on,file={ovmf_code}',
|
||||||
|
'-drive',
|
||||||
|
f'if=pflash,format=raw,readonly=on,file={ovmf_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).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():
|
||||||
|
# Create a temporary directory so that we have a writeable
|
||||||
|
# workspace.
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
build_and_run(tmp_dir)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user