From patchwork Mon Jun 13 09:38:51 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Masahisa Kojima X-Patchwork-Id: 581417 Delivered-To: patch@linaro.org Received: by 2002:a05:7000:5806:0:0:0:0 with SMTP id j6csp1798468max; Mon, 13 Jun 2022 02:39:56 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyDLQhTuUX4PQ7eSqWiYrlvHzxGfHbXyo3sWQ35Y27XeCPUGr0Hlmal32j5oJHQPqSgyUrq X-Received: by 2002:a05:6602:29c3:b0:669:25e5:72d2 with SMTP id z3-20020a05660229c300b0066925e572d2mr24981729ioq.206.1655113196408; Mon, 13 Jun 2022 02:39:56 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1655113196; cv=none; d=google.com; s=arc-20160816; b=T2cxpN5/Kg9XNeLVv7c8n2guxQ0R/dOY1EgBeEe/u+gd0l3qN//xI5exSQpNR5csvo lbN2ZVAVK3fFWwKORXqV5WWjCcf7WLOCWFpKYVkXua1Lxbn+dygbSQY/RmAsiMKUW7Yk yeX9fyEdYCnYUE8t53ikKgftUjFfYB8RtLvLXY/e9VnPhaCAuhJWe3saf39bs0mrQXTz Q7oJJKdjrDpR6B55X0HFHvnDCOzZhK5FvoV7xLt74OE6lRW9GO9hFzaFBFrfjfba50CR f2M5yTyjjW2bdos8nsuOgYgmucaO7nPTZmEdPqQRz3D1Oj/0CJwyk5j9LzYujwLeksEf avvg== 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=EjMMDCSdUiI22k9F0bDeabFjESPgWRSfO/UwCp7fPG4=; b=kAeWs+WkDZCc5BaEjvTh+b0xNARY69T0JSwCTzZPFI1kjyW/1HvXmZsbucRfCmy3wY lTl73+lwmWR/fpD5by6hlXQX/Hl6IZk1U4y75GgbbFQAqXy1TSDd08+PNJfEPPPonieX i6rt2B56XLQcOYq/5c0SeRe5j1Fx90AB27znLVhxU3DsfxhAiPQECK4IZqHYVoqSuxgC OTOIb1uzF43uqC7tF3KlqVbwz8JCOcoaneEZwN16/c2prPef+QX0g9BQ+TIXgioUQ63I D7ugK82uArSxa8+W7A8fXnmcrfarKzK+dPVSzYf9e3UyeUGxnV+twRJpJRhKUySiATGt LW/Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b="LxmKwu+/"; spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 85.214.62.61 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. [85.214.62.61]) by mx.google.com with ESMTPS id j18-20020a056e020ef200b002d3f8fd9c49si6715047ilk.128.2022.06.13.02.39.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Jun 2022 02:39:56 -0700 (PDT) Received-SPF: pass (google.com: domain of u-boot-bounces@lists.denx.de designates 85.214.62.61 as permitted sender) client-ip=85.214.62.61; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b="LxmKwu+/"; spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 85.214.62.61 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 764988444E; Mon, 13 Jun 2022 11:38:48 +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="LxmKwu+/"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id DDC2B84459; Mon, 13 Jun 2022 11:38:42 +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-pl1-x62d.google.com (mail-pl1-x62d.google.com [IPv6:2607:f8b0:4864:20::62d]) (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 1ADFF8437A for ; Mon, 13 Jun 2022 11:38:39 +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-pl1-x62d.google.com with SMTP id n18so4642259plg.5 for ; Mon, 13 Jun 2022 02:38:39 -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=EjMMDCSdUiI22k9F0bDeabFjESPgWRSfO/UwCp7fPG4=; b=LxmKwu+/KQ/Bkl58ZqPpvjxv/oLTWHPLShsWSO6grp0hEVjLVpqyWTsz0B//kURIXq 2gSjs9hny9083zq8oD/NBrG5f8ziwkALUxWL/IGEV+gLiwJFPptGRTJx48z8N78OJCI6 4fHcSgoXAWyHZ/e2DXpmAV4/Kdliny9lBZ5+Y7EZX5503AG57HHEfYbfvvkD1z1L5v7N rPuqQp+8kShUlMBYsviavERWPFUfqp120z5DcKPDXcAaTHudgvGaOYFPqER0mWoUoMra 5ldIK3vTHYneNHeNmXnLWNqv7rdvd6rydqQe7uZa/vpel7V0nAi/QZzf1nhjtFgYcqgd nhpw== 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=EjMMDCSdUiI22k9F0bDeabFjESPgWRSfO/UwCp7fPG4=; b=eNninfQsXBfGaURWERUCU33NamebZRsOMup5HoVZ/updVUMLYdxLVYG9Vp1vYstELQ twaPA0hNAs0tIhydAS+HNeCAmX4V8LQISoSWQwkpmmhHH/XAp1yHbzBsbdHiDCaOgXTy 6l8Wh6kZYGGUtUD0Zp/+Df99P3NiU4LKBK53ajJse2lxiyWTOPuapciR+i4FVNDQnbIB VlOYq+fX2GsM4IE5lCnJjsUerqPHKsObBh7WSHaNnmX2Ty8Xam6VZRYCBkgyjomWML/f hvrv0vcrWKZ44VAhyjcb/w+HF41L+44mxkjTyI52AO9F5pJsrvbxP8ZRvj0NgNLExqtY r8nQ== X-Gm-Message-State: AOAM530PU/r/GawWggOHZjwyWsv5iueXYj/DsbFl+IWlXv5E1ehqYH4U ThoFWNd22/Ber6sk6v+ZRoskOMCpKStc0g== X-Received: by 2002:a17:90b:314a:b0:1e8:5362:5620 with SMTP id ip10-20020a17090b314a00b001e853625620mr14779359pjb.9.1655113117230; Mon, 13 Jun 2022 02:38:37 -0700 (PDT) Received: from localhost.localdomain ([240d:1a:cf7:5800:82fa:5bff:fe4b:26b1]) by smtp.gmail.com with ESMTPSA id q6-20020a170902a3c600b00163f7935772sm4629227plb.46.2022.06.13.02.38.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Jun 2022 02:38:36 -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 v7 7/9] bootmenu: add removable media entries Date: Mon, 13 Jun 2022 18:38:51 +0900 Message-Id: <20220613093853.23865-8-masahisa.kojima@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20220613093853.23865-1-masahisa.kojima@linaro.org> References: <20220613093853.23865-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 v7: - rename prepare_media_device_entry() to generate_media_device_boot_option() 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 | 99 +++++++++++++++++++++++++++++++- cmd/efimenu.c | 131 +++++++++++++++++++++++++++++++++++++++++++ include/efi_loader.h | 20 +++++++ 3 files changed, 247 insertions(+), 3 deletions(-) diff --git a/cmd/bootmenu.c b/cmd/bootmenu.c index 704d36debe..49544864b1 100644 --- a/cmd/bootmenu.c +++ b/cmd/bootmenu.c @@ -221,6 +221,89 @@ static int prepare_bootmenu_entry(struct bootmenu_data *menu, } #if (CONFIG_IS_ENABLED(CMD_BOOTEFI_BOOTMGR)) +/** + * generate_media_device_boot_option() - generate the media device boot option + * + * 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 generate_media_device_boot_option(void) +{ + u32 i; + efi_status_t ret; + efi_uintn_t count; + efi_handle_t *volume_handles = NULL; + struct efimenu_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 efimenu_media_boot_option)); + if (!opt) + goto out; + + /* enumerate all devices supporting EFI_SIMPLE_FILE_SYSTEM_PROTOCOL */ + ret = efimenu_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 = efimenu_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 = efimenu_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 = efimenu_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 * @@ -342,9 +425,19 @@ static struct bootmenu_data *bootmenu_create(int delay) #if (CONFIG_IS_ENABLED(CMD_BOOTEFI_BOOTMGR)) if (i < MAX_COUNT - 1) { - ret = prepare_uefi_bootorder_entry(menu, &iter, &i); - if (ret < 0 && ret != -ENOENT) - goto cleanup; + efi_status_t efi_ret; + + /* + * UEFI specification requires booting from removal media using + * a architecture-specific default image name such as BOOTAA64.EFI. + */ + efi_ret = generate_media_device_boot_option(); + if (efi_ret != EFI_SUCCESS && efi_ret != EFI_NOT_FOUND) + goto cleanup; + + ret = prepare_uefi_bootorder_entry(menu, &iter, &i); + if (ret < 0 && ret != -ENOENT) + goto cleanup; } #endif diff --git a/cmd/efimenu.c b/cmd/efimenu.c index 1c97f265ca..21571e9334 100644 --- a/cmd/efimenu.c +++ b/cmd/efimenu.c @@ -1625,6 +1625,137 @@ static efi_status_t efimenu_process_delete_boot_option(void *data) return ret; } +efi_status_t efimenu_enumerate_boot_option(struct efimenu_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 efimenu_delete_invalid_boot_option(struct efimenu_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; +} + static efi_status_t efimenu_init(void) { efi_status_t ret; diff --git a/include/efi_loader.h b/include/efi_loader.h index 44d8ba9bf8..eab6cb5ecd 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -943,6 +943,22 @@ struct efi_signature_store { struct x509_certificate; struct pkcs7_message; +/** + * struct efimenu_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 efimenu_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); @@ -1091,6 +1107,10 @@ efi_status_t efi_console_get_u16_string efi_status_t efimenu_get_unused_bootoption(u16 *buf, efi_uintn_t buf_size, u32 *index); efi_status_t efimenu_append_bootorder(u16 index); +efi_status_t efimenu_enumerate_boot_option(struct efimenu_media_boot_option *opt, + efi_handle_t *volume_handles, efi_status_t count); +efi_status_t efimenu_delete_invalid_boot_option(struct efimenu_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);