From patchwork Fri Mar 27 05:27:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Heinrich Schuchardt X-Patchwork-Id: 1262565 X-Patchwork-Delegate: xypron.glpk@gmx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=gmx.de Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=gmx.net header.i=@gmx.net header.a=rsa-sha256 header.s=badeba3b8450 header.b=dR5SGn9R; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 48pVnB3v27z9sR4 for ; Fri, 27 Mar 2020 16:30:38 +1100 (AEDT) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 25DF7818F6; Fri, 27 Mar 2020 06:29:24 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=gmx.de Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (1024-bit key; secure) header.d=gmx.net header.i=@gmx.net header.b="dR5SGn9R"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 703CF81885; Fri, 27 Mar 2020 06:28:46 +0100 (CET) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, FREEMAIL_FROM, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.2 Received: from mout.gmx.net (mout.gmx.net [212.227.17.20]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id E5C5E81888 for ; Fri, 27 Mar 2020 06:28:27 +0100 (CET) Authentication-Results: phobos.denx.de; dmarc=none (p=none dis=none) header.from=gmx.de Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=xypron.glpk@gmx.de DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1585286898; bh=Xm60IRHsMnIaazbLH+/xTlhePDzjJWKauyPRePYDysI=; h=X-UI-Sender-Class:From:To:Cc:Subject:Date:In-Reply-To:References; b=dR5SGn9RFg4bPYr2cj6oOZiCT2nSlsgyV/2pM6ZB27+tMoKj4Un8cdAHE+gpR4YaT rvR6rj9X1HZNAtnNV0UMFVGmAPG+Er7Ue6n7ficAydvBU0EBfl2tIcwPOCvC4d6HVk ylQX9JkHYNNsOB70YDqphu3ZVOsiHuqnVj85DTHM= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Received: from LT02.fritz.box ([84.119.33.226]) by mail.gmx.com (mrgmx105 [212.227.17.174]) with ESMTPSA (Nemesis) id 1Mlw3X-1jiGmY2xmd-00izoy; Fri, 27 Mar 2020 06:28:17 +0100 From: Heinrich Schuchardt To: Alexander Graf , AKASHI Takahiro Cc: Ilias Apalodimas , Sughosh Ganu , Alexey Brodkin , Paul Emge , Faiz Abbas , Eugeniu Rosca , Urja Rannikko , Kever Yang , Simon Glass , Alistair Strachan , Ruslan Trofymenko , Igor Opaniuk , u-boot@lists.denx.de, Heinrich Schuchardt Subject: [PATCH 13/16] efi_loader: memory buffer for variables Date: Fri, 27 Mar 2020 06:27:57 +0100 Message-Id: <20200327052800.11022-14-xypron.glpk@gmx.de> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200327052800.11022-1-xypron.glpk@gmx.de> References: <20200327052800.11022-1-xypron.glpk@gmx.de> MIME-Version: 1.0 X-Provags-ID: V03:K1:7d/Tl2wqltYhhXq+j5mokS2H2ZxmLcL9CsVo806rxPhR/ghueyd jT0Z/2a/TwMi7XGe5HqMpjyyVSPhwoxPQv37o55tCT+69gtDnVVpUv1QtycM1MobwtQVqcF 4ukmB+4U94OmxtMLdVRs5UpUuo4q6C7BGmyIozgOn/oR1joYfQrvlbUHhK176iDKujeuij6 DQILI6vpzzwvjbmUsyMOQ== X-UI-Out-Filterresults: notjunk:1; V03:K0:Wh0B75hMCWI=:5bMLVCFeAlwZrVXgMByx29 7bs9h5MYv/0tMp5Ue0sbbLayEwitISZTuM8M4+mRohGGTUGWFnDH0YAKJJ228WMPDfWbh1n39 6JCQbjGh665Ne7JSrOVntTT6t9uvA//c5qVJRA6BzlaD49bW5GiqR7T1e5I92MgdSgzetv8fV 1I9z2/+18whrDdxLzYW/7FJlczOIwCMW0a4Hh1XTm3PTCVXpAjK9N2+mKnA7VpCAs+BbVdGU8 CLl9bwb6qGO+GNaRhJfit1jsDxsXZXfli1RkRJODkzBxFKgIOmR55zHtHlqLXNwqjEM5MkZG8 1UkpHVHKgUIqOJBMJAbH5oksGrjrbrC5HpvrKdzNHuEQ3tmmO8QJzll0VZg9aRRuFYYHyJgGG FR33ALacmH89tA+ekwOexLbOmZVawfY7XRnviwSlQ2xxV8cYlTx7T1F9keoO+GXgozQbi0ufV SApqxdivoy/YIVWX76ZMaKkuMKjvxrnQ8FADnqjnmugjvdHfoWDk3FmtXvqdpr9CA9Vn43kzp Lm0I844QBBmMwm+e4E2uT2l3HWU8N8I1uxyodBnlIzG9Mt7RUGvwFaauwBYfP9CleRZA39Pi6 QkZzAh8C2Kh2SRJfSS+Ey8FFu5GplzSVR0PTAMs5fbbOL2e5zwce+9LrUXRwI6Yf2YkKXV1Pb cBJnv48Gg2liV9ejVUJVh2CFXdgRjoNIlPne6crd71CRi4hnS6Tn79u+s80s0OGGmRf5yhU9o QvI4XkDG3djdwKy7cZygCmYs2BBJ5pSH96zbALlOvB/Jy6mZo+jQCjHo2iTGYymrYvogXLf++ KwoAp1Yl2hl6bWaSxZJ4ftWjIqWa4ln12LomvNigwKJIj2hEgW6oI/JGg7AeZWFC28s3UA2O0 47MXHUwcNhCdUFhX0Ny2/Qg1Ns0mv7Pl5owQsjB81CQvvPnaPYt5wwEUGu4nAJqyXe9nvS+7M Rt4UbRV3dVi9LugpmlWC+Gafmrt+Eib6k1bvA3PcM9avRkhcuPzFxMRBYp8Iz/qWlBillrnh6 erUt7MuN/Ee5Y4PR01o7ZZDgwx45MmAd+ogs9WBcZ/uPTmudRDyZpYVhSLg5daVsDPgUYUHC8 VKXaaiKUGCYtkopi954iIrmxt+HgP0H7Ga5cUgtj3xSHieayGqHR3Dpl+gIbtVQXYKp4D6CcY KuQnr9692pQmTTcqui/heXWhjKzbLWh8Vrvc2oefA9F7PcEnofJXDKKzQXW7dfuZnIssh8xnj XCfweGcJQUJd0qPrl X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.30rc1 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.102.2 at phobos.denx.de X-Virus-Status: Clean Saving UEFI variable as encoded U-Boot environment variables does not allow support at runtime. Provide functions to manage a memory buffer with UEFI variables. Signed-off-by: Heinrich Schuchardt --- include/efi_variable.h | 16 ++ lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_variables_mem.c | 317 +++++++++++++++++++++++++++++ 3 files changed, 334 insertions(+) create mode 100644 lib/efi_loader/efi_variables_mem.c -- 2.25.1 diff --git a/include/efi_variable.h b/include/efi_variable.h index fb8294fc2e..037d268085 100644 --- a/include/efi_variable.h +++ b/include/efi_variable.h @@ -33,4 +33,20 @@ efi_status_t efi_var_to_file(void); efi_status_t efi_var_from_file(void); +void efi_var_mem_memcpy(void *dest, const void *src, size_t n); + +efi_status_t efi_var_mem_init(void); + +struct efi_var_entry *efi_var_mem_find(const efi_guid_t *guid, const u16 *name, + struct efi_var_entry **next); + +void efi_var_mem_del(struct efi_var_entry *var); + +efi_status_t efi_var_mem_ins(u16 *variable_name, + const efi_guid_t *vendor, u32 attributes, + const efi_uintn_t size1, const void *data1, + const efi_uintn_t size2, const void *data2); + +u64 efi_var_mem_free(void); + #endif diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 621a767ab3..14b210e189 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -36,6 +36,7 @@ obj-y += efi_setup.o obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += efi_unicode_collation.o obj-y += efi_variable.o obj-y += efi_variables_file.o +obj-y += efi_variables_mem.o obj-y += efi_watchdog.o obj-$(CONFIG_LCD) += efi_gop.o obj-$(CONFIG_DM_VIDEO) += efi_gop.o diff --git a/lib/efi_loader/efi_variables_mem.c b/lib/efi_loader/efi_variables_mem.c new file mode 100644 index 0000000000..f70cc65f8b --- /dev/null +++ b/lib/efi_loader/efi_variables_mem.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * File interface for UEFI variables + * + * Copyright (c) 2020, Heinrich Schuchardt + */ + +#include +#include +#include +#include + +static struct efi_var_file __efi_runtime_data *efi_var_buf; +static struct efi_var_entry __efi_runtime_data *efi_current_var; + +/** + * memcpy() - copy memory area + * + * At runtime memcpy() is not available. + * + * @dest: destination buffer + * @src: source buffer + * @n: number of bytes to copy + * Return: pointer to destination buffer + */ +void __efi_runtime efi_var_mem_memcpy(void *dest, const void *src, size_t n) +{ + u8 *d = dest; + const u8 *s = src; + + for (; n; --n) + *d++ = *s++; +} + +/** + * efi_var_mem_compare() - compare GUID and name with a variable + * + * @var: variable to compare + * @guid: GUID to compare + * @name: variable name to compare + * @next: pointer to next variable + * Return: true if match + */ +static bool __efi_runtime +efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid, + const u16 *name, struct efi_var_entry **next) +{ + int i; + u8 *guid1, *guid2; + const u16 *data, *var_name; + bool match = true; + + for (guid1 = (u8 *)&var->guid, guid2 = (u8 *)guid, i = 0; + i < sizeof(efi_guid_t) && match; ++i) + match = (guid1[i] == guid2[i]); + + for (data = var->name, var_name = name;; ++data, ++var_name) { + if (match) + match = (*data == *var_name); + if (!*data) + break; + } + + ++data; + + if (next) + *next = (struct efi_var_entry *) + ALIGN((uintptr_t)data + var->length, 8); + + return match; +} + +/** + * efi_var_mem_find() - find a variable in the list + * + * @guid: GUID of the variable + * @name: name of the variable + * @next: on exit pointer to the next variable after the found one + * Return: found variable + */ +struct efi_var_entry __efi_runtime +*efi_var_mem_find(const efi_guid_t *guid, const u16 *name, + struct efi_var_entry **next) +{ + struct efi_var_entry *var, *last; + + last = (struct efi_var_entry *) + ((uintptr_t)efi_var_buf + efi_var_buf->length); + + if (!*name) { + if (next) { + *next = efi_var_buf->var; + if (*next >= last) + *next = NULL; + } + return NULL; + } + if (efi_current_var && + efi_var_mem_compare(efi_current_var, guid, name, next)) { + if (next && *next >= last) + *next = NULL; + return efi_current_var; + } + + var = efi_var_buf->var; + if (var < last) { + for (; var;) { + struct efi_var_entry *pos; + bool match; + + match = efi_var_mem_compare(var, guid, name, &pos); + if (pos >= last) + pos = NULL; + if (match) { + if (next) + *next = pos; + return var; + } + var = pos; + } + } + if (next) + *next = NULL; + return NULL; +} + +/** + * efi_var_mem_del() - delete a variable from the list of variables + * + * @var: variable to delete + */ +void __efi_runtime efi_var_mem_del(struct efi_var_entry *var) +{ + u16 *data = var->name; + struct efi_var_entry *next, *last; + u64 *from, *to; + + if (!var) + return; + + last = (struct efi_var_entry *) + ((uintptr_t)efi_var_buf + efi_var_buf->length); + if (var < efi_current_var) + efi_current_var = NULL; + + for (data = var->name; *data; ++data) + ; + ++data; + next = (struct efi_var_entry *) + ALIGN((uintptr_t)data + var->length, 8); + efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var; + + for (to = (u64 *)var, from = (u64 *)next; from < (u64 *)last; + ++to, ++from) + *to = *from; + efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var, + efi_var_buf->length - + sizeof(struct efi_var_file)); +} + +/** + * efi_var_mem_ins() - append a variable to the list of variables + * + * The variable is appended without checking if a variable of the same name + * already exists. The two data buffers are concatenated. + * + * @name: variable name + * @vendor: GUID + * @attributes: variable attributes + * @size1: size of the first data buffer + * @data1: first data buffer + * @size2: size of the second data field + * @data2: second data buffer + * Result: status code + */ +efi_status_t __efi_runtime efi_var_mem_ins( + u16 *variable_name, + const efi_guid_t *vendor, u32 attributes, + const efi_uintn_t size1, const void *data1, + const efi_uintn_t size2, const void *data2) +{ + u16 *data; + struct efi_var_entry *var; + u32 var_name_len; + + var = (struct efi_var_entry *) + ((uintptr_t)efi_var_buf + efi_var_buf->length); + for (var_name_len = 0; variable_name[var_name_len]; ++var_name_len) + ; + ++var_name_len; + data = var->name + var_name_len; + + if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 > + EFI_VAR_BUF_SIZE) + return EFI_OUT_OF_RESOURCES; + + var->attr = attributes; + var->length = size1 + size2; + + efi_var_mem_memcpy(&var->guid, vendor, sizeof(efi_guid_t)); + efi_var_mem_memcpy(var->name, variable_name, + sizeof(u16) * var_name_len); + efi_var_mem_memcpy(data, data1, size1); + efi_var_mem_memcpy((u8 *)data + size1, data2, size2); + + var = (struct efi_var_entry *) + ALIGN((uintptr_t)data + var->length, 8); + efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf; + efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var, + efi_var_buf->length - + sizeof(struct efi_var_file)); + + return EFI_SUCCESS; +} + +/** + * efi_var_mem_free() - determine free memory for variables + * + * Return: maximum data size plus variable name size + */ +u64 __efi_runtime efi_var_mem_free(void) +{ + return EFI_VAR_BUF_SIZE - efi_var_buf->length - + sizeof(struct efi_var_entry); +} + +/** + * efi_var_mem_bs_del() - delete boot service only variables + */ +static void efi_var_mem_bs_del(void) +{ + struct efi_var_entry *var = efi_var_buf->var; + + for (;;) { + struct efi_var_entry *last; + + last = (struct efi_var_entry *) + ((uintptr_t)efi_var_buf + efi_var_buf->length); + if (var >= last) + break; + if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) { + u16 *data; + + /* skip variable */ + for (data = var->name; *data; ++data) + ; + ++data; + var = (struct efi_var_entry *) + ALIGN((uintptr_t)data + var->length, 8); + } else { + /* delete variable */ + efi_var_mem_del(var); + } + } +} + +/** + * efi_var_mem_notify_exit_boot_services() - ExitBootService callback + * + * @event: callback event + * @context: callback context + */ +static void EFIAPI __efi_runtime +efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context) +{ + /* Delete boot service only variables */ + efi_var_mem_bs_del(); +} + +/** + * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback + * + * @event: callback event + * @context: callback context + */ +static void EFIAPI __efi_runtime +efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context) +{ + efi_convert_pointer(0, (void **)&efi_var_buf); +} + +/** + * efi_var_mem_init() - set-up variable list + * + * Return: status code + */ +efi_status_t efi_var_mem_init(void) +{ + u64 memory; + efi_status_t ret; + struct efi_event *event; + + ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_DATA, + efi_size_in_pages(EFI_VAR_BUF_SIZE), + &memory); + if (ret != EFI_SUCCESS) + return ret; + efi_var_buf = (struct efi_var_file *)(uintptr_t)memory; + memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE); + efi_var_buf->magic = EFI_VAR_FILE_MAGIC; + efi_var_buf->length = (uintptr_t)efi_var_buf->var - + (uintptr_t)efi_var_buf; + /* crc32 for 0 bytes = 0 */ + + ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK, + efi_var_mem_notify_exit_boot_services, NULL, + NULL, &event); + if (ret != EFI_SUCCESS) + return ret; + ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK, + efi_var_mem_notify_virtual_address_map, NULL, + NULL, &event); + if (ret != EFI_SUCCESS) + return ret; + return ret; +}