From patchwork Wed Jun 7 05:41:52 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Masahisa Kojima X-Patchwork-Id: 690167 Delivered-To: patch@linaro.org Received: by 2002:a5d:4d8a:0:0:0:0:0 with SMTP id b10csp139681wru; Tue, 6 Jun 2023 22:43:11 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5P4d72rVov+airKFlVNXmQ3AU3Q0zyQd9KZo1r5a8DGFEYcgwKTDBrYf1t/d2RO0Yz4/FG X-Received: by 2002:a17:90a:1cb:b0:253:8e59:a867 with SMTP id 11-20020a17090a01cb00b002538e59a867mr3574638pjd.42.1686116590564; Tue, 06 Jun 2023 22:43:10 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1686116590; cv=none; d=google.com; s=arc-20160816; b=xKFm8x97+3YeRjUY/XShe4pn2F8m/PRvrA31Udd9neoMMF33/aezrJuY5h2hUErEXn ZmBQyjBPyVsrK43OAgQ+3m2TYxgKWGXQnNe4xQEUWHsMLU2ooDyK6c3A1f96A+BPYwnv eg5ABFDQ+Qlk0SMcxAyp6PYbHA5Uks7Jl5eD31IpCg4GG1gP7yLbJb8tr0YdQyUXwtVk y53HlM9Aan6SiJyL6dBI+JhwRkxj6EvPk0RRh1RbRAWnG373LVqQs3SEGIalyfNy4Lfn BAVktpC/c3f/Az3siXXp8fYVggFZSKpzpZf+8JMYRXn29akTxxO97+ut58pISYMflgg3 bUXQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=2IN70eFeXz6Q9vM6tLv2AIX/QWqaoAmtJxNzK9zzo68=; b=obtfkijmwl+prC5n9wkMlKoSmjDD7FaWR5vz487OF8dFYtXS6kSNUK6XJ6ZGCQNZ3y IuO6hOSrK2C8+J6kgrkFL0a+FQi0ayYB9y+CJrQQboSOyWq2eaHcXgjxYsEJX6qC2oP2 g0ZDOtaj+bIHznrv6CSKwsJI/kq2MV6Ku5BQr5H5bDTvR4ir6h6ycksnoi/OJZdicFJa /OIYNvxHxcUDU4n5O8mUqYuPGWoG9fplbVg21tHJpdYI6+YdIukm4f8W//KPvOz0cxwy WNrOdB8iee61+NDKPaEyEYpmy4RrI7zd3O/CsWuAOdLUMWcWBc1lWIZEEau19rljOe1W S2dQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=ibiks8QI; spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 2a01:238:438b:c500:173d:9f52:ddab:ee01 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from phobos.denx.de (phobos.denx.de. [2a01:238:438b:c500:173d:9f52:ddab:ee01]) by mx.google.com with ESMTPS id br24-20020a17090b0f1800b00259b7a03125si606359pjb.29.2023.06.06.22.43.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Jun 2023 22:43:10 -0700 (PDT) Received-SPF: pass (google.com: domain of u-boot-bounces@lists.denx.de designates 2a01:238:438b:c500:173d:9f52:ddab:ee01 as permitted sender) client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=ibiks8QI; spf=pass (google.com: domain of u-boot-bounces@lists.denx.de designates 2a01:238:438b:c500:173d:9f52:ddab:ee01 as permitted sender) smtp.mailfrom=u-boot-bounces@lists.denx.de; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id E6E2C85FE0; Wed, 7 Jun 2023 07:42:48 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.b="ibiks8QI"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id C482585FD2; Wed, 7 Jun 2023 07:42:46 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,SPF_HELO_NONE,SPF_PASS, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-pj1-x1033.google.com (mail-pj1-x1033.google.com [IPv6:2607:f8b0:4864:20::1033]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 8B2B285D4E for ; Wed, 7 Jun 2023 07:42:43 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=masahisa.kojima@linaro.org Received: by mail-pj1-x1033.google.com with SMTP id 98e67ed59e1d1-2565a9107d2so5929108a91.0 for ; Tue, 06 Jun 2023 22:42:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1686116562; x=1688708562; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=2IN70eFeXz6Q9vM6tLv2AIX/QWqaoAmtJxNzK9zzo68=; b=ibiks8QIvKl+VbvEPYBEEEW0EMVO5ysH0uI/t7FHE5djUMNzxXgQTjhhjQw39VpAMx 14XM0uNotbEIc2vFrWLR67nEbQfNJhv8+SzZP2+NjDmVM2G6USXAlzW8HmQUOHSd9VRF SPyQslvFtI+TPdUPRmRLWPQ4Y0D6pw0bS6B3OX1f/urzxfSVnqwDhkISE3ciSJQnFu59 JcUmZn/aMP53+GBhoF9TgJuNg1wal+BWrT+oG8sKjgBIQVE5ov1MIsM7sR5SN64FlwXH Ri6FP23Pgf92d4AMmHEBfNJJyvBbZTwrnCUkt8VzNpMa/LnPhJqe/hQu9BduDXdB7O7C gAPw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1686116562; x=1688708562; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=2IN70eFeXz6Q9vM6tLv2AIX/QWqaoAmtJxNzK9zzo68=; b=AeC7pRQ+8VM7u1q1xxCOsapx5RG0MP4PKd2XVss/Hjj0GCjZccubxaNpc93phEDml6 g+vY7Lk9sVf/3kyLEDH8uRX/yE2AovzgTU/5mka8WEJmdQfHhGXHRmiSWF1c0WtFo6F2 m6AICYnxUReQxezaslQ1Sl7VdpVeMlRE+ICPGXnl1zEw+fRuqNJiOXPKFPzTRqLyQaC9 C4vN/IqLkcgq1xA6AZjIBb3/FMEc2dcxgTL27WUQ96kGxU/hNIHuAcKcxfNjlBuQn323 GAVdTJOjPHgVCk21RBrxnvoOf3+zMJH6NA8em39nlO3tQ+gl0xOmFX+C6Q3uWNicILNF o8mQ== X-Gm-Message-State: AC+VfDz4KGlwvjxxtY/FniEt0zrqzmr1d+yHCx9lttrA58UX06OQgsah PQW3Bs7f/laOf7hIMVbUBfWZyf+pksDV8cdDB4c= X-Received: by 2002:a17:90a:cf81:b0:255:2dde:17cc with SMTP id i1-20020a17090acf8100b002552dde17ccmr3895799pju.47.1686116561637; Tue, 06 Jun 2023 22:42:41 -0700 (PDT) Received: from ubuntu-SVE15129CJS.. ([240d:1a:cf7:5800:d3c2:bf07:d08b:b72d]) by smtp.gmail.com with ESMTPSA id fh2-20020a17090b034200b0024df2b712a7sm469033pjb.52.2023.06.06.22.42.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 06 Jun 2023 22:42:41 -0700 (PDT) From: Masahisa Kojima To: u-boot@lists.denx.de Cc: Heinrich Schuchardt , Ilias Apalodimas , Simon Glass , Takahiro Akashi , malte.schmidt-oss@weidmueller.com, Masahisa Kojima Subject: [PATCH v9 02/10] efi_loader: store firmware version into FmpState variable Date: Wed, 7 Jun 2023 14:41:52 +0900 Message-Id: <20230607054201.42702-3-masahisa.kojima@linaro.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230607054201.42702-1-masahisa.kojima@linaro.org> References: <20230607054201.42702-1-masahisa.kojima@linaro.org> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.39 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.8 at phobos.denx.de X-Virus-Status: Clean Firmware version management is not implemented in the current FMP protocol. EDK II reference implementation capsule generation script inserts the FMP Payload Header right before the payload, FMP Payload Header contains the firmware version and lowest supported version. This commit utilizes the FMP Payload Header, reads the header and stores the firmware version into "FmpStateXXXX" EFI non-volatile variable. XXXX indicates the image index, since FMP protocol handles multiple image indexes. Note that lowest supported version included in the FMP Payload Header is not used. If the platform uses file-based EFI variable storage, it can be tampered. The file-based EFI variable storage is not the right place to store the lowest supported version for anti-rollback protection. This change is compatible with the existing FMP implementation. This change does not mandate the FMP Payload Header. If no FMP Payload Header is found in the capsule file, fw_version, lowest supported version, last attempt version and last attempt status is 0 and this is the same behavior as existing FMP implementation. Signed-off-by: Masahisa Kojima --- No update since v7 Changes in v7: - simplify efi_firmware_get_fw_version() function Changed in v6: - only store the fw_version in the FmpState EFI variable Changes in v4: - move lines that are the same in both branches out of the if statement - s/EDK2/EDK II/ - create print result function - set last_attempt_version when capsule authentication failed - use log_err() instead of printf() Changes in v3: - exclude CONFIG_FWU_MULTI_BANK_UPDATE case - set image_type_id as a vendor field of FmpStateXXXX variable - set READ_ONLY flag for FmpStateXXXX variable - add error code for FIT image case Changes in v2: - modify indent lib/efi_loader/efi_firmware.c | 164 ++++++++++++++++++++++++++++++---- 1 file changed, 145 insertions(+), 19 deletions(-) diff --git a/lib/efi_loader/efi_firmware.c b/lib/efi_loader/efi_firmware.c index cc650e1443..a798d380a3 100644 --- a/lib/efi_loader/efi_firmware.c +++ b/lib/efi_loader/efi_firmware.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -36,11 +37,52 @@ struct fmp_payload_header { u32 lowest_supported_version; }; +/** + * struct fmp_state - fmp firmware update state + * + * This structure describes the state of the firmware update + * through FMP protocol. + * + * @fw_version: Firmware versions used + * @lowest_supported_version: Lowest supported version + * @last_attempt_version: Last attempt version + * @last_attempt_status: Last attempt status + */ +struct fmp_state { + u32 fw_version; + u32 lowest_supported_version; /* not used */ + u32 last_attempt_version; /* not used */ + u32 last_attempt_status; /* not used */ +}; + __weak void set_dfu_alt_info(char *interface, char *devstr) { env_set("dfu_alt_info", update_info.dfu_string); } +/** + * efi_firmware_get_image_type_id - get image_type_id + * @image_index: image index + * + * Return the image_type_id identified by the image index. + * + * Return: pointer to the image_type_id, NULL if image_index is invalid + */ +static +efi_guid_t *efi_firmware_get_image_type_id(u8 image_index) +{ + int i; + struct efi_fw_image *fw_array; + + fw_array = update_info.images; + for (i = 0; i < update_info.num_images; i++) { + if (fw_array[i].image_index == image_index) + return &fw_array[i].image_type_id; + } + + return NULL; +} + /* Place holder; not supported */ static efi_status_t EFIAPI efi_firmware_get_image_unsupported( @@ -194,8 +236,6 @@ efi_status_t efi_firmware_capsule_authenticate(const void **p_image, { const void *image = *p_image; efi_uintn_t image_size = *p_image_size; - u32 fmp_hdr_signature; - struct fmp_payload_header *header; void *capsule_payload; efi_status_t status; efi_uintn_t capsule_payload_size; @@ -222,26 +262,104 @@ efi_status_t efi_firmware_capsule_authenticate(const void **p_image, debug("Updating capsule without authenticating.\n"); } - fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE; - header = (void *)image; - - if (!memcmp(&header->signature, &fmp_hdr_signature, - sizeof(fmp_hdr_signature))) { - /* - * When building the capsule with the scripts in - * edk2, a FMP header is inserted above the capsule - * payload. Compensate for this header to get the - * actual payload that is to be updated. - */ - image += header->header_size; - image_size -= header->header_size; - } - *p_image = image; *p_image_size = image_size; return EFI_SUCCESS; } +/** + * efi_firmware_set_fmp_state_var - set FmpStateXXXX variable + * @state: Pointer to fmp state + * @image_index: image index + * + * Update the FmpStateXXXX variable with the firmware update state. + * + * Return: status code + */ +static +efi_status_t efi_firmware_set_fmp_state_var(struct fmp_state *state, u8 image_index) +{ + u16 varname[13]; /* u"FmpStateXXXX" */ + efi_status_t ret; + efi_guid_t *image_type_id; + struct fmp_state var_state = { 0 }; + + image_type_id = efi_firmware_get_image_type_id(image_index); + if (!image_type_id) + return EFI_INVALID_PARAMETER; + + efi_create_indexed_name(varname, sizeof(varname), "FmpState", + image_index); + + /* + * Only the fw_version is set here. + * lowest_supported_version in FmpState variable is ignored since + * it can be tampered if the file based EFI variable storage is used. + */ + var_state.fw_version = state->fw_version; + + ret = efi_set_variable_int(varname, image_type_id, + EFI_VARIABLE_READ_ONLY | + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(var_state), &var_state, false); + + return ret; +} + +/** + * efi_firmware_get_fw_version - get fw_version from FMP payload header + * @p_image: Pointer to new image + * @p_image_size: Pointer to size of new image + * @state: Pointer to fmp state + * + * Parse the FMP payload header and fill the fmp_state structure. + * If no FMP payload header is found, fmp_state structure is not updated. + * + */ +static void efi_firmware_get_fw_version(const void **p_image, + efi_uintn_t *p_image_size, + struct fmp_state *state) +{ + const struct fmp_payload_header *header; + u32 fmp_hdr_signature = FMP_PAYLOAD_HDR_SIGNATURE; + + header = *p_image; + if (header->signature == fmp_hdr_signature) { + /* FMP header is inserted above the capsule payload */ + state->fw_version = header->fw_version; + + *p_image += header->header_size; + *p_image_size -= header->header_size; + } +} + +/** + * efi_firmware_verify_image - verify image + * @p_image: Pointer to new image + * @p_image_size: Pointer to size of new image + * @image_index: Image index + * @state: Pointer to fmp state + * + * Verify the capsule file + * + * Return: status code + */ +static +efi_status_t efi_firmware_verify_image(const void **p_image, + efi_uintn_t *p_image_size, + u8 image_index, + struct fmp_state *state) +{ + efi_status_t ret; + + ret = efi_firmware_capsule_authenticate(p_image, p_image_size); + efi_firmware_get_fw_version(p_image, p_image_size, state); + + return ret; +} + /** * efi_firmware_get_image_info - return information about the current * firmware image @@ -331,6 +449,7 @@ efi_status_t EFIAPI efi_firmware_fit_set_image( u16 **abort_reason) { efi_status_t status; + struct fmp_state state = { 0 }; EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image, image_size, vendor_code, progress, abort_reason); @@ -338,13 +457,16 @@ efi_status_t EFIAPI efi_firmware_fit_set_image( if (!image || image_index != 1) return EFI_EXIT(EFI_INVALID_PARAMETER); - status = efi_firmware_capsule_authenticate(&image, &image_size); + status = efi_firmware_verify_image(&image, &image_size, image_index, + &state); if (status != EFI_SUCCESS) return EFI_EXIT(status); if (fit_update(image)) return EFI_EXIT(EFI_DEVICE_ERROR); + efi_firmware_set_fmp_state_var(&state, image_index); + return EFI_EXIT(EFI_SUCCESS); } @@ -392,6 +514,7 @@ efi_status_t EFIAPI efi_firmware_raw_set_image( { int ret; efi_status_t status; + struct fmp_state state = { 0 }; EFI_ENTRY("%p %d %p %zu %p %p %p\n", this, image_index, image, image_size, vendor_code, progress, abort_reason); @@ -399,7 +522,8 @@ efi_status_t EFIAPI efi_firmware_raw_set_image( if (!image) return EFI_EXIT(EFI_INVALID_PARAMETER); - status = efi_firmware_capsule_authenticate(&image, &image_size); + status = efi_firmware_verify_image(&image, &image_size, image_index, + &state); if (status != EFI_SUCCESS) return EFI_EXIT(status); @@ -419,6 +543,8 @@ efi_status_t EFIAPI efi_firmware_raw_set_image( NULL, NULL)) return EFI_EXIT(EFI_DEVICE_ERROR); + efi_firmware_set_fmp_state_var(&state, image_index); + return EFI_EXIT(EFI_SUCCESS); }