[PATCH 00/26] Continue development of the EFI app (part A)

From: Simon Glass <sjg@chromium.org> This series expand the EFI app to include more of the required functionality for booting: - access to device-path functions - disable networking (to be dealt with later) - rename BOOTMETH_EFI_LOADER since this should be usable by the app - move towards getting EFI booting going in the app - various script improvemens to support continued development Simon Glass (26): scripts: Adjust EFI script to support an OS scripts: build-qemu: Move memory size to the helper scripts: build-qemu: Move qemu program-name to the helper scripts: build-qemu: Move virtiofsd code into the helper scripts: build-qemu: Support Tianocore on ARM scripts: build-qemu: Correct networking scripts: build-qemu: Enable the display on ARM scripts: build-qemu: Always call the run() function scripts: build-efi: Enable the display on ARM scripts: Correct virtio-sci typo ide: Allow IDE_BUS() to be used without CONFIG_IDE efi_client: Provide generic malloc() and free() functions efi: Disable networking for the ARM EFI app efi: arm: Allow --gc-sections in the EFI app efi: Use variables in dp_size() efi: Move most of efi_device_path into lib/efi efi: Move EFI_FDT_USE_INTERNAL to a common header efi: Move efi_bootflow_run() to a common file efi_loader: Split out efi_binary_run_dp() et all efi_client: Allow access to the parent image efi_client: Support running an app efi: Rename BOOTMETH_EFILOADER to BOOTMETH_EFI efi: Move EFI_BINARY_EXEC to lib/efi boot: Update BOOTM_EFI to depend on EFI_LOADER fs: Avoid calling efi_set_bootdev() from the app boot: Pass the maximum size to efiload_read_file() arch/arm/config.mk | 2 - arch/arm/lib/elf_aarch64_efi.lds | 2 +- boot/Kconfig | 6 +- boot/Makefile | 2 +- boot/bootmeth_efi.c | 7 +- configs/efi-arm_app64_defconfig | 4 +- configs/khadas-vim3_android_ab_defconfig | 2 +- configs/khadas-vim3_android_defconfig | 2 +- configs/khadas-vim3l_android_ab_defconfig | 2 +- configs/khadas-vim3l_android_defconfig | 2 +- fs/fs_legacy.c | 8 +- include/blk.h | 1 + include/efi.h | 59 ++ include/efi_loader.h | 3 - include/ide.h | 4 +- lib/efi/Kconfig | 10 + lib/efi/Makefile | 2 + lib/efi/device_path.c | 1165 +++++++++++++++++++++ lib/efi/run.c | 136 +++ lib/efi_client/Makefile | 2 +- lib/efi_client/app_run.c | 90 ++ lib/efi_client/efi.c | 20 + lib/efi_loader/Kconfig | 9 - lib/efi_loader/Makefile | 2 + lib/efi_loader/efi_bootbin.c | 201 ---- lib/efi_loader/efi_device_path.c | 1132 -------------------- lib/efi_loader/efi_image_loader.c | 9 - lib/efi_loader/efi_root_node.c | 2 - lib/efi_loader/loader_run.c | 67 ++ scripts/build-efi | 3 + scripts/build-qemu | 129 +-- scripts/build_helper.py | 102 +- 32 files changed, 1703 insertions(+), 1484 deletions(-) create mode 100644 lib/efi/device_path.c create mode 100644 lib/efi/run.c create mode 100644 lib/efi_client/app_run.c create mode 100644 lib/efi_loader/loader_run.c -- 2.43.0 base-commit: 091ac902b19008601ed47587f534ede7e531d6ed branch: loadi

From: Simon Glass <sjg@chromium.org> At present the OS disk conflicts with the boot disk used to hold the app (or payload). Number the OS disk after that. Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/build_helper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/build_helper.py b/scripts/build_helper.py index 57c900f251f..327bb64ef3d 100644 --- a/scripts/build_helper.py +++ b/scripts/build_helper.py @@ -161,7 +161,8 @@ sct_mnt = /mnt/sct else: cmd.extend([ '-drive', - f'if=virtio,file={os_path},format=raw,id=hd0,readonly=on']) + f'if=virtio,file={os_path},format=raw,id=hd{base_hd},readonly=on']) + base_hd += 1 if args.disk: for i, d in enumerate(args.disk): -- 2.43.0

From: Simon Glass <sjg@chromium.org> Move this field to the helper so we can (later) have it set up the virtiofs daemon. Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/build-qemu | 14 +++++++------- scripts/build_helper.py | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/scripts/build-qemu b/scripts/build-qemu index efe79561981..f071ea071d4 100755 --- a/scripts/build-qemu +++ b/scripts/build-qemu @@ -77,14 +77,14 @@ class BuildQemu: self.mnt = Path(self.helper.get_setting('sct_mnt', '/mnt/sct')) self.qemu_extra = [] - self.mem = '512M' # Default QEMU memory + self.helper.mem = '512M' # Default QEMU memory if args.disk: - self.mem = '4G' + self.helper.mem = '4G' self.qemu_extra.extend(['-smp', '4']) if args.sct_run: - self.mem = '4G' + self.helper.mem = '4G' self.qemu_extra.extend(['-smp', '4']) # SCT usually runs headlessly self.qemu_extra.extend(['-display', 'none']) @@ -105,7 +105,7 @@ class BuildQemu: args.serial_only = True # SCT implies serial output if args.os: - self.mem = '4G' + self.helper.mem = '4G' self.qemu_extra.extend(['-smp', '4']) self.kvm_params = [] @@ -263,7 +263,7 @@ class BuildQemu: if self.bios: qemu_cmd.extend(['-bios', str(self.bios)]) qemu_cmd.extend(self.kvm_params) - qemu_cmd.extend(['-m', self.mem]) + qemu_cmd.extend(['-m', self.helper.mem]) if not self.args.sct_run and not self.qboot: qemu_cmd.extend(['-netdev', 'user,id=net0,hostfwd=tcp::2222-:22', @@ -311,8 +311,8 @@ class BuildQemu: '-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', + f'memory-backend-file,id=mem,size={self.helper.mem},' + 'mem-path=/dev/shm,share=on', '-numa', 'node,memdev=mem']) virtiofsd_cmd = [ diff --git a/scripts/build_helper.py b/scripts/build_helper.py index 327bb64ef3d..4d70575166d 100644 --- a/scripts/build_helper.py +++ b/scripts/build_helper.py @@ -34,6 +34,7 @@ class Helper: self.settings = None self.imagedir = None self.args = args + self.mem = '512' self.bitness = 32 if args.word_32bit else 64 if self.args.arch == 'arm': if self.bitness == 64: -- 2.43.0

From: Simon Glass <sjg@chromium.org> Put this in the helper so that we can (later) have it show the correct error when the virtiofs daemon fails. Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/build-qemu | 12 ++++++------ scripts/build_helper.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/build-qemu b/scripts/build-qemu index f071ea071d4..6c1f10061c4 100755 --- a/scripts/build-qemu +++ b/scripts/build-qemu @@ -136,7 +136,7 @@ class BuildQemu: else: self.board = 'qemu_arm' default_bios = 'u-boot.bin' - self.qemu = 'qemu-system-arm' + self.helper.qemu = 'qemu-system-arm' self.qemu_extra.extend(['-machine', 'virt']) if not args.kvm: self.qemu_extra.extend(['-accel', 'tcg']) @@ -145,16 +145,16 @@ class BuildQemu: self.board = 'qemu_arm64_spl' else: self.board = 'qemu_arm64' - self.qemu = 'qemu-system-aarch64' + self.helper.qemu = 'qemu-system-aarch64' self.qemu_extra.extend(['-cpu', 'cortex-a57']) elif args.arch == 'x86': self.board = 'qemu-x86' default_bios = 'u-boot.rom' - self.qemu = 'qemu-system-i386' + self.helper.qemu = 'qemu-system-i386' self.qemu_extra.extend(['-machine', 'q35']) if self.helper.bitness == 64: self.board = 'qemu-x86_64' - self.qemu = 'qemu-system-x86_64' + self.helper.qemu = 'qemu-system-x86_64' else: raise ValueError(f"Invalid arch '{args.arch}'") @@ -259,7 +259,7 @@ class BuildQemu: if not self.bios.exists(): tout.fatal(f"Error: BIOS file '{self.bios}' not found") - qemu_cmd = [str(self.qemu)] + qemu_cmd = [str(self.helper.qemu)] if self.bios: qemu_cmd.extend(['-bios', str(self.bios)]) qemu_cmd.extend(self.kvm_params) @@ -343,7 +343,7 @@ class BuildQemu: try: subprocess.run(qemu_cmd, check=True) except FileNotFoundError: - tout.fatal(f"Error: QEMU executable '{self.qemu}' not found") + tout.fatal(f"Error: QEMU executable '{self.helper.qemu}' not found") except subprocess.CalledProcessError as e: tout.fatal(f'QEMU execution failed with exit code {e.returncode}') finally: diff --git a/scripts/build_helper.py b/scripts/build_helper.py index 4d70575166d..e3cf42593d9 100644 --- a/scripts/build_helper.py +++ b/scripts/build_helper.py @@ -21,7 +21,6 @@ OUR1_PATH = os.path.dirname(OUR_PATH) 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 from u_boot_pylib import tout import fs_helper @@ -36,6 +35,7 @@ class Helper: self.args = args self.mem = '512' self.bitness = 32 if args.word_32bit else 64 + self.qemu = None if self.args.arch == 'arm': if self.bitness == 64: self.os_arch = 'arm64' -- 2.43.0

From: Simon Glass <sjg@chromium.org> Move this code into the helper so that build-efi can use it too. Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/build-qemu | 84 ++---------------------------------- scripts/build_helper.py | 94 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 81 deletions(-) diff --git a/scripts/build-qemu b/scripts/build-qemu index 6c1f10061c4..2635646f8a0 100755 --- a/scripts/build-qemu +++ b/scripts/build-qemu @@ -41,8 +41,6 @@ def parse_args(): description='Build and/or run U-Boot with QEMU', formatter_class=argparse.RawTextHelpFormatter) build_helper.add_common_args(parser) - 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', @@ -284,85 +282,9 @@ class BuildQemu: # Add other parameters gathered from options qemu_cmd.extend(self.qemu_extra) - 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.helper.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.helper.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}') + self.helper.setup_share(qemu_cmd) + + self.helper.run(qemu_cmd) def start(self): """Build and run QEMU""" diff --git a/scripts/build_helper.py b/scripts/build_helper.py index e3cf42593d9..136d27da61b 100644 --- a/scripts/build_helper.py +++ b/scripts/build_helper.py @@ -9,9 +9,11 @@ import contextlib import os from pathlib import Path import shutil +import shlex import subprocess import sys import tempfile +import time OUR_PATH = os.path.dirname(os.path.realpath(__file__)) OUR1_PATH = os.path.dirname(OUR_PATH) @@ -32,6 +34,8 @@ class Helper: def __init__(self, args): self.settings = None self.imagedir = None + self.proc = None + self.sock = None self.args = args self.mem = '512' self.bitness = 32 if args.word_32bit else 64 @@ -185,6 +189,94 @@ sct_mnt = /mnt/sct cmd.extend(['-object', 'rng-random,filename=/dev/urandom,id=rng0', '-device', 'virtio-rng-pci,rng=rng0']) + def setup_share(self, qemu_cmd): + 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') + self.proc = proc + self.sock = sock + + except (subprocess.CalledProcessError, FileNotFoundError) as exc: + tout.fatal(f'Failed to start virtiofsd: {exc}') + + def cleanup_share(self): + # Clean up virtiofsd process and socket if it was started + if self.proc: + tout.info('Terminating virtiofsd') + self.proc.terminate() + try: + self.proc.wait(timeout=5) + except subprocess.TimeoutExpired: + tout.warning( + 'virtiofsd did not terminate gracefully; killing') + self.proc.kill() + if self.sock.exists(): + try: + self.sock.unlink() + except OSError as e_os: + tout.warning('Warning: Could not remove virtiofs ' + f'socket {self.sock}: {e_os}') + + def run(self, qemu_cmd): + tout.info(f'QEMU:\n{shlex.join(qemu_cmd)}\n') + try: + if self.args.run: + 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: + self.cleanup_share() + def add_common_args(parser): """Add some arguments which are common to build-efi/qemu scripts @@ -199,6 +291,8 @@ def add_common_args(parser): help="Enable linux console (x86 only)") parser.add_argument('-d', '--disk', nargs='*', 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('-I', '--initrd', help='Initial ramdisk to run using -initrd') parser.add_argument( -- 2.43.0

From: Simon Glass <sjg@chromium.org> When ARM is selected, use the ARM Tianocore image. Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/build-qemu | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/build-qemu b/scripts/build-qemu index 2635646f8a0..da4b3aeac96 100755 --- a/scripts/build-qemu +++ b/scripts/build-qemu @@ -112,7 +112,10 @@ class BuildQemu: bios_override = None if args.use_tianocore: - bios_override = Path(self.tiano, 'OVMF-pure-efi.x64.fd') + if args.arch == 'arm': + bios_override = Path(self.tiano, 'OVMF-pure-efi.aarch64.fd.64m') + else: + 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 ' -- 2.43.0

From: Simon Glass <sjg@chromium.org> Networking is not currently enabled, since the wrong variable is used. Fix it. Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/build-qemu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build-qemu b/scripts/build-qemu index da4b3aeac96..950aafefebb 100755 --- a/scripts/build-qemu +++ b/scripts/build-qemu @@ -266,7 +266,7 @@ class BuildQemu: qemu_cmd.extend(self.kvm_params) qemu_cmd.extend(['-m', self.helper.mem]) - if not self.args.sct_run and not self.qboot: + if not self.args.sct_run and not self.args.use_qboot: qemu_cmd.extend(['-netdev', 'user,id=net0,hostfwd=tcp::2222-:22', '-device', 'virtio-net-pci,netdev=net0']) -- 2.43.0

From: Simon Glass <sjg@chromium.org> ARM devices can use a GPU display, which U-Boot only recented supports. Enable the option. Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/build-qemu | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/scripts/build-qemu b/scripts/build-qemu index 950aafefebb..f744b86de45 100755 --- a/scripts/build-qemu +++ b/scripts/build-qemu @@ -84,8 +84,6 @@ class BuildQemu: if args.sct_run: self.helper.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']) @@ -270,13 +268,14 @@ class BuildQemu: 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: + # SCT usually runs headlessly + if self.args.serial_only or self.args.sct_seq: qemu_cmd.extend(['-display', 'none']) + elif self.args.arch == 'arm': + qemu_cmd.extend(['-device', 'virtio-gpu-pci']) + qemu_cmd.extend(['-device', 'qemu-xhci', '-device', 'usb-kbd', + '-device', 'usb-tablet']) + qemu_cmd.extend(['-display', 'default,show-cursor=on']) if not any(item.startswith('-serial') for item in self.qemu_extra): qemu_cmd.extend(['-serial', 'mon:stdio']) -- 2.43.0

From: Simon Glass <sjg@chromium.org> The helper deals with whether we are actually running or not. We want it to show the arguments even if not. So always call it. Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/build-qemu | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/build-qemu b/scripts/build-qemu index f744b86de45..0bc312470de 100755 --- a/scripts/build-qemu +++ b/scripts/build-qemu @@ -297,8 +297,7 @@ class BuildQemu: if self.args.sct_run and self.seq_fname: self.update_sct_sequence() - if self.args.run: - self.run_qemu() + self.run_qemu() def main(): -- 2.43.0

From: Simon Glass <sjg@chromium.org> Enable the display on ARM devices, so it works when running Tianocore. Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/build-efi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/build-efi b/scripts/build-efi index 634d57e08ed..bbc11c2fd2d 100755 --- a/scripts/build-efi +++ b/scripts/build-efi @@ -107,6 +107,9 @@ class BuildEfi: else: if self.args.arch == 'arm': extra += ['-device', 'virtio-gpu-pci'] + extra += ['-device', 'qemu-xhci', '-device', 'usb-kbd', + '-device', 'usb-tablet'] + extra += ['-display', 'default,show-cursor=on'] extra += ['-serial', 'mon:stdio'] serial_msg = '' if self.args.kvm: -- 2.43.0

From: Simon Glass <sjg@chromium.org> Fix a small typo in the help. Signed-off-by: Simon Glass <sjg@chromium.org> --- scripts/build_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build_helper.py b/scripts/build_helper.py index 136d27da61b..72d4a8734f9 100644 --- a/scripts/build_helper.py +++ b/scripts/build_helper.py @@ -311,7 +311,7 @@ def add_common_args(parser): help='Run QEMU with serial only (no display)') parser.add_argument( '-S', '--scsi', action='store_true', - help='Attach root disk using virtio-sci instead of virtio-blk') + help='Attach root disk using virtio-scsi instead of virtio-blk') parser.add_argument( '-t', '--root', help='Pass the given root device to linux via root=xxx') -- 2.43.0

From: Simon Glass <sjg@chromium.org> Using this macro, even bracketed with 'if (IS_ENABLED(CONFIG_IDE))' causes a compile error if CONFIG_IDE is not enabled. Update the macros to resolve this, so we can avoid #ifdefs in the C code. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/ide.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/ide.h b/include/ide.h index 2c25e74ede0..705d7cd5970 100644 --- a/include/ide.h +++ b/include/ide.h @@ -9,7 +9,9 @@ #include <blk.h> -#define IDE_BUS(dev) (dev / (CONFIG_SYS_IDE_MAXDEVICE / CONFIG_SYS_IDE_MAXBUS)) +#define IDE_BUS(dev) ((dev) / \ + (IF_ENABLED_INT(CONFIG_IDE, CONFIG_SYS_IDE_MAXDEVICE) / \ + IF_ENABLED_INT(CONFIG_IDE, CONFIG_SYS_IDE_MAXBUS))) /** * ide_set_reset() - Assert or de-assert reset for the IDE device -- 2.43.0

From: Simon Glass <sjg@chromium.org> The device-path code requires memory allocation. Provide an implementation of these for use by the client. Note that this will not work for the stub, since in that case it needs to use U-Boot's memory allocation. This was a subject of a long argument on the mailing list about U-Boot using its own malloc() where possible, so it seems best to just leave this broken. Signed-off-by: Simon Glass <sjg@chromium.org> --- lib/efi_client/efi.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/efi_client/efi.c b/lib/efi_client/efi.c index 6b1b6b10e2a..86856014c3e 100644 --- a/lib/efi_client/efi.c +++ b/lib/efi_client/efi.c @@ -172,6 +172,21 @@ void efi_free(struct efi_priv *priv, void *ptr) boot->free_pool(ptr); } +void *efi_alloc(size_t size) +{ + struct efi_priv *priv = efi_get_priv(); + efi_status_t ret; + + return efi_malloc(priv, size, &ret); +} + +void efi_free_pool(void *ptr) +{ + struct efi_priv *priv = efi_get_priv(); + + efi_free(priv, ptr); +} + int efi_store_memory_map(struct efi_priv *priv) { struct efi_boot_services *boot = priv->sys_table->boottime; -- 2.43.0

From: Simon Glass <sjg@chromium.org> This is not fully functional yet, so disable it for now. Signed-off-by: Simon Glass <sjg@chromium.org> --- configs/efi-arm_app64_defconfig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/configs/efi-arm_app64_defconfig b/configs/efi-arm_app64_defconfig index 55a635b104a..cb7f31f10c0 100644 --- a/configs/efi-arm_app64_defconfig +++ b/configs/efi-arm_app64_defconfig @@ -23,8 +23,6 @@ CONFIG_CMD_MEMINFO=y CONFIG_CMD_MEMINFO_MAP=y CONFIG_CMD_DM=y CONFIG_CMD_LSBLK=y -CONFIG_CMD_DNS=y -CONFIG_CMD_WGET=y CONFIG_CMD_CACHE=y CONFIG_CMD_TIME=y CONFIG_CMD_EXT4_WRITE=y @@ -34,9 +32,9 @@ CONFIG_ENV_OVERWRITE=y CONFIG_SYS_RELOC_GD_ENV_ADDR=y CONFIG_USE_BOOTFILE=y CONFIG_BOOTFILE="bzImage" +CONFIG_NO_NET=y CONFIG_REGMAP=y CONFIG_SYSCON=y -CONFIG_EFI_NET=y CONFIG_DM_RNG=y CONFIG_SYSRESET=y CONFIG_VIDEO=y -- 2.43.0

From: Simon Glass <sjg@chromium.org> This option is used on most other boards. Leaving it disabled creates situations where the linker fails to build, due to symbols which are referenced only it code that would have been garbage-collected. To keep this disabled we would need to introduce #ifdefs not needed by other platforms, which would be annoying. The only current reason why this cannot be enabled is that the linker lists are lost. Add a KEEP() around these, and enable the option. Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/arm/config.mk | 2 -- arch/arm/lib/elf_aarch64_efi.lds | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/arm/config.mk b/arch/arm/config.mk index 783d2cda0b9..8a0167c165d 100644 --- a/arch/arm/config.mk +++ b/arch/arm/config.mk @@ -14,10 +14,8 @@ CFLAGS_NON_EFI := -fno-pic $(FIXED_REG) -ffunction-sections -fdata-sections \ CFLAGS_EFI := -fpic -fshort-wchar ifneq ($(LTO_ENABLE)$(CONFIG_USE_PRIVATE_LIBGCC),yy) -ifndef CONFIG_EFI_APP LDFLAGS_FINAL += --gc-sections endif -endif ifneq ($(LTO_ENABLE),y) PLATFORM_RELFLAGS += -ffunction-sections -fdata-sections diff --git a/arch/arm/lib/elf_aarch64_efi.lds b/arch/arm/lib/elf_aarch64_efi.lds index 2241bea4dc1..cb7081abef5 100644 --- a/arch/arm/lib/elf_aarch64_efi.lds +++ b/arch/arm/lib/elf_aarch64_efi.lds @@ -46,7 +46,7 @@ SECTIONS *(.got) /* U-Boot lists and device tree */ . = ALIGN(8); - *(SORT(__u_boot_list*)); + KEEP(*(SORT(__u_boot_list*))); . = ALIGN(8); /* -- 2.43.0

From: Simon Glass <sjg@chromium.org> This function calculates the parent size in many places and does the same addition in most cases. Create a variable for the parent_size and another for the size, so we can do the addition once at the end. The uclass array is too sparse to consider an array. Signed-off-by: Simon Glass <sjg@chromium.org> --- lib/efi_loader/efi_device_path.c | 50 ++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index e138c6c24de..ed15c87e272 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -394,66 +394,72 @@ bool efi_dp_is_multi_instance(const struct efi_device_path *dp) __maybe_unused static unsigned int dp_size(struct udevice *dev) { + uint parent_size, size; + if (!dev || !dev->driver) return sizeof(struct efi_device_path_udevice); + parent_size = dev_get_parent(dev) ? dp_size(dev_get_parent(dev)) : 0; + switch (device_get_uclass_id(dev)) { case UCLASS_ROOT: /* stop traversing parents at this point: */ return sizeof(struct efi_device_path_udevice); case UCLASS_ETH: - return dp_size(dev->parent) + - sizeof(struct efi_device_path_mac_addr); + size = sizeof(struct efi_device_path_mac_addr); + break; case UCLASS_BLK: switch (dev->parent->uclass->uc_drv->id) { #ifdef CONFIG_IDE case UCLASS_IDE: - return dp_size(dev->parent) + - sizeof(struct efi_device_path_atapi); + size = sizeof(struct efi_device_path_atapi); + break; #endif #if defined(CONFIG_SCSI) case UCLASS_SCSI: - return dp_size(dev->parent) + - sizeof(struct efi_device_path_scsi); + size = sizeof(struct efi_device_path_scsi); + break; #endif #if defined(CONFIG_MMC) case UCLASS_MMC: - return dp_size(dev->parent) + - sizeof(struct efi_device_path_sd_mmc_path); + size = sizeof(struct efi_device_path_sd_mmc_path); + break; #endif #if defined(CONFIG_AHCI) || defined(CONFIG_SATA) case UCLASS_AHCI: - return dp_size(dev->parent) + - sizeof(struct efi_device_path_sata); + size = sizeof(struct efi_device_path_sata); + break; #endif #if defined(CONFIG_NVME) case UCLASS_NVME: - return dp_size(dev->parent) + - sizeof(struct efi_device_path_nvme); + size = sizeof(struct efi_device_path_nvme); + break; #endif #ifdef CONFIG_USB case UCLASS_MASS_STORAGE: - return dp_size(dev->parent) - + sizeof(struct efi_device_path_controller); + size = sizeof(struct efi_device_path_controller); + break; #endif default: /* UCLASS_BLKMAP, UCLASS_HOST, UCLASS_VIRTIO */ - return dp_size(dev->parent) + - sizeof(struct efi_device_path_udevice); + size = sizeof(struct efi_device_path_udevice); + break; } #if defined(CONFIG_MMC) case UCLASS_MMC: - return dp_size(dev->parent) + - sizeof(struct efi_device_path_sd_mmc_path); + size = sizeof(struct efi_device_path_sd_mmc_path); + break; #endif case UCLASS_MASS_STORAGE: case UCLASS_USB_HUB: - return dp_size(dev->parent) + - sizeof(struct efi_device_path_usb); + size = sizeof(struct efi_device_path_usb); + break; default: - return dp_size(dev->parent) + - sizeof(struct efi_device_path_udevice); + size = sizeof(struct efi_device_path_udevice); + break; } + + return parent_size + size; } /* -- 2.43.0

From: Simon Glass <sjg@chromium.org> Move this constant to the efi.h header so that it can be used by the app. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/efi.h | 3 +++ include/efi_loader.h | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/efi.h b/include/efi.h index 302c662a8a7..2de732cec91 100644 --- a/include/efi.h +++ b/include/efi.h @@ -566,6 +566,9 @@ extern char _binary_u_boot_bin_start[], _binary_u_boot_bin_end[], _binary_u_boot EFI_VARIABLE_APPEND_WRITE | \ EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS) +/* Use internal device tree when starting UEFI application */ +#define EFI_FDT_USE_INTERNAL NULL + /** * efi_get_priv() - Get access to the EFI-private information * diff --git a/include/efi_loader.h b/include/efi_loader.h index 5cc4cad9697..b57196da8da 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -197,9 +197,6 @@ static inline void efi_net_set_addr(struct efi_ipv4_address *ip, EFI_GUID(0xb2ac5fc9, 0x92b7, 0x4acd, \ 0xae, 0xac, 0x11, 0xe8, 0x18, 0xc3, 0x13, 0x0c) -/* Use internal device tree when starting UEFI application */ -#define EFI_FDT_USE_INTERNAL NULL - /* Root node */ extern efi_handle_t efi_root; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Move this function and some dependencies into the lib/efi directory so that it can be used by the app, which does not enable CONFIG_EFI_LOADER Since the networking has an #ifdef add CONFIG_EFI_LOADER to it, since the definitions are in efi_loader.h for now. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/blk.h | 1 + include/efi.h | 36 +++++++++ lib/efi/Makefile | 1 + lib/efi/run.c | 139 ++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_bootbin.c | 141 +---------------------------------- 5 files changed, 181 insertions(+), 137 deletions(-) create mode 100644 lib/efi/run.c diff --git a/include/blk.h b/include/blk.h index 84c19ca887d..64e9fc09433 100644 --- a/include/blk.h +++ b/include/blk.h @@ -7,6 +7,7 @@ #ifndef BLK_H #define BLK_H +#include <linux/types.h> #include <bouncebuf.h> #include <dm/uclass-id.h> #include <efi.h> diff --git a/include/efi.h b/include/efi.h index 2de732cec91..efdd980fbc3 100644 --- a/include/efi.h +++ b/include/efi.h @@ -737,4 +737,40 @@ static inline bool efi_use_host_arch(void) */ int efi_get_pxe_arch(void); +/** + * calculate_paths() - Calculate the device and image patch from strings + * + * @dev: device, e.g. "MMC" + * @devnr: number of the device, e.g. "1:2" + * @path: path to file loaded + * @device_pathp: returns EFI device path + * @image_pathp: returns EFI image path + * Return: EFI_SUCCESS on success, else error code + */ +efi_status_t calculate_paths(const char *dev, const char *devnr, + const char *path, + struct efi_device_path **device_pathp, + struct efi_device_path **image_pathp); + +/** + * efi_binary_run_dp() - run loaded UEFI image + * + * @image: memory address of the UEFI image + * @size: size of the UEFI image + * @fdt: device-tree + * @initrd: initrd + * @initrd_sz: initrd size + * @dp_dev: EFI device-path + * @dp_img: EFI image-path + * + * Execute an EFI binary image loaded at @image. + * @size may be zero if the binary is loaded with U-Boot load command. + * + * Return: status code + */ +efi_status_t efi_binary_run_dp(void *image, size_t size, void *fdt, + void *initrd, size_t initrd_sz, + struct efi_device_path *dp_dev, + struct efi_device_path *dp_img); + #endif /* _LINUX_EFI_H */ diff --git a/lib/efi/Makefile b/lib/efi/Makefile index 8d880319696..37816a93192 100644 --- a/lib/efi/Makefile +++ b/lib/efi/Makefile @@ -5,3 +5,4 @@ obj-y += basename.o obj-y += device_path.o +obj-y += run.o diff --git a/lib/efi/run.c b/lib/efi/run.c new file mode 100644 index 00000000000..93b32396805 --- /dev/null +++ b/lib/efi/run.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2016 Alexander Graf + * Copyright 2023 Google LLC + */ + +#define LOG_CATEGORY LOGC_EFI + +#include <blk.h> +#include <bootflow.h> +#include <dm.h> +#include <efi.h> +#include <efi_device_path.h> +#include <efi_loader.h> +#include <malloc.h> +#include <mapmem.h> +#include <net-common.h> + +/** + * calculate_paths() - Calculate the device and image patch from strings + * + * @dev: device, e.g. "MMC" + * @devnr: number of the device, e.g. "1:2" + * @path: path to file loaded + * @device_pathp: returns EFI device path + * @image_pathp: returns EFI image path + * Return: EFI_SUCCESS on success, else error code + */ +efi_status_t calculate_paths(const char *dev, const char *devnr, + const char *path, + struct efi_device_path **device_pathp, + struct efi_device_path **image_pathp) +{ + struct efi_device_path *image, *device; + efi_status_t ret; + +#if IS_ENABLED(CONFIG_NETDEVICES) && IS_ENABLED(CONFIG_EFI_LOADER) + if (!strcmp(dev, "Net") || !strcmp(dev, "Http")) { + ret = efi_net_new_dp(dev, devnr, eth_get_dev()); + if (ret != EFI_SUCCESS) + return ret; + } +#endif + + ret = efi_dp_from_name(dev, devnr, path, &device, &image); + if (ret != EFI_SUCCESS) + return ret; + + *device_pathp = device; + if (image) { + /* FIXME: image should not contain device */ + struct efi_device_path *image_tmp = image; + + efi_dp_split_file_path(image, &device, &image); + efi_free_pool(image_tmp); + } + *image_pathp = image; + log_debug("- boot device %pD\n", device); + if (image) + log_debug("- image %pD\n", image); + + return EFI_SUCCESS; +} + +/** + * calc_dev_name() - Calculate the device name to give to EFI + * + * If not supported, this shows an error. + * + * Return name, or NULL if not supported + */ +static const char *calc_dev_name(struct bootflow *bflow) +{ + const struct udevice *media_dev; + + media_dev = dev_get_parent(bflow->dev); + + if (!bflow->blk) { + if (device_get_uclass_id(media_dev) == UCLASS_ETH) + return "Net"; + + log_err("Cannot boot EFI app on media '%s'\n", + dev_get_uclass_name(media_dev)); + + return NULL; + } + + if (device_get_uclass_id(media_dev) == UCLASS_MASS_STORAGE) + return "usb"; + + return blk_get_uclass_name(device_get_uclass_id(media_dev)); +} + +efi_status_t efi_bootflow_run(struct bootflow *bflow) +{ + struct efi_device_path *device, *image; + const struct udevice *media_dev; + struct blk_desc *desc = NULL; + const char *dev_name; + char devnum_str[9]; + efi_status_t ret; + void *fdt; + + media_dev = dev_get_parent(bflow->dev); + if (bflow->blk) { + desc = dev_get_uclass_plat(bflow->blk); + + snprintf(devnum_str, sizeof(devnum_str), "%x:%x", + desc ? desc->devnum : dev_seq(media_dev), bflow->part); + } else { + *devnum_str = '\0'; + } + + dev_name = calc_dev_name(bflow); + log_debug("dev_name '%s' devnum_str '%s' fname '%s' media_dev '%s'\n", + dev_name, devnum_str, bflow->fname, media_dev->name); + if (!dev_name) + return EFI_UNSUPPORTED; + ret = calculate_paths(dev_name, devnum_str, bflow->fname, &device, + &image); + if (ret) + return EFI_UNSUPPORTED; + + if (bflow->flags & BOOTFLOWF_USE_BUILTIN_FDT) { + log_debug("Booting with built-in fdt\n"); + fdt = EFI_FDT_USE_INTERNAL; + } else { + log_debug("Booting with external fdt\n"); + fdt = map_sysmem(bflow->fdt_addr, 0); + } + + if (IS_ENABLED(CONFIG_EFI_APP)) + return EFI_UNSUPPORTED; + + ret = efi_binary_run_dp(bflow->buf, bflow->size, fdt, NULL, 0, device, + image); + + return ret; +} diff --git a/lib/efi_loader/efi_bootbin.c b/lib/efi_loader/efi_bootbin.c index d232f450c66..81df1184834 100644 --- a/lib/efi_loader/efi_bootbin.c +++ b/lib/efi_loader/efi_bootbin.c @@ -49,52 +49,6 @@ void efi_clear_bootdev(void) image_size = 0; } -/** - * calculate_paths() - Calculate the device and image patch from strings - * - * @dev: device, e.g. "MMC" - * @devnr: number of the device, e.g. "1:2" - * @path: path to file loaded - * @device_pathp: returns EFI device path - * @image_pathp: returns EFI image path - * Return: EFI_SUCCESS on success, else error code - */ -static efi_status_t calculate_paths(const char *dev, const char *devnr, - const char *path, - struct efi_device_path **device_pathp, - struct efi_device_path **image_pathp) -{ - struct efi_device_path *image, *device; - efi_status_t ret; - -#if IS_ENABLED(CONFIG_NETDEVICES) - if (!strcmp(dev, "Net") || !strcmp(dev, "Http")) { - ret = efi_net_new_dp(dev, devnr, eth_get_dev()); - if (ret != EFI_SUCCESS) - return ret; - } -#endif - - ret = efi_dp_from_name(dev, devnr, path, &device, &image); - if (ret != EFI_SUCCESS) - return ret; - - *device_pathp = device; - if (image) { - /* FIXME: image should not contain device */ - struct efi_device_path *image_tmp = image; - - efi_dp_split_file_path(image, &device, &image); - efi_free_pool(image_tmp); - } - *image_pathp = image; - log_debug("- boot device %pD\n", device); - if (image) - log_debug("- image %pD\n", image); - - return EFI_SUCCESS; -} - /** * efi_set_bootdev() - set boot device * @@ -199,26 +153,10 @@ out: return ret; } -/** - * efi_binary_run_dp() - run loaded UEFI image - * - * @image: memory address of the UEFI image - * @size: size of the UEFI image - * @fdt: device-tree - * @initrd: initrd - * @initrd_sz: initrd size - * @dp_dev: EFI device-path - * @dp_img: EFI image-path - * - * Execute an EFI binary image loaded at @image. - * @size may be zero if the binary is loaded with U-Boot load command. - * - * Return: status code - */ -static efi_status_t efi_binary_run_dp(void *image, size_t size, void *fdt, - void *initrd, size_t initrd_sz, - struct efi_device_path *dp_dev, - struct efi_device_path *dp_img) +efi_status_t efi_binary_run_dp(void *image, size_t size, void *fdt, + void *initrd, size_t initrd_sz, + struct efi_device_path *dp_dev, + struct efi_device_path *dp_img) { efi_status_t ret; @@ -301,74 +239,3 @@ out: return ret; } - -/** - * calc_dev_name() - Calculate the device name to give to EFI - * - * If not supported, this shows an error. - * - * Return name, or NULL if not supported - */ -static const char *calc_dev_name(struct bootflow *bflow) -{ - const struct udevice *media_dev; - - media_dev = dev_get_parent(bflow->dev); - - if (!bflow->blk) { - if (device_get_uclass_id(media_dev) == UCLASS_ETH) - return "Net"; - - log_err("Cannot boot EFI app on media '%s'\n", - dev_get_uclass_name(media_dev)); - - return NULL; - } - - if (device_get_uclass_id(media_dev) == UCLASS_MASS_STORAGE) - return "usb"; - - return blk_get_uclass_name(device_get_uclass_id(media_dev)); -} - -efi_status_t efi_bootflow_run(struct bootflow *bflow) -{ - struct efi_device_path *device, *image; - const struct udevice *media_dev; - struct blk_desc *desc = NULL; - const char *dev_name; - char devnum_str[9]; - efi_status_t ret; - void *fdt; - - media_dev = dev_get_parent(bflow->dev); - if (bflow->blk) { - desc = dev_get_uclass_plat(bflow->blk); - - snprintf(devnum_str, sizeof(devnum_str), "%x:%x", - desc ? desc->devnum : dev_seq(media_dev), bflow->part); - } else { - *devnum_str = '\0'; - } - - dev_name = calc_dev_name(bflow); - log_debug("dev_name '%s' devnum_str '%s' fname '%s' media_dev '%s'\n", - dev_name, devnum_str, bflow->fname, media_dev->name); - if (!dev_name) - return EFI_UNSUPPORTED; - ret = calculate_paths(dev_name, devnum_str, bflow->fname, &device, - &image); - if (ret) - return EFI_UNSUPPORTED; - - if (bflow->flags & BOOTFLOWF_USE_BUILTIN_FDT) { - log_debug("Booting with built-in fdt\n"); - fdt = EFI_FDT_USE_INTERNAL; - } else { - log_debug("Booting with external fdt\n"); - fdt = map_sysmem(bflow->fdt_addr, 0); - } - ret = efi_binary_run_dp(bflow->buf, bflow->size, fdt, NULL, 0, device, image); - - return ret; -} -- 2.43.0

From: Simon Glass <sjg@chromium.org> efi_binary_run_dp() calls efi_init_obj_list() which is specific to the EFI loader. Move this into a new file within lib/efi_loader Similarly, move efi_run_image() since the app will need a different implementation. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/efi.h | 13 +++++++ lib/efi_loader/Makefile | 2 ++ lib/efi_loader/efi_bootbin.c | 68 ------------------------------------ lib/efi_loader/loader_run.c | 67 +++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 68 deletions(-) create mode 100644 lib/efi_loader/loader_run.c diff --git a/include/efi.h b/include/efi.h index efdd980fbc3..fb37de38334 100644 --- a/include/efi.h +++ b/include/efi.h @@ -773,4 +773,17 @@ efi_status_t efi_binary_run_dp(void *image, size_t size, void *fdt, struct efi_device_path *dp_dev, struct efi_device_path *dp_img); +/** + * efi_run_image() - run loaded UEFI image + * + * @source_buffer: memory address of the UEFI image + * @source_size: size of the UEFI image + * @dp_dev: EFI device-path + * @dp_img: EFI image-path + * Return: status code + */ +efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size, + struct efi_device_path *dp_dev, + struct efi_device_path *dp_img); + #endif /* _LINUX_EFI_H */ diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index a8e7f83336f..3158fc6da68 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -56,6 +56,8 @@ obj-y += efi_var_file.o obj-$(CONFIG_EFI_VARIABLES_PRESEED) += efi_var_seed.o endif obj-y += efi_watchdog.o +obj-y += loader_run.o + obj-$(CONFIG_EFI_ESRT) += efi_esrt.o obj-$(CONFIG_VIDEO) += efi_gop.o obj-$(CONFIG_BLK) += efi_disk.o diff --git a/lib/efi_loader/efi_bootbin.c b/lib/efi_loader/efi_bootbin.c index 81df1184834..bd45d775ee6 100644 --- a/lib/efi_loader/efi_bootbin.c +++ b/lib/efi_loader/efi_bootbin.c @@ -111,74 +111,6 @@ void efi_set_bootdev(const char *dev, const char *devnr, const char *path, } } -/** - * efi_run_image() - run loaded UEFI image - * - * @source_buffer: memory address of the UEFI image - * @source_size: size of the UEFI image - * @dp_dev: EFI device-path - * @dp_img: EFI image-path - * Return: status code - */ -static efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size, - struct efi_device_path *dp_dev, - struct efi_device_path *dp_img) -{ - efi_handle_t handle; - struct efi_device_path *msg_path, *file_path; - efi_status_t ret; - u16 *load_options; - - file_path = efi_dp_concat(dp_dev, dp_img, 0); - msg_path = dp_img; - - log_info("Booting %pD\n", msg_path); - - ret = EFI_CALL(efi_load_image(false, efi_root, file_path, source_buffer, - source_size, &handle)); - if (ret != EFI_SUCCESS) { - log_err("Loading image failed\n"); - goto out; - } - - /* Transfer environment variable as load options */ - ret = efi_env_set_load_options(handle, "bootargs", &load_options); - if (ret != EFI_SUCCESS) - goto out; - - ret = do_bootefi_exec(handle, load_options); - -out: - - return ret; -} - -efi_status_t efi_binary_run_dp(void *image, size_t size, void *fdt, - void *initrd, size_t initrd_sz, - struct efi_device_path *dp_dev, - struct efi_device_path *dp_img) -{ - efi_status_t ret; - - /* Initialize EFI drivers */ - ret = efi_init_obj_list(); - if (ret != EFI_SUCCESS) { - log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n", - ret & ~EFI_ERROR_MASK); - return -1; - } - - ret = efi_install_fdt(fdt); - if (ret != EFI_SUCCESS) - return ret; - - ret = efi_install_initrd(initrd, initrd_sz); - if (ret != EFI_SUCCESS) - return ret; - - return efi_run_image(image, size, dp_dev, dp_img); -} - /** * efi_binary_run() - run loaded UEFI image * diff --git a/lib/efi_loader/loader_run.c b/lib/efi_loader/loader_run.c new file mode 100644 index 00000000000..300f266ce5b --- /dev/null +++ b/lib/efi_loader/loader_run.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2024 Linaro Limited + * Copyright 2024 Canonical Ltd + */ + +#include <efi.h> +#include <efi_loader.h> + +efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size, + struct efi_device_path *dp_dev, + struct efi_device_path *dp_img) +{ + efi_handle_t handle; + struct efi_device_path *msg_path, *file_path; + efi_status_t ret; + u16 *load_options; + + file_path = efi_dp_concat(dp_dev, dp_img, 0); + msg_path = dp_img; + + log_info("Booting %pD\n", msg_path); + + ret = EFI_CALL(efi_load_image(false, efi_root, file_path, source_buffer, + source_size, &handle)); + if (ret != EFI_SUCCESS) { + log_err("Loading image failed\n"); + goto out; + } + + /* Transfer environment variable as load options */ + ret = efi_env_set_load_options(handle, "bootargs", &load_options); + if (ret != EFI_SUCCESS) + goto out; + + ret = do_bootefi_exec(handle, load_options); + +out: + + return ret; +} + +efi_status_t efi_binary_run_dp(void *image, size_t size, void *fdt, + void *initrd, size_t initrd_sz, + struct efi_device_path *dp_dev, + struct efi_device_path *dp_img) +{ + efi_status_t ret; + + /* Initialize EFI drivers */ + ret = efi_init_obj_list(); + if (ret != EFI_SUCCESS) { + log_err("Error: Cannot initialize UEFI sub-system, r = %lu\n", + ret & ~EFI_ERROR_MASK); + return -1; + } + + ret = efi_install_fdt(fdt); + if (ret != EFI_SUCCESS) + return ret; + + ret = efi_install_initrd(initrd, initrd_sz); + if (ret != EFI_SUCCESS) + return ret; + + return efi_run_image(image, size, dp_dev, dp_img); +} -- 2.43.0

From: Simon Glass <sjg@chromium.org> This value is useful when running a binary, so provide a function to obtain it. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/efi.h | 7 +++++++ lib/efi_client/efi.c | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/include/efi.h b/include/efi.h index fb37de38334..c7de1f82ac7 100644 --- a/include/efi.h +++ b/include/efi.h @@ -604,6 +604,13 @@ struct efi_system_table *efi_get_sys_table(void); */ struct efi_boot_services *efi_get_boot(void); +/** + * efi_get_parent_image() - Get the handle of the parent image + * + * Return: parent-image handle + */ +efi_handle_t efi_get_parent_image(void); + /** * efi_get_ram_base() - Find the base of RAM * diff --git a/lib/efi_client/efi.c b/lib/efi_client/efi.c index 86856014c3e..e670e1f7169 100644 --- a/lib/efi_client/efi.c +++ b/lib/efi_client/efi.c @@ -65,6 +65,11 @@ struct efi_boot_services *efi_get_boot(void) return global_priv->boot; } +efi_handle_t efi_get_parent_image(void) +{ + return global_priv->parent_image; +} + unsigned long efi_get_ram_base(void) { return global_priv->ram_base; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Add support for running another app from with the EFI app. This can be used to boot an OS, for example. Signed-off-by: Simon Glass <sjg@chromium.org> --- lib/efi/run.c | 3 -- lib/efi_client/Makefile | 2 +- lib/efi_client/app_run.c | 90 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 lib/efi_client/app_run.c diff --git a/lib/efi/run.c b/lib/efi/run.c index 93b32396805..ac2b73be0f6 100644 --- a/lib/efi/run.c +++ b/lib/efi/run.c @@ -129,9 +129,6 @@ efi_status_t efi_bootflow_run(struct bootflow *bflow) fdt = map_sysmem(bflow->fdt_addr, 0); } - if (IS_ENABLED(CONFIG_EFI_APP)) - return EFI_UNSUPPORTED; - ret = efi_binary_run_dp(bflow->buf, bflow->size, fdt, NULL, 0, device, image); diff --git a/lib/efi_client/Makefile b/lib/efi_client/Makefile index 7b53f8fb9fb..e9fb52fe233 100644 --- a/lib/efi_client/Makefile +++ b/lib/efi_client/Makefile @@ -3,7 +3,7 @@ # (C) Copyright 2015 Google, Inc obj-$(CONFIG_EFI_APP) += efi_app.o efi.o efi_app_init.o efi_vars.o efi_dtb.o -obj-$(CONFIG_EFI_APP) += sdram.o +obj-$(CONFIG_EFI_APP) += sdram.o app_run.o obj-$(CONFIG_EFI_STUB) += efi_info.o ifeq ($(CONFIG_ARM64),y) diff --git a/lib/efi_client/app_run.c b/lib/efi_client/app_run.c new file mode 100644 index 00000000000..d2519985c87 --- /dev/null +++ b/lib/efi_client/app_run.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Running programs from the EFI app + * + * Copyright 2024 Linaro Limited + * Copyright 2024 Canonical Ltd + */ + +#include <bootm.h> +#include <efi.h> +#include <efi_api.h> +#include <efi_device_path.h> +#include <log.h> +#include <malloc.h> + +/** + * do_bootefi_exec() - execute EFI binary + * + * The image indicated by @handle is started. When it returns the allocated + * memory for the @load_options is freed. + * + * @handle: handle of loaded image + * @load_options: load options + * Return: status code + */ +static efi_status_t do_bootefi_exec(efi_handle_t handle, void *load_options) +{ + struct efi_boot_services *boot = efi_get_boot(); + efi_uintn_t exit_data_size = 0; + u16 *exit_data = NULL; + efi_status_t ret; + + /* On ARM switch from EL3 or secure mode to EL2 or non-secure mode */ + switch_to_non_secure_mode(); + + /* TODO(sjg@chromium.org): Set watchdog */ + + /* Call our payload! */ + ret = boot->start_image(handle, &exit_data_size, &exit_data); + if (ret != EFI_SUCCESS) { + log_err("## Application failed, r = %lu\n", + ret & ~EFI_ERROR_MASK); + if (exit_data) { + log_err("## %ls\n", exit_data); + boot->free_pool(exit_data); + } + } + + free(load_options); + + /* TODO(sjg@chromium.org): Disable watchdog */ + + return ret; +} + +efi_status_t efi_run_image(void *source_buffer, efi_uintn_t source_size, + struct efi_device_path *dp_dev, + struct efi_device_path *dp_img) +{ + struct efi_boot_services *boot = efi_get_boot(); + efi_handle_t handle; + struct efi_device_path *msg_path, *file_path; + efi_status_t ret; + + file_path = efi_dp_concat(dp_dev, dp_img, 0); + msg_path = dp_img; + + log_info("Booting %pD\n", msg_path); + + ret = boot->load_image(false, efi_get_parent_image(), file_path, + source_buffer, source_size, &handle); + if (ret != EFI_SUCCESS) { + log_err("Loading image failed\n"); + goto out; + } + + ret = do_bootefi_exec(handle, NULL); + +out: + + return ret; +} + +efi_status_t efi_binary_run_dp(void *image, size_t size, void *fdt, + void *initrd, size_t initrd_sz, + struct efi_device_path *dp_dev, + struct efi_device_path *dp_img) +{ + return efi_run_image(image, size, dp_dev, dp_img); +} -- 2.43.0

From: Simon Glass <sjg@chromium.org> Once the EFI app supports booting an OS, this bootmeth will work without CONFIG_EFI_LOADER being enabled. Rename it so this is clear. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/Kconfig | 4 ++-- boot/Makefile | 2 +- configs/khadas-vim3_android_ab_defconfig | 2 +- configs/khadas-vim3_android_defconfig | 2 +- configs/khadas-vim3l_android_ab_defconfig | 2 +- configs/khadas-vim3l_android_defconfig | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/boot/Kconfig b/boot/Kconfig index a68d8af047b..9899b13f376 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -672,7 +672,7 @@ config BOOTMETH_EXTLINUX_LOCALBOOT This attempts to find a kernel and initrd on the disk and boot it, in the case where there is no "localcmd" in the environment. -config BOOTMETH_EFILOADER +config BOOTMETH_EFI bool "Bootdev support for EFI boot" depends on EFI_BINARY_EXEC imply CMD_TFTPBOOT if CMD_NET @@ -743,7 +743,7 @@ config BOOTMETH_DISTRO select BOOTMETH_SCRIPT if CMDLINE # E.g. Armbian uses scripts select BOOTMETH_EXTLINUX # E.g. Debian uses these select BOOTMETH_EXTLINUX_PXE if CMD_PXE && CMD_NET && DM_ETH - select BOOTMETH_EFILOADER if EFI_BINARY_EXEC # E.g. Ubuntu uses this + select BOOTMETH_EFI if EFI_BINARY_EXEC # E.g. Ubuntu uses this config SPL_BOOTMETH_VBE bool "Bootdev support for Verified Boot for Embedded (SPL)" diff --git a/boot/Makefile b/boot/Makefile index 7f4126ec4ea..3ab1fbb11c0 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -27,7 +27,7 @@ obj-$(CONFIG_$(PHASE_)BOOTSTD_PROG) += prog_boot.o obj-$(CONFIG_$(PHASE_)BOOTMETH_EXTLINUX) += ext_pxe_common.o bootmeth_extlinux.o obj-$(CONFIG_$(PHASE_)BOOTMETH_EXTLINUX_PXE) += ext_pxe_common.o bootmeth_pxe.o -obj-$(CONFIG_$(PHASE_)BOOTMETH_EFILOADER) += bootmeth_efi.o +obj-$(CONFIG_$(PHASE_)BOOTMETH_EFI) += bootmeth_efi.o obj-$(CONFIG_$(PHASE_)BOOTMETH_CROS) += bootmeth_cros.o obj-$(CONFIG_$(PHASE_)BOOTMETH_FEL) += bootmeth_fel.o obj-$(CONFIG_$(PHASE_)BOOTMETH_QFW) += bootmeth_qfw.o diff --git a/configs/khadas-vim3_android_ab_defconfig b/configs/khadas-vim3_android_ab_defconfig index a078c5d363a..3208a7d4e3a 100644 --- a/configs/khadas-vim3_android_ab_defconfig +++ b/configs/khadas-vim3_android_ab_defconfig @@ -27,7 +27,7 @@ CONFIG_FIT_VERBOSE=y CONFIG_BOOTMETH_ANDROID=y # CONFIG_BOOTMETH_EXTLINUX is not set # CONFIG_BOOTMETH_EXTLINUX_PXE is not set -# CONFIG_BOOTMETH_EFILOADER is not set +# CONFIG_BOOTMETH_EFI is not set # CONFIG_BOOTMETH_EFI_BOOTMGR is not set # CONFIG_BOOTMETH_VBE is not set CONFIG_LEGACY_IMAGE_FORMAT=y diff --git a/configs/khadas-vim3_android_defconfig b/configs/khadas-vim3_android_defconfig index b77a44ce859..dde075eba84 100644 --- a/configs/khadas-vim3_android_defconfig +++ b/configs/khadas-vim3_android_defconfig @@ -27,7 +27,7 @@ CONFIG_FIT_VERBOSE=y CONFIG_BOOTMETH_ANDROID=y # CONFIG_BOOTMETH_EXTLINUX is not set # CONFIG_BOOTMETH_EXTLINUX_PXE is not set -# CONFIG_BOOTMETH_EFILOADER is not set +# CONFIG_BOOTMETH_EFI is not set # CONFIG_BOOTMETH_EFI_BOOTMGR is not set # CONFIG_BOOTMETH_VBE is not set CONFIG_LEGACY_IMAGE_FORMAT=y diff --git a/configs/khadas-vim3l_android_ab_defconfig b/configs/khadas-vim3l_android_ab_defconfig index 43db61056ba..5e824bdb2dc 100644 --- a/configs/khadas-vim3l_android_ab_defconfig +++ b/configs/khadas-vim3l_android_ab_defconfig @@ -27,7 +27,7 @@ CONFIG_FIT_VERBOSE=y CONFIG_BOOTMETH_ANDROID=y # CONFIG_BOOTMETH_EXTLINUX is not set # CONFIG_BOOTMETH_EXTLINUX_PXE is not set -# CONFIG_BOOTMETH_EFILOADER is not set +# CONFIG_BOOTMETH_EFI is not set # CONFIG_BOOTMETH_EFI_BOOTMGR is not set # CONFIG_BOOTMETH_VBE is not set CONFIG_LEGACY_IMAGE_FORMAT=y diff --git a/configs/khadas-vim3l_android_defconfig b/configs/khadas-vim3l_android_defconfig index 32d57a5b909..a1378327398 100644 --- a/configs/khadas-vim3l_android_defconfig +++ b/configs/khadas-vim3l_android_defconfig @@ -27,7 +27,7 @@ CONFIG_FIT_VERBOSE=y CONFIG_BOOTMETH_ANDROID=y # CONFIG_BOOTMETH_EXTLINUX is not set # CONFIG_BOOTMETH_EXTLINUX_PXE is not set -# CONFIG_BOOTMETH_EFILOADER is not set +# CONFIG_BOOTMETH_EFI is not set # CONFIG_BOOTMETH_EFI_BOOTMGR is not set # CONFIG_BOOTMETH_VBE is not set CONFIG_LEGACY_IMAGE_FORMAT=y -- 2.43.0

From: Simon Glass <sjg@chromium.org> The EFI app will eventually execute binaries, but it does not enable the CONFIG_EFI_LOADER option. So move the option to a common directory. Signed-off-by: Simon Glass <sjg@chromium.org> --- lib/efi/Kconfig | 10 ++++++++++ lib/efi_loader/Kconfig | 9 --------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/efi/Kconfig b/lib/efi/Kconfig index fc6d5b6d6c2..16de786b1e2 100644 --- a/lib/efi/Kconfig +++ b/lib/efi/Kconfig @@ -10,3 +10,13 @@ config EFI EFI_LOADER This is used to provide libraries shared by both. + +config EFI_BINARY_EXEC + bool "Execute UEFI binary" + default y + depends on EFI_LOADER + help + Select this option if you want to execute the UEFI binary after + loading it with U-Boot load commands or other methods. + You may enable CMD_BOOTEFI_BINARY so that you can use bootefi + command to do that. diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 80549382694..6f67e4897f3 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -39,15 +39,6 @@ config EFI_LOADER if EFI_LOADER -config EFI_BINARY_EXEC - bool "Execute UEFI binary" - default y - help - Select this option if you want to execute the UEFI binary after - loading it with U-Boot load commands or other methods. - You may enable CMD_BOOTEFI_BINARY so that you can use bootefi - command to do that. - config EFI_SECURE_BOOT bool "Enable EFI secure boot support" depends on EFI_LOADER && FIT_SIGNATURE -- 2.43.0

From: Simon Glass <sjg@chromium.org> The EFI loader uses a rather unpleasant global for starting images. For now the only way we intended to support booting EFI apps from the U-Boot EFI app is via a bootmeth. Update the condition so that the 'bootefi' command only works from the EFI loader. Future work may enable the 'bootefi' command for the app. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boot/Kconfig b/boot/Kconfig index 9899b13f376..13036fd3a09 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -398,7 +398,7 @@ if BOOT config BOOTM_EFI bool "Support booting UEFI FIT images" - depends on EFI_BINARY_EXEC && FIT + depends on EFI_BINARY_EXEC && EFI_LOADER && FIT default y help Support booting UEFI FIT images via the bootm command. -- 2.43.0

From: Simon Glass <sjg@chromium.org> This function is used by the EFI loader but we want to avoid it for the app, since it records global state. We don't intend to support running arbitrary EFI apps outside of a bootstd context, so drop this call for the EFI app. Signed-off-by: Simon Glass <sjg@chromium.org> --- fs/fs_legacy.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/fs_legacy.c b/fs/fs_legacy.c index f91b27fa266..34d763b256a 100644 --- a/fs/fs_legacy.c +++ b/fs/fs_legacy.c @@ -902,9 +902,11 @@ int do_load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[], return 1; } - efi_set_bootdev(argv[1], (argc > 2) ? argv[2] : "", - (argc > 4) ? argv[4] : "", map_sysmem(addr, 0), - len_read); + if (IS_ENABLED(CONFIG_EFI_LOADER)) { + efi_set_bootdev(argv[1], (argc > 2) ? argv[2] : "", + (argc > 4) ? argv[4] : "", map_sysmem(addr, 0), + len_read); + } printf("%llu bytes read in %lu ms", len_read, time); if (time > 0) { -- 2.43.0

From: Simon Glass <sjg@chromium.org> Some callers may want a maximum size smaller than 1G so add a parameter for the caller to use. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootmeth_efi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c index 703c73c273d..2499fd3f219 100644 --- a/boot/bootmeth_efi.c +++ b/boot/bootmeth_efi.c @@ -52,7 +52,7 @@ static bool bootmeth_uses_network(struct bootflow *bflow) device_get_uclass_id(media) == UCLASS_ETH; } -static int efiload_read_file(struct bootflow *bflow, ulong addr) +static int efiload_read_file(struct bootflow *bflow, ulong addr, ulong max_size) { struct blk_desc *desc = NULL; ulong size; @@ -61,12 +61,13 @@ static int efiload_read_file(struct bootflow *bflow, ulong addr) if (bflow->blk) desc = dev_get_uclass_plat(bflow->blk); - size = SZ_1G; + size = max_size; ret = bootmeth_common_read_file(bflow->method, bflow, bflow->fname, addr, BFI_EFI, &size); if (ret) return log_msg_ret("rdf", ret); bflow->buf = map_sysmem(addr, bflow->size); + bflow->size = size; return 0; } @@ -286,7 +287,7 @@ static int distro_efi_boot(struct udevice *dev, struct bootflow *bflow) log_debug("distro EFI boot\n"); kernel = env_get_hex("kernel_addr_r", 0); if (!bootmeth_uses_network(bflow)) { - ret = efiload_read_file(bflow, kernel); + ret = efiload_read_file(bflow, kernel, SZ_1G); if (ret) return log_msg_ret("read", ret); -- 2.43.0

From: Simon Glass <sjg@chromium.org> Most of these utility functions are useful for the app, so move them into the common directory. Move the GUIDs used by this file from their various other location, so they are available to the app, even if it doesn't enabled EFI_LOADER Fix the rather large number of checkpath warnings in this file, except for the lwip one, best left to the maintainer to sort out. One noteable change is adding a return value to dp_fill() for the case where an option is not enabled but its uclass is referenced. This presuambly never happens during execution, since if the uclass is not supported there can be no device in that uclass and therefore nothing will ever request its path. So just handle this like an invalid device. Signed-off-by: Simon Glass <sjg@chromium.org> --- lib/efi/Makefile | 1 + lib/efi/device_path.c | 1165 +++++++++++++++++++++++++++++ lib/efi_loader/efi_device_path.c | 1138 ---------------------------- lib/efi_loader/efi_image_loader.c | 9 - lib/efi_loader/efi_root_node.c | 2 - 5 files changed, 1166 insertions(+), 1149 deletions(-) create mode 100644 lib/efi/device_path.c diff --git a/lib/efi/Makefile b/lib/efi/Makefile index a7dee7d4a6b..8d880319696 100644 --- a/lib/efi/Makefile +++ b/lib/efi/Makefile @@ -4,3 +4,4 @@ # obj-y += basename.o +obj-y += device_path.o diff --git a/lib/efi/device_path.c b/lib/efi/device_path.c new file mode 100644 index 00000000000..4c2c0bb614f --- /dev/null +++ b/lib/efi/device_path.c @@ -0,0 +1,1165 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * EFI device path from u-boot device-model mapping + * + * (C) Copyright 2017 Rob Clark + */ + +#define LOG_CATEGORY LOGC_EFI + +#include <blk.h> +#include <dm.h> +#include <dm/root.h> +#include <efi_device_path.h> +#include <log.h> +#include <mapmem.h> +#include <net.h> +#include <usb.h> +#include <mmc.h> +#include <nvme.h> +#include <efi_loader.h> +#include <part.h> +#include <u-boot/uuid.h> +#include <asm-generic/unaligned.h> +#include <linux/compat.h> /* U16_MAX */ + +const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID; +const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID; +const efi_guid_t efi_guid_loaded_image = EFI_LOADED_IMAGE_PROTOCOL_GUID; +const efi_guid_t efi_guid_loaded_image_device_path = + EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; +const efi_guid_t efi_simple_file_system_protocol_guid = + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; +const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID; +const efi_guid_t efi_u_boot_guid = U_BOOT_GUID; + +/* template EFI_DP_END node: */ +const struct efi_device_path EFI_DP_END = { + .type = DEVICE_PATH_TYPE_END, + .sub_type = DEVICE_PATH_SUB_TYPE_END, + .length = sizeof(EFI_DP_END), +}; + +/* + * Determine if an MMC device is an SD card. + * + * @desc block device descriptor + * Return: true if the device is an SD card + */ +static bool is_sd(struct blk_desc *desc) +{ + struct mmc *mmc = find_mmc_device(desc->devnum); + + if (!mmc) + return false; + + return IS_SD(mmc) != 0U; +} + +struct efi_device_path *efi_dp_next(const struct efi_device_path *dp) +{ + if (!dp) + return NULL; + if (dp->type == DEVICE_PATH_TYPE_END) + return NULL; + dp = ((void *)dp) + dp->length; + if (dp->type == DEVICE_PATH_TYPE_END) + return NULL; + return (struct efi_device_path *)dp; +} + +int efi_dp_match(const struct efi_device_path *a, + const struct efi_device_path *b) +{ + while (1) { + int ret; + + ret = memcmp(&a->length, &b->length, sizeof(a->length)); + if (ret) + return ret; + + ret = memcmp(a, b, a->length); + if (ret) + return ret; + + a = efi_dp_next(a); + b = efi_dp_next(b); + + if (!a || !b) + return 0; + } +} + +struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp) +{ + while (dp) { + if (EFI_DP_TYPE(dp, MESSAGING_DEVICE, MSG_USB_WWI) || + EFI_DP_TYPE(dp, MEDIA_DEVICE, HARD_DRIVE_PATH) || + EFI_DP_TYPE(dp, MEDIA_DEVICE, FILE_PATH)) + return dp; + + dp = efi_dp_next(dp); + } + + return dp; +} + +const struct efi_device_path *efi_dp_last_node(const struct efi_device_path *dp) +{ + struct efi_device_path *ret; + + if (!dp || dp->type == DEVICE_PATH_TYPE_END) + return NULL; + while (dp) { + ret = (struct efi_device_path *)dp; + dp = efi_dp_next(dp); + } + return ret; +} + +efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp) +{ + efi_uintn_t sz = 0; + + if (!dp || dp->type == DEVICE_PATH_TYPE_END) + return 0; + while (dp) { + sz += dp->length; + dp = efi_dp_next(dp); + } + + return sz; +} + +efi_uintn_t efi_dp_size(const struct efi_device_path *dp) +{ + const struct efi_device_path *p = dp; + + if (!p) + return 0; + while (p->type != DEVICE_PATH_TYPE_END || + p->sub_type != DEVICE_PATH_SUB_TYPE_END) + p = (void *)p + p->length; + + return (void *)p - (void *)dp; +} + +struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp) +{ + struct efi_device_path *ndp; + size_t sz = efi_dp_size(dp) + sizeof(EFI_DP_END); + + if (!dp) + return NULL; + + ndp = efi_alloc(sz); + if (!ndp) + return NULL; + memcpy(ndp, dp, sz); + + return ndp; +} + +struct +efi_device_path *efi_dp_concat(const struct efi_device_path *dp1, + const struct efi_device_path *dp2, + size_t split_end_node) +{ + struct efi_device_path *ret; + size_t end_size; + + if (!dp1 && !dp2) { + /* return an end node */ + ret = efi_dp_dup(&EFI_DP_END); + } else if (!dp1) { + ret = efi_dp_dup(dp2); + } else if (!dp2) { + ret = efi_dp_dup(dp1); + } else { + /* both dp1 and dp2 are non-null */ + size_t sz1; + size_t sz2 = efi_dp_size(dp2); + void *p; + + if (split_end_node < sizeof(struct efi_device_path)) + sz1 = efi_dp_size(dp1); + else + sz1 = split_end_node; + + if (split_end_node) + end_size = 2 * sizeof(EFI_DP_END); + else + end_size = sizeof(EFI_DP_END); + p = efi_alloc(sz1 + sz2 + end_size); + if (!p) + return NULL; + ret = p; + memcpy(p, dp1, sz1); + p += sz1; + + if (split_end_node) { + memcpy(p, &EFI_DP_END, sizeof(EFI_DP_END)); + p += sizeof(EFI_DP_END); + } + + /* the end node of the second device path has to be retained */ + memcpy(p, dp2, sz2); + p += sz2; + memcpy(p, &EFI_DP_END, sizeof(EFI_DP_END)); + } + + return ret; +} + +struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp, + const struct efi_device_path *node) +{ + struct efi_device_path *ret; + + if (!node && !dp) { + ret = efi_dp_dup(&EFI_DP_END); + } else if (!node) { + ret = efi_dp_dup(dp); + } else if (!dp) { + size_t sz = node->length; + void *p = efi_alloc(sz + sizeof(EFI_DP_END)); + + if (!p) + return NULL; + memcpy(p, node, sz); + memcpy(p + sz, &EFI_DP_END, sizeof(EFI_DP_END)); + ret = p; + } else { + /* both dp and node are non-null */ + size_t sz = efi_dp_size(dp); + void *p = efi_alloc(sz + node->length + sizeof(EFI_DP_END)); + + if (!p) + return NULL; + memcpy(p, dp, sz); + memcpy(p + sz, node, node->length); + memcpy(p + sz + node->length, &EFI_DP_END, sizeof(EFI_DP_END)); + ret = p; + } + + return ret; +} + +struct efi_device_path *efi_dp_create_device_node(const u8 type, + const u8 sub_type, + const u16 length) +{ + struct efi_device_path *ret; + + if (length < sizeof(struct efi_device_path)) + return NULL; + + ret = efi_alloc(length); + if (!ret) + return ret; + ret->type = type; + ret->sub_type = sub_type; + ret->length = length; + return ret; +} + +struct efi_device_path * +efi_dp_append_instance(const struct efi_device_path *dp, + const struct efi_device_path *dpi) +{ + size_t sz, szi; + struct efi_device_path *p, *ret; + + if (!dpi) + return NULL; + if (!dp) + return efi_dp_dup(dpi); + sz = efi_dp_size(dp); + szi = efi_dp_instance_size(dpi); + p = efi_alloc(sz + szi + 2 * sizeof(EFI_DP_END)); + if (!p) + return NULL; + ret = p; + memcpy(p, dp, sz + sizeof(EFI_DP_END)); + p = (void *)p + sz; + p->sub_type = DEVICE_PATH_SUB_TYPE_INSTANCE_END; + p = (void *)p + sizeof(EFI_DP_END); + memcpy(p, dpi, szi); + p = (void *)p + szi; + memcpy(p, &EFI_DP_END, sizeof(EFI_DP_END)); + return ret; +} + +struct efi_device_path *efi_dp_get_next_instance(struct efi_device_path **dp, + efi_uintn_t *size) +{ + size_t sz; + struct efi_device_path *p; + + if (size) + *size = 0; + if (!dp || !*dp) + return NULL; + sz = efi_dp_instance_size(*dp); + p = efi_alloc(sz + sizeof(EFI_DP_END)); + if (!p) + return NULL; + memcpy(p, *dp, sz + sizeof(EFI_DP_END)); + *dp = (void *)*dp + sz; + if ((*dp)->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END) + *dp = (void *)*dp + sizeof(EFI_DP_END); + else + *dp = NULL; + if (size) + *size = sz + sizeof(EFI_DP_END); + return p; +} + +bool efi_dp_is_multi_instance(const struct efi_device_path *dp) +{ + const struct efi_device_path *p = dp; + + if (!p) + return false; + while (p->type != DEVICE_PATH_TYPE_END) + p = (void *)p + p->length; + return p->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END; +} + +__maybe_unused static unsigned int dp_size(struct udevice *dev) +{ + uint parent_size, size; + + if (!dev || !dev->driver) + return sizeof(struct efi_device_path_udevice); + + parent_size = dev_get_parent(dev) ? dp_size(dev_get_parent(dev)) : 0; + switch (device_get_uclass_id(dev)) { + case UCLASS_ROOT: + /* stop traversing parents at this point: */ + return sizeof(struct efi_device_path_udevice); + case UCLASS_ETH: + return parent_size + sizeof(struct efi_device_path_mac_addr); + case UCLASS_BLK: + switch (dev->parent->uclass->uc_drv->id) { + case UCLASS_IDE: + if (IS_ENABLED(CONFIG_IDE)) + size = sizeof(struct efi_device_path_atapi); + break; + case UCLASS_SCSI: + if (IS_ENABLED(CONFIG_SCSI)) + size = sizeof(struct efi_device_path_scsi); + break; + case UCLASS_MMC: + if (IS_ENABLED(CONFIG_MMC)) + size = + sizeof(struct efi_device_path_sd_mmc_path); + break; + case UCLASS_AHCI: + if (IS_ENABLED(CONFIG_AHCI) || + IS_ENABLED(CONFIG_SATA)) + size = sizeof(struct efi_device_path_sata); + break; + case UCLASS_NVME: + if (IS_ENABLED(CONFIG_NVME)) + size = sizeof(struct efi_device_path_nvme); + break; + case UCLASS_MASS_STORAGE: + if (IS_ENABLED(CONFIG_USB)) + size = + sizeof(struct efi_device_path_controller); + break; + default: + /* UCLASS_BLKMAP, UCLASS_HOST, UCLASS_VIRTIO */ + size = sizeof(struct efi_device_path_udevice); + break; + } + case UCLASS_MMC: + if (IS_ENABLED(CONFIG_MMC)) + size = sizeof(struct efi_device_path_sd_mmc_path); + break; + case UCLASS_MASS_STORAGE: + case UCLASS_USB_HUB: + size = sizeof(struct efi_device_path_usb); + break; + default: + size = sizeof(struct efi_device_path_udevice); + break; + } + + return parent_size + size; +} + +/* + * Recursively build a device path. + * + * @buf pointer to the end of the device path + * @dev device + * Return: pointer to the end of the device path + */ +__maybe_unused static void *dp_fill(void *buf, struct udevice *dev) +{ + enum uclass_id uclass_id; + + if (!dev || !dev->driver) + return buf; + + uclass_id = device_get_uclass_id(dev); + if (uclass_id != UCLASS_ROOT) + buf = dp_fill(buf, dev->parent); + + switch (uclass_id) { + case UCLASS_ETH: + if (IS_ENABLED(CONFIG_NETDEVICES)) { + struct efi_device_path_mac_addr *dp = buf; + struct eth_pdata *pdata = dev_get_plat(dev); + + dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR; + dp->dp.length = sizeof(*dp); + memset(&dp->mac, 0, sizeof(dp->mac)); + /* We only support IPv4 */ + memcpy(&dp->mac, &pdata->enetaddr, ARP_HLEN); + /* Ethernet */ + dp->if_type = 1; + return &dp[1]; + } + break; + case UCLASS_BLK: + switch (device_get_uclass_id(dev->parent)) { + case UCLASS_IDE: + if (IS_ENABLED(CONFIG_IDE)) { + struct efi_device_path_atapi *dp = buf; + struct blk_desc *desc = dev_get_uclass_plat(dev); + + dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_ATAPI; + dp->dp.length = sizeof(*dp); + dp->logical_unit_number = desc->devnum; + dp->primary_secondary = IDE_BUS(desc->devnum); + dp->slave_master = desc->devnum % + (IF_ENABLED_INT(CONFIG_IDE, + CONFIG_SYS_IDE_MAXDEVICE) / + IF_ENABLED_INT(CONFIG_IDE, + CONFIG_SYS_IDE_MAXBUS)); + return &dp[1]; + } + break; + case UCLASS_SCSI: + if (IS_ENABLED(CONFIG_SCSI)) { + struct efi_device_path_scsi *dp = buf; + struct blk_desc *desc = dev_get_uclass_plat(dev); + + dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SCSI; + dp->dp.length = sizeof(*dp); + dp->logical_unit_number = desc->lun; + dp->target_id = desc->target; + return &dp[1]; + } + break; + case UCLASS_MMC: + if (IS_ENABLED(CONFIG_MMC)) { + struct efi_device_path_sd_mmc_path *sddp = buf; + struct blk_desc *desc = dev_get_uclass_plat(dev); + + sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + sddp->dp.sub_type = is_sd(desc) ? + DEVICE_PATH_SUB_TYPE_MSG_SD : + DEVICE_PATH_SUB_TYPE_MSG_MMC; + sddp->dp.length = sizeof(*sddp); + sddp->slot_number = dev_seq(dev); + return &sddp[1]; + } + break; + case UCLASS_AHCI: + if (IS_ENABLED(CONFIG_AHCI) || IS_ENABLED(CONFIG_SATA)) { + struct efi_device_path_sata *dp = buf; + struct blk_desc *desc = dev_get_uclass_plat(dev); + + dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SATA; + dp->dp.length = sizeof(*dp); + dp->hba_port = desc->devnum; + /* default 0xffff implies no port multiplier */ + dp->port_multiplier_port = 0xffff; + dp->logical_unit_number = desc->lun; + return &dp[1]; + } + break; + case UCLASS_NVME: + if (IS_ENABLED(CONFIG_NVME)) { + struct efi_device_path_nvme *dp = buf; + u32 ns_id; + + dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_NVME; + dp->dp.length = sizeof(*dp); + nvme_get_namespace_id(dev, &ns_id, dp->eui64); + memcpy(&dp->ns_id, &ns_id, sizeof(ns_id)); + return &dp[1]; + } + break; + case UCLASS_MASS_STORAGE: + if (IS_ENABLED(CONFIG_USB)) { + struct blk_desc *desc = dev_get_uclass_plat(dev); + struct efi_device_path_controller *dp = buf; + + dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; + dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CONTROLLER; + dp->dp.length = sizeof(*dp); + dp->controller_number = desc->lun; + return &dp[1]; + } + break; + default: { + /* UCLASS_BLKMAP, UCLASS_HOST, UCLASS_VIRTIO */ + struct efi_device_path_udevice *dp = buf; + struct blk_desc *desc = dev_get_uclass_plat(dev); + + dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; + dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR; + dp->dp.length = sizeof(*dp); + memcpy(&dp->guid, &efi_u_boot_guid, + sizeof(efi_guid_t)); + dp->uclass_id = (UCLASS_BLK & 0xffff) | + (desc->uclass_id << 16); + dp->dev_number = desc->devnum; + + return &dp[1]; + } + } + case UCLASS_MMC: + if (IS_ENABLED(CONFIG_MMC)) { + struct efi_device_path_sd_mmc_path *sddp = buf; + struct mmc *mmc = mmc_get_mmc_dev(dev); + struct blk_desc *desc = mmc_get_blk_desc(mmc); + + sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + sddp->dp.sub_type = is_sd(desc) ? + DEVICE_PATH_SUB_TYPE_MSG_SD : + DEVICE_PATH_SUB_TYPE_MSG_MMC; + sddp->dp.length = sizeof(*sddp); + sddp->slot_number = dev_seq(dev); + + return &sddp[1]; + } + break; + case UCLASS_MASS_STORAGE: + case UCLASS_USB_HUB: { + struct efi_device_path_usb *udp = buf; + + switch (device_get_uclass_id(dev->parent)) { + case UCLASS_USB_HUB: { + struct usb_device *udev = dev_get_parent_priv(dev); + + udp->parent_port_number = udev->portnr; + break; + } + default: + udp->parent_port_number = 0; + } + udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB; + udp->dp.length = sizeof(*udp); + udp->usb_interface = 0; + + return &udp[1]; + } + default: { + struct efi_device_path_udevice *vdp = buf; + + vdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; + vdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR; + vdp->dp.length = sizeof(*vdp); + memcpy(&vdp->guid, &efi_u_boot_guid, sizeof(efi_guid_t)); + vdp->uclass_id = uclass_id; + vdp->dev_number = dev->seq_; + + return &vdp[1]; + } + } + + return buf; +} + +static uint dp_part_size(struct blk_desc *desc, int part) +{ + struct udevice *dev = desc->bdev; + uint dpsize; + + dpsize = dp_size(dev); + + if (part == 0) /* the actual disk, not a partition */ + return dpsize; + + if (desc->part_type == PART_TYPE_ISO) + dpsize += sizeof(struct efi_device_path_cdrom_path); + else + dpsize += sizeof(struct efi_device_path_hard_drive_path); + + return dpsize; +} + +/* + * Create a device node for a block device partition. + * + * @buf buffer to which the device path is written + * @desc block device descriptor + * @part partition number, 0 identifies a block device + * + * Return: pointer to position after the node + */ +static void *dp_part_node(void *buf, struct blk_desc *desc, int part) +{ + struct disk_partition info; + int ret; + + ret = part_get_info(desc, part, &info); + if (ret < 0) + return buf; + + if (desc->part_type == PART_TYPE_ISO) { + struct efi_device_path_cdrom_path *cddp = buf; + + cddp->boot_entry = part; + cddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; + cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH; + cddp->dp.length = sizeof(*cddp); + cddp->partition_start = info.start; + cddp->partition_size = info.size; + + buf = &cddp[1]; + } else { + struct efi_device_path_hard_drive_path *hddp = buf; + + hddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; + hddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH; + hddp->dp.length = sizeof(*hddp); + hddp->partition_number = part; + hddp->partition_start = info.start; + hddp->partition_end = info.size; + if (desc->part_type == PART_TYPE_EFI) + hddp->partmap_type = 2; + else + hddp->partmap_type = 1; + + switch (desc->sig_type) { + case SIG_TYPE_NONE: + default: + hddp->signature_type = 0; + memset(hddp->partition_signature, 0, + sizeof(hddp->partition_signature)); + break; + case SIG_TYPE_MBR: + hddp->signature_type = 1; + memset(hddp->partition_signature, 0, + sizeof(hddp->partition_signature)); + memcpy(hddp->partition_signature, &desc->mbr_sig, + sizeof(desc->mbr_sig)); + break; + case SIG_TYPE_GUID: + hddp->signature_type = 2; + if (uuid_str_to_bin(disk_partition_uuid(&info), + hddp->partition_signature, + UUID_STR_FORMAT_GUID)) { + log_warning("Partition %d: invalid GUID %s\n", + part, disk_partition_uuid(&info)); + } + break; + } + + buf = &hddp[1]; + } + + return buf; +} + +/* + * Create a device path for a block device or one of its partitions. + * + * @buf buffer to which the device path is written + * @desc block device descriptor + * @part partition number, 0 identifies a block device + */ +static void *dp_part_fill(void *buf, struct blk_desc *desc, int part) +{ + struct udevice *dev = desc->bdev; + + buf = dp_fill(buf, dev); + + if (part == 0) /* the actual disk, not a partition */ + return buf; + + return dp_part_node(buf, desc, part); +} + +struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part) +{ + void *buf, *start; + + start = efi_alloc(dp_part_size(desc, part) + sizeof(EFI_DP_END)); + if (!start) + return NULL; + + buf = dp_part_fill(start, desc, part); + + *((struct efi_device_path *)buf) = EFI_DP_END; + + return start; +} + +struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part) +{ + efi_uintn_t dpsize; + void *buf; + + if (desc->part_type == PART_TYPE_ISO) + dpsize = sizeof(struct efi_device_path_cdrom_path); + else + dpsize = sizeof(struct efi_device_path_hard_drive_path); + buf = efi_alloc(dpsize); + + if (buf) + dp_part_node(buf, desc, part); + + return buf; +} + +/** + * path_to_uefi() - convert UTF-8 path to an UEFI style path + * + * Convert UTF-8 path to a UEFI style path (i.e. with backslashes as path + * separators and UTF-16). + * + * @src: source buffer + * @uefi: target buffer, possibly unaligned + */ +static void path_to_uefi(void *uefi, const char *src) +{ + u16 *pos = uefi; + + /* + * efi_set_bootdev() calls this routine indirectly before the UEFI + * subsystem is initialized. So we cannot assume unaligned access to be + * enabled. + */ + allow_unaligned(); + + while (*src) { + s32 code = utf8_get(&src); + + if (code < 0) + code = '?'; + else if (code == '/') + code = '\\'; + utf16_put(code, &pos); + } + *pos = 0; +} + +struct efi_device_path *efi_dp_from_file(const struct efi_device_path *dp, + const char *path) +{ + struct efi_device_path_file_path *fp; + void *buf, *pos; + size_t dpsize, fpsize; + + dpsize = efi_dp_size(dp); + fpsize = sizeof(struct efi_device_path) + + 2 * (utf8_utf16_strlen(path) + 1); + if (fpsize > U16_MAX) + return NULL; + + buf = efi_alloc(dpsize + fpsize + sizeof(EFI_DP_END)); + if (!buf) + return NULL; + + memcpy(buf, dp, dpsize); + pos = buf + dpsize; + + /* add file-path: */ + if (*path) { + fp = pos; + fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; + fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH; + fp->dp.length = (u16)fpsize; + path_to_uefi(fp->str, path); + pos += fpsize; + } + + memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END)); + + return buf; +} + +struct efi_device_path *efi_dp_from_uart(void) +{ + void *buf, *pos; + struct efi_device_path_uart *uart; + size_t dpsize = dp_size(dm_root()) + sizeof(*uart) + sizeof(EFI_DP_END); + + buf = efi_alloc(dpsize); + if (!buf) + return NULL; + pos = dp_fill(buf, dm_root()); + uart = pos; + uart->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + uart->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_UART; + uart->dp.length = sizeof(*uart); + pos += sizeof(*uart); + memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END)); + + return buf; +} + +struct efi_device_path *efi_dp_from_eth(struct udevice *dev) +{ + void *buf, *start; + uint dpsize = 0; + + assert(dev); + + dpsize += dp_size(dev); + + start = efi_alloc(dpsize + sizeof(EFI_DP_END)); + if (!start) + return NULL; + + buf = dp_fill(start, dev); + + *((struct efi_device_path *)buf) = EFI_DP_END; + + return start; +} + +/** + * efi_dp_from_ipv4() - set device path from IPv4 address + * + * Set the device path to an ethernet device path as provided by + * efi_dp_from_eth() concatenated with a device path of subtype + * DEVICE_PATH_SUB_TYPE_MSG_IPV4, and an EFI_DP_END node. + * + * @ip: IPv4 local address + * @mask: network mask + * @srv: IPv4 remote/server address + * @dev: net udevice + * Return: pointer to device path, NULL on error + */ +static struct efi_device_path *efi_dp_from_ipv4(struct efi_ipv4_address *ip, + struct efi_ipv4_address *mask, + struct efi_ipv4_address *srv, + struct udevice *dev) +{ + struct efi_device_path *dp1, *dp2, *pos; + struct { + struct efi_device_path_ipv4 ipv4dp; + struct efi_device_path end; + } dp; + + memset(&dp.ipv4dp, 0, sizeof(dp.ipv4dp)); + dp.ipv4dp.dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + dp.ipv4dp.dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_IPV4; + dp.ipv4dp.dp.length = sizeof(dp.ipv4dp); + dp.ipv4dp.protocol = 6; + if (ip) + memcpy(&dp.ipv4dp.local_ip_address, ip, sizeof(*ip)); + if (mask) + memcpy(&dp.ipv4dp.subnet_mask, mask, sizeof(*mask)); + if (srv) + memcpy(&dp.ipv4dp.remote_ip_address, srv, sizeof(*srv)); + pos = &dp.end; + memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END)); + + dp1 = efi_dp_from_eth(dev); + if (!dp1) + return NULL; + + dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)&dp, 0); + + efi_free_pool(dp1); + + return dp2; +} + +struct efi_device_path *efi_dp_from_http(const char *server, struct udevice *dev) +{ + struct efi_device_path *dp1, *dp2; + struct efi_device_path_uri *uridp; + efi_uintn_t uridp_len; + char *pos; + char tmp[128]; + struct efi_ipv4_address ip; + struct efi_ipv4_address mask; + + if ((server && strlen("http://") + strlen(server) + 1 > sizeof(tmp)) || + (!server && IS_ENABLED(CONFIG_NET_LWIP))) + return NULL; + + efi_net_get_addr(&ip, &mask, NULL, dev); + + dp1 = efi_dp_from_ipv4(&ip, &mask, NULL, dev); + if (!dp1) + return NULL; + + strcpy(tmp, "http://"); + + if (server) { + strlcat(tmp, server, sizeof(tmp)); +#if !IS_ENABLED(CONFIG_NET_LWIP) + } + else { + ip_to_string(net_server_ip, tmp + strlen("http://")); +#endif + } + + uridp_len = sizeof(struct efi_device_path) + strlen(tmp) + 1; + uridp = efi_alloc(uridp_len + sizeof(EFI_DP_END)); + if (!uridp) { + log_err("Out of memory\n"); + return NULL; + } + uridp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + uridp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_URI; + uridp->dp.length = uridp_len; + debug("device path: setting uri device path to %s\n", tmp); + memcpy(uridp->uri, tmp, strlen(tmp) + 1); + + pos = (char *)uridp + uridp_len; + memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END)); + + dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)uridp, 0); + + efi_free_pool(uridp); + efi_free_pool(dp1); + + return dp2; +} + +/* Construct a device-path for memory-mapped image */ +struct efi_device_path *efi_dp_from_mem(u32 memory_type, void *start_ptr, + size_t size) +{ + struct efi_device_path_memory *mdp; + void *buf, *start; + + start = efi_alloc(sizeof(*mdp) + sizeof(EFI_DP_END)); + if (!start) + return NULL; + + mdp = start; + mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; + mdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY; + mdp->dp.length = sizeof(*mdp); + mdp->memory_type = memory_type; + mdp->start_address = (uintptr_t)start_ptr; + mdp->end_address = mdp->start_address + size; + buf = &mdp[1]; + + *((struct efi_device_path *)buf) = EFI_DP_END; + + return start; +} + +/** + * efi_dp_split_file_path() - split of relative file path from device path + * + * Given a device path indicating a file on a device, separate the device + * path in two: the device path of the actual device and the file path + * relative to this device. + * + * @full_path: device path including device and file path + * @device_path: path of the device + * @file_path: relative path of the file or NULL if there is none + * Return: status code + */ +efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path, + struct efi_device_path **device_path, + struct efi_device_path **file_path) +{ + struct efi_device_path *p, *dp, *fp = NULL; + + *device_path = NULL; + *file_path = NULL; + dp = efi_dp_dup(full_path); + if (!dp) + return EFI_OUT_OF_RESOURCES; + p = dp; + while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) { + p = efi_dp_next(p); + if (!p) + goto out; + } + fp = efi_dp_dup(p); + if (!fp) + return EFI_OUT_OF_RESOURCES; + p->type = DEVICE_PATH_TYPE_END; + p->sub_type = DEVICE_PATH_SUB_TYPE_END; + p->length = sizeof(*p); + +out: + *device_path = dp; + *file_path = fp; + return EFI_SUCCESS; +} + +/** + * efi_dp_from_name() - convert U-Boot device and file path to device path + * + * @dev: U-Boot device, e.g. 'mmc' + * @devnr: U-Boot device number, e.g. 1 for 'mmc:1' + * @path: file path relative to U-Boot device, may be NULL + * @device: pointer to receive device path of the device + * @file: pointer to receive device path for the file + * Return: status code + */ +efi_status_t efi_dp_from_name(const char *dev, const char *devnr, + const char *path, + struct efi_device_path **device, + struct efi_device_path **file) +{ + struct blk_desc *desc = NULL; + struct efi_device_path *dp; + struct disk_partition fs_partition; + size_t image_size; + void *image_ptr; + int part = 0; + + if (path && !file) + return EFI_INVALID_PARAMETER; + + if (IS_ENABLED(CONFIG_EFI_LOADER) && + (!strcmp(dev, "Mem") || !strcmp(dev, "hostfs"))) { + /* loadm command and semihosting */ + efi_get_image_parameters(&image_ptr, &image_size); + + dp = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, image_ptr, + image_size); + } else if (IS_ENABLED(CONFIG_NETDEVICES) && + (!strcmp(dev, "Net") || !strcmp(dev, "Http"))) { + efi_net_dp_from_dev(&dp, eth_get_dev(), false); + } else if (!strcmp(dev, "Uart")) { + dp = efi_dp_from_uart(); + } else { + part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition, + 1); + if (part < 0 || !desc) + return EFI_INVALID_PARAMETER; + + dp = efi_dp_from_part(desc, part); + } + if (device) + *device = dp; + + if (!path) + return EFI_SUCCESS; + + *file = efi_dp_from_file(dp, path); + if (!*file) + return EFI_OUT_OF_RESOURCES; + + return EFI_SUCCESS; +} + +/** + * efi_dp_check_length() - check length of a device path + * + * @dp: pointer to device path + * @maxlen: maximum length of the device path + * Return: + * * length of the device path if it is less or equal @maxlen + * * -1 if the device path is longer then @maxlen + * * -1 if a device path node has a length of less than 4 + * * -EINVAL if maxlen exceeds SSIZE_MAX + */ +ssize_t efi_dp_check_length(const struct efi_device_path *dp, + const size_t maxlen) +{ + ssize_t ret = 0; + u16 len; + + if (maxlen > SSIZE_MAX) + return -EINVAL; + for (;;) { + len = dp->length; + if (len < 4) + return -1; + ret += len; + if (ret > maxlen) + return -1; + if (dp->type == DEVICE_PATH_TYPE_END && + dp->sub_type == DEVICE_PATH_SUB_TYPE_END) + return ret; + dp = (const struct efi_device_path *)((const u8 *)dp + len); + } +} + +/** + * efi_dp_from_lo() - get device-path from load option + * + * The load options in U-Boot may contain multiple concatenated device-paths. + * The first device-path indicates the EFI binary to execute. Subsequent + * device-paths start with a VenMedia node where the GUID identifies the + * function (initrd or fdt). + * + * @lo: EFI load option containing a valid device path + * @guid: GUID identifying device-path or NULL for the EFI binary + * + * Return: + * device path excluding the matched VenMedia node or NULL. + * Caller must efi_free_pool the returned value. + */ +struct +efi_device_path *efi_dp_from_lo(struct efi_load_option *lo, + const efi_guid_t *guid) +{ + struct efi_device_path *fp = lo->file_path; + struct efi_device_path_vendor *vendor; + int lo_len = lo->file_path_length; + + if (!guid) + return efi_dp_dup(fp); + + for (; lo_len >= sizeof(struct efi_device_path); + lo_len -= fp->length, fp = (void *)fp + fp->length) { + if (lo_len < 0 || efi_dp_check_length(fp, lo_len) < 0) + break; + if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE || + fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) + continue; + + vendor = (struct efi_device_path_vendor *)fp; + if (!guidcmp(&vendor->guid, guid)) + return efi_dp_dup(efi_dp_next(fp)); + } + log_debug("VenMedia(%pUl) not found in %ls\n", &guid, lo->label); + + return NULL; +} + +/** + * search_gpt_dp_node() - search gpt device path node + * + * @device_path: device path + * + * Return: pointer to the gpt device path node + */ +struct efi_device_path *search_gpt_dp_node(struct efi_device_path *device_path) +{ + struct efi_device_path *dp = device_path; + + while (dp) { + if (dp->type == DEVICE_PATH_TYPE_MEDIA_DEVICE && + dp->sub_type == DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH) { + struct efi_device_path_hard_drive_path *hd_dp = + (struct efi_device_path_hard_drive_path *)dp; + + if (hd_dp->partmap_type == PART_FORMAT_GPT && + hd_dp->signature_type == SIG_TYPE_GUID) + return dp; + } + dp = efi_dp_next(dp); + } + + return NULL; +} diff --git a/lib/efi_loader/efi_device_path.c b/lib/efi_loader/efi_device_path.c index ed15c87e272..878dfcbc265 100644 --- a/lib/efi_loader/efi_device_path.c +++ b/lib/efi_loader/efi_device_path.c @@ -23,79 +23,6 @@ #include <asm-generic/unaligned.h> #include <linux/compat.h> /* U16_MAX */ -/* template EFI_DP_END node: */ -const struct efi_device_path EFI_DP_END = { - .type = DEVICE_PATH_TYPE_END, - .sub_type = DEVICE_PATH_SUB_TYPE_END, - .length = sizeof(EFI_DP_END), -}; - -#if defined(CONFIG_MMC) -/* - * Determine if an MMC device is an SD card. - * - * @desc block device descriptor - * Return: true if the device is an SD card - */ -static bool is_sd(struct blk_desc *desc) -{ - struct mmc *mmc = find_mmc_device(desc->devnum); - - if (!mmc) - return false; - - return IS_SD(mmc) != 0U; -} -#endif - -struct efi_device_path *efi_dp_next(const struct efi_device_path *dp) -{ - if (dp == NULL) - return NULL; - if (dp->type == DEVICE_PATH_TYPE_END) - return NULL; - dp = ((void *)dp) + dp->length; - if (dp->type == DEVICE_PATH_TYPE_END) - return NULL; - return (struct efi_device_path *)dp; -} - -int efi_dp_match(const struct efi_device_path *a, - const struct efi_device_path *b) -{ - while (1) { - int ret; - - ret = memcmp(&a->length, &b->length, sizeof(a->length)); - if (ret) - return ret; - - ret = memcmp(a, b, a->length); - if (ret) - return ret; - - a = efi_dp_next(a); - b = efi_dp_next(b); - - if (!a || !b) - return 0; - } -} - -struct efi_device_path *efi_dp_shorten(struct efi_device_path *dp) -{ - while (dp) { - if (EFI_DP_TYPE(dp, MESSAGING_DEVICE, MSG_USB_WWI) || - EFI_DP_TYPE(dp, MEDIA_DEVICE, HARD_DRIVE_PATH) || - EFI_DP_TYPE(dp, MEDIA_DEVICE, FILE_PATH)) - return dp; - - dp = efi_dp_next(dp); - } - - return dp; -} - /** * find_handle() - find handle by device path and installed protocol * @@ -171,1068 +98,3 @@ efi_handle_t efi_dp_find_obj(struct efi_device_path *dp, return handle; } - -const struct efi_device_path *efi_dp_last_node(const struct efi_device_path *dp) -{ - struct efi_device_path *ret; - - if (!dp || dp->type == DEVICE_PATH_TYPE_END) - return NULL; - while (dp) { - ret = (struct efi_device_path *)dp; - dp = efi_dp_next(dp); - } - return ret; -} - -efi_uintn_t efi_dp_instance_size(const struct efi_device_path *dp) -{ - efi_uintn_t sz = 0; - - if (!dp || dp->type == DEVICE_PATH_TYPE_END) - return 0; - while (dp) { - sz += dp->length; - dp = efi_dp_next(dp); - } - - return sz; -} - -efi_uintn_t efi_dp_size(const struct efi_device_path *dp) -{ - const struct efi_device_path *p = dp; - - if (!p) - return 0; - while (p->type != DEVICE_PATH_TYPE_END || - p->sub_type != DEVICE_PATH_SUB_TYPE_END) - p = (void *)p + p->length; - - return (void *)p - (void *)dp; -} - -struct efi_device_path *efi_dp_dup(const struct efi_device_path *dp) -{ - struct efi_device_path *ndp; - size_t sz = efi_dp_size(dp) + sizeof(EFI_DP_END); - - if (!dp) - return NULL; - - ndp = efi_alloc(sz); - if (!ndp) - return NULL; - memcpy(ndp, dp, sz); - - return ndp; -} - -struct -efi_device_path *efi_dp_concat(const struct efi_device_path *dp1, - const struct efi_device_path *dp2, - size_t split_end_node) -{ - struct efi_device_path *ret; - size_t end_size; - - if (!dp1 && !dp2) { - /* return an end node */ - ret = efi_dp_dup(&EFI_DP_END); - } else if (!dp1) { - ret = efi_dp_dup(dp2); - } else if (!dp2) { - ret = efi_dp_dup(dp1); - } else { - /* both dp1 and dp2 are non-null */ - size_t sz1; - size_t sz2 = efi_dp_size(dp2); - void *p; - - if (split_end_node < sizeof(struct efi_device_path)) - sz1 = efi_dp_size(dp1); - else - sz1 = split_end_node; - - if (split_end_node) - end_size = 2 * sizeof(EFI_DP_END); - else - end_size = sizeof(EFI_DP_END); - p = efi_alloc(sz1 + sz2 + end_size); - if (!p) - return NULL; - ret = p; - memcpy(p, dp1, sz1); - p += sz1; - - if (split_end_node) { - memcpy(p, &EFI_DP_END, sizeof(EFI_DP_END)); - p += sizeof(EFI_DP_END); - } - - /* the end node of the second device path has to be retained */ - memcpy(p, dp2, sz2); - p += sz2; - memcpy(p, &EFI_DP_END, sizeof(EFI_DP_END)); - } - - return ret; -} - -struct efi_device_path *efi_dp_append_node(const struct efi_device_path *dp, - const struct efi_device_path *node) -{ - struct efi_device_path *ret; - - if (!node && !dp) { - ret = efi_dp_dup(&EFI_DP_END); - } else if (!node) { - ret = efi_dp_dup(dp); - } else if (!dp) { - size_t sz = node->length; - void *p = efi_alloc(sz + sizeof(EFI_DP_END)); - if (!p) - return NULL; - memcpy(p, node, sz); - memcpy(p + sz, &EFI_DP_END, sizeof(EFI_DP_END)); - ret = p; - } else { - /* both dp and node are non-null */ - size_t sz = efi_dp_size(dp); - void *p = efi_alloc(sz + node->length + sizeof(EFI_DP_END)); - if (!p) - return NULL; - memcpy(p, dp, sz); - memcpy(p + sz, node, node->length); - memcpy(p + sz + node->length, &EFI_DP_END, sizeof(EFI_DP_END)); - ret = p; - } - - return ret; -} - -struct efi_device_path *efi_dp_create_device_node(const u8 type, - const u8 sub_type, - const u16 length) -{ - struct efi_device_path *ret; - - if (length < sizeof(struct efi_device_path)) - return NULL; - - ret = efi_alloc(length); - if (!ret) - return ret; - ret->type = type; - ret->sub_type = sub_type; - ret->length = length; - return ret; -} - -struct efi_device_path *efi_dp_append_instance( - const struct efi_device_path *dp, - const struct efi_device_path *dpi) -{ - size_t sz, szi; - struct efi_device_path *p, *ret; - - if (!dpi) - return NULL; - if (!dp) - return efi_dp_dup(dpi); - sz = efi_dp_size(dp); - szi = efi_dp_instance_size(dpi); - p = efi_alloc(sz + szi + 2 * sizeof(EFI_DP_END)); - if (!p) - return NULL; - ret = p; - memcpy(p, dp, sz + sizeof(EFI_DP_END)); - p = (void *)p + sz; - p->sub_type = DEVICE_PATH_SUB_TYPE_INSTANCE_END; - p = (void *)p + sizeof(EFI_DP_END); - memcpy(p, dpi, szi); - p = (void *)p + szi; - memcpy(p, &EFI_DP_END, sizeof(EFI_DP_END)); - return ret; -} - -struct efi_device_path *efi_dp_get_next_instance(struct efi_device_path **dp, - efi_uintn_t *size) -{ - size_t sz; - struct efi_device_path *p; - - if (size) - *size = 0; - if (!dp || !*dp) - return NULL; - sz = efi_dp_instance_size(*dp); - p = efi_alloc(sz + sizeof(EFI_DP_END)); - if (!p) - return NULL; - memcpy(p, *dp, sz + sizeof(EFI_DP_END)); - *dp = (void *)*dp + sz; - if ((*dp)->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END) - *dp = (void *)*dp + sizeof(EFI_DP_END); - else - *dp = NULL; - if (size) - *size = sz + sizeof(EFI_DP_END); - return p; -} - -bool efi_dp_is_multi_instance(const struct efi_device_path *dp) -{ - const struct efi_device_path *p = dp; - - if (!p) - return false; - while (p->type != DEVICE_PATH_TYPE_END) - p = (void *)p + p->length; - return p->sub_type == DEVICE_PATH_SUB_TYPE_INSTANCE_END; -} - -__maybe_unused static unsigned int dp_size(struct udevice *dev) -{ - uint parent_size, size; - - if (!dev || !dev->driver) - return sizeof(struct efi_device_path_udevice); - - parent_size = dev_get_parent(dev) ? dp_size(dev_get_parent(dev)) : 0; - - switch (device_get_uclass_id(dev)) { - case UCLASS_ROOT: - /* stop traversing parents at this point: */ - return sizeof(struct efi_device_path_udevice); - case UCLASS_ETH: - size = sizeof(struct efi_device_path_mac_addr); - break; - case UCLASS_BLK: - switch (dev->parent->uclass->uc_drv->id) { -#ifdef CONFIG_IDE - case UCLASS_IDE: - size = sizeof(struct efi_device_path_atapi); - break; -#endif -#if defined(CONFIG_SCSI) - case UCLASS_SCSI: - size = sizeof(struct efi_device_path_scsi); - break; -#endif -#if defined(CONFIG_MMC) - case UCLASS_MMC: - size = sizeof(struct efi_device_path_sd_mmc_path); - break; -#endif -#if defined(CONFIG_AHCI) || defined(CONFIG_SATA) - case UCLASS_AHCI: - size = sizeof(struct efi_device_path_sata); - break; -#endif -#if defined(CONFIG_NVME) - case UCLASS_NVME: - size = sizeof(struct efi_device_path_nvme); - break; -#endif -#ifdef CONFIG_USB - case UCLASS_MASS_STORAGE: - size = sizeof(struct efi_device_path_controller); - break; -#endif - default: - /* UCLASS_BLKMAP, UCLASS_HOST, UCLASS_VIRTIO */ - size = sizeof(struct efi_device_path_udevice); - break; - } -#if defined(CONFIG_MMC) - case UCLASS_MMC: - size = sizeof(struct efi_device_path_sd_mmc_path); - break; -#endif - case UCLASS_MASS_STORAGE: - case UCLASS_USB_HUB: - size = sizeof(struct efi_device_path_usb); - break; - default: - size = sizeof(struct efi_device_path_udevice); - break; - } - - return parent_size + size; -} - -/* - * Recursively build a device path. - * - * @buf pointer to the end of the device path - * @dev device - * Return: pointer to the end of the device path - */ -__maybe_unused static void *dp_fill(void *buf, struct udevice *dev) -{ - enum uclass_id uclass_id; - - if (!dev || !dev->driver) - return buf; - - uclass_id = device_get_uclass_id(dev); - if (uclass_id != UCLASS_ROOT) - buf = dp_fill(buf, dev->parent); - - switch (uclass_id) { -#ifdef CONFIG_NETDEVICES - case UCLASS_ETH: { - struct efi_device_path_mac_addr *dp = buf; - struct eth_pdata *pdata = dev_get_plat(dev); - - dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; - dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_MAC_ADDR; - dp->dp.length = sizeof(*dp); - memset(&dp->mac, 0, sizeof(dp->mac)); - /* We only support IPv4 */ - memcpy(&dp->mac, &pdata->enetaddr, ARP_HLEN); - /* Ethernet */ - dp->if_type = 1; - return &dp[1]; - } -#endif - case UCLASS_BLK: - switch (device_get_uclass_id(dev->parent)) { -#ifdef CONFIG_IDE - case UCLASS_IDE: { - struct efi_device_path_atapi *dp = buf; - struct blk_desc *desc = dev_get_uclass_plat(dev); - - dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; - dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_ATAPI; - dp->dp.length = sizeof(*dp); - dp->logical_unit_number = desc->devnum; - dp->primary_secondary = IDE_BUS(desc->devnum); - dp->slave_master = desc->devnum % - (CONFIG_SYS_IDE_MAXDEVICE / - CONFIG_SYS_IDE_MAXBUS); - return &dp[1]; - } -#endif -#if defined(CONFIG_SCSI) - case UCLASS_SCSI: { - struct efi_device_path_scsi *dp = buf; - struct blk_desc *desc = dev_get_uclass_plat(dev); - - dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; - dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SCSI; - dp->dp.length = sizeof(*dp); - dp->logical_unit_number = desc->lun; - dp->target_id = desc->target; - return &dp[1]; - } -#endif -#if defined(CONFIG_MMC) - case UCLASS_MMC: { - struct efi_device_path_sd_mmc_path *sddp = buf; - struct blk_desc *desc = dev_get_uclass_plat(dev); - - sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; - sddp->dp.sub_type = is_sd(desc) ? - DEVICE_PATH_SUB_TYPE_MSG_SD : - DEVICE_PATH_SUB_TYPE_MSG_MMC; - sddp->dp.length = sizeof(*sddp); - sddp->slot_number = dev_seq(dev); - return &sddp[1]; - } -#endif -#if defined(CONFIG_AHCI) || defined(CONFIG_SATA) - case UCLASS_AHCI: { - struct efi_device_path_sata *dp = buf; - struct blk_desc *desc = dev_get_uclass_plat(dev); - - dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; - dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SATA; - dp->dp.length = sizeof(*dp); - dp->hba_port = desc->devnum; - /* default 0xffff implies no port multiplier */ - dp->port_multiplier_port = 0xffff; - dp->logical_unit_number = desc->lun; - return &dp[1]; - } -#endif -#if defined(CONFIG_NVME) - case UCLASS_NVME: { - struct efi_device_path_nvme *dp = buf; - u32 ns_id; - - dp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; - dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_NVME; - dp->dp.length = sizeof(*dp); - nvme_get_namespace_id(dev, &ns_id, dp->eui64); - memcpy(&dp->ns_id, &ns_id, sizeof(ns_id)); - return &dp[1]; - } -#endif -#if defined(CONFIG_USB) - case UCLASS_MASS_STORAGE: { - struct blk_desc *desc = dev_get_uclass_plat(dev); - struct efi_device_path_controller *dp = buf; - - dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; - dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CONTROLLER; - dp->dp.length = sizeof(*dp); - dp->controller_number = desc->lun; - return &dp[1]; - } -#endif - default: { - /* UCLASS_BLKMAP, UCLASS_HOST, UCLASS_VIRTIO */ - struct efi_device_path_udevice *dp = buf; - struct blk_desc *desc = dev_get_uclass_plat(dev); - - dp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; - dp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR; - dp->dp.length = sizeof(*dp); - memcpy(&dp->guid, &efi_u_boot_guid, - sizeof(efi_guid_t)); - dp->uclass_id = (UCLASS_BLK & 0xffff) | - (desc->uclass_id << 16); - dp->dev_number = desc->devnum; - - return &dp[1]; - } - } -#if defined(CONFIG_MMC) - case UCLASS_MMC: { - struct efi_device_path_sd_mmc_path *sddp = buf; - struct mmc *mmc = mmc_get_mmc_dev(dev); - struct blk_desc *desc = mmc_get_blk_desc(mmc); - - sddp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; - sddp->dp.sub_type = is_sd(desc) ? - DEVICE_PATH_SUB_TYPE_MSG_SD : - DEVICE_PATH_SUB_TYPE_MSG_MMC; - sddp->dp.length = sizeof(*sddp); - sddp->slot_number = dev_seq(dev); - - return &sddp[1]; - } -#endif - case UCLASS_MASS_STORAGE: - case UCLASS_USB_HUB: { - struct efi_device_path_usb *udp = buf; - - switch (device_get_uclass_id(dev->parent)) { - case UCLASS_USB_HUB: { - struct usb_device *udev = dev_get_parent_priv(dev); - - udp->parent_port_number = udev->portnr; - break; - } - default: - udp->parent_port_number = 0; - } - udp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; - udp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_USB; - udp->dp.length = sizeof(*udp); - udp->usb_interface = 0; - - return &udp[1]; - } - default: { - struct efi_device_path_udevice *vdp = buf; - - vdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; - vdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_VENDOR; - vdp->dp.length = sizeof(*vdp); - memcpy(&vdp->guid, &efi_u_boot_guid, sizeof(efi_guid_t)); - vdp->uclass_id = uclass_id; - vdp->dev_number = dev->seq_; - - return &vdp[1]; - } - } -} - -static unsigned dp_part_size(struct blk_desc *desc, int part) -{ - unsigned dpsize; - struct udevice *dev = desc->bdev; - - dpsize = dp_size(dev); - - if (part == 0) /* the actual disk, not a partition */ - return dpsize; - - if (desc->part_type == PART_TYPE_ISO) - dpsize += sizeof(struct efi_device_path_cdrom_path); - else - dpsize += sizeof(struct efi_device_path_hard_drive_path); - - return dpsize; -} - -/* - * Create a device node for a block device partition. - * - * @buf buffer to which the device path is written - * @desc block device descriptor - * @part partition number, 0 identifies a block device - * - * Return: pointer to position after the node - */ -static void *dp_part_node(void *buf, struct blk_desc *desc, int part) -{ - struct disk_partition info; - int ret; - - ret = part_get_info(desc, part, &info); - if (ret < 0) - return buf; - - if (desc->part_type == PART_TYPE_ISO) { - struct efi_device_path_cdrom_path *cddp = buf; - - cddp->boot_entry = part; - cddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; - cddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_CDROM_PATH; - cddp->dp.length = sizeof(*cddp); - cddp->partition_start = info.start; - cddp->partition_size = info.size; - - buf = &cddp[1]; - } else { - struct efi_device_path_hard_drive_path *hddp = buf; - - hddp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; - hddp->dp.sub_type = DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH; - hddp->dp.length = sizeof(*hddp); - hddp->partition_number = part; - hddp->partition_start = info.start; - hddp->partition_end = info.size; - if (desc->part_type == PART_TYPE_EFI) - hddp->partmap_type = 2; - else - hddp->partmap_type = 1; - - switch (desc->sig_type) { - case SIG_TYPE_NONE: - default: - hddp->signature_type = 0; - memset(hddp->partition_signature, 0, - sizeof(hddp->partition_signature)); - break; - case SIG_TYPE_MBR: - hddp->signature_type = 1; - memset(hddp->partition_signature, 0, - sizeof(hddp->partition_signature)); - memcpy(hddp->partition_signature, &desc->mbr_sig, - sizeof(desc->mbr_sig)); - break; - case SIG_TYPE_GUID: - hddp->signature_type = 2; -#if CONFIG_IS_ENABLED(PARTITION_UUIDS) - /* info.uuid exists only with PARTITION_UUIDS */ - if (uuid_str_to_bin(info.uuid, - hddp->partition_signature, - UUID_STR_FORMAT_GUID)) { - log_warning( - "Partition %d: invalid GUID %s\n", - part, info.uuid); - } -#endif - break; - } - - buf = &hddp[1]; - } - - return buf; -} - -/* - * Create a device path for a block device or one of its partitions. - * - * @buf buffer to which the device path is written - * @desc block device descriptor - * @part partition number, 0 identifies a block device - */ -static void *dp_part_fill(void *buf, struct blk_desc *desc, int part) -{ - struct udevice *dev = desc->bdev; - - buf = dp_fill(buf, dev); - - if (part == 0) /* the actual disk, not a partition */ - return buf; - - return dp_part_node(buf, desc, part); -} - -struct efi_device_path *efi_dp_from_part(struct blk_desc *desc, int part) -{ - void *buf, *start; - - start = efi_alloc(dp_part_size(desc, part) + sizeof(EFI_DP_END)); - if (!start) - return NULL; - - buf = dp_part_fill(start, desc, part); - - *((struct efi_device_path *)buf) = EFI_DP_END; - - return start; -} - -struct efi_device_path *efi_dp_part_node(struct blk_desc *desc, int part) -{ - efi_uintn_t dpsize; - void *buf; - - if (desc->part_type == PART_TYPE_ISO) - dpsize = sizeof(struct efi_device_path_cdrom_path); - else - dpsize = sizeof(struct efi_device_path_hard_drive_path); - buf = efi_alloc(dpsize); - - if (buf) - dp_part_node(buf, desc, part); - - return buf; -} - -/** - * path_to_uefi() - convert UTF-8 path to an UEFI style path - * - * Convert UTF-8 path to a UEFI style path (i.e. with backslashes as path - * separators and UTF-16). - * - * @src: source buffer - * @uefi: target buffer, possibly unaligned - */ -static void path_to_uefi(void *uefi, const char *src) -{ - u16 *pos = uefi; - - /* - * efi_set_bootdev() calls this routine indirectly before the UEFI - * subsystem is initialized. So we cannot assume unaligned access to be - * enabled. - */ - allow_unaligned(); - - while (*src) { - s32 code = utf8_get(&src); - - if (code < 0) - code = '?'; - else if (code == '/') - code = '\\'; - utf16_put(code, &pos); - } - *pos = 0; -} - -struct efi_device_path *efi_dp_from_file(const struct efi_device_path *dp, - const char *path) -{ - struct efi_device_path_file_path *fp; - void *buf, *pos; - size_t dpsize, fpsize; - - dpsize = efi_dp_size(dp); - fpsize = sizeof(struct efi_device_path) + - 2 * (utf8_utf16_strlen(path) + 1); - if (fpsize > U16_MAX) - return NULL; - - buf = efi_alloc(dpsize + fpsize + sizeof(EFI_DP_END)); - if (!buf) - return NULL; - - memcpy(buf, dp, dpsize); - pos = buf + dpsize; - - /* add file-path: */ - if (*path) { - fp = pos; - fp->dp.type = DEVICE_PATH_TYPE_MEDIA_DEVICE; - fp->dp.sub_type = DEVICE_PATH_SUB_TYPE_FILE_PATH; - fp->dp.length = (u16)fpsize; - path_to_uefi(fp->str, path); - pos += fpsize; - } - - memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END)); - - return buf; -} - -struct efi_device_path *efi_dp_from_uart(void) -{ - void *buf, *pos; - struct efi_device_path_uart *uart; - size_t dpsize = dp_size(dm_root()) + sizeof(*uart) + sizeof(EFI_DP_END); - - buf = efi_alloc(dpsize); - if (!buf) - return NULL; - pos = dp_fill(buf, dm_root()); - uart = pos; - uart->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; - uart->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_UART; - uart->dp.length = sizeof(*uart); - pos += sizeof(*uart); - memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END)); - - return buf; -} - -struct efi_device_path __maybe_unused *efi_dp_from_eth(struct udevice *dev) -{ - void *buf, *start; - unsigned dpsize = 0; - - assert(dev); - - dpsize += dp_size(dev); - - start = efi_alloc(dpsize + sizeof(EFI_DP_END)); - if (!start) - return NULL; - - buf = dp_fill(start, dev); - - *((struct efi_device_path *)buf) = EFI_DP_END; - - return start; -} - -/** - * efi_dp_from_ipv4() - set device path from IPv4 address - * - * Set the device path to an ethernet device path as provided by - * efi_dp_from_eth() concatenated with a device path of subtype - * DEVICE_PATH_SUB_TYPE_MSG_IPV4, and an EFI_DP_END node. - * - * @ip: IPv4 local address - * @mask: network mask - * @srv: IPv4 remote/server address - * @dev: net udevice - * Return: pointer to device path, NULL on error - */ -static struct efi_device_path *efi_dp_from_ipv4(struct efi_ipv4_address *ip, - struct efi_ipv4_address *mask, - struct efi_ipv4_address *srv, - struct udevice *dev) -{ - struct efi_device_path *dp1, *dp2, *pos; - struct { - struct efi_device_path_ipv4 ipv4dp; - struct efi_device_path end; - } dp; - - memset(&dp.ipv4dp, 0, sizeof(dp.ipv4dp)); - dp.ipv4dp.dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; - dp.ipv4dp.dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_IPV4; - dp.ipv4dp.dp.length = sizeof(dp.ipv4dp); - dp.ipv4dp.protocol = 6; - if (ip) - memcpy(&dp.ipv4dp.local_ip_address, ip, sizeof(*ip)); - if (mask) - memcpy(&dp.ipv4dp.subnet_mask, mask, sizeof(*mask)); - if (srv) - memcpy(&dp.ipv4dp.remote_ip_address, srv, sizeof(*srv)); - pos = &dp.end; - memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END)); - - dp1 = efi_dp_from_eth(dev); - if (!dp1) - return NULL; - - dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)&dp, 0); - - efi_free_pool(dp1); - - return dp2; -} - -struct efi_device_path *efi_dp_from_http(const char *server, struct udevice *dev) -{ - struct efi_device_path *dp1, *dp2; - struct efi_device_path_uri *uridp; - efi_uintn_t uridp_len; - char *pos; - char tmp[128]; - struct efi_ipv4_address ip; - struct efi_ipv4_address mask; - - if ((server && strlen("http://") + strlen(server) + 1 > sizeof(tmp)) || - (!server && IS_ENABLED(CONFIG_NET_LWIP))) - return NULL; - - efi_net_get_addr(&ip, &mask, NULL, dev); - - dp1 = efi_dp_from_ipv4(&ip, &mask, NULL, dev); - if (!dp1) - return NULL; - - - strcpy(tmp, "http://"); - - if (server) { - strlcat(tmp, server, sizeof(tmp)); -#if !IS_ENABLED(CONFIG_NET_LWIP) - } - else { - ip_to_string(net_server_ip, tmp + strlen("http://")); -#endif - } - - uridp_len = sizeof(struct efi_device_path) + strlen(tmp) + 1; - uridp = efi_alloc(uridp_len + sizeof(EFI_DP_END)); - if (!uridp) { - log_err("Out of memory\n"); - return NULL; - } - uridp->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; - uridp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_URI; - uridp->dp.length = uridp_len; - debug("device path: setting uri device path to %s\n", tmp); - memcpy(uridp->uri, tmp, strlen(tmp) + 1); - - pos = (char *)uridp + uridp_len; - memcpy(pos, &EFI_DP_END, sizeof(EFI_DP_END)); - - dp2 = efi_dp_concat(dp1, (const struct efi_device_path *)uridp, 0); - - efi_free_pool(uridp); - efi_free_pool(dp1); - - return dp2; -} - -/* Construct a device-path for memory-mapped image */ -struct efi_device_path *efi_dp_from_mem(uint32_t memory_type, void *start_ptr, - size_t size) -{ - struct efi_device_path_memory *mdp; - void *buf, *start; - - start = efi_alloc(sizeof(*mdp) + sizeof(EFI_DP_END)); - if (!start) - return NULL; - - mdp = start; - mdp->dp.type = DEVICE_PATH_TYPE_HARDWARE_DEVICE; - mdp->dp.sub_type = DEVICE_PATH_SUB_TYPE_MEMORY; - mdp->dp.length = sizeof(*mdp); - mdp->memory_type = memory_type; - mdp->start_address = (uintptr_t)start_ptr; - mdp->end_address = mdp->start_address + size; - buf = &mdp[1]; - - *((struct efi_device_path *)buf) = EFI_DP_END; - - return start; -} - -/** - * efi_dp_split_file_path() - split of relative file path from device path - * - * Given a device path indicating a file on a device, separate the device - * path in two: the device path of the actual device and the file path - * relative to this device. - * - * @full_path: device path including device and file path - * @device_path: path of the device - * @file_path: relative path of the file or NULL if there is none - * Return: status code - */ -efi_status_t efi_dp_split_file_path(struct efi_device_path *full_path, - struct efi_device_path **device_path, - struct efi_device_path **file_path) -{ - struct efi_device_path *p, *dp, *fp = NULL; - - *device_path = NULL; - *file_path = NULL; - dp = efi_dp_dup(full_path); - if (!dp) - return EFI_OUT_OF_RESOURCES; - p = dp; - while (!EFI_DP_TYPE(p, MEDIA_DEVICE, FILE_PATH)) { - p = efi_dp_next(p); - if (!p) - goto out; - } - fp = efi_dp_dup(p); - if (!fp) - return EFI_OUT_OF_RESOURCES; - p->type = DEVICE_PATH_TYPE_END; - p->sub_type = DEVICE_PATH_SUB_TYPE_END; - p->length = sizeof(*p); - -out: - *device_path = dp; - *file_path = fp; - return EFI_SUCCESS; -} - -/** - * efi_dp_from_name() - convert U-Boot device and file path to device path - * - * @dev: U-Boot device, e.g. 'mmc' - * @devnr: U-Boot device number, e.g. 1 for 'mmc:1' - * @path: file path relative to U-Boot device, may be NULL - * @device: pointer to receive device path of the device - * @file: pointer to receive device path for the file - * Return: status code - */ -efi_status_t efi_dp_from_name(const char *dev, const char *devnr, - const char *path, - struct efi_device_path **device, - struct efi_device_path **file) -{ - struct blk_desc *desc = NULL; - struct efi_device_path *dp; - struct disk_partition fs_partition; - size_t image_size; - void *image_ptr; - int part = 0; - - if (path && !file) - return EFI_INVALID_PARAMETER; - - if (IS_ENABLED(CONFIG_EFI_BINARY_EXEC) && - (!strcmp(dev, "Mem") || !strcmp(dev, "hostfs"))) { - /* loadm command and semihosting */ - efi_get_image_parameters(&image_ptr, &image_size); - - dp = efi_dp_from_mem(EFI_RESERVED_MEMORY_TYPE, image_ptr, - image_size); - } else if (IS_ENABLED(CONFIG_NETDEVICES) && - (!strcmp(dev, "Net") || !strcmp(dev, "Http"))) { - efi_net_dp_from_dev(&dp, eth_get_dev(), false); - } else if (!strcmp(dev, "Uart")) { - dp = efi_dp_from_uart(); - } else { - part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition, - 1); - if (part < 0 || !desc) - return EFI_INVALID_PARAMETER; - - dp = efi_dp_from_part(desc, part); - } - if (device) - *device = dp; - - if (!path) - return EFI_SUCCESS; - - *file = efi_dp_from_file(dp, path); - if (!*file) - return EFI_OUT_OF_RESOURCES; - - return EFI_SUCCESS; -} - -/** - * efi_dp_check_length() - check length of a device path - * - * @dp: pointer to device path - * @maxlen: maximum length of the device path - * Return: - * * length of the device path if it is less or equal @maxlen - * * -1 if the device path is longer then @maxlen - * * -1 if a device path node has a length of less than 4 - * * -EINVAL if maxlen exceeds SSIZE_MAX - */ -ssize_t efi_dp_check_length(const struct efi_device_path *dp, - const size_t maxlen) -{ - ssize_t ret = 0; - u16 len; - - if (maxlen > SSIZE_MAX) - return -EINVAL; - for (;;) { - len = dp->length; - if (len < 4) - return -1; - ret += len; - if (ret > maxlen) - return -1; - if (dp->type == DEVICE_PATH_TYPE_END && - dp->sub_type == DEVICE_PATH_SUB_TYPE_END) - return ret; - dp = (const struct efi_device_path *)((const u8 *)dp + len); - } -} - -/** - * efi_dp_from_lo() - get device-path from load option - * - * The load options in U-Boot may contain multiple concatenated device-paths. - * The first device-path indicates the EFI binary to execute. Subsequent - * device-paths start with a VenMedia node where the GUID identifies the - * function (initrd or fdt). - * - * @lo: EFI load option containing a valid device path - * @guid: GUID identifying device-path or NULL for the EFI binary - * - * Return: - * device path excluding the matched VenMedia node or NULL. - * Caller must free the returned value. - */ -struct -efi_device_path *efi_dp_from_lo(struct efi_load_option *lo, - const efi_guid_t *guid) -{ - struct efi_device_path *fp = lo->file_path; - struct efi_device_path_vendor *vendor; - int lo_len = lo->file_path_length; - - if (!guid) - return efi_dp_dup(fp); - - for (; lo_len >= sizeof(struct efi_device_path); - lo_len -= fp->length, fp = (void *)fp + fp->length) { - if (lo_len < 0 || efi_dp_check_length(fp, lo_len) < 0) - break; - if (fp->type != DEVICE_PATH_TYPE_MEDIA_DEVICE || - fp->sub_type != DEVICE_PATH_SUB_TYPE_VENDOR_PATH) - continue; - - vendor = (struct efi_device_path_vendor *)fp; - if (!guidcmp(&vendor->guid, guid)) - return efi_dp_dup(efi_dp_next(fp)); - } - log_debug("VenMedia(%pUl) not found in %ls\n", &guid, lo->label); - - return NULL; -} - -/** - * search_gpt_dp_node() - search gpt device path node - * - * @device_path: device path - * - * Return: pointer to the gpt device path node - */ -struct efi_device_path *search_gpt_dp_node(struct efi_device_path *device_path) -{ - struct efi_device_path *dp = device_path; - - while (dp) { - if (dp->type == DEVICE_PATH_TYPE_MEDIA_DEVICE && - dp->sub_type == DEVICE_PATH_SUB_TYPE_HARD_DRIVE_PATH) { - struct efi_device_path_hard_drive_path *hd_dp = - (struct efi_device_path_hard_drive_path *)dp; - - if (hd_dp->partmap_type == PART_FORMAT_GPT && - hd_dp->signature_type == SIG_TYPE_GUID) - return dp; - } - dp = efi_dp_next(dp); - } - - return NULL; -} diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 2e6da0d1cf3..18ed6f77763 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -20,15 +20,6 @@ #include <crypto/pkcs7_parser.h> #include <linux/err.h> -const efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID; -const efi_guid_t efi_guid_device_path = EFI_DEVICE_PATH_PROTOCOL_GUID; -const efi_guid_t efi_guid_loaded_image = EFI_LOADED_IMAGE_PROTOCOL_GUID; -const efi_guid_t efi_guid_loaded_image_device_path = - EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; -const efi_guid_t efi_simple_file_system_protocol_guid = - EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; -const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID; - static int machines[] = { #if defined(__aarch64__) IMAGE_FILE_MACHINE_ARM64, diff --git a/lib/efi_loader/efi_root_node.c b/lib/efi_loader/efi_root_node.c index e5246f65df2..b9f86deda17 100644 --- a/lib/efi_loader/efi_root_node.c +++ b/lib/efi_loader/efi_root_node.c @@ -11,8 +11,6 @@ #include <efi_dt_fixup.h> #include <efi_loader.h> -const efi_guid_t efi_u_boot_guid = U_BOOT_GUID; - efi_handle_t efi_root = NULL; struct efi_root_dp { -- 2.43.0
participants (1)
-
Simon Glass