[PATCH v5 01/25] RFC: scripts: Add scripts for running QEMU
Simon Glass
sjg at chromium.org
Wed May 28 14:32:03 CEST 2025
These is present in my tree. Provide it as a patch to simplify testing
this series with QEMU.
Signed-off-by: Simon Glass <sjg at chromium.org>
---
(no changes since v1)
scripts/build-efi | 173 +++++++++++++++++
scripts/build-qemu | 415 ++++++++++++++++++++++++++++++++++++++++
scripts/build_helper.py | 126 ++++++++++++
3 files changed, 714 insertions(+)
create mode 100755 scripts/build-efi
create mode 100755 scripts/build-qemu
create mode 100644 scripts/build_helper.py
diff --git a/scripts/build-efi b/scripts/build-efi
new file mode 100755
index 00000000000..2f4b916c49b
--- /dev/null
+++ b/scripts/build-efi
@@ -0,0 +1,173 @@
+#!/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
+import shutil
+
+from build_helper import Helper
+
+# pylint: disable=C0413
+from u_boot_pylib import command
+from u_boot_pylib import tools
+
+
+def parse_args():
+ """Parse the program arguments
+
+ Return:
+ Namespace object
+ """
+ parser = ArgumentParser(
+ epilog='Script for running U-Boot as an EFI app/payload')
+ parser.add_argument('-a', '--app', action='store_true',
+ help='Package up the app')
+ parser.add_argument('-A', '--arm', action='store_true',
+ help='Run on ARM architecture')
+ parser.add_argument('-B', '--no-build', action='store_true',
+ help="Don't build (an existing build must be present")
+ parser.add_argument('-k', '--kernel', action='store_true',
+ help='Add a kernel')
+ 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')
+ parser.add_argument('-P', '--partition', action='store_true',
+ help='Create a partition table')
+ parser.add_argument('-r', '--run', action='store_true',
+ help='Run QEMU with the image')
+ parser.add_argument('-s', '--serial', action='store_true',
+ help='Run QEMU with serial only (no display)')
+ parser.add_argument('-w', '--word', action='store_true',
+ help='Use word version (32-bit) rather than 64-bit')
+
+ args = parser.parse_args()
+
+ if args.app and args.payload:
+ raise ValueError('Please choose either app or payload, not both')
+ return args
+
+
+class BuildEfi:
+ """Class to collect together the various bits of state while running"""
+ def __init__(self, args):
+ self.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
+
+ 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.arm:
+ 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-pure-efi.x64.fd'
+ else:
+ qemu_arch = 'i386'
+ bios = 'OVMF-pure-efi.i386.fd'
+ extra += ['-bios', os.path.join(efi_dir, bios)]
+ 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.arm:
+ extra += ['-device', 'virtio-gpu-pci']
+ extra += ['-serial', 'mon:stdio']
+ serial_msg = ''
+ print(f'Running {qemu}{serial_msg}')
+
+ # Use 512MB since U-Boot EFI likes to have 256MB to play with
+ cmd = [qemu]
+ cmd += '-m', '512'
+ cmd += '-nic', 'none'
+ cmd += extra
+ 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 else 64
+ arch = 'arm' if args.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.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)
+
+
+if __name__ == "__main__":
+ efi = BuildEfi(parse_args())
+ efi.start()
diff --git a/scripts/build-qemu b/scripts/build-qemu
new file mode 100755
index 00000000000..5798da3775c
--- /dev/null
+++ b/scripts/build-qemu
@@ -0,0 +1,415 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0+
+#
+"""Script to build/run U-Boot with QEMU
+
+It assumes that
+
+- you build U-Boot in ${ubdir}/<name> where <name> is the U-Boot
+ board config
+- your OS images are in ${imagedir}/{distroname}/
+
+So far the script supports only ARM and x86
+"""
+
+import sys
+import os
+import argparse
+import subprocess
+import shlex
+import time
+from pathlib import Path
+
+from build_helper import Helper
+
+OUR_PATH = os.path.dirname(os.path.realpath(__file__))
+OUR1_PATH = os.path.dirname(OUR_PATH)
+
+# Bring in the patman and dtoc libraries (but don't override the first path
+# in PYTHONPATH)
+sys.path.insert(2, os.path.join(OUR1_PATH, 'tools'))
+
+# pylint: disable=C0413
+from u_boot_pylib import command
+from u_boot_pylib import tools
+from u_boot_pylib import tout
+
+
+def parse_args():
+ """Parses command-line arguments"""
+ parser = argparse.ArgumentParser(
+ description='Build and/or run U-Boot with QEMU',
+ formatter_class=argparse.RawTextHelpFormatter)
+ parser.add_argument('-a', '--arch', default='arm', choices=['arm', 'x86'],
+ help='Select architecture (arm, x86) Default: arm')
+ parser.add_argument('-B', '--no-build', action='store_true',
+ help="Don't build; assume a build exists")
+ parser.add_argument('-d', '--disk',
+ help='Root disk image file to use with QEMU')
+ parser.add_argument('-D', '--share-dir', metavar='DIR',
+ help='Directory to share into the guest via virtiofs')
+ parser.add_argument('-e', '--sct-run', action='store_true',
+ help='Run UEFI Self-Certification Test (SCT)')
+ parser.add_argument('-E', '--use-tianocore', action='store_true',
+ help='Run Tianocore (OVMF) instead of U-Boot')
+ parser.add_argument(
+ '-k', '--kvm', action='store_true',
+ help='Use KVM (Kernel-based Virtual Machine) for acceleration')
+ parser.add_argument('-o', '--os', metavar='NAME', choices=['ubuntu'],
+ help='Run a specified Operating System')
+ parser.add_argument('-v', '--verbose', action='store_true',
+ help='Show executed commands')
+ parser.add_argument('-r', '--run', action='store_true',
+ help='Run QEMU with the built/specified image')
+ parser.add_argument(
+ '-R', '--release', default='24.04.1',
+ help='Select OS release version (e.g, 24.04) Default: 24.04.1')
+ parser.add_argument(
+ '-s', '--serial-only', action='store_true',
+ help='Use serial console only (no graphical display for QEMU)')
+ parser.add_argument(
+ '-S', '--sct-seq',
+ help='SCT sequence-file to be written into the SCT image if -e')
+ parser.add_argument('-w', '--word-32bit', action='store_true',
+ help='Use 32-bit version for the build/architecture')
+
+ return parser.parse_args()
+
+
+class BuildQemu:
+ """Build and/or run U-Boot with QEMU based on command line arguments"""
+
+ def __init__(self, args):
+ """Set up arguments and configure paths"""
+ self.args = args
+
+ self.helper = Helper()
+ self.helper.read_settings()
+ self.imagedir = Path(self.helper.get_setting('image_dir', '~/dev'))
+ self.ubdir = Path(self.helper.get_setting('build_dir', '/tmp/b'))
+ self.sctdir = Path(self.helper.get_setting('sct_dir', '~/dev/efi/sct'))
+ self.tiano = Path(self.helper.get_setting('tianocore_dir',
+ '~/dev/tiano'))
+ self.mnt = Path(self.helper.get_setting('sct_mnt', '/mnt/sct'))
+
+ self.bitness = 32 if args.word_32bit else 64
+ self.qemu_extra = []
+ self.mem = '512M' # Default QEMU memory
+
+ if args.disk:
+ self.mem = '4G'
+ self.qemu_extra.extend(['-smp', '4'])
+
+ if args.sct_run:
+ self.mem = '4G'
+ self.qemu_extra.extend(['-smp', '4'])
+ # SCT usually runs headlessly
+ self.qemu_extra.extend(['-display', 'none'])
+ # For potential interaction within SCT
+ self.qemu_extra.extend(['-device', 'qemu-xhci'])
+ self.qemu_extra.extend(['-device', 'usb-kbd'])
+ sct_image_path = self.sctdir / 'sct.img'
+ if not sct_image_path.exists():
+ tout.fatal(f'Error: SCT image {sct_image_path} not found, '
+ 'required for -e')
+ self.qemu_extra.extend([
+ '-drive', f'file={sct_image_path},format=raw,if=none,id=vda',
+ '-device', 'virtio-blk-pci,drive=vda,bootindex=1'])
+ # Basic networking for SCT, if needed
+ self.qemu_extra.extend([
+ '-device', 'virtio-net-pci,netdev=net0',
+ '-netdev', 'user,id=net0'])
+ args.serial_only = True # SCT implies serial output
+
+ if args.os:
+ self.mem = '4G'
+ self.qemu_extra.extend(['-smp', '4'])
+
+ self.kvm_params = []
+ if args.kvm:
+ self.kvm_params = ['-enable-kvm', '-cpu', 'host']
+
+ bios_override = None
+ if args.use_tianocore:
+ bios_override = Path(self.tiano, 'OVMF-pure-efi.x64.fd')
+ if not bios_override.exists():
+ tout.fatal(
+ 'Error: Tianocore BIOS specified (-E) but not found at '
+ f'{bios_override}')
+
+ self.seq_fname = Path(args.sct_seq) if args.sct_seq else None
+ self.img_fname = Path(args.disk) if args.disk else None
+
+ # arch-specific setup
+ if args.arch == 'arm':
+ self.board = 'qemu_arm'
+ default_bios = 'u-boot.bin'
+ self.qemu = 'qemu-system-arm'
+ self.qemu_extra.extend(['-machine', 'virt'])
+ if not args.kvm:
+ self.qemu_extra.extend(['-accel', 'tcg'])
+ qemu_arch = 'arm'
+ if self.bitness == 64:
+ self.board = 'qemu_arm64'
+ self.qemu = 'qemu-system-aarch64'
+ self.qemu_extra.extend(['-cpu', 'cortex-a57'])
+ qemu_arch = 'arm64'
+ elif args.arch == 'x86':
+ self.board = 'qemu-x86'
+ default_bios = 'u-boot.rom'
+ self.qemu = 'qemu-system-i386'
+ qemu_arch = 'i386' # For OS image naming
+ if self.bitness == 64:
+ self.board = 'qemu-x86_64'
+ self.qemu = 'qemu-system-x86_64'
+ qemu_arch = 'amd64'
+ else:
+ raise ValueError(f"Invalid arch '{args.arch}'")
+
+ self.os_path = None
+ if args.os == 'ubuntu':
+ img_name = (f'{args.os}-{args.release}-desktop-{qemu_arch}.iso')
+ self.os_path = self.imagedir / args.os / img_name
+
+ self.build_dir = self.ubdir / self.board
+ self.bios = (bios_override if bios_override
+ else self.build_dir / default_bios)
+
+ @staticmethod
+ def execute_command(cmd_list, desc, check=True, **kwargs):
+ """Execute a shell command and handle errors
+
+ Args:
+ cmd_list (list of str): The command and its arguments as a list
+ desc (str): A description of the command being executed
+ check (bool): Raise CalledProcessError on non-zero exit code
+ kwargs: Additional arguments for subprocess.run
+
+ Return:
+ subprocess.CompletedProcess: The result of the subprocess.run call
+
+ Raises:
+ SystemExit: If the command is not found or fails and check is True
+ """
+ tout.info(f"Executing: {desc} -> {shlex.join(cmd_list)}")
+ try:
+ # Decode stdout/stderr by default if text=True
+ if 'text' not in kwargs:
+ kwargs['text'] = True
+ return subprocess.run(cmd_list, check=check, **kwargs)
+ except FileNotFoundError:
+ tout.fatal(f"Error: Command '{cmd_list[0]}' not found")
+ except subprocess.CalledProcessError as proc:
+ tout.error(f'Error {desc}: Command failed with exit code '
+ f'{proc.returncode}')
+ if proc.stdout:
+ tout.error(f'Stdout:\n{proc.stdout}')
+ if proc.stderr:
+ tout.error(f'Stderr:\n{proc.stderr}')
+ tout.fatal('Failed')
+
+ def build_u_boot(self):
+ """Build U-Boot using buildman
+ """
+ self.build_dir.mkdir(parents=True, exist_ok=True)
+ cmd = ['buildman', '-w', '-o', str(self.build_dir), '--board',
+ self.board, '-I']
+
+ self.execute_command(
+ cmd,
+ f'Building U-Boot for {self.board} in {self.build_dir}')
+
+ def update_sct_sequence(self):
+ """Update the SCT image with a specified sequence file
+
+ Requires sudo for loop device setup and mounting
+ """
+ if not (self.args.sct_run and self.seq_fname and
+ self.seq_fname.exists()):
+ if (self.args.sct_run and self.seq_fname and
+ not self.seq_fname.exists()):
+ tout.warning(f'Warning: SCT sequence file {self.seq_fname}'
+ 'not found')
+ return
+
+ fname = self.sctdir / 'sct.img'
+ if not fname.exists():
+ tout.fatal(f'Error: SCT image {fname} not found')
+
+ loopdev = None
+ try:
+ # Find free loop device and attach
+ loopdev = command.output_one_line(
+ 'sudo', 'losetup', '--show', '-f', '-P', str(fname))
+ partition_path_str = f'{loopdev}p1'
+
+ uid, gid = os.getuid(), os.getgid()
+ mount_cmd = ['sudo', 'mount', partition_path_str,
+ str(self.mnt), '-o', f'uid={uid},gid={gid},rw']
+ mount_cmd.extend(['-t', 'vfat'])
+
+ self.execute_command(mount_cmd,
+ f'Mounting {partition_path_str} to {self.mnt}')
+
+ target_sct_path = self.mnt / self.seq_fname.name
+ self.execute_command(
+ ['sudo', 'cp', str(self.seq_fname), str(target_sct_path)],
+ f'Copying {self.seq_fname.name} to {self.mnt}'
+ )
+ tout.info(f"Copied {self.seq_fname} to {target_sct_path}")
+
+ finally:
+ if Path(self.mnt).is_mount():
+ self.execute_command(['sudo', 'umount', str(self.mnt)],
+ f'Unmounting {self.mnt}', check=False)
+ if loopdev:
+ self.execute_command(['sudo', 'losetup', '-d', loopdev],
+ f'Detaching loop device {loopdev}',
+ check=False)
+
+ def run_qemu(self):
+ """Construct and run the QEMU command"""
+ if not self.bios.exists():
+ tout.fatal(f"Error: BIOS file '{self.bios}' not found")
+
+ qemu_cmd = [str(self.qemu)]
+ if self.bios:
+ qemu_cmd.extend(['-bios', str(self.bios)])
+ qemu_cmd.extend(self.kvm_params)
+ qemu_cmd.extend(['-m', self.mem])
+
+ if not self.args.sct_run:
+ qemu_cmd.extend(['-netdev', 'user,id=net0,hostfwd=tcp::2222-:22',
+ '-device', 'virtio-net-pci,netdev=net0'])
+
+ # Display and Serial
+ # If -e (sct_run) is used, "-display none" is in qemu_extra
+ # If -s (serial_only) is used, we want no display
+ has_display_option = any(
+ item.startswith('-display') for item in self.qemu_extra)
+ if self.args.serial_only and not has_display_option:
+ qemu_cmd.extend(['-display', 'none'])
+ if not any(item.startswith('-serial') for item in self.qemu_extra):
+ qemu_cmd.extend(['-serial', 'mon:stdio'])
+
+ # Add other parameters gathered from options
+ qemu_cmd.extend(self.qemu_extra)
+ if self.os_path:
+ if not self.os_path.exists():
+ tout.error(f'OS image {self.os_path} specified but not found')
+ qemu_cmd.extend([
+ '-drive',
+ f'if=virtio,file={self.os_path},format=raw,id=hd0,readonly=on'])
+
+ if self.img_fname:
+ if self.img_fname.exists():
+ qemu_cmd.extend([
+ '-drive',
+ f'if=virtio,file={self.img_fname},format=raw,id=hd1'])
+ else:
+ tout.warning(f"Disk image '{self.img_fname}' not found")
+
+ sock = Path('/tmp/virtiofs.sock')
+ proc = None
+ if self.args.share_dir:
+ virtfs_dir = Path(self.args.share_dir)
+ if not virtfs_dir.is_dir():
+ tout.fatal(f'Error: VirtFS share directory {virtfs_dir} '
+ f'is not a valid directory')
+
+ virtiofsd = Path('/usr/libexec/virtiofsd')
+ if not virtiofsd.exists():
+ tout.fatal(f'Error: virtiofsd not found at {virtiofsd}')
+
+ # Clean up potential old socket file
+ if sock.exists():
+ try:
+ sock.unlink()
+ tout.info(f'Removed old socket file {sock}')
+ except OSError as e:
+ tout.warning(
+ f'Warning: Could not remove old socket file {sock}: '
+ f'{e}')
+
+ qemu_cmd.extend([
+ '-chardev', f'socket,id=char0,path={sock}',
+ '-device',
+ 'vhost-user-fs-pci,queue-size=1024,chardev=char0,tag=hostshare',
+ '-object',
+ f'memory-backend-file,id=mem,size={self.mem},mem-path=/dev/shm'
+ ',share=on',
+ '-numa', 'node,memdev=mem'])
+
+ virtiofsd_cmd = [
+ str(virtiofsd),
+ '--socket-path', str(sock),
+ '--shared-dir', str(virtfs_dir),
+ '--cache', 'auto']
+ try:
+ # Use Popen to run virtiofsd in the background
+ proc = subprocess.Popen(virtiofsd_cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ # Give virtiofsd a moment to start and create the socket
+ time.sleep(0.5)
+ if not sock.exists() and proc.poll() is not None:
+ stdout, stderr = proc.communicate()
+ tout.error('Error starting virtiofsd. Exit code: '
+ f'{proc.returncode}')
+ if stdout:
+ tout.error(f"virtiofsd stdout:\n{stdout.decode()}")
+ if stderr:
+ tout.error(f"virtiofsd stderr:\n{stderr.decode()}")
+ tout.fatal('Failed')
+
+ except (subprocess.CalledProcessError, FileNotFoundError) as exc:
+ tout.fatal(f'Failed to start virtiofsd: {exc}')
+
+ tout.info(f'QEMU:\n{shlex.join(qemu_cmd)}\n')
+ try:
+ subprocess.run(qemu_cmd, check=True)
+ except FileNotFoundError:
+ tout.fatal(f"Error: QEMU executable '{self.qemu}' not found")
+ except subprocess.CalledProcessError as e:
+ tout.fatal(f'QEMU execution failed with exit code {e.returncode}')
+ finally:
+ # Clean up virtiofsd process and socket if it was started
+ if proc:
+ tout.info('Terminating virtiofsd')
+ proc.terminate()
+ try:
+ proc.wait(timeout=5)
+ except subprocess.TimeoutExpired:
+ tout.warning(
+ 'virtiofsd did not terminate gracefully; killing')
+ proc.kill()
+ if sock.exists():
+ try:
+ sock.unlink()
+ except OSError as e_os:
+ tout.warning('Warning: Could not remove virtiofs '
+ f'socket {sock}: {e_os}')
+
+ def start(self):
+ """Build and run QEMU"""
+ if not self.args.no_build and not self.args.use_tianocore:
+ self.build_u_boot()
+
+ # Update SCT sequence if -e and -S are given
+ if self.args.sct_run and self.seq_fname:
+ self.update_sct_sequence()
+
+ if self.args.run:
+ self.run_qemu()
+
+
+def main():
+ """Parses arguments and initiates the BuildQemu process
+ """
+ args = parse_args()
+ tout.init(tout.INFO if args.verbose else tout.WARNING)
+
+ qemu = BuildQemu(args)
+ qemu.start()
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/build_helper.py b/scripts/build_helper.py
new file mode 100644
index 00000000000..a8385ed1545
--- /dev/null
+++ b/scripts/build_helper.py
@@ -0,0 +1,126 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+"""Common script for build- scripts
+
+"""
+
+import configparser
+import contextlib
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+OUR_PATH = os.path.dirname(os.path.realpath(__file__))
+OUR1_PATH = os.path.dirname(OUR_PATH)
+
+# Bring in the patman and test libraries (but don't override the first path in
+# PYTHONPATH)
+sys.path.insert(2, os.path.join(OUR1_PATH, 'tools'))
+sys.path.insert(2, os.path.join(OUR1_PATH, 'test/py/tests'))
+
+from u_boot_pylib import command
+from u_boot_pylib import tools
+import fs_helper
+
+
+class Helper:
+ def __init__(self):
+ self.settings = None
+
+ def read_settings(self):
+ """Get settings from the settings file"""
+ self.settings = configparser.ConfigParser()
+ fname = f'{os.getenv("HOME")}/.u_boot_qemu'
+ if not os.path.exists(fname):
+ print('No config file found: {fname}\nCreating one...\n')
+ tools.write_file(fname, '''# U-Boot QEMU-scripts config
+
+[DEFAULT]
+# Set ubdir to the build directory where you build U-Boot out-of-tree
+# We avoid in-tree build because it gets confusing trying different builds
+# Each board gets a build in a separate subdir
+build_dir = /tmp/b
+
+# Image directory (for OS images)
+image_dir = ~/dev/os
+
+# Build the kernel with: make O=/tmp/kernel
+bzimage = /tmp/kernel/arch/x86/boot/bzImage
+
+# EFI image-output filename
+efi_image_file = try.img
+
+# Directory where OVMF-pure-efi.i386.fd etc. are kept
+efi_dir = ~/dev/efi
+
+# Directory where SCT image (sct.img) is kept
+sct_dir = ~/dev/efi/sct
+
+# Directory where the SCT image is temporarily mounted for modification
+sct_mnt = /mnt/sct
+''', binary=False)
+ self.settings.read(fname)
+
+ def get_setting(self, name, fallback=None):
+ """Get a setting by name
+
+ Args:
+ name (str): Name of setting to retrieve
+ fallback (str or None): Value to return if the setting is missing
+ """
+ raw = self.settings.get('DEFAULT', name, fallback=fallback)
+ return os.path.expandvars(os.path.expanduser(raw))
+
+ def stage(self, name):
+ """Context manager to count requests across a range of patchwork calls
+
+ Args:
+ name (str): Stage name
+
+ Return:
+ _Stage: contect object
+
+ Usage:
+ with self.stage('name'):
+ ...do things
+
+ Note that the output only appears if the -N flag is used
+ """
+ return self._Stage(name)
+
+ @contextlib.contextmanager
+ def make_disk(self, fname, size_mb=20, fs_type='ext4', use_part=False):
+ """Create a raw disk image with files on it
+
+ Args:
+ fname (str): Filename to write the images to
+ fs_type (str): Filesystem type to create (ext4 or vfat)
+ size_mb (int): Size in MiB
+ use_part (bool): True to create a partition table, False to use a
+ raw disk image
+
+ Yields:
+ str: Directory to write the files into
+ """
+ with tempfile.NamedTemporaryFile() as tmp:
+ with tempfile.TemporaryDirectory(prefix='build_helper.') as dirname:
+ try:
+ yield dirname
+ fs_helper.mk_fs(None, fs_type, size_mb << 20, None, dirname,
+ fs_img=tmp.name, quiet=True)
+ finally:
+ pass
+
+ if use_part:
+ with open(fname, 'wb') as img:
+ img.truncate(size_mb << 20)
+ img.seek(1 << 20, 0)
+ img.write(tools.read_file(tmp.name))
+ subprocess.run(
+ ['sfdisk', fname], text=True, check=True,
+ capture_output=True,
+ input=f'type=c, size={size_mb-1}M, start=1M,bootable')
+ else:
+ shutil.copy2(tmp.name, fname)
--
2.43.0
More information about the U-Boot
mailing list