Skip to content

Commit 0466026

Browse files
authored
Merge pull request #79 from nathanchance/efi
boot-qemu.py: Add support for booting arm64 and x86_64 guests under UEFI
2 parents 3edfe3a + 7b8d05b commit 0466026

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
@@ -42,6 +42,9 @@ def parse_arguments():
4242
default="",
4343
type=str,
4444
help="A string of values to pass to the kernel command line.")
45+
parser.add_argument("--efi",
46+
action="store_true",
47+
help="Boot kernel using UEFI (arm64 and x86_64 only).")
4548
parser.add_argument(
4649
"-g",
4750
"--gdb",
@@ -210,6 +213,8 @@ def setup_cfg(args):
210213
* append: The additional values to pass to the kernel command line.
211214
* architecture: The guest architecture from the list of supported
212215
architectures.
216+
* efi: Whether or not to boot the guest under UEFI (arm64 and x86_64
217+
only).
213218
* gdb: Whether or not the user wants to debug the kernel using GDB.
214219
* gdb_bin: The name of or path to the GDB executable that the user
215220
wants to debug with.
@@ -237,6 +242,7 @@ def setup_cfg(args):
237242

238243
# Optional
239244
"append": args.append,
245+
"efi": args.efi,
240246
"gdb": args.gdb,
241247
"gdb_bin": args.gdb_bin,
242248
"interactive": args.interactive or args.gdb,
@@ -355,6 +361,90 @@ def get_and_decomp_rootfs(cfg):
355361
return rootfs
356362

357363

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

521-
if use_kvm:
612+
if use_kvm and not efi:
522613
qemu_args += ["-d", "unimp,guest_errors"]
523614
elif arch == "x86_64":
524615
qemu_args += ["-cpu", "Nehalem"]
@@ -564,6 +655,10 @@ def get_qemu_args(cfg):
564655
if len(append) > 0:
565656
qemu_args += ["-append", append.strip()]
566657

658+
# Handle UEFI firmware if necessary
659+
if efi:
660+
qemu_args += get_efi_args(arch)
661+
567662
# KVM and '-smp'
568663
if use_kvm:
569664
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
@@ -85,3 +85,13 @@ def red(string):
8585
string (str): String to print in bold red.
8686
"""
8787
print(f"\n\033[01;31m{string}\033[0m", flush=True)
88+
89+
90+
def yellow(string):
91+
"""
92+
Prints string in bold yellow.
93+
94+
Parameters:
95+
string (str): String to print in bold yellow.
96+
"""
97+
print(f"\n\033[01;33m{string}\033[0m", flush=True)

0 commit comments

Comments
 (0)