From patchwork Mon May 16 11:00:40 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Masahisa Kojima X-Patchwork-Id: 572967 Delivered-To: patch@linaro.org Received: by 2002:a5d:5051:0:0:0:0:0 with SMTP id h17csp499568wrt; Mon, 16 May 2022 04:01:29 -0700 (PDT) X-Google-Smtp-Source: ABdhPJzDXnY+Efn0+MZx1vqNwRg6oZ2SWtGrVeIqU1PkERTCgj9OzaHEe9phUygMbS7cisp93DgB X-Received: by 2002:ac2:4f03:0:b0:443:5d9d:819d with SMTP id k3-20020ac24f03000000b004435d9d819dmr12462596lfr.165.1652698888863; Mon, 16 May 2022 04:01:28 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1652698888; cv=none; d=google.com; s=arc-20160816; b=OPLIpeA1iHLRLwbz4Rlv5iKP+wDg2mAla8gB0kpWW2kd17SrDnGp+29gbt/Piosin+ TntIeakp3AwsNPYHna6uQcWD/7bEsXPFgB/6amCpQ2dHIPS4rX4lQKFTmHRjqH1b1m6a wVlP9Cdx6W/glZVMjxWlSpESboTJib1WTeQoJvdMd9fYf0rEmcVjogWdVdSqzc2bnPu3 X/iTw5Z9DL4JXKQYshvoFCWUyGDi8iBz8kx4fM0xdOMHlkdH8I8O4S1kvifKQ7nlsKZj 5DnXkmCRzhg+NpVzW/x4HFqsmU1un/asxdu6MuJft6T4HPReDodKdNLzRUY+PLMzni+J 1Ttg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature; bh=VvQitp4hSLlU0qNDdKaJ/ultuui2EoYaspq0srhJx4k=; b=DhJyHgR++WeyYQ278qLOfwJzBxu6hWVsKvENRia/5k5iwsvBwOGIq1Q2la88OCu6l8 0GV5LA4m9xtmtM8pdOXGXFqnaWyoGZmtKWFQI+tJW2UNLTkJtMjmMVEbK8ilzFNVKZQB w6eK2Zkkivs4Q3CyqpiHJEizGbBo77l8VGzYbN1bm97LArsTPaf8tGM4UnyijrUnGT7e HM52sYymzX2TK0t+j4d3jzupX14oONp+NQL59leXxurYqc+Uzbz+cdjl8NBDcX5T6nqu wcRNqTtfXJ2+dTLhT4kYtV/HFuCyrUXF6Xkmwqr3aO2ezxB6xqmy4PkddyHn7868/UWd 6R6Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=nquXIjL+; spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 2a01:238:438b:c500:173d:9f52:ddab:ee01 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from phobos.denx.de (phobos.denx.de. [2a01:238:438b:c500:173d:9f52:ddab:ee01]) by mx.google.com with ESMTPS id c28-20020ac25f7c000000b00473ac8732e5si10374328lfc.432.2022.05.16.04.01.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 May 2022 04:01:28 -0700 (PDT) Received-SPF: pass (google.com: domain of u-boot-bounces@lists.denx.de designates 2a01:238:438b:c500:173d:9f52:ddab:ee01 as permitted sender) client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=nquXIjL+; spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 2a01:238:438b:c500:173d:9f52:ddab:ee01 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 5C43B84010; Mon, 16 May 2022 13:01:27 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.b="nquXIjL+"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id F33C384245; Mon, 16 May 2022 13:00:53 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE,SPF_PASS, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-pj1-x1033.google.com (mail-pj1-x1033.google.com [IPv6:2607:f8b0:4864:20::1033]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 4B22B84099 for ; Mon, 16 May 2022 13:00:37 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=masahisa.kojima@linaro.org Received: by mail-pj1-x1033.google.com with SMTP id pq9-20020a17090b3d8900b001df622bf81dso1548834pjb.3 for ; Mon, 16 May 2022 04:00:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=VvQitp4hSLlU0qNDdKaJ/ultuui2EoYaspq0srhJx4k=; b=nquXIjL+lX2fcdasJ3QI5oYzPcAR9jpSpIlhuOFRHJJIEnHJUFMp2mYEZCG70TnXOM 4zXljODuo0Zg+HWBHrsujj8Tgm8uixlnTY4MmysowaFEhi9gXVFRkLT6jMamF+aqW4UE q+XpGygKkbPEWn4ewInicQlaWZ2i2hkoJ1t9PdiPHMTW3v18JcpKsNOGPw9ShG3cFmqM JMjGvn6uG3xqAUpUdrPKM1qAbl+s1D93CbVPapguaneadyBhmRp5+l4sTduHHWvQ5/z+ exz/gTk+KOwKq/Mgd2e4R51S2mmSddzGcGsM2JCL3sCRf/xa8ZZjhnYbnrqr2hY5OAJ6 LI1g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=VvQitp4hSLlU0qNDdKaJ/ultuui2EoYaspq0srhJx4k=; b=UgiY4TpRJgr5VxcKhERyqQ8Z72fBxW3WRhVnKYwb2DTHVnZtoFpfSB/0hVafU3czHc IiEv5fJYuwfYbrrytmxUyTO6ljgZ1JEFb30YzI5a7pm+QdfkZrgvL5j4LrggsUi2mmqm 8G9ZXp2y2k6TBjOH3UBXW0IKQeabI/E+Oc3rcRr3EU5RcrLR/gTzkdmHQOQY7wHAjmVb 8iNNoIFuj0b3S6xHqmwt2nQb1GkkMb8z4WVSe91p10uBM+nXqhu3dA5C+d6Wfw4nfcjY zfZKftU7M2Pra9u0EDyg7f+9sBrDZ+SPZYHsFruoGoByl74RIwfxtKEaS6rcm369Srzp vgZg== X-Gm-Message-State: AOAM5318SVaUYew2YwuweWEUyTkdfk7hEXUGphNKji3RiFJdRK3z1ZMu rtRB/QnNmN5ZynGfctWiw/uzLneXHpYJqA== X-Received: by 2002:a17:90b:1808:b0:1dc:8904:76a1 with SMTP id lw8-20020a17090b180800b001dc890476a1mr18794987pjb.202.1652698834623; Mon, 16 May 2022 04:00:34 -0700 (PDT) Received: from localhost.localdomain ([240d:1a:cf7:5800:82fa:5bff:fe4b:26b1]) by smtp.gmail.com with ESMTPSA id q2-20020a170902edc200b0016173113c50sm2446480plk.92.2022.05.16.04.00.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 May 2022 04:00:34 -0700 (PDT) From: Masahisa Kojima To: u-boot@lists.denx.de Cc: Heinrich Schuchardt , Ilias Apalodimas , Simon Glass , Takahiro Akashi , Francois Ozog , Mark Kettenis , Masahisa Kojima Subject: [PATCH v6 4/6] bootmenu: add removable media entries Date: Mon, 16 May 2022 20:00:40 +0900 Message-Id: <20220516110043.31480-5-masahisa.kojima@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220516110043.31480-1-masahisa.kojima@linaro.org> References: <20220516110043.31480-1-masahisa.kojima@linaro.org> X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.5 at phobos.denx.de X-Virus-Status: Clean UEFI specification requires booting from removal media using a architecture-specific default image name such as BOOTAA64.EFI. This commit adds the removable media entries into bootmenu, so that user can select the removable media and boot with default image. The bootmenu automatically enumerates the possible bootable media devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, add it as new UEFI boot option(BOOT####) and update BootOrder variable. This automatically generated UEFI boot option has the dedicated guid in the optional_data to distinguish it from the UEFI boot option user adds manually. This optional_data is removed when the efi bootmgr loads the selected UEFI boot option. This commit also provides the BOOT#### variable maintenance feature. Depending on the system hardware setup, some devices may not exist at a later system boot, so bootmenu checks the available device in each bootmenu invocation and automatically removes the BOOT#### variable corrensponding to the non-existent media device. Signed-off-by: Masahisa Kojima --- Changes in v6: - optional_data size is changed to 16bytes - check the load option size before comparison - remove guid included in optional_data of auto generated entry when loading Changes in v5: - Return EFI_SUCCESS if there is no BootOrder defined - correctly handle the case if no removable device found - use guid to identify the automatically generated entry by bootmenu Newly created in v4 cmd/bootmenu.c | 94 +++++++++++++++ include/efi_loader.h | 23 ++++ lib/efi_loader/efi_bootmenu_maintenance.c | 138 ++++++++++++++++++++++ lib/efi_loader/efi_bootmgr.c | 4 + 4 files changed, 259 insertions(+) diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c index 4b846332b0..911086c97e 100644 --- a/cmd/bootmenu.c +++ b/cmd/bootmenu.c @@ -234,6 +234,89 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu, return 1; } +/** + * prepare_media_device_entry() - generate the media device entries + * + * This function enumerates all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL + * and generate the bootmenu entries. + * This function also provide the BOOT#### variable maintenance for + * the media device entries. + * - Automatically create the BOOT#### variable for the newly detected device, + * this BOOT#### variable is distinguished by the special GUID + * stored in the EFI_LOAD_OPTION.optional_data + * - If the device is not attached to the system, the associated BOOT#### variable + * is automatically deleted. + * + * Return: status code + */ +static efi_status_t prepare_media_device_entry(void) +{ + u32 i; + efi_status_t ret; + efi_uintn_t count; + efi_handle_t *volume_handles = NULL; + struct efi_bootmenu_media_boot_option *opt = NULL; + + ret = efi_locate_handle_buffer_int(BY_PROTOCOL, &efi_simple_file_system_protocol_guid, + NULL, &count, (efi_handle_t **)&volume_handles); + if (ret != EFI_SUCCESS) + return ret; + + opt = calloc(count, sizeof(struct efi_bootmenu_media_boot_option)); + if (!opt) + goto out; + + /* enumerate all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL */ + ret = efi_bootmenu_enumerate_boot_option(opt, volume_handles, count); + if (ret != EFI_SUCCESS) + goto out; + + /* + * System hardware configuration may vary depending on the user setup. + * The boot option is automatically added by the bootmenu. + * If the device is not attached to the system, the boot option needs + * to be deleted. + */ + ret = efi_bootmenu_delete_invalid_boot_option(opt, count); + if (ret != EFI_SUCCESS) + goto out; + + /* add non-existent boot option */ + for (i = 0; i < count; i++) { + u32 boot_index; + u16 var_name[9]; + + if (!opt[i].exist) { + ret = efi_bootmenu_get_unused_bootoption(var_name, sizeof(var_name), + &boot_index); + if (ret != EFI_SUCCESS) + goto out; + + ret = efi_set_variable_int(var_name, &efi_global_variable_guid, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + opt[i].size, opt[i].lo, false); + if (ret != EFI_SUCCESS) + goto out; + + ret = efi_bootmenu_append_bootorder(boot_index); + if (ret != EFI_SUCCESS) + goto out; + } + } + +out: + if (opt) { + for (i = 0; i < count; i++) + free(opt[i].lo); + } + free(opt); + efi_free_pool(volume_handles); + + return ret; +} + /** * prepare_uefi_bootorder_entry() - generate the uefi bootmenu entries * @@ -326,6 +409,7 @@ static int prepare_uefi_bootorder_entry(struct bootmenu_data *menu, static struct bootmenu_data *bootmenu_create(int delay) { int ret; + efi_status_t efi_ret; unsigned short int i = 0; struct bootmenu_data *menu; struct bootmenu_entry *iter = NULL; @@ -349,6 +433,16 @@ static struct bootmenu_data *bootmenu_create(int delay) goto cleanup; if (IS_ENABLED(CONFIG_CMD_BOOTEFI_BOOTMGR) && IS_ENABLED(CONFIG_AUTOBOOT_MENU_SHOW)) { + if (i < MAX_DYNAMIC_ENTRY) { + /* + * UEFI specification requires booting from removal media using + * a architecture-specific default image name such as BOOTAA64.EFI. + */ + efi_ret = prepare_media_device_entry(); + if (efi_ret != EFI_SUCCESS && efi_ret != EFI_NOT_FOUND) + goto cleanup; + } + if (i < MAX_DYNAMIC_ENTRY) { ret = prepare_uefi_bootorder_entry(menu, &iter, &i); if (ret < 0 && ret != -ENOENT) diff --git a/include/efi_loader.h b/include/efi_loader.h index 49f326e585..48485e8da9 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -252,6 +252,9 @@ extern const struct efi_hii_string_protocol efi_hii_string; uint16_t *efi_dp_str(struct efi_device_path *dp); +/* GUID for the auto generated boot menu entry */ +extern const efi_guid_t efi_guid_bootmenu_auto_generated; + /* GUID of the U-Boot root node */ extern const efi_guid_t efi_u_boot_guid; #ifdef CONFIG_SANDBOX @@ -934,6 +937,22 @@ struct efi_signature_store { struct x509_certificate; struct pkcs7_message; +/** + * struct efi_bootmenu_media_boot_option - boot option for (removable) media device + * + * This structure is used to enumerate possible boot option + * + * @lo: Serialized load option data + * @size: Size of serialized load option data + * @exist: Flag to indicate the load option already exists + * in Non-volatile load option + */ +struct efi_bootmenu_media_boot_option { + void *lo; + efi_uintn_t size; + bool exist; +}; + bool efi_signature_lookup_digest(struct efi_image_regions *regs, struct efi_signature_store *db, bool dbx); @@ -1082,6 +1101,10 @@ efi_status_t efi_console_get_u16_string efi_status_t efi_bootmenu_get_unused_bootoption(u16 *buf, efi_uintn_t buf_size, u32 *index); efi_status_t efi_bootmenu_append_bootorder(u16 index); +efi_status_t efi_bootmenu_enumerate_boot_option(struct efi_bootmenu_media_boot_option *opt, + efi_handle_t *volume_handles, efi_status_t count); +efi_status_t efi_bootmenu_delete_invalid_boot_option(struct efi_bootmenu_media_boot_option *opt, + efi_status_t count); efi_status_t efi_disk_get_device_name(struct efi_block_io *this, char *buf, int size); diff --git a/lib/efi_loader/efi_bootmenu_maintenance.c b/lib/efi_loader/efi_bootmenu_maintenance.c index be67fca95f..58f2999c61 100644 --- a/lib/efi_loader/efi_bootmenu_maintenance.c +++ b/lib/efi_loader/efi_bootmenu_maintenance.c @@ -26,6 +26,13 @@ static struct efi_simple_text_output_protocol *cout; #define EFI_BOOTMENU_BOOT_NAME_MAX 32 #define EFI_BOOT_ORDER_MAX_SIZE_IN_DECIMAL 6 +#define EFI_BOOTMENU_AUTO_GENERATED_ENTRY_GUID \ + EFI_GUID(0x38c1acc1, 0x9fc0, 0x41f0, \ + 0xb9, 0x01, 0xfa, 0x74, 0xd6, 0xd6, 0xe4, 0xde) + +const efi_guid_t efi_guid_bootmenu_auto_generated = + EFI_BOOTMENU_AUTO_GENERATED_ENTRY_GUID; + typedef efi_status_t (*efi_bootmenu_entry_func)(void *data, bool *exit); /** @@ -1142,3 +1149,134 @@ efi_status_t efi_bootmenu_show_maintenance_menu(void) ARRAY_SIZE(maintenance_menu_items), -1); } + +efi_status_t efi_bootmenu_enumerate_boot_option(struct efi_bootmenu_media_boot_option *opt, + efi_handle_t *volume_handles, efi_status_t count) +{ + u32 i; + struct efi_handler *handler; + efi_status_t ret = EFI_SUCCESS; + + for (i = 0; i < count; i++) { + char *optional_data; + u16 *dev_name, *p; + struct efi_load_option lo; + struct efi_block_io *block_io; + char buf[BOOTMENU_DEVICE_NAME_MAX]; + struct efi_device_path *device_path; + + ret = efi_search_protocol(volume_handles[i], &efi_guid_device_path, &handler); + if (ret != EFI_SUCCESS) + continue; + ret = efi_protocol_open(handler, (void **)&device_path, + efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS) + continue; + + ret = efi_search_protocol(volume_handles[i], &efi_block_io_guid, &handler); + if (ret != EFI_SUCCESS) + continue; + ret = efi_protocol_open(handler, (void **)&block_io, + efi_root, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (ret != EFI_SUCCESS) + continue; + + efi_disk_get_device_name(block_io, buf, BOOTMENU_DEVICE_NAME_MAX); + dev_name = calloc(1, (strlen(buf) + 1) * sizeof(u16)); + if (!dev_name) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + p = dev_name; + utf8_utf16_strncpy(&p, buf, strlen(buf)); + + lo.label = dev_name; + lo.attributes = LOAD_OPTION_ACTIVE; + lo.file_path = device_path; + lo.file_path_length = efi_dp_size(device_path) + sizeof(END); + /* + * Set the dedicated guid to optional_data, it is used to identify + * the boot option that automatically generated by the bootmenu. + * efi_serialize_load_option() expects optional_data is null-terminated + * utf8 string, so set the "1234567" string to allocate enough space + * to store guid, instead of realloc the load_option. + */ + lo.optional_data = "1234567"; + opt[i].size = efi_serialize_load_option(&lo, (u8 **)&opt[i].lo); + if (!opt[i].size) { + ret = EFI_OUT_OF_RESOURCES; + free(dev_name); + goto out; + } + /* set the guid */ + optional_data = (char *)opt[i].lo + (opt[i].size - u16_strsize(u"1234567")); + memcpy(optional_data, &efi_guid_bootmenu_auto_generated, sizeof(efi_guid_t)); + free(dev_name); + } + +out: + return ret; +} + +efi_status_t efi_bootmenu_delete_invalid_boot_option(struct efi_bootmenu_media_boot_option *opt, + efi_status_t count) +{ + u16 *bootorder; + u32 i, j; + efi_status_t ret; + efi_uintn_t num, size, bootorder_size; + void *load_option; + struct efi_load_option lo; + u16 varname[] = u"Boot####"; + + bootorder = efi_get_var(u"BootOrder", &efi_global_variable_guid, &bootorder_size); + if (!bootorder) + return EFI_SUCCESS; /* BootOrder is not defined, nothing to do */ + + num = bootorder_size / sizeof(u16); + for (i = 0; i < num;) { + efi_uintn_t tmp; + + efi_create_indexed_name(varname, sizeof(varname), + "Boot", bootorder[i]); + load_option = efi_get_var(varname, &efi_global_variable_guid, &size); + if (!load_option) + goto next; + + tmp = size; + ret = efi_deserialize_load_option(&lo, load_option, &size); + if (ret != EFI_SUCCESS) + goto next; + + if (size >= sizeof(efi_guid_bootmenu_auto_generated)) { + if (guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated) == 0) { + for (j = 0; j < count; j++) { + if (opt[j].size == tmp && + memcmp(opt[j].lo, load_option, tmp) == 0) { + opt[j].exist = true; + break; + } + } + + if (j == count) { + ret = delete_boot_option(bootorder, i, bootorder_size); + if (ret != EFI_SUCCESS) { + free(load_option); + goto out; + } + + num--; + bootorder_size -= sizeof(u16); + free(load_option); + continue; + } + } + } +next: + free(load_option); + i++; + } + +out: + return ret; +} diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index 631a25d76e..ca2fbd26ce 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -146,6 +146,10 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle, } /* Set load options */ + if (size >= sizeof(efi_guid_t) && + !guidcmp(lo.optional_data, &efi_guid_bootmenu_auto_generated)) + size = 0; + if (size) { *load_options = malloc(size); if (!*load_options) {