Skip to content

Commit 7b8d05b

Browse files
committed
boot-qemu.py: Add support for booting arm64 and x86_64 guests under UEFI
Booting under UEFI can expose potential issues that will impact real hardware, like CFI failures. Add support for booting under UEFI with arm64 and x86_64 by searching for firmare on the user's system, copying and modifying it as necessary, then passing it along to QEMU. This is deliberately a separate option that is off by default, as I am not sure we can turn this on by default without regressing older trees or toolchains. It has only been tested with tip of tree Linux and LLVM. Closes: #14 Signed-off-by: Nathan Chancellor <nathan@kernel.org>
1 parent 9ef7866 commit 7b8d05b

3 files changed

Lines changed: 109 additions & 2 deletions

File tree

boot-qemu.py

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ def parse_arguments():
4040
default="",
4141
type=str,
4242
help="A string of values to pass to the kernel command line.")
43+
parser.add_argument("--efi",
44+
action="store_true",
45+
help="Boot kernel using UEFI (arm64 and x86_64 only).")
4346
parser.add_argument(
4447
"-g",
4548
"--gdb",
@@ -207,6 +210,8 @@ def setup_cfg(args):
207210
* append: The additional values to pass to the kernel command line.
208211
* architecture: The guest architecture from the list of supported
209212
architectures.
213+
* efi: Whether or not to boot the guest under UEFI (arm64 and x86_64
214+
only).
210215
* gdb: Whether or not the user wants to debug the kernel using GDB.
211216
* gdb_bin: The name of or path to the GDB executable that the user
212217
wants to debug with.
@@ -234,6 +239,7 @@ def setup_cfg(args):
234239

235240
# Optional
236241
"append": args.append,
242+
"efi": args.efi,
237243
"gdb": args.gdb,
238244
"gdb_bin": args.gdb_bin,
239245
"interactive": args.interactive or args.gdb,
@@ -352,6 +358,90 @@ def get_and_decomp_rootfs(cfg):
352358
return rootfs
353359

354360

361+
def get_efi_args(guest_arch):
362+
"""
363+
Generate QEMU arguments for EFI and performing any necessary setup steps
364+
like preparing firmware files.
365+
366+
Parameters:
367+
guest_arch (str): The architecture of the guest.
368+
369+
Return:
370+
efi_args (list): A list of arguments for QEMU to boot using UEFI.
371+
"""
372+
efi_img_locations = {
373+
"arm64": [
374+
Path("edk2/aarch64/QEMU_EFI.silent.fd"), # Fedora
375+
Path("edk2/aarch64/QEMU_EFI.fd"), # Arch Linux (current)
376+
Path("edk2-armvirt/aarch64/QEMU_EFI.fd"), # Arch Linux (old)
377+
Path("qemu-efi-aarch64/QEMU_EFI.fd"), # Debian and Ubuntu
378+
None # Terminator
379+
],
380+
"x86_64": [
381+
Path("edk2/x64/OVMF_CODE.fd"), # Arch Linux (current), Fedora
382+
Path("edk2-ovmf/x64/OVMF_CODE.fd"), # Arch Linux (old)
383+
Path("OVMF/OVMF_CODE.fd"), # Debian and Ubuntu
384+
None # Terminator
385+
]
386+
} # yapf: disable
387+
388+
if guest_arch not in efi_img_locations:
389+
utils.yellow(
390+
f"Found '--efi' with supported architecture ('{guest_arch}'), continuing as if it was not passed..."
391+
)
392+
return []
393+
394+
for efi_img_location in efi_img_locations[guest_arch]:
395+
if efi_img_location is None:
396+
raise Exception(f"edk2 could not be found for {guest_arch}!")
397+
efi_img = Path("/usr/share", efi_img_location)
398+
if efi_img.exists():
399+
break
400+
401+
if guest_arch == "arm64":
402+
# Sizing the images to 64M is recommended by "Prepare the firmware" section at
403+
# https://mirrors.edge.kernel.org/pub/linux/kernel/people/will/docs/qemu/qemu-arm64-howto.html
404+
efi_img_size = 64 * 1024 * 1024 # 64M
405+
406+
efi_img_qemu = base_folder.joinpath("images", guest_arch, "efi.img")
407+
shutil.copyfile(efi_img, efi_img_qemu)
408+
efi_img_qemu.open(mode="r+b").truncate(efi_img_size)
409+
410+
efi_vars_qemu = base_folder.joinpath("images", guest_arch,
411+
"efivars.img")
412+
efi_vars_qemu.unlink(missing_ok=True)
413+
efi_vars_qemu.open(mode="xb").truncate(efi_img_size)
414+
415+
elif guest_arch == "x86_64":
416+
efi_img_qemu = efi_img # This is just usable, it is marked read only
417+
418+
# Copy base EFI variables file
419+
efi_vars_locations = [
420+
Path("edk2/x64/OVMF_VARS.fd"), # Arch Linux and Fedora
421+
Path("OVMF/OVMF_VARS.fd"), # Debian and Ubuntu
422+
None # Terminator
423+
]
424+
for efi_vars_location in efi_vars_locations:
425+
if efi_vars_location is None:
426+
raise Exception("OVMF_VARS.fd could not be found!")
427+
efi_vars = Path('/usr/share', efi_vars_location)
428+
if efi_vars.exists():
429+
break
430+
431+
efi_vars_qemu = base_folder.joinpath("images", guest_arch,
432+
efi_vars.name)
433+
shutil.copyfile(efi_vars, efi_vars_qemu)
434+
435+
# The RNG is included to get the benefits of a KASLR seed on arm64
436+
# and it does not hurt x86_64.
437+
return [
438+
"-drive", f"if=pflash,format=raw,file={efi_img_qemu},readonly=on",
439+
"-drive", f"if=pflash,format=raw,file={efi_vars_qemu}",
440+
"-object", "rng-random,filename=/dev/urandom,id=rng0",
441+
"-device", "virtio-rng-pci"
442+
] # yapf: disable
443+
444+
355445
def get_qemu_args(cfg):
356446
"""
357447
Generate the QEMU command from the QEMU executable and parameters, based on
@@ -370,6 +460,7 @@ def get_qemu_args(cfg):
370460
"""
371461
# Static values from cfg
372462
arch = cfg["architecture"]
463+
efi = cfg["efi"]
373464
kernel_location = cfg["kernel_location"]
374465
gdb = cfg["gdb"]
375466
interactive = cfg["interactive"]
@@ -515,7 +606,7 @@ def get_qemu_args(cfg):
515606
append += " console=ttyS0 earlycon=uart8250,io,0x3f8"
516607
kernel_image = "bzImage"
517608

518-
if use_kvm:
609+
if use_kvm and not efi:
519610
qemu_args += ["-d", "unimp,guest_errors"]
520611
elif arch == "x86_64":
521612
qemu_args += ["-cpu", "Nehalem"]
@@ -561,6 +652,10 @@ def get_qemu_args(cfg):
561652
if len(append) > 0:
562653
qemu_args += ["-append", append.strip()]
563654

655+
# Handle UEFI firmware if necessary
656+
if efi:
657+
qemu_args += get_efi_args(arch)
658+
564659
# KVM and '-smp'
565660
if use_kvm:
566661
qemu_args += ["-cpu", kvm_cpu]

images/.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
debian.img
1+
efi.img
2+
efivars.img
23
rootfs.cpio
34
rootfs.ext4
5+
OVMF_VARS.fd

utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,13 @@ def red(string):
8484
string (str): String to print in bold red.
8585
"""
8686
print(f"\n\033[01;31m{string}\033[0m", flush=True)
87+
88+
89+
def yellow(string):
90+
"""
91+
Prints string in bold yellow.
92+
93+
Parameters:
94+
string (str): String to print in bold yellow.
95+
"""
96+
print(f"\n\033[01;33m{string}\033[0m", flush=True)

0 commit comments

Comments
 (0)