diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/efi_loader/Kconfig | 8 | ||||
-rw-r--r-- | lib/efi_loader/Makefile | 3 | ||||
-rw-r--r-- | lib/efi_loader/efi_bootmgr.c | 28 | ||||
-rw-r--r-- | lib/efi_loader/efi_boottime.c | 12 | ||||
-rw-r--r-- | lib/efi_loader/efi_image_loader.c | 164 | ||||
-rw-r--r-- | lib/efi_loader/efi_runtime.c | 38 | ||||
-rw-r--r-- | lib/efi_loader/efi_setup.c | 59 | ||||
-rw-r--r-- | lib/efi_loader/efi_signature.c | 435 | ||||
-rw-r--r-- | lib/efi_loader/efi_var_common.c | 140 | ||||
-rw-r--r-- | lib/efi_loader/efi_var_file.c | 239 | ||||
-rw-r--r-- | lib/efi_loader/efi_var_mem.c | 266 | ||||
-rw-r--r-- | lib/efi_loader/efi_variable.c | 830 | ||||
-rw-r--r-- | lib/efi_loader/efi_variable_tee.c | 138 | ||||
-rw-r--r-- | lib/efi_selftest/efi_selftest_variables_runtime.c | 13 | ||||
-rw-r--r-- | lib/rsa/rsa-verify.c | 8 |
15 files changed, 1302 insertions, 1079 deletions
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 6c9df3a767..4324694d48 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -27,6 +27,14 @@ config EFI_LOADER if EFI_LOADER +config EFI_VARIABLE_FILE_STORE + bool "Store non-volatile UEFI variables as file" + depends on FAT_WRITE + default y + help + Select tis option if you want non-volatile UEFI variables to be stored + as file /ubootefi.var on the EFI system partition. + config EFI_GET_TIME bool "GetTime() runtime service" depends on DM_RTC diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 57c7e66ea0..f81ec8d277 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -35,10 +35,13 @@ obj-y += efi_root_node.o obj-y += efi_runtime.o obj-y += efi_setup.o obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += efi_unicode_collation.o +obj-y += efi_var_common.o +obj-y += efi_var_mem.o ifeq ($(CONFIG_EFI_MM_COMM_TEE),y) obj-y += efi_variable_tee.o else obj-y += efi_variable.o +obj-y += efi_var_file.o endif obj-y += efi_watchdog.o obj-$(CONFIG_LCD) += efi_gop.o diff --git a/lib/efi_loader/efi_bootmgr.c b/lib/efi_loader/efi_bootmgr.c index e268e9c4b8..e03198b57a 100644 --- a/lib/efi_loader/efi_bootmgr.c +++ b/lib/efi_loader/efi_bootmgr.c @@ -12,6 +12,7 @@ #include <log.h> #include <malloc.h> #include <efi_loader.h> +#include <efi_variable.h> #include <asm/unaligned.h> static const struct efi_boot_services *bs; @@ -147,15 +148,14 @@ unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data) static void *get_var(u16 *name, const efi_guid_t *vendor, efi_uintn_t *size) { - efi_guid_t *v = (efi_guid_t *)vendor; efi_status_t ret; void *buf = NULL; *size = 0; - EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf)); + ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL); if (ret == EFI_BUFFER_TOO_SMALL) { buf = malloc(*size); - EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf)); + ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL); } if (ret != EFI_SUCCESS) { @@ -219,10 +219,9 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle) attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; size = sizeof(n); - ret = EFI_CALL(efi_set_variable( - L"BootCurrent", - (efi_guid_t *)&efi_global_variable_guid, - attributes, size, &n)); + ret = efi_set_variable_int(L"BootCurrent", + &efi_global_variable_guid, + attributes, size, &n, false); if (ret != EFI_SUCCESS) { if (EFI_CALL(efi_unload_image(*handle)) != EFI_SUCCESS) @@ -262,22 +261,19 @@ efi_status_t efi_bootmgr_load(efi_handle_t *handle) rs = systab.runtime; /* BootNext */ - bootnext = 0; size = sizeof(bootnext); - ret = EFI_CALL(efi_get_variable(L"BootNext", - (efi_guid_t *)&efi_global_variable_guid, - NULL, &size, &bootnext)); + ret = efi_get_variable_int(L"BootNext", + &efi_global_variable_guid, + NULL, &size, &bootnext, NULL); if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) { /* BootNext does exist here */ if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16)) log_err("BootNext must be 16-bit integer\n"); /* delete BootNext */ - ret = EFI_CALL(efi_set_variable( - L"BootNext", - (efi_guid_t *)&efi_global_variable_guid, - EFI_VARIABLE_NON_VOLATILE, 0, - &bootnext)); + ret = efi_set_variable_int(L"BootNext", + &efi_global_variable_guid, + 0, 0, NULL, false); /* load BootNext */ if (ret == EFI_SUCCESS) { diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 1591ad8300..0b16554ba2 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -3459,6 +3459,8 @@ static efi_status_t efi_get_child_controllers( * the buffer will be too large. But that does not harm. */ *number_of_children = 0; + if (!count) + return EFI_SUCCESS; *child_handle_buffer = calloc(count, sizeof(efi_handle_t)); if (!*child_handle_buffer) return EFI_OUT_OF_RESOURCES; @@ -3536,10 +3538,12 @@ static efi_status_t EFIAPI efi_disconnect_controller( number_of_children = 1; child_handle_buffer = &child_handle; } else { - efi_get_child_controllers(efiobj, - driver_image_handle, - &number_of_children, - &child_handle_buffer); + r = efi_get_child_controllers(efiobj, + driver_image_handle, + &number_of_children, + &child_handle_buffer); + if (r != EFI_SUCCESS) + return r; } /* Get the driver binding protocol */ diff --git a/lib/efi_loader/efi_image_loader.c b/lib/efi_loader/efi_image_loader.c index 06a2ebdb90..fef0bb870c 100644 --- a/lib/efi_loader/efi_image_loader.c +++ b/lib/efi_loader/efi_image_loader.c @@ -267,6 +267,8 @@ bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp, dos = (void *)efi; nt = (void *)(efi + dos->e_lfanew); + authoff = 0; + authsz = 0; /* * Count maximum number of regions to be digested. @@ -305,25 +307,36 @@ bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp, efi_image_region_add(regs, &opt->DataDirectory[ctidx] + 1, efi + opt->SizeOfHeaders, 0); + + authoff = opt->DataDirectory[ctidx].VirtualAddress; + authsz = opt->DataDirectory[ctidx].Size; } bytes_hashed = opt->SizeOfHeaders; align = opt->FileAlignment; - authoff = opt->DataDirectory[ctidx].VirtualAddress; - authsz = opt->DataDirectory[ctidx].Size; } else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader; + /* Skip CheckSum */ efi_image_region_add(regs, efi, &opt->CheckSum, 0); - efi_image_region_add(regs, &opt->Subsystem, - &opt->DataDirectory[ctidx], 0); - efi_image_region_add(regs, &opt->DataDirectory[ctidx] + 1, - efi + opt->SizeOfHeaders, 0); + if (nt->OptionalHeader.NumberOfRvaAndSizes <= ctidx) { + efi_image_region_add(regs, + &opt->Subsystem, + efi + opt->SizeOfHeaders, 0); + } else { + /* Skip Certificates Table */ + efi_image_region_add(regs, &opt->Subsystem, + &opt->DataDirectory[ctidx], 0); + efi_image_region_add(regs, + &opt->DataDirectory[ctidx] + 1, + efi + opt->SizeOfHeaders, 0); + + authoff = opt->DataDirectory[ctidx].VirtualAddress; + authsz = opt->DataDirectory[ctidx].Size; + } bytes_hashed = opt->SizeOfHeaders; align = opt->FileAlignment; - authoff = opt->DataDirectory[ctidx].VirtualAddress; - authsz = opt->DataDirectory[ctidx].Size; } else { EFI_PRINT("%s: Invalid optional header magic %x\n", __func__, nt->OptionalHeader.Magic); @@ -369,7 +382,7 @@ bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp, /* 3. Extra data excluding Certificates Table */ if (bytes_hashed + authsz < len) { - EFI_PRINT("extra data for hash: %lu\n", + EFI_PRINT("extra data for hash: %zu\n", len - (bytes_hashed + authsz)); efi_image_region_add(regs, efi + bytes_hashed, efi + len - authsz, 0); @@ -435,16 +448,16 @@ static bool efi_image_unsigned_authenticate(struct efi_image_regions *regs) } /* try black-list first */ - if (efi_signature_verify_with_sigdb(regs, NULL, dbx, NULL)) { - EFI_PRINT("Image is not signed and rejected by \"dbx\"\n"); + if (efi_signature_lookup_digest(regs, dbx)) { + EFI_PRINT("Image is not signed and its digest found in \"dbx\"\n"); goto out; } /* try white-list */ - if (efi_signature_verify_with_sigdb(regs, NULL, db, NULL)) + if (efi_signature_lookup_digest(regs, db)) ret = true; else - EFI_PRINT("Image is not signed and not found in \"db\" or \"dbx\"\n"); + EFI_PRINT("Image is not signed and its digest not found in \"db\" or \"dbx\"\n"); out: efi_sigstore_free(db); @@ -481,11 +494,13 @@ static bool efi_image_authenticate(void *efi, size_t efi_size) size_t wincerts_len; struct pkcs7_message *msg = NULL; struct efi_signature_store *db = NULL, *dbx = NULL; - struct x509_certificate *cert = NULL; void *new_efi = NULL; - size_t new_efi_size; + u8 *auth, *wincerts_end; + size_t new_efi_size, auth_size; bool ret = false; + debug("%s: Enter, %d\n", __func__, ret); + if (!efi_secure_boot_enabled()) return true; @@ -531,61 +546,122 @@ static bool efi_image_authenticate(void *efi, size_t efi_size) goto err; } - /* go through WIN_CERTIFICATE list */ - for (wincert = wincerts; - (void *)wincert < (void *)wincerts + wincerts_len; - wincert = (void *)wincert + ALIGN(wincert->dwLength, 8)) { - if (wincert->dwLength < sizeof(*wincert)) { - EFI_PRINT("%s: dwLength too small: %u < %zu\n", - __func__, wincert->dwLength, - sizeof(*wincert)); - goto err; + /* + * go through WIN_CERTIFICATE list + * NOTE: + * We may have multiple signatures either as WIN_CERTIFICATE's + * in PE header, or as pkcs7 SignerInfo's in SignedData. + * So the verification policy here is: + * - Success if, at least, one of signatures is verified + * - unless + * any of signatures is rejected explicitly, or + * none of digest algorithms are supported + */ + for (wincert = wincerts, wincerts_end = (u8 *)wincerts + wincerts_len; + (u8 *)wincert < wincerts_end; + wincert = (WIN_CERTIFICATE *) + ((u8 *)wincert + ALIGN(wincert->dwLength, 8))) { + if ((u8 *)wincert + sizeof(*wincert) >= wincerts_end) + break; + + if (wincert->dwLength <= sizeof(*wincert)) { + EFI_PRINT("dwLength too small: %u < %zu\n", + wincert->dwLength, sizeof(*wincert)); + continue; } - msg = pkcs7_parse_message((void *)wincert + sizeof(*wincert), - wincert->dwLength - sizeof(*wincert)); + + EFI_PRINT("WIN_CERTIFICATE_TYPE: 0x%x\n", + wincert->wCertificateType); + + auth = (u8 *)wincert + sizeof(*wincert); + auth_size = wincert->dwLength - sizeof(*wincert); + if (wincert->wCertificateType == WIN_CERT_TYPE_EFI_GUID) { + if (auth + sizeof(efi_guid_t) >= wincerts_end) + break; + + if (auth_size <= sizeof(efi_guid_t)) { + EFI_PRINT("dwLength too small: %u < %zu\n", + wincert->dwLength, sizeof(*wincert)); + continue; + } + if (guidcmp(auth, &efi_guid_cert_type_pkcs7)) { + EFI_PRINT("Certificate type not supported: %pUl\n", + auth); + continue; + } + + auth += sizeof(efi_guid_t); + auth_size -= sizeof(efi_guid_t); + } else if (wincert->wCertificateType + != WIN_CERT_TYPE_PKCS_SIGNED_DATA) { + EFI_PRINT("Certificate type not supported\n"); + continue; + } + + msg = pkcs7_parse_message(auth, auth_size); if (IS_ERR(msg)) { EFI_PRINT("Parsing image's signature failed\n"); msg = NULL; - goto err; + continue; } + /* + * NOTE: + * UEFI specification defines two signature types possible + * in signature database: + * a. x509 certificate, where a signature in image is + * a message digest encrypted by RSA public key + * (EFI_CERT_X509_GUID) + * b. bare hash value of message digest + * (EFI_CERT_SHAxxx_GUID) + * + * efi_signature_verify() handles case (a), while + * efi_signature_lookup_digest() handles case (b). + * + * There is a third type: + * c. message digest of a certificate + * (EFI_CERT_X509_SHAAxxx_GUID) + * This type of signature is used only in revocation list + * (dbx) and handled as part of efi_signatgure_verify(). + */ /* try black-list first */ - if (efi_signature_verify_with_sigdb(regs, msg, dbx, NULL)) { + if (efi_signature_verify_one(regs, msg, dbx)) { EFI_PRINT("Signature was rejected by \"dbx\"\n"); goto err; } - if (!efi_signature_verify_signers(msg, dbx)) { - EFI_PRINT("Signer was rejected by \"dbx\"\n"); + if (!efi_signature_check_signers(msg, dbx)) { + EFI_PRINT("Signer(s) in \"dbx\"\n"); goto err; - } else { - ret = true; } - /* try white-list */ - if (!efi_signature_verify_with_sigdb(regs, msg, db, &cert)) { - EFI_PRINT("Verifying signature with \"db\" failed\n"); + if (efi_signature_lookup_digest(regs, dbx)) { + EFI_PRINT("Image's digest was found in \"dbx\"\n"); goto err; - } else { - ret = true; } - if (!efi_signature_verify_cert(cert, dbx)) { - EFI_PRINT("Certificate was rejected by \"dbx\"\n"); - goto err; - } else { - ret = true; - } + /* try white-list */ + if (efi_signature_verify_with_sigdb(regs, msg, db, dbx)) + continue; + + debug("Signature was not verified by \"db\"\n"); + + if (efi_signature_lookup_digest(regs, db)) + continue; + + debug("Image's digest was not found in \"db\" or \"dbx\"\n"); + goto err; } + ret = true; err: - x509_free_certificate(cert); efi_sigstore_free(db); efi_sigstore_free(dbx); pkcs7_free_message(msg); free(regs); free(new_efi); + debug("%s: Exit, %d\n", __func__, ret); return ret; } #else diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index c0bd99b867..91a4551448 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -121,6 +121,8 @@ efi_status_t efi_init_runtime_supported(void) rt_table->version = EFI_RT_PROPERTIES_TABLE_VERSION; rt_table->length = sizeof(struct efi_rt_properties_table); rt_table->runtime_services_supported = + EFI_RT_SUPPORTED_GET_VARIABLE | + EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME | EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP | EFI_RT_SUPPORTED_CONVERT_POINTER; @@ -138,6 +140,25 @@ efi_status_t efi_init_runtime_supported(void) } /** + * efi_memcpy_runtime() - 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_memcpy_runtime(void *dest, const void *src, size_t n) +{ + u8 *d = dest; + const u8 *s = src; + + for (; n; --n) + *d++ = *s++; +} + +/** * efi_update_table_header_crc32() - Update crc32 in table header * * @table: EFI table @@ -496,15 +517,13 @@ static __efi_runtime efi_status_t EFIAPI efi_convert_pointer_runtime( * @address: pointer to be converted * Return: status code */ -static __efi_runtime efi_status_t EFIAPI efi_convert_pointer( - efi_uintn_t debug_disposition, void **address) +__efi_runtime efi_status_t EFIAPI +efi_convert_pointer(efi_uintn_t debug_disposition, void **address) { - efi_physical_addr_t addr = (uintptr_t)*address; + efi_physical_addr_t addr; efi_uintn_t i; efi_status_t ret = EFI_NOT_FOUND; - EFI_ENTRY("%zu %p", debug_disposition, address); - if (!efi_virtmap) { ret = EFI_UNSUPPORTED; goto out; @@ -514,7 +533,14 @@ static __efi_runtime efi_status_t EFIAPI efi_convert_pointer( ret = EFI_INVALID_PARAMETER; goto out; } + if (!*address) { + if (debug_disposition & EFI_OPTIONAL_PTR) + return EFI_SUCCESS; + else + return EFI_INVALID_PARAMETER; + } + addr = (uintptr_t)*address; for (i = 0; i < efi_descriptor_count; i++) { struct efi_mem_desc *map = (void *)efi_virtmap + (efi_descriptor_size * i); @@ -532,7 +558,7 @@ static __efi_runtime efi_status_t EFIAPI efi_convert_pointer( } out: - return EFI_EXIT(ret); + return ret; } static __efi_runtime void efi_relocate_runtime_table(ulong offset) diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index a3b05a4a9b..6196c0a06c 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -8,6 +8,7 @@ #include <common.h> #include <bootm.h> #include <efi_loader.h> +#include <efi_variable.h> #define OBJ_LIST_NOT_INITIALIZED 1 @@ -40,12 +41,13 @@ static efi_status_t efi_init_platform_lang(void) * Variable PlatformLangCodes defines the language codes that the * machine can support. */ - ret = EFI_CALL(efi_set_variable(L"PlatformLangCodes", - &efi_global_variable_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS, - sizeof(CONFIG_EFI_PLATFORM_LANG_CODES), - CONFIG_EFI_PLATFORM_LANG_CODES)); + ret = efi_set_variable_int(L"PlatformLangCodes", + &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_READ_ONLY, + sizeof(CONFIG_EFI_PLATFORM_LANG_CODES), + CONFIG_EFI_PLATFORM_LANG_CODES, false); if (ret != EFI_SUCCESS) goto out; @@ -53,9 +55,9 @@ static efi_status_t efi_init_platform_lang(void) * Variable PlatformLang defines the language that the machine has been * configured for. */ - ret = EFI_CALL(efi_get_variable(L"PlatformLang", - &efi_global_variable_guid, - NULL, &data_size, &pos)); + ret = efi_get_variable_int(L"PlatformLang", + &efi_global_variable_guid, + NULL, &data_size, &pos, NULL); if (ret == EFI_BUFFER_TOO_SMALL) { /* The variable is already set. Do not change it. */ ret = EFI_SUCCESS; @@ -70,12 +72,12 @@ static efi_status_t efi_init_platform_lang(void) if (pos) *pos = 0; - ret = EFI_CALL(efi_set_variable(L"PlatformLang", - &efi_global_variable_guid, - EFI_VARIABLE_NON_VOLATILE | - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS, - 1 + strlen(lang), lang)); + ret = efi_set_variable_int(L"PlatformLang", + &efi_global_variable_guid, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + 1 + strlen(lang), lang, false); out: if (ret != EFI_SUCCESS) printf("EFI: cannot initialize platform language settings\n"); @@ -96,13 +98,13 @@ static efi_status_t efi_init_secure_boot(void) }; efi_status_t ret; - /* TODO: read-only */ - ret = EFI_CALL(efi_set_variable(L"SignatureSupport", - &efi_global_variable_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS - | EFI_VARIABLE_RUNTIME_ACCESS, - sizeof(signature_types), - &signature_types)); + ret = efi_set_variable_int(L"SignatureSupport", + &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_READ_ONLY, + sizeof(signature_types), + &signature_types, false); if (ret != EFI_SUCCESS) printf("EFI: cannot initialize SignatureSupport variable\n"); @@ -160,12 +162,13 @@ efi_status_t efi_init_obj_list(void) goto out; /* Indicate supported features */ - ret = EFI_CALL(efi_set_variable(L"OsIndicationsSupported", - &efi_global_variable_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS, - sizeof(os_indications_supported), - &os_indications_supported)); + ret = efi_set_variable_int(L"OsIndicationsSupported", + &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_READ_ONLY, + sizeof(os_indications_supported), + &os_indications_supported, false); if (ret != EFI_SUCCESS) goto out; diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c index e05c471c61..fc0314e6d4 100644 --- a/lib/efi_loader/efi_signature.c +++ b/lib/efi_loader/efi_signature.c @@ -28,7 +28,8 @@ const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID; /** * efi_hash_regions - calculate a hash value - * @regs: List of regions + * @regs: Array of regions + * @count: Number of regions * @hash: Pointer to a pointer to buffer holding a hash value * @size: Size of buffer to be returned * @@ -36,18 +37,20 @@ const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID; * * Return: true on success, false on error */ -static bool efi_hash_regions(struct efi_image_regions *regs, void **hash, - size_t *size) +static bool efi_hash_regions(struct image_region *regs, int count, + void **hash, size_t *size) { - *size = 0; - *hash = calloc(1, SHA256_SUM_LEN); if (!*hash) { - EFI_PRINT("Out of memory\n"); - return false; + *hash = calloc(1, SHA256_SUM_LEN); + if (!*hash) { + EFI_PRINT("Out of memory\n"); + return false; + } } - *size = SHA256_SUM_LEN; + if (size) + *size = SHA256_SUM_LEN; - hash_calculate("sha256", regs->reg, regs->num, *hash); + hash_calculate("sha256", regs, count, *hash); #ifdef DEBUG EFI_PRINT("hash calculated:\n"); print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, @@ -72,26 +75,10 @@ static bool efi_hash_msg_content(struct pkcs7_message *msg, void **hash, { struct image_region regtmp; - *size = 0; - *hash = calloc(1, SHA256_SUM_LEN); - if (!*hash) { - EFI_PRINT("Out of memory\n"); - free(msg); - return false; - } - *size = SHA256_SUM_LEN; - regtmp.data = msg->data; regtmp.size = msg->data_len; - hash_calculate("sha256", ®tmp, 1, *hash); -#ifdef DEBUG - EFI_PRINT("hash calculated based on contentInfo:\n"); - print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, - *hash, SHA256_SUM_LEN, false); -#endif - - return true; + return efi_hash_regions(®tmp, 1, hash, size); } /** @@ -169,9 +156,10 @@ static bool efi_signature_verify(struct efi_image_regions *regs, false); #endif /* against contentInfo first */ + hash = NULL; if ((msg->data && efi_hash_msg_content(msg, &hash, &size)) || /* for signed image */ - efi_hash_regions(regs, &hash, &size)) { + efi_hash_regions(regs->reg, regs->num, &hash, &size)) { /* for authenticated variable */ if (ps_info->msgdigest_len != size || memcmp(hash, ps_info->msgdigest, size)) { @@ -210,55 +198,43 @@ out: } /** - * efi_signature_verify_with_list - verify a signature with signature list - * @regs: List of regions to be authenticated - * @msg: Signature - * @signed_info: Pointer to PKCS7's signed_info - * @siglist: Signature list for certificates - * @valid_cert: x509 certificate that verifies this signature + * efi_signature_lookup_digest - search for an image's digest in sigdb + * @regs: List of regions to be authenticated + * @db: Signature database for trusted certificates * - * Signature pointed to by @signed_info against image pointed to by @regs - * is verified by signature list pointed to by @siglist. - * Signature database is a simple concatenation of one or more - * signature list(s). + * A message digest of image pointed to by @regs is calculated and + * its hash value is compared to entries in signature database pointed + * to by @db. * - * Return: true if signature is verified, false if not + * Return: true if found, false if not */ -static -bool efi_signature_verify_with_list(struct efi_image_regions *regs, - struct pkcs7_message *msg, - struct pkcs7_signed_info *signed_info, - struct efi_signature_store *siglist, - struct x509_certificate **valid_cert) +bool efi_signature_lookup_digest(struct efi_image_regions *regs, + struct efi_signature_store *db) { - struct x509_certificate *cert; + struct efi_signature_store *siglist; struct efi_sig_data *sig_data; - bool verified = false; + void *hash = NULL; + size_t size = 0; + bool found = false; - EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__, - regs, signed_info, siglist, valid_cert); + EFI_PRINT("%s: Enter, %p, %p\n", __func__, regs, db); - if (!signed_info) { - void *hash; - size_t size; + if (!regs || !db || !db->sig_data_list) + goto out; - EFI_PRINT("%s: unsigned image\n", __func__); - /* - * verify based on calculated hash value - * TODO: support other hash algorithms - */ + for (siglist = db; siglist; siglist = siglist->next) { + /* TODO: support other hash algorithms */ if (guidcmp(&siglist->sig_type, &efi_guid_sha256)) { EFI_PRINT("Digest algorithm is not supported: %pUl\n", &siglist->sig_type); - goto out; + break; } - if (!efi_hash_regions(regs, &hash, &size)) { - EFI_PRINT("Digesting unsigned image failed\n"); - goto out; + if (!efi_hash_regions(regs->reg, regs->num, &hash, &size)) { + EFI_PRINT("Digesting an image failed\n"); + break; } - /* go through the list */ for (sig_data = siglist->sig_data_list; sig_data; sig_data = sig_data->next) { #ifdef DEBUG @@ -266,18 +242,52 @@ bool efi_signature_verify_with_list(struct efi_image_regions *regs, print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1, sig_data->data, sig_data->size, false); #endif - if ((sig_data->size == size) && + if (sig_data->size == size && !memcmp(sig_data->data, hash, size)) { - verified = true; + found = true; free(hash); goto out; } } + free(hash); - goto out; + hash = NULL; } - EFI_PRINT("%s: signed image\n", __func__); +out: + EFI_PRINT("%s: Exit, found: %d\n", __func__, found); + return found; +} + +/** + * efi_signature_verify_with_list - verify a signature with signature list + * @regs: List of regions to be authenticated + * @msg: Signature + * @signed_info: Pointer to PKCS7's signed_info + * @siglist: Signature list for certificates + * @valid_cert: x509 certificate that verifies this signature + * + * Signature pointed to by @signed_info against image pointed to by @regs + * is verified by signature list pointed to by @siglist. + * Signature database is a simple concatenation of one or more + * signature list(s). + * + * Return: true if signature is verified, false if not + */ +static +bool efi_signature_verify_with_list(struct efi_image_regions *regs, + struct pkcs7_message *msg, + struct pkcs7_signed_info *signed_info, + struct efi_signature_store *siglist, + struct x509_certificate **valid_cert) +{ + struct x509_certificate *cert; + struct efi_sig_data *sig_data; + bool verified = false; + + EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__, + regs, signed_info, siglist, valid_cert); + if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509)) { EFI_PRINT("Signature type is not supported: %pUl\n", &siglist->sig_type); @@ -313,211 +323,222 @@ out: } /** - * efi_signature_verify_with_sigdb - verify a signature with db - * @regs: List of regions to be authenticated - * @msg: Signature - * @db: Signature database for trusted certificates - * @cert: x509 certificate that verifies this signature + * efi_signature_check_revocation - check revocation with dbx + * @sinfo: Signer's info + * @cert: x509 certificate + * @dbx: Revocation signature database * - * Signature pointed to by @msg against image pointed to by @regs - * is verified by signature database pointed to by @db. + * Search revocation signature database pointed to by @dbx and find + * an entry matching to certificate pointed to by @cert. * - * Return: true if signature is verified, false if not + * While this entry contains revocation time, we don't support timestamp + * protocol at this time and any image will be unconditionally revoked + * when this match occurs. + * + * Return: true if check passed, false otherwise. */ -bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs, - struct pkcs7_message *msg, - struct efi_signature_store *db, - struct x509_certificate **cert) +static bool efi_signature_check_revocation(struct pkcs7_signed_info *sinfo, + struct x509_certificate *cert, + struct efi_signature_store *dbx) { - struct pkcs7_signed_info *info; struct efi_signature_store *siglist; - bool verified = false; - - EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__, regs, msg, db, cert); + struct efi_sig_data *sig_data; + struct image_region reg[1]; + void *hash = NULL; + size_t size = 0; + time64_t revoc_time; + bool revoked = false; - if (!db) - goto out; + EFI_PRINT("%s: Enter, %p, %p, %p\n", __func__, sinfo, cert, dbx); - if (!db->sig_data_list) + if (!sinfo || !cert || !dbx || !dbx->sig_data_list) goto out; - /* for unsigned image */ - if (!msg) { - EFI_PRINT("%s: Verify unsigned image with db\n", __func__); - for (siglist = db; siglist; siglist = siglist->next) - if (efi_signature_verify_with_list(regs, NULL, NULL, - siglist, cert)) { - verified = true; - goto out; - } - - goto out; - } + EFI_PRINT("Checking revocation against %s\n", cert->subject); + for (siglist = dbx; siglist; siglist = siglist->next) { + if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509_sha256)) + continue; - /* for signed image or variable */ - EFI_PRINT("%s: Verify signed image with db\n", __func__); - for (info = msg->signed_infos; info; info = info->next) { - EFI_PRINT("Signed Info: digest algo: %s, pkey algo: %s\n", - info->sig->hash_algo, info->sig->pkey_algo); + /* calculate hash of TBSCertificate */ + reg[0].data = cert->tbs; + reg[0].size = cert->tbs_size; + if (!efi_hash_regions(reg, 1, &hash, &size)) + goto out; - for (siglist = db; siglist; siglist = siglist->next) { - if (efi_signature_verify_with_list(regs, msg, info, - siglist, cert)) { - verified = true; - goto out; + for (sig_data = siglist->sig_data_list; sig_data; + sig_data = sig_data->next) { + /* + * struct efi_cert_x509_sha256 { + * u8 tbs_hash[256/8]; + * time64_t revocation_time; + * }; + */ +#ifdef DEBUG + if (sig_data->size >= size) { + EFI_PRINT("hash in db:\n"); + print_hex_dump(" ", DUMP_PREFIX_OFFSET, + 16, 1, + sig_data->data, size, false); } +#endif + if ((sig_data->size < size + sizeof(time64_t)) || + memcmp(sig_data->data, hash, size)) + continue; + + memcpy(&revoc_time, sig_data->data + size, + sizeof(revoc_time)); + EFI_PRINT("revocation time: 0x%llx\n", revoc_time); + /* + * TODO: compare signing timestamp in sinfo + * with revocation time + */ + + revoked = true; + free(hash); + goto out; } + free(hash); + hash = NULL; } - out: - EFI_PRINT("%s: Exit, verified: %d\n", __func__, verified); - return verified; + EFI_PRINT("%s: Exit, revoked: %d\n", __func__, revoked); + return !revoked; } /** - * efi_search_siglist - search signature list for a certificate - * @cert: x509 certificate - * @siglist: Signature list - * @revoc_time: Pointer to buffer for revocation time + * efi_signature_verify_one - verify signatures with database + * @regs: List of regions to be authenticated + * @msg: Signature + * @db: Signature database * - * Search signature list pointed to by @siglist and find a certificate - * pointed to by @cert. - * If found, revocation time that is specified in signature database is - * returned in @revoc_time. + * All the signature pointed to by @msg against image pointed to by @regs + * will be verified by signature database pointed to by @db. * - * Return: true if certificate is found, false if not + * Return: true if verification for one of signatures passed, false + * otherwise */ -static bool efi_search_siglist(struct x509_certificate *cert, - struct efi_signature_store *siglist, - time64_t *revoc_time) +bool efi_signature_verify_one(struct efi_image_regions *regs, + struct pkcs7_message *msg, + struct efi_signature_store *db) { - struct image_region reg[1]; - void *hash = NULL, *msg = NULL; - struct efi_sig_data *sig_data; - bool found = false; - - /* can be null */ - if (!siglist->sig_data_list) - return false; + struct pkcs7_signed_info *sinfo; + struct efi_signature_store *siglist; + struct x509_certificate *cert; + bool verified = false; - if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509_sha256)) { - /* TODO: other hash algos */ - EFI_PRINT("Certificate's digest type is not supported: %pUl\n", - &siglist->sig_type); - goto out; - } + EFI_PRINT("%s: Enter, %p, %p, %p\n", __func__, regs, msg, db); - /* calculate hash of TBSCertificate */ - msg = calloc(1, SHA256_SUM_LEN); - if (!msg) { - EFI_PRINT("Out of memory\n"); + if (!db) goto out; - } - hash = calloc(1, SHA256_SUM_LEN); - if (!hash) { - EFI_PRINT("Out of memory\n"); + if (!db->sig_data_list) goto out; - } - reg[0].data = cert->tbs; - reg[0].size = cert->tbs_size; - hash_calculate("sha256", reg, 1, msg); + EFI_PRINT("%s: Verify signed image with db\n", __func__); + for (sinfo = msg->signed_infos; sinfo; sinfo = sinfo->next) { + EFI_PRINT("Signed Info: digest algo: %s, pkey algo: %s\n", + sinfo->sig->hash_algo, sinfo->sig->pkey_algo); - /* go through signature list */ - for (sig_data = siglist->sig_data_list; sig_data; - sig_data = sig_data->next) { - /* - * struct efi_cert_x509_sha256 { - * u8 tbs_hash[256/8]; - * time64_t revocation_time; - * }; - */ - if ((sig_data->size == SHA256_SUM_LEN) && - !memcmp(sig_data->data, hash, SHA256_SUM_LEN)) { - memcpy(revoc_time, sig_data->data + SHA256_SUM_LEN, - sizeof(*revoc_time)); - found = true; - goto out; - } + for (siglist = db; siglist; siglist = siglist->next) + if (efi_signature_verify_with_list(regs, msg, sinfo, + siglist, &cert)) { + verified = true; + goto out; + } + EFI_PRINT("Valid certificate not in \"db\"\n"); } out: - free(hash); - free(msg); - - return found; + EFI_PRINT("%s: Exit, verified: %d\n", __func__, verified); + return verified; } /** - * efi_signature_verify_cert - verify a certificate with dbx - * @cert: x509 certificate - * @dbx: Signature database + * efi_signature_verify_with_sigdb - verify signatures with db and dbx + * @regs: List of regions to be authenticated + * @msg: Signature + * @db: Signature database for trusted certificates + * @dbx: Revocation signature database * - * Search signature database pointed to by @dbx and find a certificate - * pointed to by @cert. - * This function is expected to be used against "dbx". + * All the signature pointed to by @msg against image pointed to by @regs + * will be verified by signature database pointed to by @db and @dbx. * - * Return: true if a certificate is not rejected, false otherwise. + * Return: true if verification for all signatures passed, false otherwise */ -bool efi_signature_verify_cert(struct x509_certificate *cert, - struct efi_signature_store *dbx) +bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs, + struct pkcs7_message *msg, + struct efi_signature_store *db, + struct efi_signature_store *dbx) { + struct pkcs7_signed_info *info; struct efi_signature_store *siglist; - time64_t revoc_time; - bool found = false; + struct x509_certificate *cert; + bool verified = false; - EFI_PRINT("%s: Enter, %p, %p\n", __func__, dbx, cert); + EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__, regs, msg, db, dbx); - if (!cert) - return false; + if (!regs || !msg || !db || !db->sig_data_list) + goto out; - for (siglist = dbx; siglist; siglist = siglist->next) { - if (efi_search_siglist(cert, siglist, &revoc_time)) { - /* TODO */ - /* compare signing time with revocation time */ + for (info = msg->signed_infos; info; info = info->next) { + EFI_PRINT("Signed Info: digest algo: %s, pkey algo: %s\n", + info->sig->hash_algo, info->sig->pkey_algo); - found = true; - break; + for (siglist = db; siglist; siglist = siglist->next) { + if (efi_signature_verify_with_list(regs, msg, info, + siglist, &cert)) + break; + } + if (!siglist) { + EFI_PRINT("Valid certificate not in \"db\"\n"); + goto out; } + + if (!dbx || efi_signature_check_revocation(info, cert, dbx)) + continue; + + EFI_PRINT("Certificate in \"dbx\"\n"); + goto out; } + verified = true; - EFI_PRINT("%s: Exit, verified: %d\n", __func__, !found); - return !found; +out: + EFI_PRINT("%s: Exit, verified: %d\n", __func__, verified); + return verified; } /** - * efi_signature_verify_signers - verify signers' certificates with dbx + * efi_signature_check_signers - check revocation against all signers with dbx * @msg: Signature - * @dbx: Signature database + * @dbx: Revocation signature database * - * Determine if any of signers' certificates in @msg may be verified - * by any of certificates in signature database pointed to by @dbx. - * This function is expected to be used against "dbx". + * Determine if none of signers' certificates in @msg are revoked + * by signature database pointed to by @dbx. * - * Return: true if none of certificates is rejected, false otherwise. + * Return: true if all signers passed, false otherwise. */ -bool efi_signature_verify_signers(struct pkcs7_message *msg, - struct efi_signature_store *dbx) +bool efi_signature_check_signers(struct pkcs7_message *msg, + struct efi_signature_store *dbx) { - struct pkcs7_signed_info *info; - bool found = false; + struct pkcs7_signed_info *sinfo; + bool revoked = false; EFI_PRINT("%s: Enter, %p, %p\n", __func__, msg, dbx); - if (!msg) + if (!msg || !dbx) goto out; - for (info = msg->signed_infos; info; info = info->next) { - if (info->signer && - !efi_signature_verify_cert(info->signer, dbx)) { - found = true; - goto out; + for (sinfo = msg->signed_infos; sinfo; sinfo = sinfo->next) { + if (sinfo->signer && + !efi_signature_check_revocation(sinfo, sinfo->signer, + dbx)) { + revoked = true; + break; } } out: - EFI_PRINT("%s: Exit, verified: %d\n", __func__, !found); - return !found; + EFI_PRINT("%s: Exit, revoked: %d\n", __func__, revoked); + return !revoked; } /** diff --git a/lib/efi_loader/efi_var_common.c b/lib/efi_loader/efi_var_common.c new file mode 100644 index 0000000000..1e2be1135b --- /dev/null +++ b/lib/efi_loader/efi_var_common.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * UEFI runtime variable services + * + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <efi_loader.h> +#include <efi_variable.h> + +/** + * efi_efi_get_variable() - retrieve value of a UEFI variable + * + * This function implements the GetVariable runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @variable_name: name of the variable + * @vendor: vendor GUID + * @attributes: attributes of the variable + * @data_size: size of the buffer to which the variable value is copied + * @data: buffer to which the variable value is copied + * Return: status code + */ +efi_status_t EFIAPI efi_get_variable(u16 *variable_name, + const efi_guid_t *vendor, u32 *attributes, + efi_uintn_t *data_size, void *data) +{ + efi_status_t ret; + + EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes, + data_size, data); + + ret = efi_get_variable_int(variable_name, vendor, attributes, + data_size, data, NULL); + + /* Remove EFI_VARIABLE_READ_ONLY flag */ + if (attributes) + *attributes &= EFI_VARIABLE_MASK; + + return EFI_EXIT(ret); +} + +/** + * efi_set_variable() - set value of a UEFI variable + * + * This function implements the SetVariable runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @variable_name: name of the variable + * @vendor: vendor GUID + * @attributes: attributes of the variable + * @data_size: size of the buffer with the variable value + * @data: buffer with the variable value + * Return: status code + */ +efi_status_t EFIAPI efi_set_variable(u16 *variable_name, + const efi_guid_t *vendor, u32 attributes, + efi_uintn_t data_size, const void *data) +{ + efi_status_t ret; + + EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes, + data_size, data); + + /* Make sure that the EFI_VARIABLE_READ_ONLY flag is not set */ + if (attributes & ~(u32)EFI_VARIABLE_MASK) + ret = EFI_INVALID_PARAMETER; + else + ret = efi_set_variable_int(variable_name, vendor, attributes, + data_size, data, true); + + return EFI_EXIT(ret); +} + +/** + * efi_get_next_variable_name() - enumerate the current variable names + * + * @variable_name_size: size of variable_name buffer in byte + * @variable_name: name of uefi variable's name in u16 + * @vendor: vendor's guid + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: status code + */ +efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size, + u16 *variable_name, + efi_guid_t *vendor) +{ + efi_status_t ret; + + EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor); + + ret = efi_get_next_variable_name_int(variable_name_size, variable_name, + vendor); + + return EFI_EXIT(ret); +} + +/** + * efi_query_variable_info() - get information about EFI variables + * + * This function implements the QueryVariableInfo() runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @attributes: bitmask to select variables to be + * queried + * @maximum_variable_storage_size: maximum size of storage area for the + * selected variable types + * @remaining_variable_storage_size: remaining size of storage are for the + * selected variable types + * @maximum_variable_size: maximum size of a variable of the + * selected type + * Returns: status code + */ +efi_status_t EFIAPI efi_query_variable_info( + u32 attributes, u64 *maximum_variable_storage_size, + u64 *remaining_variable_storage_size, + u64 *maximum_variable_size) +{ + efi_status_t ret; + + EFI_ENTRY("%x %p %p %p", attributes, maximum_variable_storage_size, + remaining_variable_storage_size, maximum_variable_size); + + ret = efi_query_variable_info_int(attributes, + maximum_variable_storage_size, + remaining_variable_storage_size, + maximum_variable_size); + + return EFI_EXIT(ret); +} diff --git a/lib/efi_loader/efi_var_file.c b/lib/efi_loader/efi_var_file.c new file mode 100644 index 0000000000..880c279aef --- /dev/null +++ b/lib/efi_loader/efi_var_file.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * File interface for UEFI variables + * + * Copyright (c) 2020, Heinrich Schuchardt + */ + +#define LOG_CATEGORY LOGC_EFI + +#include <common.h> +#include <charset.h> +#include <fs.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <efi_loader.h> +#include <efi_variable.h> +#include <u-boot/crc.h> + +#define PART_STR_LEN 10 + +/** + * efi_set_blk_dev_to_system_partition() - select EFI system partition + * + * Set the EFI system partition as current block device. + * + * Return: status code + */ +static efi_status_t __maybe_unused efi_set_blk_dev_to_system_partition(void) +{ + char part_str[PART_STR_LEN]; + int r; + + if (!efi_system_partition.if_type) { + log_err("No EFI system partition\n"); + return EFI_DEVICE_ERROR; + } + snprintf(part_str, PART_STR_LEN, "%u:%u", + efi_system_partition.devnum, efi_system_partition.part); + r = fs_set_blk_dev(blk_get_if_type_name(efi_system_partition.if_type), + part_str, FS_TYPE_ANY); + if (r) { + log_err("Cannot read EFI system partition\n"); + return EFI_DEVICE_ERROR; + } + return EFI_SUCCESS; +} + +/** + * efi_var_collect() - collect non-volatile variables in buffer + * + * A buffer is allocated and filled with all non-volatile variables in a + * format ready to be written to disk. + * + * @bufp: pointer to pointer of buffer with collected variables + * @lenp: pointer to length of buffer + * Return: status code + */ +static efi_status_t __maybe_unused efi_var_collect(struct efi_var_file **bufp, + loff_t *lenp) +{ + size_t len = EFI_VAR_BUF_SIZE; + struct efi_var_file *buf; + struct efi_var_entry *var, *old_var; + size_t old_var_name_length = 2; + + *bufp = NULL; /* Avoid double free() */ + buf = calloc(1, len); + if (!buf) + return EFI_OUT_OF_RESOURCES; + var = buf->var; + old_var = var; + for (;;) { + efi_uintn_t data_length, var_name_length; + u8 *data; + efi_status_t ret; + + if ((uintptr_t)buf + len <= + (uintptr_t)var->name + old_var_name_length) + return EFI_BUFFER_TOO_SMALL; + + var_name_length = (uintptr_t)buf + len - (uintptr_t)var->name; + memcpy(var->name, old_var->name, old_var_name_length); + guidcpy(&var->guid, &old_var->guid); + ret = efi_get_next_variable_name_int( + &var_name_length, var->name, &var->guid); + if (ret == EFI_NOT_FOUND) + break; + if (ret != EFI_SUCCESS) { + free(buf); + return ret; + } + old_var_name_length = var_name_length; + old_var = var; + + data = (u8 *)var->name + old_var_name_length; + data_length = (uintptr_t)buf + len - (uintptr_t)data; + ret = efi_get_variable_int(var->name, &var->guid, + &var->attr, &data_length, data, + &var->time); + if (ret != EFI_SUCCESS) { + free(buf); + return ret; + } + if (!(var->attr & EFI_VARIABLE_NON_VOLATILE)) + continue; + var->length = data_length; + var = (struct efi_var_entry *) + ALIGN((uintptr_t)data + data_length, 8); + } + + buf->reserved = 0; + buf->magic = EFI_VAR_FILE_MAGIC; + len = (uintptr_t)var - (uintptr_t)buf; + buf->crc32 = crc32(0, (u8 *)buf->var, + len - sizeof(struct efi_var_file)); + buf->length = len; + *bufp = buf; + *lenp = len; + + return EFI_SUCCESS; +} + +/** + * efi_var_to_file() - save non-volatile variables as file + * + * File ubootefi.var is created on the EFI system partion. + * + * Return: status code + */ +efi_status_t efi_var_to_file(void) +{ +#ifdef CONFIG_EFI_VARIABLE_FILE_STORE + efi_status_t ret; + struct efi_var_file *buf; + loff_t len; + loff_t actlen; + int r; + + ret = efi_var_collect(&buf, &len); + if (ret != EFI_SUCCESS) + goto error; + + ret = efi_set_blk_dev_to_system_partition(); + if (ret != EFI_SUCCESS) + goto error; + + r = fs_write(EFI_VAR_FILE_NAME, map_to_sysmem(buf), 0, len, &actlen); + if (r || len != actlen) + ret = EFI_DEVICE_ERROR; + +error: + if (ret != EFI_SUCCESS) + log_err("Failed to persist EFI variables\n"); + free(buf); + return ret; +#else + return EFI_SUCCESS; +#endif +} + +/** + * efi_var_restore() - restore EFI variables from buffer + * + * @buf: buffer + * Return: status code + */ +static efi_status_t __maybe_unused efi_var_restore(struct efi_var_file *buf) +{ + struct efi_var_entry *var, *last_var; + efi_status_t ret; + + if (buf->reserved || buf->magic != EFI_VAR_FILE_MAGIC || + buf->crc32 != crc32(0, (u8 *)buf->var, + buf->length - sizeof(struct efi_var_file))) { + log_err("Invalid EFI variables file\n"); + return EFI_INVALID_PARAMETER; + } + + var = buf->var; + last_var = (struct efi_var_entry *)((u8 *)buf + buf->length); + while (var < last_var) { + u16 *data = var->name + u16_strlen(var->name) + 1; + + if (var->attr & EFI_VARIABLE_NON_VOLATILE && var->length) { + ret = efi_var_mem_ins(var->name, &var->guid, var->attr, + var->length, data, 0, NULL, + var->time); + if (ret != EFI_SUCCESS) + log_err("Failed to set EFI variable %ls\n", + var->name); + } + var = (struct efi_var_entry *) + ALIGN((uintptr_t)data + var->length, 8); + } + return EFI_SUCCESS; +} + +/** + * efi_var_from_file() - read variables from file + * + * File ubootefi.var is read from the EFI system partitions and the variables + * stored in the file are created. + * + * In case the file does not exist yet or a variable cannot be set EFI_SUCCESS + * is returned. + * + * Return: status code + */ +efi_status_t efi_var_from_file(void) +{ +#ifdef CONFIG_EFI_VARIABLE_FILE_STORE + struct efi_var_file *buf; + loff_t len; + efi_status_t ret; + int r; + + buf = calloc(1, EFI_VAR_BUF_SIZE); + if (!buf) { + log_err("Out of memory\n"); + return EFI_OUT_OF_RESOURCES; + } + + ret = efi_set_blk_dev_to_system_partition(); + if (ret != EFI_SUCCESS) + goto error; + r = fs_read(EFI_VAR_FILE_NAME, map_to_sysmem(buf), 0, EFI_VAR_BUF_SIZE, + &len); + if (r || len < sizeof(struct efi_var_file)) { + log_err("Failed to load EFI variables\n"); + goto error; + } + if (buf->length != len || efi_var_restore(buf) != EFI_SUCCESS) + log_err("Invalid EFI variables file\n"); +error: + free(buf); +#endif + return EFI_SUCCESS; +} diff --git a/lib/efi_loader/efi_var_mem.c b/lib/efi_loader/efi_var_mem.c new file mode 100644 index 0000000000..ac55d8f8dc --- /dev/null +++ b/lib/efi_loader/efi_var_mem.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * File interface for UEFI variables + * + * Copyright (c) 2020, Heinrich Schuchardt + */ + +#include <common.h> +#include <efi_loader.h> +#include <efi_variable.h> +#include <u-boot/crc.h> + +static struct efi_var_file __efi_runtime_data *efi_var_buf; +static struct efi_var_entry __efi_runtime_data *efi_current_var; + +/** + * 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; +} + +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; +} + +void __efi_runtime efi_var_mem_del(struct efi_var_entry *var) +{ + u16 *data; + 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_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, + const u64 time) +{ + 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; + var->time = time; + + efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t)); + efi_memcpy_runtime(var->name, variable_name, + sizeof(u16) * var_name_len); + efi_memcpy_runtime(data, data1, size1); + efi_memcpy_runtime((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; +} + +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) +{ + EFI_ENTRY("%p, %p", event, context); + + /* Delete boot service only variables */ + efi_var_mem_bs_del(); + + EFI_EXIT(EFI_SUCCESS); +} + +/** + * 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_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; +} diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index efaba869ef..77282beee4 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -7,6 +7,7 @@ #include <common.h> #include <efi_loader.h> +#include <efi_variable.h> #include <env.h> #include <env_internal.h> #include <hexdump.h> @@ -15,7 +16,6 @@ #include <search.h> #include <uuid.h> #include <crypto/pkcs7_parser.h> -#include <linux/bitops.h> #include <linux/compat.h> #include <u-boot/crc.h> @@ -30,160 +30,6 @@ static bool efi_secure_boot; static enum efi_secure_mode efi_secure_mode; static u8 efi_vendor_keys; -#define READ_ONLY BIT(31) - -static efi_status_t efi_get_variable_common(u16 *variable_name, - const efi_guid_t *vendor, - u32 *attributes, - efi_uintn_t *data_size, void *data, - u64 *timep); - -static efi_status_t efi_set_variable_common(u16 *variable_name, - const efi_guid_t *vendor, - u32 attributes, - efi_uintn_t data_size, - const void *data, - bool ro_check); - -/* - * Mapping between EFI variables and u-boot variables: - * - * efi_$guid_$varname = {attributes}(type)value - * - * For example: - * - * efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_OsIndicationsSupported= - * "{ro,boot,run}(blob)0000000000000000" - * efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder= - * "(blob)00010000" - * - * The attributes are a comma separated list of these possible - * attributes: - * - * + ro - read-only - * + boot - boot-services access - * + run - runtime access - * - * NOTE: with current implementation, no variables are available after - * ExitBootServices, and all are persisted (if possible). - * - * If not specified, the attributes default to "{boot}". - * - * The required type is one of: - * - * + utf8 - raw utf8 string - * + blob - arbitrary length hex string - * - * Maybe a utf16 type would be useful to for a string value to be auto - * converted to utf16? - */ - -#define PREFIX_LEN (strlen("efi_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_")) - -/** - * efi_to_native() - convert the UEFI variable name and vendor GUID to U-Boot - * variable name - * - * The U-Boot variable name is a concatenation of prefix 'efi', the hexstring - * encoded vendor GUID, and the UTF-8 encoded UEFI variable name separated by - * underscores, e.g. 'efi_8be4df61-93ca-11d2-aa0d-00e098032b8c_BootOrder'. - * - * @native: pointer to pointer to U-Boot variable name - * @variable_name: UEFI variable name - * @vendor: vendor GUID - * Return: status code - */ -static efi_status_t efi_to_native(char **native, const u16 *variable_name, - const efi_guid_t *vendor) -{ - size_t len; - char *pos; - - len = PREFIX_LEN + utf16_utf8_strlen(variable_name) + 1; - *native = malloc(len); - if (!*native) - return EFI_OUT_OF_RESOURCES; - - pos = *native; - pos += sprintf(pos, "efi_%pUl_", vendor); - utf16_utf8_strcpy(&pos, variable_name); - - return EFI_SUCCESS; -} - -/** - * prefix() - skip over prefix - * - * Skip over a prefix string. - * - * @str: string with prefix - * @prefix: prefix string - * Return: string without prefix, or NULL if prefix not found - */ -static const char *prefix(const char *str, const char *prefix) -{ - size_t n = strlen(prefix); - if (!strncmp(prefix, str, n)) - return str + n; - return NULL; -} - -/** - * parse_attr() - decode attributes part of variable value - * - * Convert the string encoded attributes of a UEFI variable to a bit mask. - * TODO: Several attributes are not supported. - * - * @str: value of U-Boot variable - * @attrp: pointer to UEFI attributes - * @timep: pointer to time attribute - * Return: pointer to remainder of U-Boot variable value - */ -static const char *parse_attr(const char *str, u32 *attrp, u64 *timep) -{ - u32 attr = 0; - char sep = '{'; - - if (*str != '{') { - *attrp = EFI_VARIABLE_BOOTSERVICE_ACCESS; - return str; - } - - while (*str == sep) { - const char *s; - - str++; - - if ((s = prefix(str, "ro"))) { - attr |= READ_ONLY; - } else if ((s = prefix(str, "nv"))) { - attr |= EFI_VARIABLE_NON_VOLATILE; - } else if ((s = prefix(str, "boot"))) { - attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS; - } else if ((s = prefix(str, "run"))) { - attr |= EFI_VARIABLE_RUNTIME_ACCESS; - } else if ((s = prefix(str, "time="))) { - attr |= EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; - hex2bin((u8 *)timep, s, sizeof(*timep)); - s += sizeof(*timep) * 2; - } else if (*str == '}') { - break; - } else { - printf("invalid attribute: %s\n", str); - break; - } - - str = s; - sep = ','; - } - - str++; - - *attrp = attr; - - return str; -} - /** * efi_set_secure_state - modify secure boot state variables * @secure_boot: value of SecureBoot @@ -198,34 +44,40 @@ static const char *parse_attr(const char *str, u32 *attrp, u64 *timep) static efi_status_t efi_set_secure_state(u8 secure_boot, u8 setup_mode, u8 audit_mode, u8 deployed_mode) { - u32 attributes; efi_status_t ret; + const u32 attributes_ro = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_READ_ONLY; + const u32 attributes_rw = EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS; + + efi_secure_boot = secure_boot; - attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS | - READ_ONLY; - ret = efi_set_variable_common(L"SecureBoot", &efi_global_variable_guid, - attributes, sizeof(secure_boot), - &secure_boot, false); + ret = efi_set_variable_int(L"SecureBoot", &efi_global_variable_guid, + attributes_ro, sizeof(secure_boot), + &secure_boot, false); if (ret != EFI_SUCCESS) goto err; - ret = efi_set_variable_common(L"SetupMode", &efi_global_variable_guid, - attributes, sizeof(setup_mode), - &setup_mode, false); + ret = efi_set_variable_int(L"SetupMode", &efi_global_variable_guid, + attributes_ro, sizeof(setup_mode), + &setup_mode, false); if (ret != EFI_SUCCESS) goto err; - ret = efi_set_variable_common(L"AuditMode", &efi_global_variable_guid, - attributes, sizeof(audit_mode), - &audit_mode, false); + ret = efi_set_variable_int(L"AuditMode", &efi_global_variable_guid, + audit_mode || setup_mode ? + attributes_ro : attributes_rw, + sizeof(audit_mode), &audit_mode, false); if (ret != EFI_SUCCESS) goto err; - ret = efi_set_variable_common(L"DeployedMode", - &efi_global_variable_guid, attributes, - sizeof(deployed_mode), &deployed_mode, - false); + ret = efi_set_variable_int(L"DeployedMode", + &efi_global_variable_guid, + audit_mode || deployed_mode || setup_mode ? + attributes_ro : attributes_rw, + sizeof(deployed_mode), &deployed_mode, + false); err: return ret; } @@ -235,7 +87,7 @@ err: * @mode: new state * * Depending on @mode, secure boot related variables are updated. - * Those variables are *read-only* for users, efi_set_variable_common() + * Those variables are *read-only* for users, efi_set_variable_int() * is called here. * * Return: status code @@ -251,27 +103,21 @@ static efi_status_t efi_transfer_secure_state(enum efi_secure_mode mode) ret = efi_set_secure_state(1, 0, 0, 1); if (ret != EFI_SUCCESS) goto err; - - efi_secure_boot = true; } else if (mode == EFI_MODE_AUDIT) { - ret = efi_set_variable_common(L"PK", &efi_global_variable_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS, - 0, NULL, false); + ret = efi_set_variable_int(L"PK", &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + 0, NULL, false); if (ret != EFI_SUCCESS) goto err; ret = efi_set_secure_state(0, 1, 1, 0); if (ret != EFI_SUCCESS) goto err; - - efi_secure_boot = true; } else if (mode == EFI_MODE_USER) { ret = efi_set_secure_state(1, 0, 0, 0); if (ret != EFI_SUCCESS) goto err; - - efi_secure_boot = true; } else if (mode == EFI_MODE_SETUP) { ret = efi_set_secure_state(0, 1, 0, 0); if (ret != EFI_SUCCESS) @@ -297,45 +143,29 @@ err: */ static efi_status_t efi_init_secure_state(void) { - enum efi_secure_mode mode; - efi_uintn_t size; + enum efi_secure_mode mode = EFI_MODE_SETUP; + efi_uintn_t size = 0; efi_status_t ret; - /* - * TODO: - * Since there is currently no "platform-specific" installation - * method of Platform Key, we can't say if VendorKeys is 0 or 1 - * precisely. - */ - - size = 0; - ret = efi_get_variable_common(L"PK", &efi_global_variable_guid, - NULL, &size, NULL, NULL); + ret = efi_get_variable_int(L"PK", &efi_global_variable_guid, + NULL, &size, NULL, NULL); if (ret == EFI_BUFFER_TOO_SMALL) { if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT)) mode = EFI_MODE_USER; - else - mode = EFI_MODE_SETUP; - - efi_vendor_keys = 0; - } else if (ret == EFI_NOT_FOUND) { - mode = EFI_MODE_SETUP; - efi_vendor_keys = 1; - } else { - goto err; } ret = efi_transfer_secure_state(mode); - if (ret == EFI_SUCCESS) - ret = efi_set_variable_common(L"VendorKeys", - &efi_global_variable_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS | - READ_ONLY, - sizeof(efi_vendor_keys), - &efi_vendor_keys, false); + if (ret != EFI_SUCCESS) + return ret; -err: + /* As we do not provide vendor keys this variable is always 0. */ + ret = efi_set_variable_int(L"VendorKeys", + &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_READ_ONLY, + sizeof(efi_vendor_keys), + &efi_vendor_keys, false); return ret; } @@ -599,346 +429,115 @@ static efi_status_t efi_variable_authenticate(u16 *variable, } #endif /* CONFIG_EFI_SECURE_BOOT */ -static efi_status_t efi_get_variable_common(u16 *variable_name, - const efi_guid_t *vendor, - u32 *attributes, - efi_uintn_t *data_size, void *data, - u64 *timep) +efi_status_t __efi_runtime +efi_get_variable_int(u16 *variable_name, const efi_guid_t *vendor, + u32 *attributes, efi_uintn_t *data_size, void *data, + u64 *timep) { - char *native_name; - efi_status_t ret; - unsigned long in_size; - const char *val = NULL, *s; - u64 time = 0; - u32 attr; + efi_uintn_t old_size; + struct efi_var_entry *var; + u16 *pdata; if (!variable_name || !vendor || !data_size) return EFI_INVALID_PARAMETER; - - ret = efi_to_native(&native_name, variable_name, vendor); - if (ret) - return ret; - - EFI_PRINT("get '%s'\n", native_name); - - val = env_get(native_name); - free(native_name); - if (!val) + var = efi_var_mem_find(vendor, variable_name, NULL); + if (!var) return EFI_NOT_FOUND; - val = parse_attr(val, &attr, &time); - + if (attributes) + *attributes = var->attr; if (timep) - *timep = time; - - in_size = *data_size; - - if ((s = prefix(val, "(blob)"))) { - size_t len = strlen(s); - - /* number of hexadecimal digits must be even */ - if (len & 1) - return EFI_DEVICE_ERROR; - - /* two characters per byte: */ - len /= 2; - *data_size = len; - - if (in_size < len) { - ret = EFI_BUFFER_TOO_SMALL; - goto out; - } + *timep = var->time; - if (!data) { - EFI_PRINT("Variable with no data shouldn't exist.\n"); - return EFI_INVALID_PARAMETER; - } - - if (hex2bin(data, s, len)) - return EFI_DEVICE_ERROR; - - EFI_PRINT("got value: \"%s\"\n", s); - } else if ((s = prefix(val, "(utf8)"))) { - unsigned len = strlen(s) + 1; - - *data_size = len; - - if (in_size < len) { - ret = EFI_BUFFER_TOO_SMALL; - goto out; - } + old_size = *data_size; + *data_size = var->length; + if (old_size < var->length) + return EFI_BUFFER_TOO_SMALL; - if (!data) { - EFI_PRINT("Variable with no data shouldn't exist.\n"); - return EFI_INVALID_PARAMETER; - } + if (!data) + return EFI_INVALID_PARAMETER; - memcpy(data, s, len); - ((char *)data)[len] = '\0'; + for (pdata = var->name; *pdata; ++pdata) + ; + ++pdata; - EFI_PRINT("got value: \"%s\"\n", (char *)data); - } else { - EFI_PRINT("invalid value: '%s'\n", val); - return EFI_DEVICE_ERROR; - } + efi_memcpy_runtime(data, pdata, var->length); -out: - if (attributes) - *attributes = attr & EFI_VARIABLE_MASK; - - return ret; -} - -/** - * efi_efi_get_variable() - retrieve value of a UEFI variable - * - * This function implements the GetVariable runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @variable_name: name of the variable - * @vendor: vendor GUID - * @attributes: attributes of the variable - * @data_size: size of the buffer to which the variable value is copied - * @data: buffer to which the variable value is copied - * Return: status code - */ -efi_status_t EFIAPI efi_get_variable(u16 *variable_name, - const efi_guid_t *vendor, u32 *attributes, - efi_uintn_t *data_size, void *data) -{ - efi_status_t ret; - - EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes, - data_size, data); - - ret = efi_get_variable_common(variable_name, vendor, attributes, - data_size, data, NULL); - return EFI_EXIT(ret); + return EFI_SUCCESS; } -static char *efi_variables_list; -static char *efi_cur_variable; - -/** - * parse_uboot_variable() - parse a u-boot variable and get uefi-related - * information - * @variable: whole data of u-boot variable (ie. name=value) - * @variable_name_size: size of variable_name buffer in byte - * @variable_name: name of uefi variable in u16, null-terminated - * @vendor: vendor's guid - * @attributes: attributes - * - * A uefi variable is encoded into a u-boot variable as described above. - * This function parses such a u-boot variable and retrieve uefi-related - * information into respective parameters. In return, variable_name_size - * is the size of variable name including NULL. - * - * Return: EFI_SUCCESS if parsing is OK, EFI_NOT_FOUND when - * the entire variable list has been returned, - * otherwise non-zero status code - */ -static efi_status_t parse_uboot_variable(char *variable, - efi_uintn_t *variable_name_size, - u16 *variable_name, - const efi_guid_t *vendor, - u32 *attributes) +efi_status_t __efi_runtime +efi_get_next_variable_name_int(efi_uintn_t *variable_name_size, + u16 *variable_name, efi_guid_t *vendor) { - char *guid, *name, *end, c; - size_t name_len; - efi_uintn_t old_variable_name_size; - u64 time; - u16 *p; - - guid = strchr(variable, '_'); - if (!guid) - return EFI_INVALID_PARAMETER; - guid++; - name = strchr(guid, '_'); - if (!name) - return EFI_INVALID_PARAMETER; - name++; - end = strchr(name, '='); - if (!end) - return EFI_INVALID_PARAMETER; - - name_len = end - name; - old_variable_name_size = *variable_name_size; - *variable_name_size = sizeof(u16) * (name_len + 1); - if (old_variable_name_size < *variable_name_size) - return EFI_BUFFER_TOO_SMALL; - - end++; /* point to value */ - - /* variable name */ - p = variable_name; - utf8_utf16_strncpy(&p, name, name_len); - variable_name[name_len] = 0; + struct efi_var_entry *var; + efi_uintn_t old_size; + u16 *pdata; - /* guid */ - c = *(name - 1); - *(name - 1) = '\0'; /* guid need be null-terminated here */ - if (uuid_str_to_bin(guid, (unsigned char *)vendor, - UUID_STR_FORMAT_GUID)) - /* The only error would be EINVAL. */ + if (!variable_name_size || !variable_name || !vendor) return EFI_INVALID_PARAMETER; - *(name - 1) = c; - - /* attributes */ - parse_attr(end, attributes, &time); - return EFI_SUCCESS; -} - -/** - * efi_get_next_variable_name() - enumerate the current variable names - * - * @variable_name_size: size of variable_name buffer in byte - * @variable_name: name of uefi variable's name in u16 - * @vendor: vendor's guid - * - * This function implements the GetNextVariableName service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * Return: status code - */ -efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size, - u16 *variable_name, - efi_guid_t *vendor) -{ - char *native_name, *variable; - ssize_t name_len, list_len; - char regex[256]; - char * const regexlist[] = {regex}; - u32 attributes; - int i; - efi_status_t ret; + efi_var_mem_find(vendor, variable_name, &var); - EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor); + if (!var) + return EFI_NOT_FOUND; - if (!variable_name_size || !variable_name || !vendor) - return EFI_EXIT(EFI_INVALID_PARAMETER); - - if (variable_name[0]) { - /* check null-terminated string */ - for (i = 0; i < *variable_name_size; i++) - if (!variable_name[i]) - break; - if (i >= *variable_name_size) - return EFI_EXIT(EFI_INVALID_PARAMETER); - - /* search for the last-returned variable */ - ret = efi_to_native(&native_name, variable_name, vendor); - if (ret) - return EFI_EXIT(ret); - - name_len = strlen(native_name); - for (variable = efi_variables_list; variable && *variable;) { - if (!strncmp(variable, native_name, name_len) && - variable[name_len] == '=') - break; - - variable = strchr(variable, '\n'); - if (variable) - variable++; - } + for (pdata = var->name; *pdata; ++pdata) + ; + ++pdata; - free(native_name); - if (!(variable && *variable)) - return EFI_EXIT(EFI_INVALID_PARAMETER); + old_size = *variable_name_size; + *variable_name_size = (uintptr_t)pdata - (uintptr_t)var->name; - /* next variable */ - variable = strchr(variable, '\n'); - if (variable) - variable++; - if (!(variable && *variable)) - return EFI_EXIT(EFI_NOT_FOUND); - } else { - /* - *new search: free a list used in the previous search - */ - free(efi_variables_list); - efi_variables_list = NULL; - efi_cur_variable = NULL; - - snprintf(regex, 256, "efi_.*-.*-.*-.*-.*_.*"); - list_len = hexport_r(&env_htab, '\n', - H_MATCH_REGEX | H_MATCH_KEY, - &efi_variables_list, 0, 1, regexlist); - - if (list_len <= 1) - return EFI_EXIT(EFI_NOT_FOUND); - - variable = efi_variables_list; - } + if (old_size < *variable_name_size) + return EFI_BUFFER_TOO_SMALL; - ret = parse_uboot_variable(variable, variable_name_size, variable_name, - vendor, &attributes); + efi_memcpy_runtime(variable_name, var->name, *variable_name_size); + efi_memcpy_runtime(vendor, &var->guid, sizeof(efi_guid_t)); - return EFI_EXIT(ret); + return EFI_SUCCESS; } -static efi_status_t efi_set_variable_common(u16 *variable_name, - const efi_guid_t *vendor, - u32 attributes, - efi_uintn_t data_size, - const void *data, - bool ro_check) +efi_status_t efi_set_variable_int(u16 *variable_name, const efi_guid_t *vendor, + u32 attributes, efi_uintn_t data_size, + const void *data, bool ro_check) { - char *native_name = NULL, *old_data = NULL, *val = NULL, *s; - efi_uintn_t old_size; + struct efi_var_entry *var; + efi_uintn_t ret; bool append, delete; u64 time = 0; - u32 attr; - efi_status_t ret = EFI_SUCCESS; if (!variable_name || !*variable_name || !vendor || ((attributes & EFI_VARIABLE_RUNTIME_ACCESS) && - !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS))) { - ret = EFI_INVALID_PARAMETER; - goto err; - } - - ret = efi_to_native(&native_name, variable_name, vendor); - if (ret) - goto err; + !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS))) + return EFI_INVALID_PARAMETER; /* check if a variable exists */ - old_size = 0; - attr = 0; - ret = efi_get_variable_common(variable_name, vendor, &attr, - &old_size, NULL, &time); + var = efi_var_mem_find(vendor, variable_name, NULL); append = !!(attributes & EFI_VARIABLE_APPEND_WRITE); attributes &= ~(u32)EFI_VARIABLE_APPEND_WRITE; delete = !append && (!data_size || !attributes); /* check attributes */ - if (old_size) { - if (ro_check && (attr & READ_ONLY)) { - ret = EFI_WRITE_PROTECTED; - goto err; - } + if (var) { + if (ro_check && (var->attr & EFI_VARIABLE_READ_ONLY)) + return EFI_WRITE_PROTECTED; /* attributes won't be changed */ if (!delete && - ((ro_check && attr != attributes) || - (!ro_check && ((attr & ~(u32)READ_ONLY) - != (attributes & ~(u32)READ_ONLY))))) { - ret = EFI_INVALID_PARAMETER; - goto err; + ((ro_check && var->attr != attributes) || + (!ro_check && ((var->attr & ~(u32)EFI_VARIABLE_READ_ONLY) + != (attributes & ~(u32)EFI_VARIABLE_READ_ONLY))))) { + return EFI_INVALID_PARAMETER; } + time = var->time; } else { - if (delete || append) { + if (delete || append) /* * Trying to delete or to update a non-existent * variable. */ - ret = EFI_NOT_FOUND; - goto err; - } + return EFI_NOT_FOUND; } if (((!u16_strcmp(variable_name, L"PK") || @@ -950,27 +549,26 @@ static efi_status_t efi_set_variable_common(u16 *variable_name, /* authentication is mandatory */ if (!(attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) { - EFI_PRINT("%ls: AUTHENTICATED_WRITE_ACCESS required\n", + EFI_PRINT("%ls: TIME_BASED_AUTHENTICATED_WRITE_ACCESS required\n", variable_name); - ret = EFI_INVALID_PARAMETER; - goto err; + return EFI_INVALID_PARAMETER; } } /* authenticate a variable */ if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT)) { - if (attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) { - ret = EFI_INVALID_PARAMETER; - goto err; - } + if (attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) + return EFI_INVALID_PARAMETER; if (attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { + u32 env_attr; + ret = efi_variable_authenticate(variable_name, vendor, &data_size, &data, - attributes, &attr, + attributes, &env_attr, &time); if (ret != EFI_SUCCESS) - goto err; + return ret; /* last chance to check for delete */ if (!data_size) @@ -981,168 +579,65 @@ static efi_status_t efi_set_variable_common(u16 *variable_name, (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) { EFI_PRINT("Secure boot is not configured\n"); - ret = EFI_INVALID_PARAMETER; - goto err; + return EFI_INVALID_PARAMETER; } } - /* delete a variable */ + efi_var_mem_del(var); if (delete) { - /* !old_size case has been handled before */ - val = NULL; + /* EFI_NOT_FOUND has been handled before */ ret = EFI_SUCCESS; goto out; } if (append) { - old_data = malloc(old_size); - if (!old_data) { - ret = EFI_OUT_OF_RESOURCES; - goto err; - } - ret = efi_get_variable_common(variable_name, vendor, - &attr, &old_size, old_data, NULL); - if (ret != EFI_SUCCESS) - goto err; + u16 *old_data = var->name; + + for (; *old_data; ++old_data) + ; + ++old_data; + ret = efi_var_mem_ins(variable_name, vendor, attributes, + var->length, old_data, data_size, data, + time); } else { - old_size = 0; - } - - val = malloc(2 * old_size + 2 * data_size - + strlen("{ro,run,boot,nv,time=0123456701234567}(blob)") - + 1); - if (!val) { - ret = EFI_OUT_OF_RESOURCES; - goto err; + ret = efi_var_mem_ins(variable_name, vendor, attributes, + data_size, data, 0, NULL, time); } - s = val; - - /* - * store attributes - */ - attributes &= (READ_ONLY | - EFI_VARIABLE_NON_VOLATILE | - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS | - EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS); - s += sprintf(s, "{"); - while (attributes) { - attr = 1 << (ffs(attributes) - 1); - - if (attr == READ_ONLY) { - s += sprintf(s, "ro"); - } else if (attr == EFI_VARIABLE_NON_VOLATILE) { - s += sprintf(s, "nv"); - } else if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) { - s += sprintf(s, "boot"); - } else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) { - s += sprintf(s, "run"); - } else if (attr == - EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { - s += sprintf(s, "time="); - s = bin2hex(s, (u8 *)&time, sizeof(time)); - } - - attributes &= ~attr; - if (attributes) - s += sprintf(s, ","); - } - s += sprintf(s, "}"); - s += sprintf(s, "(blob)"); - - /* store payload: */ - if (append) - s = bin2hex(s, old_data, old_size); - s = bin2hex(s, data, data_size); - *s = '\0'; - - EFI_PRINT("setting: %s=%s\n", native_name, val); + if (ret != EFI_SUCCESS) + return ret; out: - if (env_set(native_name, val)) { - ret = EFI_DEVICE_ERROR; - } else { - bool vendor_keys_modified = false; - - if ((u16_strcmp(variable_name, L"PK") == 0 && - guidcmp(vendor, &efi_global_variable_guid) == 0)) { - ret = efi_transfer_secure_state( - (delete ? EFI_MODE_SETUP : - EFI_MODE_USER)); - if (ret != EFI_SUCCESS) - goto err; - - if (efi_secure_mode != EFI_MODE_SETUP) - vendor_keys_modified = true; - } else if ((u16_strcmp(variable_name, L"KEK") == 0 && - guidcmp(vendor, &efi_global_variable_guid) == 0)) { - if (efi_secure_mode != EFI_MODE_SETUP) - vendor_keys_modified = true; - } - - /* update VendorKeys */ - if (vendor_keys_modified & efi_vendor_keys) { - efi_vendor_keys = 0; - ret = efi_set_variable_common( - L"VendorKeys", - &efi_global_variable_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS - | EFI_VARIABLE_RUNTIME_ACCESS - | READ_ONLY, - sizeof(efi_vendor_keys), - &efi_vendor_keys, - false); - } else { - ret = EFI_SUCCESS; - } - } + if (!u16_strcmp(variable_name, L"PK")) + ret = efi_init_secure_state(); + else + ret = EFI_SUCCESS; -err: - free(native_name); - free(old_data); - free(val); + /* Write non-volatile EFI variables to file */ + if (attributes & EFI_VARIABLE_NON_VOLATILE && + ret == EFI_SUCCESS && efi_obj_list_initialized == EFI_SUCCESS) + efi_var_to_file(); - return ret; + return EFI_SUCCESS; } -/** - * efi_set_variable() - set value of a UEFI variable - * - * This function implements the SetVariable runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @variable_name: name of the variable - * @vendor: vendor GUID - * @attributes: attributes of the variable - * @data_size: size of the buffer with the variable value - * @data: buffer with the variable value - * Return: status code - */ -efi_status_t EFIAPI efi_set_variable(u16 *variable_name, - const efi_guid_t *vendor, u32 attributes, - efi_uintn_t data_size, const void *data) +efi_status_t efi_query_variable_info_int(u32 attributes, + u64 *maximum_variable_storage_size, + u64 *remaining_variable_storage_size, + u64 *maximum_variable_size) { - EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes, - data_size, data); - - /* READ_ONLY bit is not part of API */ - attributes &= ~(u32)READ_ONLY; - - return EFI_EXIT(efi_set_variable_common(variable_name, vendor, - attributes, data_size, data, - true)); + *maximum_variable_storage_size = EFI_VAR_BUF_SIZE - + sizeof(struct efi_var_file); + *remaining_variable_storage_size = efi_var_mem_free(); + *maximum_variable_size = EFI_VAR_BUF_SIZE - + sizeof(struct efi_var_file) - + sizeof(struct efi_var_entry); + return EFI_SUCCESS; } /** - * efi_query_variable_info() - get information about EFI variables - * - * This function implements the QueryVariableInfo() runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. + * efi_query_variable_info_runtime() - runtime implementation of + * QueryVariableInfo() * * @attributes: bitmask to select variables to be * queried @@ -1154,7 +649,7 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name, * selected type * Returns: status code */ -efi_status_t __efi_runtime EFIAPI efi_query_variable_info( +efi_status_t __efi_runtime EFIAPI efi_query_variable_info_runtime( u32 attributes, u64 *maximum_variable_storage_size, u64 *remaining_variable_storage_size, @@ -1177,7 +672,16 @@ static efi_status_t __efi_runtime EFIAPI efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *vendor, u32 *attributes, efi_uintn_t *data_size, void *data) { - return EFI_UNSUPPORTED; + efi_status_t ret; + + ret = efi_get_variable_int(variable_name, vendor, attributes, + data_size, data, NULL); + + /* Remove EFI_VARIABLE_READ_ONLY flag */ + if (attributes) + *attributes &= EFI_VARIABLE_MASK; + + return ret; } /** @@ -1193,7 +697,8 @@ static efi_status_t __efi_runtime EFIAPI efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size, u16 *variable_name, efi_guid_t *vendor) { - return EFI_UNSUPPORTED; + return efi_get_next_variable_name_int(variable_name_size, variable_name, + vendor); } /** @@ -1219,10 +724,13 @@ efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *vendor, */ void efi_variables_boot_exit_notify(void) { + /* Switch variable services functions to runtime version */ efi_runtime_services.get_variable = efi_get_variable_runtime; efi_runtime_services.get_next_variable_name = efi_get_next_variable_name_runtime; efi_runtime_services.set_variable = efi_set_variable_runtime; + efi_runtime_services.query_variable_info = + efi_query_variable_info_runtime; efi_update_table_header_crc32(&efi_runtime_services.hdr); } @@ -1235,7 +743,13 @@ efi_status_t efi_init_variables(void) { efi_status_t ret; + ret = efi_var_mem_init(); + if (ret != EFI_SUCCESS) + return ret; + ret = efi_init_secure_state(); + if (ret != EFI_SUCCESS) + return ret; - return ret; + return efi_var_from_file(); } diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index cacc76e23d..ff90aa8e81 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -10,6 +10,7 @@ #include <efi.h> #include <efi_api.h> #include <efi_loader.h> +#include <efi_variable.h> #include <tee.h> #include <malloc.h> #include <mm_communication.h> @@ -243,24 +244,9 @@ out: return ret; } -/** - * efi_get_variable() - retrieve value of a UEFI variable - * - * This function implements the GetVariable runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @name: name of the variable - * @guid: vendor GUID - * @attr: attributes of the variable - * @data_size: size of the buffer to which the variable value is copied - * @data: buffer to which the variable value is copied - * Return: status code - */ -efi_status_t EFIAPI efi_get_variable(u16 *name, const efi_guid_t *guid, - u32 *attr, efi_uintn_t *data_size, - void *data) +efi_status_t efi_get_variable_int(u16 *variable_name, const efi_guid_t *vendor, + u32 *attributes, efi_uintn_t *data_size, + void *data, u64 *timep) { struct smm_variable_access *var_acc; efi_uintn_t payload_size; @@ -269,15 +255,13 @@ efi_status_t EFIAPI efi_get_variable(u16 *name, const efi_guid_t *guid, u8 *comm_buf = NULL; efi_status_t ret; - EFI_ENTRY("\"%ls\" %pUl %p %p %p", name, guid, attr, data_size, data); - - if (!name || !guid || !data_size) { + if (!variable_name || !vendor || !data_size) { ret = EFI_INVALID_PARAMETER; goto out; } /* Check payload size */ - name_size = u16_strsize(name); + name_size = u16_strsize(variable_name); if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) { ret = EFI_INVALID_PARAMETER; goto out; @@ -300,11 +284,11 @@ efi_status_t EFIAPI efi_get_variable(u16 *name, const efi_guid_t *guid, goto out; /* Fill in contents */ - guidcpy(&var_acc->guid, guid); + guidcpy(&var_acc->guid, vendor); var_acc->data_size = tmp_dsize; var_acc->name_size = name_size; - var_acc->attr = attr ? *attr : 0; - memcpy(var_acc->name, name, name_size); + var_acc->attr = attributes ? *attributes : 0; + memcpy(var_acc->name, variable_name, name_size); /* Communicate */ ret = mm_communicate(comm_buf, payload_size); @@ -315,8 +299,8 @@ efi_status_t EFIAPI efi_get_variable(u16 *name, const efi_guid_t *guid, if (ret != EFI_SUCCESS) goto out; - if (attr) - *attr = var_acc->attr; + if (attributes) + *attributes = var_acc->attr; if (data) memcpy(data, (u8 *)var_acc->name + var_acc->name_size, var_acc->data_size); @@ -325,38 +309,21 @@ efi_status_t EFIAPI efi_get_variable(u16 *name, const efi_guid_t *guid, out: free(comm_buf); - return EFI_EXIT(ret); + return ret; } -/** - * efi_get_next_variable_name() - enumerate the current variable names - * - * @variable_name_size: size of variable_name buffer in bytes - * @variable_name: name of uefi variable's name in u16 - * @guid: vendor's guid - * - * This function implements the GetNextVariableName service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * Return: status code - */ -efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size, - u16 *variable_name, - efi_guid_t *guid) +efi_status_t efi_get_next_variable_name_int(efi_uintn_t *variable_name_size, + u16 *variable_name, + efi_guid_t *guid) { struct smm_variable_getnext *var_getnext; efi_uintn_t payload_size; efi_uintn_t out_name_size; efi_uintn_t in_name_size; efi_uintn_t tmp_dsize; - efi_uintn_t name_size; u8 *comm_buf = NULL; efi_status_t ret; - EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, guid); - if (!variable_name_size || !variable_name || !guid) { ret = EFI_INVALID_PARAMETER; goto out; @@ -370,19 +337,18 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size, goto out; } - name_size = u16_strsize(variable_name); - if (name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) { + if (in_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) { ret = EFI_INVALID_PARAMETER; goto out; } /* Trim output buffer size */ tmp_dsize = *variable_name_size; - if (name_size + tmp_dsize > + if (in_name_size + tmp_dsize > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) { tmp_dsize = max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE - - name_size; + in_name_size; } payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size; @@ -414,27 +380,12 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size, out: free(comm_buf); - return EFI_EXIT(ret); + return ret; } -/** - * efi_set_variable() - set value of a UEFI variable - * - * This function implements the SetVariable runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @name: name of the variable - * @guid: vendor GUID - * @attr: attributes of the variable - * @data_size: size of the buffer with the variable value - * @data: buffer with the variable value - * Return: status code - */ -efi_status_t EFIAPI efi_set_variable(u16 *name, const efi_guid_t *guid, - u32 attr, efi_uintn_t data_size, - const void *data) +efi_status_t efi_set_variable_int(u16 *variable_name, const efi_guid_t *vendor, + u32 attributes, efi_uintn_t data_size, + const void *data, bool ro_check) { struct smm_variable_access *var_acc; efi_uintn_t payload_size; @@ -442,9 +393,7 @@ efi_status_t EFIAPI efi_set_variable(u16 *name, const efi_guid_t *guid, u8 *comm_buf = NULL; efi_status_t ret; - EFI_ENTRY("\"%ls\" %pUl %x %zu %p", name, guid, attr, data_size, data); - - if (!name || name[0] == 0 || !guid) { + if (!variable_name || variable_name[0] == 0 || !vendor) { ret = EFI_INVALID_PARAMETER; goto out; } @@ -454,7 +403,7 @@ efi_status_t EFIAPI efi_set_variable(u16 *name, const efi_guid_t *guid, } /* Check payload size */ - name_size = u16_strsize(name); + name_size = u16_strsize(variable_name); payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size; if (payload_size > max_payload_size) { ret = EFI_INVALID_PARAMETER; @@ -468,11 +417,11 @@ efi_status_t EFIAPI efi_set_variable(u16 *name, const efi_guid_t *guid, goto out; /* Fill in contents */ - guidcpy(&var_acc->guid, guid); + guidcpy(&var_acc->guid, vendor); var_acc->data_size = data_size; var_acc->name_size = name_size; - var_acc->attr = attr; - memcpy(var_acc->name, name, name_size); + var_acc->attr = attributes; + memcpy(var_acc->name, variable_name, name_size); memcpy((u8 *)var_acc->name + name_size, data, data_size); /* Communicate */ @@ -480,40 +429,19 @@ efi_status_t EFIAPI efi_set_variable(u16 *name, const efi_guid_t *guid, out: free(comm_buf); - return EFI_EXIT(ret); + return ret; } -/** - * efi_query_variable_info() - get information about EFI variables - * - * This function implements the QueryVariableInfo() runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @attributes: bitmask to select variables to be - * queried - * @maximum_variable_storage_size: maximum size of storage area for the - * selected variable types - * @remaining_variable_storage_size: remaining size of storage are for the - * selected variable types - * @maximum_variable_size: maximum size of a variable of the - * selected type - * Returns: status code - */ -efi_status_t EFIAPI __efi_runtime -efi_query_variable_info(u32 attributes, u64 *max_variable_storage_size, - u64 *remain_variable_storage_size, - u64 *max_variable_size) +efi_status_t efi_query_variable_info_int(u32 attributes, + u64 *max_variable_storage_size, + u64 *remain_variable_storage_size, + u64 *max_variable_size) { struct smm_variable_query_info *mm_query_info; efi_uintn_t payload_size; efi_status_t ret; u8 *comm_buf; - EFI_ENTRY("%x %p %p %p", attributes, max_variable_storage_size, - remain_variable_storage_size, max_variable_size); - payload_size = sizeof(*mm_query_info); comm_buf = setup_mm_hdr((void **)&mm_query_info, payload_size, SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO, @@ -532,7 +460,7 @@ efi_query_variable_info(u32 attributes, u64 *max_variable_storage_size, out: free(comm_buf); - return EFI_EXIT(ret); + return ret; } /** diff --git a/lib/efi_selftest/efi_selftest_variables_runtime.c b/lib/efi_selftest/efi_selftest_variables_runtime.c index b3b40ad2cf..3226069c0b 100644 --- a/lib/efi_selftest/efi_selftest_variables_runtime.c +++ b/lib/efi_selftest/efi_selftest_variables_runtime.c @@ -16,9 +16,7 @@ static struct efi_boot_services *boottime; static struct efi_runtime_services *runtime; -static const efi_guid_t guid_vendor0 = - EFI_GUID(0x67029eb5, 0x0af2, 0xf6b1, - 0xda, 0x53, 0xfc, 0xb5, 0x66, 0xdd, 0x1c, 0xe6); +static const efi_guid_t guid_vendor0 = EFI_GLOBAL_VARIABLE_GUID; /* * Setup unit test. @@ -68,17 +66,18 @@ static int execute(void) efi_st_error("SetVariable failed\n"); return EFI_ST_FAILURE; } - len = 3; - ret = runtime->get_variable(L"efi_st_var0", &guid_vendor0, + len = EFI_ST_MAX_DATA_SIZE; + ret = runtime->get_variable(L"PlatformLangCodes", &guid_vendor0, &attr, &len, data); - if (ret != EFI_UNSUPPORTED) { + if (ret != EFI_SUCCESS) { efi_st_error("GetVariable failed\n"); return EFI_ST_FAILURE; } memset(&guid, 0, 16); *varname = 0; + len = 2 * EFI_ST_MAX_VARNAME_SIZE; ret = runtime->get_next_variable_name(&len, varname, &guid); - if (ret != EFI_UNSUPPORTED) { + if (ret != EFI_SUCCESS) { efi_st_error("GetNextVariableName failed\n"); return EFI_ST_FAILURE; } diff --git a/lib/rsa/rsa-verify.c b/lib/rsa/rsa-verify.c index 6c4bbc4625..2057f6819d 100644 --- a/lib/rsa/rsa-verify.c +++ b/lib/rsa/rsa-verify.c @@ -387,8 +387,8 @@ static int rsa_verify_key(struct image_sign_info *info, * * Return 0 if verified, -ve on error */ -static int rsa_verify_with_pkey(struct image_sign_info *info, - const void *hash, uint8_t *sig, uint sig_len) +int rsa_verify_with_pkey(struct image_sign_info *info, + const void *hash, uint8_t *sig, uint sig_len) { struct key_prop *prop; int ret; @@ -408,8 +408,8 @@ static int rsa_verify_with_pkey(struct image_sign_info *info, return ret; } #else -static int rsa_verify_with_pkey(struct image_sign_info *info, - const void *hash, uint8_t *sig, uint sig_len) +int rsa_verify_with_pkey(struct image_sign_info *info, + const void *hash, uint8_t *sig, uint sig_len) { return -EACCES; } |