Files
u-boot/scripts/build-efi
Simon Glass f8de2c4048 scripts: Move some QEMU-arg building into build_helper
Providing a kernel, initrd and related options are useful in the EFI app
too, so move them into the common code.

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-06-30 14:14:58 -06:00

217 lines
7.6 KiB
Python
Executable File

#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0+
"""
Script to build an EFI thing suitable for booting with QEMU, possibly running
it also.
UEFI binaries for QEMU used for testing this script:
OVMF-pure-efi.i386.fd at
https://drive.google.com/file/d/1jWzOAZfQqMmS2_dAK2G518GhIgj9r2RY/view?usp=sharing
OVMF-pure-efi.x64.fd at
https://drive.google.com/file/d/1c39YI9QtpByGQ4V0UNNQtGqttEzS-eFV/view?usp=sharing
Use ~/.build-efi to configure the various paths used by this script.
"""
from argparse import ArgumentParser
import os
from pathlib import Path
import shutil
import build_helper
# pylint: disable=C0413
from u_boot_pylib import command
from u_boot_pylib import tools
from u_boot_pylib import tout
def parse_args():
"""Parse the program arguments
Return:
Namespace object
"""
parser = ArgumentParser(
epilog='Script for running U-Boot as an EFI app/payload')
build_helper.add_common_args(parser)
parser.add_argument('-g', '--debug', action='store_true',
help="Run QEMU with gdb")
parser.add_argument('--write-kernel', action='store_true',
help='Add a kernel to the disk image')
parser.add_argument('-O', '--old', action='store_true',
help='Use old EFI app build (before 32/64 split)')
parser.add_argument('-p', '--payload', action='store_true',
help='Package up the payload instead of the app')
parser.add_argument('-P', '--partition', action='store_true',
help='Create a partition table')
parser.add_argument('-v', '--verbose', action='store_true',
help='Show executed commands')
args = parser.parse_args()
return args
class BuildEfi:
"""Class to collect together the various bits of state while running"""
def __init__(self, args):
self.helper = build_helper.Helper()
self.helper.read_settings()
self.img = self.helper.get_setting('efi_image_file', 'efi.img')
self.build_dir = self.helper.get_setting("build_dir", '/tmp')
self.args = args
self.imagedir = Path(self.helper.get_setting('image_dir', '~/dev'))
def run_qemu(self, bitness, serial_only):
"""Run QEMU
Args:
bitness (int): Bitness to use, 32 or 64
serial_only (bool): True to run without a display
"""
extra = []
efi_dir = self.helper.get_setting('efi_dir')
if self.args.arch == 'arm':
os_arch = 'arm64'
qemu_arch = 'aarch64'
extra += ['--machine', 'virt', '-cpu', 'max']
bios = os.path.join(efi_dir, 'OVMF-pure-efi.aarch64.fd.64m')
var_store = os.path.join(efi_dir, 'varstore.img')
extra += [
'-drive', f'if=pflash,format=raw,file={bios},readonly=on',
'-drive', f'if=pflash,format=raw,file={var_store}'
]
extra += ['-drive',
f'id=hd0,file={self.img},if=none,format=raw',
'-device', 'virtio-blk-device,drive=hd0']
else: # x86
if bitness == 64:
qemu_arch = 'x86_64'
bios = 'OVMF_CODE_4M.fd'
os_arch = 'amd64'
else:
qemu_arch = 'i386'
bios = 'OVMF-pure-efi.i386.fd'
os_arch = 'i386'
bios = os.path.join(efi_dir, bios)
var_store = os.path.join(efi_dir, 'OVMF_VARS_4M.fd')
extra += [
'-drive', f'if=pflash,format=raw,file={bios},readonly=on',
'-drive', f'if=pflash,format=raw,file={var_store}'
]
extra += ['-drive', f'id=disk,file={self.img},if=none,format=raw']
extra += ['-device', 'ahci,id=ahci']
extra += ['-device', 'ide-hd,drive=disk,bus=ahci.0']
qemu = f'qemu-system-{qemu_arch}'
if serial_only:
extra += ['-display', 'none', '-serial', 'mon:stdio']
serial_msg = ' (Ctrl-a x to quit)'
else:
if self.args.arch == 'arm':
extra += ['-device', 'virtio-gpu-pci']
extra += ['-serial', 'mon:stdio']
serial_msg = ''
if self.args.kvm:
extra.extend(['-enable-kvm', '-cpu', 'host'])
os_path = None
if self.args.os == 'ubuntu':
img_name = f'{self.args.os}-{self.args.release}-desktop-{os_arch}.iso'
os_path = self.imagedir / self.args.os / img_name
if not os_path.exists():
tout.error(f'OS image {os_path} specified but not found')
else:
extra.extend([
'-drive',
f'if=virtio,file={os_path},format=raw,id=hd0,readonly=on'])
img_fname = Path(self.args.disk) if self.args.disk else None
if img_fname:
if img_fname.exists():
extra.extend([
'-drive',
f'if=virtio,file={img_fname},format=raw,id=hd1'])
else:
tout.warning(f"Disk image '{img_fname}' not found")
print(f'Running {qemu}{serial_msg}')
# Use 512MB since U-Boot EFI likes to have 256MB to play with
if self.args.os or self.args.disk:
mem = '4G'
extra.extend(['-smp', '4'])
else:
mem = '512'
if self.args.debug:
extra.extend(['-s', '-S'])
cmd = [qemu]
cmd += '-m', mem
cmd += '-nic', 'none'
self.helper.add_qemu_args(self.args, cmd)
cmd += extra
tout.info(' '.join(cmd))
command.run(*cmd)
def setup_files(self, build, build_type, dst):
"""Set up files in the staging area
Args:
build (str): Name of build being packaged, e.g. 'efi-x86_app32'
build_type (str): Build type ('app' or 'payload')
dst (str): Destination directory
"""
print(f'Packaging {build}')
fname = f'u-boot-{build_type}.efi'
tools.write_file(f'{dst}/startup.nsh', f'fs0:{fname}', binary=False)
shutil.copy(f'{self.build_dir}/{build}/{fname}', dst)
def do_build(self, build):
"""Build U-Boot for the selected board"""
res = command.run_one('buildman', '-w', '-o',
f'{self.build_dir}/{build}', '--board', build,
'-I', raise_on_error=False)
if res.return_code and res.return_code != 101: # Allow warnings
raise ValueError(
f'buildman exited with {res.return_code}: {res.combined}')
def start(self):
"""This does all the work"""
args = self.args
bitness = 32 if args.word_32bit else 64
arch = 'arm' if self.args.arch == 'arm' else 'x86'
build_type = 'payload' if args.payload else 'app'
build = f'efi-{arch}_{build_type}{bitness}'
if not args.no_build:
self.do_build(build)
if args.old and bitness == 32:
build = f'efi-{arch}_{build_type}'
with self.helper.make_disk(self.img, fs_type='vfat',
use_part=args.partition) as dirpath:
self.setup_files(build, build_type, dirpath)
if self.args.write_kernel:
bzimage = self.helper.get_setting('bzimage_file', 'bzImage')
command.run('cp', bzimage, f'{dirpath}/vmlinuz')
if args.run:
self.run_qemu(bitness, args.serial_only)
def main():
"""Parse arguments and start the program"""
args = parse_args()
tout.init(tout.INFO if args.verbose else tout.WARNING)
qemu = BuildEfi(args)
qemu.start()
if __name__ == '__main__':
main()