@@ -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+
355445def 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 ]
0 commit comments