[PATCH 00/14] efi: app: Support booting an OS

From: Simon Glass <sjg@chromium.org> This series completes the work to get the EFI app booting an EFI application (e.g. Ubuntu): - efidebug dh now works, and is slightly enhanced - device paths which refer to the underlying EFI layer can now be requested by the app - a test is provided, for EFI on ARM booting into Ubuntu using the sjg lab Simon Glass (14): test/py: Support a timing report when max_count is 0 efi: Drop EFI_CALL() from efidebug command efi: Move driver-binding guid to a common file efi: Drop the BS macro with efi debug efi: Use a function to obtain boot services in efi debug efi: Use a local var for a handle with efidebug dh efi: Show the device name only with the EFI loader efi: Update efidebug dh to work with the app efi: Add the component-name2 protocol to efidebug dh efi: Show component names and other info with efidebug dh efi: Add device-path support for EFI media devices efi: Add a little debugging to calculate_paths() efi: app: Allow booting an EFI app test: efi: Add a test for the app booting Ubuntu via EFI cmd/efidebug.c | 62 ++++++++++++------ include/efi.h | 3 + include/efi_api.h | 15 +++++ include/efi_loader.h | 2 - lib/efi/Kconfig | 2 +- lib/efi/device_path.c | 120 +++++++++++++++++++++++++++++++++- lib/efi/run.c | 8 ++- lib/efi_loader/efi_boottime.c | 8 ++- lib/uuid.c | 1 + test/py/conftest.py | 5 +- test/py/tests/test_distro.py | 57 ++++++++++++++-- 11 files changed, 251 insertions(+), 32 deletions(-) -- 2.43.0 base-commit: 9e77a93eda06d17d0f9fa20d4936caf4b055576c branch: loadr

From: Simon Glass <sjg@chromium.org> If only a single test runs, it does not set the max_count variable, so it remains as zero. Handle this situation as a special case. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/py/conftest.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/py/conftest.py b/test/py/conftest.py index f0101ed6b3d..2baceda51b2 100644 --- a/test/py/conftest.py +++ b/test/py/conftest.py @@ -579,7 +579,10 @@ def show_timings(): value (int): Value to display, i.e. the relative length of the bar """ if value: - bar_length = int((value / max_count) * max_bar_length) + if max_count: + bar_length = int((value / max_count) * max_bar_length) + else: + bar_length = 1 print(f"{key:>8} : {get_time_delta(msecs):>7} |{'#' * bar_length} {value}", file=buf) # Create the buckets we will use, each has a count and a total time -- 2.43.0

From: Simon Glass <sjg@chromium.org> The intend of this macro is to debug calls from an app. But the efidebug command is not an app, so it really doesn't make a lot of sense to call via that macro. Remove EFI_CALL() from do_efi_show_handles() so that the app can use this code. Signed-off-by: Simon Glass <sjg@chromium.org> --- cmd/efidebug.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 7755585c778..2df0fc4e221 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -474,8 +474,7 @@ static int do_efi_show_handles(struct cmd_tbl *cmdtp, int flag, if (app_not_supported("show_handles")) return CMD_RET_FAILURE; - ret = EFI_CALL(efi_locate_handle_buffer(ALL_HANDLES, NULL, NULL, - &num, &handles)); + ret = efi_locate_handle_buffer(ALL_HANDLES, NULL, NULL, &num, &handles); if (ret != EFI_SUCCESS) return CMD_RET_FAILURE; @@ -494,8 +493,7 @@ static int do_efi_show_handles(struct cmd_tbl *cmdtp, int flag, &handler); if (ret == EFI_SUCCESS) printf(" %pD\n", handler->protocol_interface); - ret = EFI_CALL(BS->protocols_per_handle(handles[i], &guid, - &count)); + ret = BS->protocols_per_handle(handles[i], &guid, &count); /* Print other protocols */ for (j = 0; j < count; j++) { if (guidcmp(guid[j], &efi_guid_device_path)) -- 2.43.0

From: Simon Glass <sjg@chromium.org> Move efi_guid_driver_binding_protocol to lib/efi so that it can be used from the app. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/efi.h | 2 ++ include/efi_loader.h | 2 -- lib/efi/device_path.c | 3 +++ lib/efi_loader/efi_boottime.c | 3 --- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/efi.h b/include/efi.h index 54da1540d0b..60afb18784d 100644 --- a/include/efi.h +++ b/include/efi.h @@ -154,6 +154,8 @@ typedef struct efi_object *efi_handle_t; extern const efi_guid_t efi_global_variable_guid; extern const efi_guid_t efi_guid_fdt; +/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */ +extern const efi_guid_t efi_guid_driver_binding_protocol; /* Generic EFI table header */ struct efi_table_hdr { diff --git a/include/efi_loader.h b/include/efi_loader.h index 2d5e8de0132..a475ebb5413 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -321,8 +321,6 @@ extern const efi_guid_t efi_guid_console_control; extern const efi_guid_t efi_guid_device_path; /* GUID of the EFI system partition */ extern const efi_guid_t efi_system_partition_guid; -/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */ -extern const efi_guid_t efi_guid_driver_binding_protocol; /* event group ExitBootServices() invoked */ extern const efi_guid_t efi_guid_event_group_exit_boot_services; /* event group SetVirtualAddressMap() invoked */ diff --git a/lib/efi/device_path.c b/lib/efi/device_path.c index db5a686ccb2..3fd5c75aeee 100644 --- a/lib/efi/device_path.c +++ b/lib/efi/device_path.c @@ -34,6 +34,9 @@ const efi_guid_t efi_file_info_guid = EFI_FILE_INFO_GUID; const efi_guid_t efi_u_boot_guid = U_BOOT_GUID; /* GUID of the device tree table */ const efi_guid_t efi_guid_fdt = EFI_FDT_GUID; +/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */ +const efi_guid_t efi_guid_driver_binding_protocol = + EFI_DRIVER_BINDING_PROTOCOL_GUID; /* template EFI_DP_END node: */ const struct efi_device_path EFI_DP_END = { diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 0f9a654ca03..6c5ebdad95e 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -68,9 +68,6 @@ efi_status_t efi_uninstall_protocol /* 1 if inside U-Boot code, 0 if inside EFI payload code */ static int entry_count = 1; static int nesting_level; -/* GUID of the EFI_DRIVER_BINDING_PROTOCOL */ -const efi_guid_t efi_guid_driver_binding_protocol = - EFI_DRIVER_BINDING_PROTOCOL_GUID; /* event group ExitBootServices() invoked */ const efi_guid_t efi_guid_event_group_exit_boot_services = -- 2.43.0

From: Simon Glass <sjg@chromium.org> Even if this is a subtle dig at EFI, it is only used once and there is not much benefit in retaining it. Access the system-table directly, instead. Signed-off-by: Simon Glass <sjg@chromium.org> --- cmd/efidebug.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 2df0fc4e221..3a4429a3928 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -26,8 +26,6 @@ #include <linux/ctype.h> #include <linux/err.h> -#define BS systab.boottime - static bool app_not_supported(const char *cmd) { if (!IS_ENABLED(CONFIG_EFI_APP)) @@ -493,7 +491,8 @@ static int do_efi_show_handles(struct cmd_tbl *cmdtp, int flag, &handler); if (ret == EFI_SUCCESS) printf(" %pD\n", handler->protocol_interface); - ret = BS->protocols_per_handle(handles[i], &guid, &count); + ret = systab.boottime->protocols_per_handle(handles[i], &guid, + &count); /* Print other protocols */ for (j = 0; j < count; j++) { if (guidcmp(guid[j], &efi_guid_device_path)) -- 2.43.0

From: Simon Glass <sjg@chromium.org> Rather than accessing the EFI loader's structure, create an efi_get_boot() function to match that used by the app, so that the call to protocols_per_handle() can operate in the app as well. Signed-off-by: Simon Glass <sjg@chromium.org> --- cmd/efidebug.c | 4 ++-- lib/efi_loader/efi_boottime.c | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 3a4429a3928..d7f88692d8c 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -491,8 +491,8 @@ static int do_efi_show_handles(struct cmd_tbl *cmdtp, int flag, &handler); if (ret == EFI_SUCCESS) printf(" %pD\n", handler->protocol_interface); - ret = systab.boottime->protocols_per_handle(handles[i], &guid, - &count); + ret = efi_get_boot()->protocols_per_handle(handles[i], &guid, + &count); /* Print other protocols */ for (j = 0; j < count; j++) { if (guidcmp(guid[j], &efi_guid_device_path)) diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 6c5ebdad95e..8b46e43f1ea 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -4026,3 +4026,8 @@ struct efi_system_table *efi_get_sys_table(void) { return &systab; } + +struct efi_boot_services *efi_get_boot(void) +{ + return systab.boottime; +} -- 2.43.0

From: Simon Glass <sjg@chromium.org> Rather than repeating handles[i], put it in a variable. Signed-off-by: Simon Glass <sjg@chromium.org> --- cmd/efidebug.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cmd/efidebug.c b/cmd/efidebug.c index d7f88692d8c..a6a0e877798 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -480,18 +480,24 @@ static int do_efi_show_handles(struct cmd_tbl *cmdtp, int flag, return CMD_RET_SUCCESS; for (i = 0; i < num; i++) { + /* + * this cannot be dereferenced in the APP since the format is + * defined by the underlying EFI implementation, which is likely + * not U-Boot + */ + efi_handle_t handle = handles[i]; struct efi_handler *handler; - printf("\n%p", handles[i]); - if (handles[i]->dev) - printf(" (%s)", handles[i]->dev->name); + printf("\n%p", handle); + if (handle->dev) + printf(" (%s)", handle->dev->name); printf("\n"); /* Print device path */ - ret = efi_search_protocol(handles[i], &efi_guid_device_path, + ret = efi_search_protocol(handle, &efi_guid_device_path, &handler); if (ret == EFI_SUCCESS) printf(" %pD\n", handler->protocol_interface); - ret = efi_get_boot()->protocols_per_handle(handles[i], &guid, + ret = efi_get_boot()->protocols_per_handle(handle, &guid, &count); /* Print other protocols */ for (j = 0; j < count; j++) { -- 2.43.0

From: Simon Glass <sjg@chromium.org> The 'efidebug dh' command dereferences a handle to obtain the device name. The format of an EFI handle is not defined by the spec, so when running in the app we cannot assume that the handle can be dereferenced. Even if the EFI provider happens to be U-Boot it might be a different version. So don't try display the device name with the app. Signed-off-by: Simon Glass <sjg@chromium.org> --- cmd/efidebug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/efidebug.c b/cmd/efidebug.c index a6a0e877798..cec4165a5e1 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -489,7 +489,7 @@ static int do_efi_show_handles(struct cmd_tbl *cmdtp, int flag, struct efi_handler *handler; printf("\n%p", handle); - if (handle->dev) + if (!IS_ENABLED(CONFIG_EFI_APP) && handle->dev) printf(" (%s)", handle->dev->name); printf("\n"); /* Print device path */ -- 2.43.0

From: Simon Glass <sjg@chromium.org> Make use of the boot-time services rather than internal functions, so that the app can implement this command. Signed-off-by: Simon Glass <sjg@chromium.org> --- cmd/efidebug.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/cmd/efidebug.c b/cmd/efidebug.c index cec4165a5e1..dee0fc7efc2 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -464,15 +464,14 @@ static int do_efi_show_drivers(struct cmd_tbl *cmdtp, int flag, static int do_efi_show_handles(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { + struct efi_boot_services *boot = efi_get_boot(); efi_handle_t *handles; efi_guid_t **guid; efi_uintn_t num, count, i, j; efi_status_t ret; - if (app_not_supported("show_handles")) - return CMD_RET_FAILURE; - - ret = efi_locate_handle_buffer(ALL_HANDLES, NULL, NULL, &num, &handles); + ret = boot->locate_handle_buffer(ALL_HANDLES, NULL, NULL, &num, + &handles); if (ret != EFI_SUCCESS) return CMD_RET_FAILURE; @@ -486,19 +485,20 @@ static int do_efi_show_handles(struct cmd_tbl *cmdtp, int flag, * not U-Boot */ efi_handle_t handle = handles[i]; - struct efi_handler *handler; + void *iface; printf("\n%p", handle); if (!IS_ENABLED(CONFIG_EFI_APP) && handle->dev) printf(" (%s)", handle->dev->name); printf("\n"); /* Print device path */ - ret = efi_search_protocol(handle, &efi_guid_device_path, - &handler); - if (ret == EFI_SUCCESS) - printf(" %pD\n", handler->protocol_interface); - ret = efi_get_boot()->protocols_per_handle(handle, &guid, - &count); + ret = boot->handle_protocol(handle, &efi_guid_device_path, + &iface); + if (!ret) + printf(" %pD\n", iface); + else + printf(" (no device-path)\n"); + ret = boot->protocols_per_handle(handle, &guid, &count); /* Print other protocols */ for (j = 0; j < count; j++) { if (guidcmp(guid[j], &efi_guid_device_path)) -- 2.43.0

From: Simon Glass <sjg@chromium.org> Add the GUID and API for this protocol so that we can use it in the 'efidebug dh' command. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/efi.h | 1 + include/efi_api.h | 15 +++++++++++++++ lib/efi/device_path.c | 1 + lib/uuid.c | 1 + 4 files changed, 18 insertions(+) diff --git a/include/efi.h b/include/efi.h index 60afb18784d..5e15a87ed3b 100644 --- a/include/efi.h +++ b/include/efi.h @@ -156,6 +156,7 @@ extern const efi_guid_t efi_global_variable_guid; extern const efi_guid_t efi_guid_fdt; /* GUID of the EFI_DRIVER_BINDING_PROTOCOL */ extern const efi_guid_t efi_guid_driver_binding_protocol; +extern const efi_guid_t efi_guid_component_name2; /* Generic EFI table header */ struct efi_table_hdr { diff --git a/include/efi_api.h b/include/efi_api.h index dfc13588213..876c1ac2bbe 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -483,6 +483,10 @@ struct efi_runtime_services { EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, \ 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) +#define EFI_COMPONENT_NAME2_PROTOCOL_GUID \ + EFI_GUID(0x6a7a5cff, 0xe8d9, 0x4f70, \ + 0xba, 0xda, 0x75, 0xab, 0x30, 0x25, 0xce, 0x14) + /** * struct efi_configuration_table - EFI Configuration Table * @@ -2426,4 +2430,15 @@ struct efi_disk { void *buffer); }; +struct efi_component_name2_protocol { + efi_status_t (EFIAPI *get_driver_name) + (struct efi_component_name2_protocol *this, + char *language, u16 **driver_name); + efi_status_t (EFIAPI *get_controller_name) + (struct efi_component_name2_protocol *this, + efi_handle_t controller_handle, efi_handle_t child_handle, + char *language, u16 **controller_name); + char *supported_langs; +}; + #endif diff --git a/lib/efi/device_path.c b/lib/efi/device_path.c index 3fd5c75aeee..0b2eb7561b1 100644 --- a/lib/efi/device_path.c +++ b/lib/efi/device_path.c @@ -37,6 +37,7 @@ const efi_guid_t efi_guid_fdt = EFI_FDT_GUID; /* GUID of the EFI_DRIVER_BINDING_PROTOCOL */ const efi_guid_t efi_guid_driver_binding_protocol = EFI_DRIVER_BINDING_PROTOCOL_GUID; +const efi_guid_t efi_guid_component_name2 = EFI_COMPONENT_NAME2_PROTOCOL_GUID; /* template EFI_DP_END node: */ const struct efi_device_path EFI_DP_END = { diff --git a/lib/uuid.c b/lib/uuid.c index b5f486182f4..3f7885d0877 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -279,6 +279,7 @@ static const struct { { "Debug Image Info", EFI_DEBUG_IMAGE_INFO_TABLE }, { "Memory Attribute", EFI_ACPI_MCFG_TABLE_GUID }, { "Random-number-generator Algorithms", EFI_RNG_ALGORITHM_GUID }, + { "Component-name2 Protocol", EFI_COMPONENT_NAME2_PROTOCOL_GUID }, #endif #endif /* !USE_HOSTCC */ }; -- 2.43.0

From: Simon Glass <sjg@chromium.org> If a component name is available, show it, along with the supported languages. Also indicate at the top if the handle is a driver, rather than listing that protocol at the end. Signed-off-by: Simon Glass <sjg@chromium.org> --- cmd/efidebug.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/cmd/efidebug.c b/cmd/efidebug.c index dee0fc7efc2..1515a508e2d 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -488,8 +488,27 @@ static int do_efi_show_handles(struct cmd_tbl *cmdtp, int flag, void *iface; printf("\n%p", handle); - if (!IS_ENABLED(CONFIG_EFI_APP) && handle->dev) + ret = boot->handle_protocol(handle, + &efi_guid_driver_binding_protocol, + &iface); + if (!ret) + printf(" <driver>"); + if (IS_ENABLED(CONFIG_EFI_APP)) { + struct efi_component_name2_protocol *comp; + u16 *name; + + ret = boot->handle_protocol(handle, + &efi_guid_component_name2, + (void **)&comp); + if (!ret) { + printf(" [langs: %s]", comp->supported_langs); + ret = comp->get_driver_name(comp, "en", &name); + if (!ret) + printf(" (%ls)", name); + } + } else if (handle->dev) { printf(" (%s)", handle->dev->name); + } printf("\n"); /* Print device path */ ret = boot->handle_protocol(handle, &efi_guid_device_path, @@ -501,7 +520,9 @@ static int do_efi_show_handles(struct cmd_tbl *cmdtp, int flag, ret = boot->protocols_per_handle(handle, &guid, &count); /* Print other protocols */ for (j = 0; j < count; j++) { - if (guidcmp(guid[j], &efi_guid_device_path)) + if (guidcmp(guid[j], &efi_guid_device_path) && + guidcmp(guid[j], &efi_guid_component_name2) && + guidcmp(guid[j], &efi_guid_driver_binding_protocol)) printf(" %pUs\n", guid[j]); } efi_free_pool(guid); -- 2.43.0

From: Simon Glass <sjg@chromium.org> Add proper device-path handling for UCLASS_EFI_MEDIA devices in both dp_size() and dp_fill() functions. This enables EFI applications to use firmware device-paths for media devices accessed through the EFI block I/O protocol. Add some debugging while we are here. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- lib/efi/device_path.c | 116 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/lib/efi/device_path.c b/lib/efi/device_path.c index 0b2eb7561b1..449b9d4239b 100644 --- a/lib/efi/device_path.c +++ b/lib/efi/device_path.c @@ -375,6 +375,10 @@ __maybe_unused static unsigned int dp_size(struct udevice *dev) size = sizeof(struct efi_device_path_controller); break; + case UCLASS_EFI_MEDIA: + /* EFI app */ + size = sizeof(struct efi_device_path_udevice); + break; default: /* UCLASS_BLKMAP, UCLASS_HOST, UCLASS_VIRTIO */ size = sizeof(struct efi_device_path_udevice); @@ -389,6 +393,9 @@ __maybe_unused static unsigned int dp_size(struct udevice *dev) case UCLASS_USB_HUB: size = sizeof(struct efi_device_path_usb); break; + case UCLASS_EFI_MEDIA: + size = sizeof(struct efi_device_path_udevice); + break; default: size = sizeof(struct efi_device_path_udevice); break; @@ -519,6 +526,22 @@ __maybe_unused static void *dp_fill(void *buf, struct udevice *dev) return &dp[1]; } break; + case UCLASS_EFI_MEDIA: + if (IS_ENABLED(CONFIG_EFI_APP)) { + 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]; + } + break; default: { /* UCLASS_BLKMAP, UCLASS_HOST, UCLASS_VIRTIO */ struct efi_device_path_udevice *dp = buf; @@ -1013,6 +1036,86 @@ out: return EFI_SUCCESS; } +/** + * efi_dp_from_efi_app() - create device path for EFI app + * + * Create a device path for EFI applications using firmware device paths + * from EFI media devices + * + * @devnr: device number string (format: "dev:part") + * @descp: pointer to store block device descriptor + * @partp: pointer to store partition number + * @dp: pointer to store created device path + * Return: U-Boot error code (0 on success, negative on error) + */ +static int efi_dp_from_efi_app(const char *devnr, + struct blk_desc **descp, int *partp, + struct efi_device_path **dpp) +{ + struct efi_media_plat *plat; + struct efi_device_path *dp; + struct udevice *media_dev; + struct blk_desc *desc; + int part, dev_num; + char *ep; + int ret; + + log_debug("using EFI app firmware device path for devnr='%s'\n", devnr); + + /* parse device number from devnr (format: "devnum:part") */ + dev_num = hextoul(devnr, &ep); + if (*ep != ':') { + log_err("invalid EFI device format: '%s'\n", devnr); + return log_msg_ret("eda", -EINVAL); + } + + /* find the EFI media device */ + ret = uclass_get_device(UCLASS_EFI_MEDIA, dev_num, &media_dev); + if (ret) { + log_err("cannot find EFI media device %d\n", dev_num); + return log_msg_ret("eda", -ENODEV); + } + plat = dev_get_plat(media_dev); + + log_debug("found EFI media device %d with firmware device path: %pD\n", + dev_num, plat->device_path); + + /* use the firmware device path and append partition */ + part = simple_strtoul(ep + 1, NULL, 16); + if (part > 0) { + struct efi_device_path *part_dp; + struct disk_partition pinfo; + + /* Get partition info */ + part = blk_get_device_part_str("efi", devnr, &desc, &pinfo, 1); + if (part < 0 || !desc) { + log_err("cannot get partition info for '%s'\n", devnr); + return log_msg_ret("edb", part < 0 ? part : -ENODEV); + } + + /* Create partition node */ + part_dp = efi_dp_part_node(desc, part); + if (!part_dp) + return log_msg_ret("edn", -ENOMEM); + + /* Combine firmware device path with partition */ + dp = efi_dp_append_node(plat->device_path, part_dp); + efi_free_pool(part_dp); + } else { + /* Use whole device */ + dp = efi_dp_dup(plat->device_path); + } + if (!dp) + return log_msg_ret("ede", -ENOMEM); + + log_debug("created final device path: %pD\n", dp); + *descp = desc; + *partp = part; + *dpp = dp; + + return 0; +} + /** * efi_dp_from_name() - convert U-Boot device and file path to device path * @@ -1050,11 +1153,22 @@ efi_status_t efi_dp_from_name(const char *dev, const char *devnr, efi_net_dp_from_dev(&dp, eth_get_dev(), false); } else if (!strcmp(dev, "Uart")) { dp = efi_dp_from_uart(); + } else if (IS_ENABLED(CONFIG_EFI_APP) && !strcmp(dev, "efi")) { + int ret; + + ret = efi_dp_from_efi_app(devnr, &desc, &part, &dp); + if (ret) + return EFI_INVALID_PARAMETER; } else { + log_debug("calling blk_get_device_part_str dev='%s', devnr='%s'\n", + dev, devnr); part = blk_get_device_part_str(dev, devnr, &desc, &fs_partition, 1); - if (part < 0 || !desc) + if (part < 0 || !desc) { + log_err("Failed to find fs: dev='%s', devnr='%s', part=%d, desc=%p\n", + dev, devnr, part, desc); return EFI_INVALID_PARAMETER; + } dp = efi_dp_from_part(desc, part); } -- 2.43.0

From: Simon Glass <sjg@chromium.org> Provide some debugging for when things go wrong. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- lib/efi/run.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/efi/run.c b/lib/efi/run.c index ac2b73be0f6..4f47743476d 100644 --- a/lib/efi/run.c +++ b/lib/efi/run.c @@ -41,7 +41,7 @@ efi_status_t calculate_paths(const char *dev, const char *devnr, return ret; } #endif - + log_debug("dev '%s' devnr '%s' path '%s'\n", dev, devnr, path); ret = efi_dp_from_name(dev, devnr, path, &device, &image); if (ret != EFI_SUCCESS) return ret; @@ -74,6 +74,9 @@ static const char *calc_dev_name(struct bootflow *bflow) const struct udevice *media_dev; media_dev = dev_get_parent(bflow->dev); + log_debug("bflow->dev='%s', media_dev='%s', uclass_id=%d\n", + bflow->dev->name, media_dev->name, + device_get_uclass_id(media_dev)); if (!bflow->blk) { if (device_get_uclass_id(media_dev) == UCLASS_ETH) @@ -88,6 +91,9 @@ static const char *calc_dev_name(struct bootflow *bflow) if (device_get_uclass_id(media_dev) == UCLASS_MASS_STORAGE) return "usb"; + if (device_get_uclass_id(media_dev) == UCLASS_EFI_MEDIA) + return "efi"; + return blk_get_uclass_name(device_get_uclass_id(media_dev)); } -- 2.43.0

From: Simon Glass <sjg@chromium.org> Enable booting an app from the U-Boot app, since many distros package their bootloader as an EFI app. Signed-off-by: Simon Glass <sjg@chromium.org> --- lib/efi/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/efi/Kconfig b/lib/efi/Kconfig index ed12741f61a..1570741db17 100644 --- a/lib/efi/Kconfig +++ b/lib/efi/Kconfig @@ -11,7 +11,7 @@ config EFI This is used to provide libraries shared by both. -if EFI_LOADER +if EFI_LOADER || EFI_APP config EFI_BINARY_EXEC bool "Execute UEFI binary" -- 2.43.0

From: Simon Glass <sjg@chromium.org> Now that the EFI app supports booting Ubuntu via the EFI bootmeth, add a test for this, for ARM. This uses the sjg lab. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/py/tests/test_distro.py | 57 +++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/test/py/tests/test_distro.py b/test/py/tests/test_distro.py index 17190b7205b..77caf32145e 100644 --- a/test/py/tests/test_distro.py +++ b/test/py/tests/test_distro.py @@ -78,11 +78,10 @@ def test_distro_script(ubman): @pytest.mark.boardspec('efi-arm_app64') @pytest.mark.role('efi-aarch64') -def test_distro_arm_app(ubman): - """Test that the ARM EFI app can boot into Ubuntu 25.04""" - # with ubman.log.section('build'): - # utils.run_and_log(ubman, ['scripts/build-efi', '-a', 'arm']) +def test_distro_arm_app_extlinux(ubman): + """Test that the ARM EFI app can boot into Ubuntu 25.04 via extlinux""" with ubman.log.section('boot'): + ubman.run_command('bootmeth order extlinux') ubman.run_command('boot', wait_for_prompt=False) ubman.expect(["Booting bootflow 'efi_media.bootdev.part_2' with extlinux"]) @@ -94,3 +93,53 @@ def test_distro_arm_app(ubman): ubman.expect(['Welcome to Ubuntu 25.04!']) ubman.restart_uboot() + +@pytest.mark.boardspec('efi-arm_app64') +@pytest.mark.role('efi-aarch64') +def test_distro_arm_app_efi(ubman): + """Test that the ARM EFI app can boot into Ubuntu 25.04 via EFI""" + with ubman.log.section('boot'): + ubman.run_command('bootmeth order efi') + ubman.run_command('boot', wait_for_prompt=False) + + ubman.expect(["Booting bootflow 'efi_media.bootdev.part_1' with efi"]) + + # Press Escape to force GRUB to appear, even if the silent menu was + # enabled by a previous boot + ubman.send('\x1b') + + # Wait until we see the editor appear + with ubman.log.section('grub'): + ubman.expect(['ESC to return previous']) + # ubman.expect(['The highlighted entry will be executed automatically in 29s']) + + # Press 'e' to edit the command line + ubman.log.info("Pressing 'e'") + ubman.send('e') + for _ in range(10): + ubman.ctrl('N') + expected = '\tlinux\t/boot/vmlinuz-6.14.0-27-generic ' + expected += 'root=UUID=e5665fb4-e1de-4335-86da-357ad5422319 ro ' + for _ in expected: + ubman.ctrl('F') + + to_erase = 'quiet splash' + for _ in to_erase: + ubman.ctrl('D') + ubman.ctrl('X') + ubman.expect(['Booting a command list']) + + with ubman.log.section('exit boot-services'): + ubman.expect(['EFI stub: Exiting boot services...']) + + ubman.log.info("boot") + ubman.expect(['Booting Linux on physical CPU']) + + with ubman.log.section('initrd'): + ubman.expect(['Freeing initrd memory:']) + ubman.expect(['Run /init as init process']) + + with ubman.temporary_timeout(200 * 1000): + ubman.expect(['Ubuntu 25.04 qarm ttyAMA0']) + + ubman.restart_uboot() -- 2.43.0
participants (1)
-
Simon Glass