From patchwork Tue May 12 15:19:24 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Harsimran Singh Tungal X-Patchwork-Id: 87904 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0F248CD4F25 for ; Tue, 12 May 2026 15:19:44 +0000 (UTC) Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.78438.1778599183324077664 for ; Tue, 12 May 2026 08:19:43 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: body hash did not verify" header.i=@arm.com header.s=foss header.b=NMWgELV7; spf=pass (domain: arm.com, ip: 217.140.110.172, mailfrom: harsimransingh.tungal@arm.com) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 9449C1691; Tue, 12 May 2026 08:19:37 -0700 (PDT) Received: from e132995.cambridge.arm.com (e132995.arm.com [10.1.31.30]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id E205B3F85F; Tue, 12 May 2026 08:19:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1778599182; bh=QhoCmNnUFXMScCQxWkUxronxaBCtjpSMpCLCkVy7FuI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NMWgELV76dsarAvxmuaGT7vCntg0DA78nskyXCKN4GtiG+GiaaEaj3MPBIQm4Lsv8 YNeUUjeuZgwbPoOTj1uir7LnMKL6m+oh7K/FDaJ9xWJ8WPgU/c/CHjYpfeTMMCjv/I CqhOHR8PhAEyVKFNea+268k5NzCeXV0hXWXYzgOE= From: Harsimran Singh Tungal To: meta-arm@lists.yoctoproject.org Cc: Harsimran Singh Tungal Subject: [PATCH 1/1] arm-bsp: corstone1000: Enable FF-A-backed EFI runtime variables and selftests Date: Tue, 12 May 2026 16:19:24 +0100 Message-Id: <20260512151924.89401-2-harsimransingh.tungal@arm.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260512151924.89401-1-harsimransingh.tungal@arm.com> References: <20260512151924.89401-1-harsimransingh.tungal@arm.com> MIME-Version: 1.0 List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Tue, 12 May 2026 15:19:44 -0000 X-Groupsio-URL: https://lists.yoctoproject.org/g/meta-arm/message/7040 This series wires up Arm FF-A support for EFI runtime services by factoring runtime-safe helpers into the FF-A bus, layer the EFI variable TEE transport on top, and then rehome the helper exports so there’s a single implementation shared between boot and runtime paths. Corstone1000 enables the self-test command and expands the runtime variable selftest so it exercises non-volatile storage across reboots. Key Changes =========== - Add the FF-A runtime transport patch stack to the corstone1000 U-Boot recipe. - Enable EFI runtime variable handling over FF-A and include the bootefi selftests and sandbox FF-A runtime transport test. - Increase the FIP partition sizes from 2MiB to 2.5MiB and update TFA_FIP_RE_SIGN_BIN_SIZE from 0x00200000 to 0x00280000 to reflect the new allocation. The size of u-boot-EFI-2025.10-r0.bin has increased from ~707KB to ~944KB following the FF-A/EFI runtime related changes. As this binary is packaged within the FIP, the overall signed FIP size has grown accordingly. - Add a Yocto patch to increase the FIP partition size from 2MB to 2.5MB in the TF-M flash layout for Corstone-1000. Signed-off-by: Harsimran Singh Tungal --- .../corstone1000-flash-firmware-fvp.wks.in | 2 +- .../corstone1000-flash-firmware-image.bb | 2 +- ...tone1000-Increase-FIP-partition-size.patch | 32 + .../trusted-firmware-m-corstone1000.inc | 1 + .../u-boot/u-boot-corstone1000.inc | 18 + ...efi_loader-add-runtime-memset-helper.patch | 63 ++ ...arm-ffa-add-FF-A-bus-runtime-support.patch | 975 ++++++++++++++++++ ...F-A-runtime-support-in-EFI-variable-.patch | 478 +++++++++ ...e-EFI-runtime-SetVariable-GetVariabl.patch | 418 ++++++++ ...runtime-GetVariable-helpers-to-efi_v.patch | 103 ++ ...corstone1000-enable-bootefi-selftest.patch | 38 + ...-runtime-variable-tests-with-non-vol.patch | 176 ++++ ...sandbox-FF-A-runtime-transport-tests.patch | 163 +++ ...e-synthetic-partition-metadata-via-m.patch | 96 ++ ...nt-FF-A-runtime-path-for-EFI-variabl.patch | 176 ++++ ...-two-phase-runtime-variables-selftes.patch | 47 + ...-FF-A-cache-maintenance-with-runtime.patch | 75 ++ ...der-fix-AllocatePages-overlap-status.patch | 50 + ...one1000-a320-enable-bootefi-selftest.patch | 37 + 19 files changed, 2948 insertions(+), 2 deletions(-) create mode 100644 meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0060-platform-corstone1000-Increase-FIP-partition-size.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0041-efi_loader-add-runtime-memset-helper.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0042-arm-ffa-add-FF-A-bus-runtime-support.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0043-efi_loader-add-FF-A-runtime-support-in-EFI-variable-.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0044-efi_loader-enable-EFI-runtime-SetVariable-GetVariabl.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0045-efi_loader-move-runtime-GetVariable-helpers-to-efi_v.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0046-corstone1000-enable-bootefi-selftest.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0047-efi-selftest-add-runtime-variable-tests-with-non-vol.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0048-test-dm-add-sandbox-FF-A-runtime-transport-tests.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0049-sandbox-ffa-share-synthetic-partition-metadata-via-m.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0050-doc-arm64-document-FF-A-runtime-path-for-EFI-variabl.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0051-doc-bootefi-note-two-phase-runtime-variables-selftes.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0052-efi_loader-align-FF-A-cache-maintenance-with-runtime.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0053-efi_loader-fix-AllocatePages-overlap-status.patch create mode 100644 meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0054-corstone1000-a320-enable-bootefi-selftest.patch diff --git a/meta-arm-bsp/files/wic/corstone1000-flash-firmware-fvp.wks.in b/meta-arm-bsp/files/wic/corstone1000-flash-firmware-fvp.wks.in index 33699512..89e68426 100644 --- a/meta-arm-bsp/files/wic/corstone1000-flash-firmware-fvp.wks.in +++ b/meta-arm-bsp/files/wic/corstone1000-flash-firmware-fvp.wks.in @@ -23,7 +23,7 @@ part --source rawcopy --size 144k --sourceparams="file=trusted-firmware-m/bl2_si part --source rawcopy --size 320k --sourceparams="file=trusted-firmware-m/tfm_s_signed.bin" --align 4 --part-name="tfm_primary" --uuid 07F9616C-1233-439C-ACBA-72D75421BF70 --part-type 7FAD470E-5EC5-5C03-A2C1-4756B495DE61 # Rawcopy of the FIP binary -part --source rawcopy --size 2 --sourceparams="file=signed_fip.bin" --align 4 --part-name="FIP_A" --uuid B9C7AC9D-40FF-4675-956B-EEF4DE9DF1C5 --part-type F1933675-5A8C-5B6D-9EF4-846739E89BC8 +part --source rawcopy --size 2560k --sourceparams="file=signed_fip.bin" --align 4 --part-name="FIP_A" --uuid B9C7AC9D-40FF-4675-956B-EEF4DE9DF1C5 --part-type F1933675-5A8C-5B6D-9EF4-846739E89BC8 # Rawcopy of kernel with initramfs part --source rawcopy --size 12 --sourceparams="file=Image.gz-initramfs-${MACHINE}.bin" --align 4 --part-name="kernel_primary" --uuid BF7A6142-0662-47FD-9434-6A8811980816 --part-type F771AFF9-C7E9-5F99-9EDA-2369DD694F61 diff --git a/meta-arm-bsp/recipes-bsp/images/corstone1000-flash-firmware-image.bb b/meta-arm-bsp/recipes-bsp/images/corstone1000-flash-firmware-image.bb index a8c74086..5ce621bb 100644 --- a/meta-arm-bsp/recipes-bsp/images/corstone1000-flash-firmware-image.bb +++ b/meta-arm-bsp/recipes-bsp/images/corstone1000-flash-firmware-image.bb @@ -160,7 +160,7 @@ TFA_FIP_BINARY = "fip.bin" TFA_BL2_RE_IMAGE_LOAD_ADDRESS = "0x62353000" TFA_BL2_RE_SIGN_BIN_SIZE = "0x2d000" TFA_FIP_RE_IMAGE_LOAD_ADDRESS = "0x68130000" -TFA_FIP_RE_SIGN_BIN_SIZE = "0x00200000" +TFA_FIP_RE_SIGN_BIN_SIZE = "0x00280000" RE_LAYOUT_WRAPPER_VERSION = "0.0.7" TFM_SIGN_PRIVATE_KEY = "${libdir}/tfm-scripts/root-EC-P256_1.pem" RE_IMAGE_OFFSET = "0x1000" diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0060-platform-corstone1000-Increase-FIP-partition-size.patch b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0060-platform-corstone1000-Increase-FIP-partition-size.patch new file mode 100644 index 00000000..57cb3071 --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/files/corstone1000/0060-platform-corstone1000-Increase-FIP-partition-size.patch @@ -0,0 +1,32 @@ +From 9edcdd272a7d2d872f7e04b3a9db5185fd24fd97 Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Tue, 28 Apr 2026 08:57:06 +0100 +Subject: [PATCH] platform: corstone1000: Increase FIP partition size + +Increase the FIP partition size from 2MB to 2.5MB in the +Corstone-1000 flash layout. + +The previous size is insufficient for current FIP images, +which may lead to overflow during image generation or +deployment. Expanding the partition ensures adequate +space for firmware components packaged in the FIP. + +Upstream-Status: Submitted [https://review.trustedfirmware.org/c/TF-M/trusted-firmware-m/+/50628] +Signed-off-by: Harsimran Singh Tungal +--- + platform/ext/target/arm/corstone1000/partition/flash_layout.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/platform/ext/target/arm/corstone1000/partition/flash_layout.h b/platform/ext/target/arm/corstone1000/partition/flash_layout.h +index e2219d80a..b7282beb2 100644 +--- a/platform/ext/target/arm/corstone1000/partition/flash_layout.h ++++ b/platform/ext/target/arm/corstone1000/partition/flash_layout.h +@@ -139,7 +139,7 @@ + #define TFM_PARTITION_SIZE (0x50000) /* 320 KB */ + #define TFM_PARTITION_BANK_OFFSET (SE_BL2_PARTITION_SIZE) + +-#define FIP_PARTITION_SIZE (0x200000) /* 2 MB */ ++#define FIP_PARTITION_SIZE (0x280000) /* 2.5 MB */ + #define FIP_PARTITION_BANK_OFFSET (TFM_PARTITION_BANK_OFFSET + TFM_PARTITION_SIZE) + + #define INITRAMFS_PARTITION_SIZE (0xC00000) /* 12 MB */ diff --git a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-corstone1000.inc b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-corstone1000.inc index 6ee13b30..2f96cb90 100644 --- a/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-corstone1000.inc +++ b/meta-arm-bsp/recipes-bsp/trusted-firmware-m/trusted-firmware-m-corstone1000.inc @@ -91,6 +91,7 @@ SRC_URI:append:corstone1000 = " \ file://0057-plat-cs1k-Add-flash-erase-protection.patch \ file://0058-plat-cs1k-Remove-unused-FWU-partitions-upon-version-.patch \ file://0059-plat-cs1k-Duplicate-old-images-in-FWU.patch \ + file://0060-platform-corstone1000-Increase-FIP-partition-size.patch \ " SRCREV_tfm-psa-adac:corstone1000 = "f2809ae231be33a1afcd7714f40756c67d846c88" diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot-corstone1000.inc b/meta-arm-bsp/recipes-bsp/u-boot/u-boot-corstone1000.inc index e3dec3c0..fe5ffafc 100644 --- a/meta-arm-bsp/recipes-bsp/u-boot/u-boot-corstone1000.inc +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot-corstone1000.inc @@ -86,6 +86,24 @@ SRC_URI:append = " \ file://0040-configs-corstone1000-disable-EFI-debug-support.patch \ " +# Add FF-A bus runtime support +SRC_URI:append = " \ + file://0041-efi_loader-add-runtime-memset-helper.patch \ + file://0042-arm-ffa-add-FF-A-bus-runtime-support.patch \ + file://0043-efi_loader-add-FF-A-runtime-support-in-EFI-variable-.patch \ + file://0044-efi_loader-enable-EFI-runtime-SetVariable-GetVariabl.patch \ + file://0045-efi_loader-move-runtime-GetVariable-helpers-to-efi_v.patch \ + file://0046-corstone1000-enable-bootefi-selftest.patch \ + file://0047-efi-selftest-add-runtime-variable-tests-with-non-vol.patch \ + file://0048-test-dm-add-sandbox-FF-A-runtime-transport-tests.patch \ + file://0049-sandbox-ffa-share-synthetic-partition-metadata-via-m.patch \ + file://0050-doc-arm64-document-FF-A-runtime-path-for-EFI-variabl.patch \ + file://0051-doc-bootefi-note-two-phase-runtime-variables-selftes.patch \ + file://0052-efi_loader-align-FF-A-cache-maintenance-with-runtime.patch \ + file://0053-efi_loader-fix-AllocatePages-overlap-status.patch \ + file://0054-corstone1000-a320-enable-bootefi-selftest.patch \ +" + uboot_configure_config:append() { openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=CRT/ -keyout ${B}/CRT.key -out ${S}/CRT.crt -nodes -days 365 } diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0041-efi_loader-add-runtime-memset-helper.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0041-efi_loader-add-runtime-memset-helper.patch new file mode 100644 index 00000000..b0da200c --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0041-efi_loader-add-runtime-memset-helper.patch @@ -0,0 +1,63 @@ +From 7e878429dc5f186ff945281513593c54beaf03f6 Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Fri, 10 Apr 2026 17:20:59 +0100 +Subject: [PATCH 41/53] efi_loader: add runtime memset helper + +Add efi_memset_runtime() for EFI runtime paths + +This keeps buffer initialization in runtime-resident code after +ExitBootServices() and avoids relying on non-runtime helpers. + +Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/] +Signed-off-by: Harsimran Singh Tungal +--- + include/efi_loader.h | 3 +++ + lib/efi_loader/efi_runtime.c | 21 +++++++++++++++++++++ + 2 files changed, 24 insertions(+) + +diff --git a/include/efi_loader.h b/include/efi_loader.h +index 0340847c0b4..ec30042665b 100644 +--- a/include/efi_loader.h ++++ b/include/efi_loader.h +@@ -1149,6 +1149,9 @@ struct pkcs7_message *efi_parse_pkcs7_header(const void *buf, + /* runtime implementation of memcpy() */ + void efi_memcpy_runtime(void *dest, const void *src, size_t n); + ++/* runtime implementation of memset */ ++__efi_runtime void *efi_memset_runtime(void *dest, int c, size_t n); ++ + /* commonly used helper functions */ + u16 *efi_create_indexed_name(u16 *buffer, size_t buffer_size, const char *name, + unsigned int index); +diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c +index 35eb6a77766..e91b1583b6e 100644 +--- a/lib/efi_loader/efi_runtime.c ++++ b/lib/efi_loader/efi_runtime.c +@@ -209,6 +209,27 @@ void __efi_runtime efi_memcpy_runtime(void *dest, const void *src, size_t n) + *d++ = *s++; + } + ++/** ++ * efi_memset_runtime() - fill a memory region with a byte value ++ * ++ * At runtime memset() is not available. ++ * ++ * @dest: pointer to the block of memory to fill. ++ * @c: value to be set. The value is passed as an int, but the ++ * function fills the block of memory using the u8 conversion of this value. ++ * @n: number of bytes to be set to the value. ++ * Return: dest is returned ++ */ ++__efi_runtime void *efi_memset_runtime(void *dest, int c, size_t n) ++{ ++ u8 *d = dest; ++ ++ for (; n; --n) ++ *d++ = c; ++ ++ return dest; ++} ++ + /** + * efi_update_table_header_crc32() - Update crc32 in table header + * diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0042-arm-ffa-add-FF-A-bus-runtime-support.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0042-arm-ffa-add-FF-A-bus-runtime-support.patch new file mode 100644 index 00000000..94d24a7f --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0042-arm-ffa-add-FF-A-bus-runtime-support.patch @@ -0,0 +1,975 @@ +From 2f4000043db3a5658e946a66f1e2b8b8c48dffc7 Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Thu, 20 Nov 2025 21:56:22 +0000 +Subject: [PATCH 42/53] arm-ffa: add FF-A bus runtime support + +Enable FF-A runtime transport for EFI services + +This patch introduces the FF-A runtime infrastructure that enables +U-Boot to use the Arm Firmware Framework for Arm A-profile (FF-A) +transport layer after ExitBootServices(). +The runtime transport provides persistent, runtime-safe +implementations of key FF-A functions used by EFI runtime services. + +Summary of key changes: +----------------------- + - Add drivers/firmware/arm-ffa/arm-ffa-runtime.c implementing FF-A + runtime operations. + - Introduce include/arm_ffa_runtime.h exporting FF-A runtime + functions and data structures. + - Move FF-A error mapping into runtime context. + - Introduce invoke_ffa_fn_runtime() as runtime safe SMC wrapper. + - Add runtime SMC wrapper for sandbox emulation. + - Add ARM_FFA_RT_MODE Kconfig to enable runtime support. + - Register ExitBootServices hook for runtime context. + - Copy ffa_priv runtime fields into resident storage at + ExitBootServices. + - Extend Makefile to build arm-ffa-runtime.c. + +All runtime functions and global data are tagged with __efi_runtime or +__efi_runtime_data to ensure persistence after ExitBootServices(). + +Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/] +Signed-off-by: Harsimran Singh Tungal +--- + drivers/firmware/arm-ffa/Kconfig | 11 + + drivers/firmware/arm-ffa/Makefile | 4 +- + drivers/firmware/arm-ffa/arm-ffa-runtime.c | 293 +++++++++++++++++++++ + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 113 ++------ + drivers/firmware/arm-ffa/arm-ffa.c | 16 +- + drivers/firmware/arm-ffa/ffa-emul-uclass.c | 12 + + include/arm_ffa.h | 16 +- + include/arm_ffa_priv.h | 24 +- + include/arm_ffa_runtime.h | 189 +++++++++++++ + test/dm/ffa.c | 6 +- + 10 files changed, 564 insertions(+), 120 deletions(-) + create mode 100644 drivers/firmware/arm-ffa/arm-ffa-runtime.c + create mode 100644 include/arm_ffa_runtime.h + +diff --git a/drivers/firmware/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig +index d75f8b53fd8..1be9adeb1db 100644 +--- a/drivers/firmware/arm-ffa/Kconfig ++++ b/drivers/firmware/arm-ffa/Kconfig +@@ -17,6 +17,9 @@ config ARM_FFA_TRANSPORT + The FF-A support in U-Boot is based on FF-A specification v1.0 and uses SMC32 + calling convention. + ++ The FF-A bus also provides a runtime layer to keep a minimal set of FF-A ++ operations available after ExitBootServices(). ++ + FF-A specification: + + https://developer.arm.com/documentation/den0077/a/?lang=en +@@ -40,3 +43,11 @@ config ARM_FFA_TRANSPORT + Secure World (sandbox_ffa.c). + + For more details about the FF-A support, please refer to doc/arch/arm64.ffa.rst ++ ++config ARM_FFA_RT_MODE ++ bool "Enable FF-A runtime support" ++ depends on ARM_FFA_TRANSPORT && EFI_LOADER ++ default y ++ help ++ Enable the FF-A runtime layer, keeping a minimal set of FF-A ++ operations available after ExitBootServices(). +diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile +index 318123a7f42..9deb59ba640 100644 +--- a/drivers/firmware/arm-ffa/Makefile ++++ b/drivers/firmware/arm-ffa/Makefile +@@ -1,12 +1,12 @@ + # SPDX-License-Identifier: GPL-2.0+ + # +-# Copyright 2022-2023 Arm Limited and/or its affiliates ++# Copyright 2022-2023, 2026 Arm Limited and/or its affiliates + # + # Authors: + # Abdellatif El Khlifi + + # build the generic FF-A methods +-obj-y += arm-ffa-uclass.o ++obj-y += arm-ffa-uclass.o arm-ffa-runtime.o + ifeq ($(CONFIG_SANDBOX),y) + # build the FF-A sandbox emulator and driver + obj-y += ffa-emul-uclass.o sandbox_ffa.o +diff --git a/drivers/firmware/arm-ffa/arm-ffa-runtime.c b/drivers/firmware/arm-ffa/arm-ffa-runtime.c +new file mode 100644 +index 00000000000..82c75da7d4b +--- /dev/null ++++ b/drivers/firmware/arm-ffa/arm-ffa-runtime.c +@@ -0,0 +1,293 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Copyright 2026 Arm Limited and/or its affiliates ++ * ++ * Authors: ++ * Harsimran Singh Tungal ++ * Abdellatif El Khlifi ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++DECLARE_GLOBAL_DATA_PTR; ++ ++/* Error mapping declarations */ ++ ++int __ffa_runtime_data ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = { ++ [NOT_SUPPORTED] = -EOPNOTSUPP, ++ [INVALID_PARAMETERS] = -EINVAL, ++ [NO_MEMORY] = -ENOMEM, ++ [BUSY] = -EBUSY, ++ [INTERRUPTED] = -EINTR, ++ [DENIED] = -EACCES, ++ [RETRY] = -EAGAIN, ++ [ABORTED] = -ECANCELED, ++}; ++ ++static __ffa_runtime_data struct ffa_priv_runtime ffa_priv_rt = {0}; ++ ++/* Arm FF-A driver runtime operations */ ++static const __ffa_runtime_data struct ffa_bus_ops_runtime ffa_ops_rt = { ++ .sync_send_receive = ffa_msg_send_direct_req_hdlr_runtime, ++}; ++ ++#define ffa_get_ops_runtime() (&ffa_ops_rt) ++#define ffa_get_priv_runtime() (&ffa_priv_rt) ++ ++#if CONFIG_IS_ENABLED(ARM_FFA_RT_MODE) ++static void EFIAPI ffa_rt_exit_boot_services_notify(struct efi_event *event, ++ void *context) ++{ ++ struct ffa_priv *priv = context; ++ ++ if (priv) ++ ffa_copy_runtime_priv(&priv->rt); ++ else ++ log_err("FF-A: Entering RT mode without FF-A runtime data information\n"); ++ ++ ffa_enable_runtime_context(); ++} ++ ++/** ++ * ffa_setup_efi_exit_boot_services_event() - register ExitBootServices event ++ * @priv: pointer to the FF-A private data ++ * ++ * Register an EFI event that enables the FF-A runtime context when ++ * ExitBootServices() is invoked. ++ * ++ * Return: 0 on success, -EPERM on failure to create the event. ++ */ ++int ffa_setup_efi_exit_boot_services_event(struct ffa_priv *priv) ++{ ++ efi_status_t efi_ret; ++ struct efi_event *evt = NULL; ++ ++ efi_ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK, ++ ffa_rt_exit_boot_services_notify, priv, ++ &efi_guid_event_group_exit_boot_services, ++ &evt); ++ if (efi_ret != EFI_SUCCESS) { ++ log_err("FF-A: cannot install ExitBootServices event %p, err (%lu)\n", ++ evt, efi_ret); ++ return -EPERM; ++ } ++ ++ return 0; ++} ++#endif ++ ++/** ++ * ffa_copy_runtime_priv() - copy runtime data into resident storage ++ * @priv: pointer to the runtime private data ++ * ++ * Copy boot-time runtime data into the resident runtime storage to be used ++ * after ExitBootServices(). ++ */ ++void ffa_copy_runtime_priv(const struct ffa_priv_runtime *priv) ++{ ++ struct ffa_priv_runtime *priv_rt = ffa_get_priv_runtime(); ++ ++ if (priv) ++ *priv_rt = *priv; ++} ++ ++/** ++ * ffa_enable_runtime_context() - Enable FF-A runtime context ++ * ++ * This function marks the FF-A runtime environment as ready for use by ++ * EFI runtime services. It is called when ExitBootServices() is invoked, ++ * after the FF-A bus device has successfully probed and U-Boot’s FF-A ++ * endpoint ID has been discovered and stored in the runtime private data ++ * structure. ++ * ++ * The FF-A runtime flag allows the EFI runtime layer to verify that the ++ * FF-A transport was initialized during the boot phase and that all ++ * runtime-safe FF-A operations may now be used after ExitBootServices(). ++ * ++ */ ++void ffa_enable_runtime_context(void) ++{ ++ struct ffa_priv_runtime *priv_rt = ffa_get_priv_runtime(); ++ ++ priv_rt->use_ffa_runtime = true; ++} ++ ++/** ++ * ffa_get_status_runtime_context() - Query FF-A runtime readiness ++ * ++ * This helper returns whether the FF-A runtime environment has been ++ * enabled during the boot phase. Runtime FF-A operations must check this ++ * flag before attempting any FF-A access, as the U-Boot driver model ++ * (DM/uclass) is no longer available after ExitBootServices(). ++ * ++ * The runtime context becomes enabled when ffa_enable_runtime_context() ++ * is called, typically after the FF-A bus device has probed and the ++ * endpoint ID has been discovered and stored in the runtime private ++ * data structure. ++ * ++ * Return: ++ * true FF-A runtime support is ready ++ * false FF-A runtime environment is not ready ++ */ ++bool __ffa_runtime ffa_get_status_runtime_context(void) ++{ ++ struct ffa_priv_runtime *priv_rt = ffa_get_priv_runtime(); ++ ++ return priv_rt->use_ffa_runtime; ++} ++ ++/** ++ * ffa_to_std_errno() - convert FF-A error code to standard error code ++ * @ffa_errno: Error code returned by the FF-A ABI ++ * ++ * Map the given FF-A error code as specified ++ * by the spec to a u-boot standard error code. ++ * ++ * Return: ++ * ++ * The standard error code on success. . Otherwise, failure ++ */ ++int __ffa_runtime ffa_to_std_errno(int ffa_errno) ++{ ++ int err_idx = -ffa_errno; ++ ++ /* Map the FF-A error code to the standard u-boot error code */ ++ if (err_idx > 0 && err_idx < MAX_NUMBER_FFA_ERR) ++ return ffa_to_std_errmap[err_idx]; ++ return -EINVAL; ++} ++ ++/** ++ * ffa_invoke_msg_send_direct_req() - Invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} ++ * @endpoint_id: u-boot endpoint id ++ * @dst_part_id: destination partition ID ++ * @msg: pointer to the message data preallocated by the client (in/out) ++ * @is_smc64: select 64-bit or 32-bit FF-A ABI ++ * ++ * This function invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A functions. ++ * ++ * FFA_MSG_SEND_DIRECT_REQ is used to send the data to the secure partition. ++ * The response from the secure partition is handled by reading the ++ * FFA_MSG_SEND_DIRECT_RESP arguments. ++ * ++ * The maximum size of the data that can be exchanged is 40 bytes which is ++ * sizeof(struct ffa_send_direct_data) as defined by the FF-A specification 1.0 ++ * in the section relevant to FFA_MSG_SEND_DIRECT_{REQ,RESP} ++ * ++ * Return: ++ * ++ * 0 on success. Otherwise, error on failure ++ */ ++int __ffa_runtime ffa_invoke_msg_send_direct_req(u16 endpoint_id, u16 dst_part_id, ++ struct ffa_send_direct_data *msg, bool is_smc64) ++{ ++ int ffa_errno; ++ u64 req_mode; ++ ffa_value_t ffa_args_rt; ++ ffa_value_t ffa_res_rt; ++ ++ if (is_smc64) { ++ req_mode = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ); ++ } else { ++ req_mode = FFA_SMC_32(FFA_MSG_SEND_DIRECT_REQ); ++ } ++ ++ efi_memset_runtime(&ffa_args_rt, 0, sizeof(ffa_args_rt)); ++ efi_memset_runtime(&ffa_res_rt, 0, sizeof(ffa_res_rt)); ++ ffa_args_rt.a0 = req_mode; ++ ffa_args_rt.a1 = PREP_SELF_ENDPOINT_ID(endpoint_id) | ++ PREP_PART_ENDPOINT_ID(dst_part_id); ++ ffa_args_rt.a2 = 0; ++ ffa_args_rt.a3 = msg->data0; ++ ffa_args_rt.a4 = msg->data1; ++ ffa_args_rt.a5 = msg->data2; ++ ffa_args_rt.a6 = msg->data3; ++ ffa_args_rt.a7 = msg->data4; ++ ++ invoke_ffa_fn_runtime(&ffa_args_rt, &ffa_res_rt); ++ ++ while (ffa_res_rt.a0 == FFA_SMC_32(FFA_INTERRUPT) || ++ ffa_res_rt.a0 == FFA_SMC_64(FFA_INTERRUPT)) { ++ efi_memset_runtime(&ffa_args_rt, 0, sizeof(ffa_args_rt)); ++ ffa_args_rt.a0 = (ffa_res_rt.a0 == FFA_SMC_64(FFA_INTERRUPT)) ? ++ FFA_SMC_64(FFA_RUN) : FFA_SMC_32(FFA_RUN); ++ ffa_args_rt.a1 = ffa_res_rt.a1; ++ ++ invoke_ffa_fn_runtime(&ffa_args_rt, &ffa_res_rt); ++ } ++ if (ffa_res_rt.a0 == FFA_SMC_32(FFA_SUCCESS) || ++ ffa_res_rt.a0 == FFA_SMC_64(FFA_SUCCESS)) { ++ /* Message sent with no response */ ++ return 0; ++ } ++ ++ if (ffa_res_rt.a0 == FFA_SMC_32(FFA_MSG_SEND_DIRECT_RESP) || ++ ffa_res_rt.a0 == FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP)) { ++ /* Message sent with response extract the return data */ ++ msg->data0 = ffa_res_rt.a3; ++ msg->data1 = ffa_res_rt.a4; ++ msg->data2 = ffa_res_rt.a5; ++ msg->data3 = ffa_res_rt.a6; ++ msg->data4 = ffa_res_rt.a7; ++ return 0; ++ } ++ ++ ffa_errno = ffa_res_rt.a2; ++ return ffa_to_std_errno(ffa_errno); ++} ++ ++/** ++ * ffa_msg_send_direct_req_hdlr_runtime() - Runtime implementation of ++ * FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function ++ * @dst_part_id: destination partition ID ++ * @msg: pointer to the message data preallocated by the client (in/out) ++ * @is_smc64: select 64-bit or 32-bit FF-A ABI ++ * ++ * This function calls the ffa_invoke_msg_send_direct_req() function which ++ * invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A functions. ++ * ++ * Return: ++ * ++ * 0 on success. Otherwise, failure ++ */ ++int __ffa_runtime ffa_msg_send_direct_req_hdlr_runtime(u16 dst_part_id, ++ struct ffa_send_direct_data *msg, ++ bool is_smc64) ++{ ++ struct ffa_priv_runtime *priv_rt = ffa_get_priv_runtime(); ++ ++ return ffa_invoke_msg_send_direct_req(priv_rt->id, dst_part_id, msg, is_smc64); ++} ++ ++/** ++ * ffa_sync_send_receive_runtime() - Runtime implementation of ++ * ffa_sync_send_receive() ++ * @dst_part_id: destination partition ID ++ * @msg: pointer to the message data preallocated by the client (in/out) ++ * @is_smc64: select 64-bit or 32-bit FF-A ABI ++ * ++ * Please see ffa_msg_send_direct_req_hdlr_runtime() description for more details. ++ * ++ * Return: ++ * ++ * 0 on success. Otherwise, failure ++ */ ++int __ffa_runtime ffa_sync_send_receive_runtime(u16 dst_part_id, ++ struct ffa_send_direct_data *msg, ++ bool is_smc64) ++{ ++ const struct ffa_bus_ops_runtime *ops_rt = ffa_get_ops_runtime(); ++ ++ if (!ffa_get_status_runtime_context()) ++ return -EPERM; ++ ++ if (!ops_rt->sync_send_receive) ++ return -ENOSYS; ++ ++ return ops_rt->sync_send_receive(dst_part_id, msg, is_smc64); ++} +diff --git a/drivers/firmware/arm-ffa/arm-ffa-uclass.c b/drivers/firmware/arm-ffa/arm-ffa-uclass.c +index 597b4e994b4..f769e0e9b05 100644 +--- a/drivers/firmware/arm-ffa/arm-ffa-uclass.c ++++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c +@@ -1,12 +1,13 @@ + // SPDX-License-Identifier: GPL-2.0+ + /* +- * Copyright 2022-2023, 2025 Arm Limited and/or its affiliates ++ * Copyright 2022-2023, 2026 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + #include + #include ++#include + #include + #include + #include +@@ -21,19 +22,6 @@ + + DECLARE_GLOBAL_DATA_PTR; + +-/* Error mapping declarations */ +- +-int ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = { +- [NOT_SUPPORTED] = -EOPNOTSUPP, +- [INVALID_PARAMETERS] = -EINVAL, +- [NO_MEMORY] = -ENOMEM, +- [BUSY] = -EBUSY, +- [INTERRUPTED] = -EINTR, +- [DENIED] = -EACCES, +- [RETRY] = -EAGAIN, +- [ABORTED] = -ECANCELED, +-}; +- + static struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = { + [FFA_ID_TO_ERRMAP_ID(FFA_VERSION)] = { + { +@@ -123,27 +111,6 @@ static struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = { + }, + }; + +-/** +- * ffa_to_std_errno() - convert FF-A error code to standard error code +- * @ffa_errno: Error code returned by the FF-A ABI +- * +- * Map the given FF-A error code as specified +- * by the spec to a u-boot standard error code. +- * +- * Return: +- * +- * The standard error code on success. . Otherwise, failure +- */ +-static int ffa_to_std_errno(int ffa_errno) +-{ +- int err_idx = -ffa_errno; +- +- /* Map the FF-A error code to the standard u-boot error code */ +- if (err_idx > 0 && err_idx < MAX_NUMBER_FFA_ERR) +- return ffa_to_std_errmap[err_idx]; +- return -EINVAL; +-} +- + /** + * ffa_print_error_log() - print the error log corresponding to the selected FF-A ABI + * @ffa_id: FF-A ABI ID +@@ -233,7 +200,7 @@ int ffa_get_version_hdlr(struct udevice *dev) + if (dev) { + uc_priv = dev_get_uclass_priv(dev); + if (uc_priv) +- uc_priv->fwk_version = res.a0; ++ uc_priv->rt.fwk_version = res.a0; + } + + return 0; +@@ -267,8 +234,8 @@ static int ffa_get_endpoint_id(struct udevice *dev) + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { +- uc_priv->id = GET_SELF_ENDPOINT_ID((u32)res.a2); +- log_debug("FF-A endpoint ID is %u\n", uc_priv->id); ++ uc_priv->rt.id = GET_SELF_ENDPOINT_ID((u32)res.a2); ++ log_debug("FF-A endpoint ID is %u\n", uc_priv->rt.id); + + return 0; + } +@@ -490,7 +457,7 @@ int ffa_unmap_rxtx_buffers_hdlr(struct udevice *dev) + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RXTX_UNMAP), +- .a1 = PREP_SELF_ENDPOINT_ID(uc_priv->id), ++ .a1 = PREP_SELF_ENDPOINT_ID(uc_priv->rt.id), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { +@@ -880,16 +847,8 @@ static int ffa_cache_partitions_info(struct udevice *dev) + * @msg: pointer to the message data preallocated by the client (in/out) + * @is_smc64: select 64-bit or 32-bit FF-A ABI + * +- * Implement FFA_MSG_SEND_DIRECT_{REQ,RESP} +- * FF-A functions. +- * +- * FFA_MSG_SEND_DIRECT_REQ is used to send the data to the secure partition. +- * The response from the secure partition is handled by reading the +- * FFA_MSG_SEND_DIRECT_RESP arguments. +- * +- * The maximum size of the data that can be exchanged is 40 bytes which is +- * sizeof(struct ffa_send_direct_data) as defined by the FF-A specification 1.0 +- * in the section relevant to FFA_MSG_SEND_DIRECT_{REQ,RESP} ++ * This function calls the ffa_invoke_msg_send_direct_req() function which ++ * invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A functions. + * + * Return: + * +@@ -898,9 +857,6 @@ static int ffa_cache_partitions_info(struct udevice *dev) + int ffa_msg_send_direct_req_hdlr(struct udevice *dev, u16 dst_part_id, + struct ffa_send_direct_data *msg, bool is_smc64) + { +- ffa_value_t res = {0}; +- int ffa_errno; +- u64 req_mode, resp_mode; + struct ffa_priv *uc_priv; + + uc_priv = dev_get_uclass_priv(dev); +@@ -909,50 +865,7 @@ int ffa_msg_send_direct_req_hdlr(struct udevice *dev, u16 dst_part_id, + if (!uc_priv->partitions.count || !uc_priv->partitions.descs) + return -ENODEV; + +- if (is_smc64) { +- req_mode = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ); +- resp_mode = FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP); +- } else { +- req_mode = FFA_SMC_32(FFA_MSG_SEND_DIRECT_REQ); +- resp_mode = FFA_SMC_32(FFA_MSG_SEND_DIRECT_RESP); +- } +- +- invoke_ffa_fn((ffa_value_t){ +- .a0 = req_mode, +- .a1 = PREP_SELF_ENDPOINT_ID(uc_priv->id) | +- PREP_PART_ENDPOINT_ID(dst_part_id), +- .a2 = 0, +- .a3 = msg->data0, +- .a4 = msg->data1, +- .a5 = msg->data2, +- .a6 = msg->data3, +- .a7 = msg->data4, +- }, &res); +- +- while (res.a0 == FFA_SMC_32(FFA_INTERRUPT)) +- invoke_ffa_fn((ffa_value_t){ +- .a0 = FFA_SMC_32(FFA_RUN), +- .a1 = res.a1, +- }, &res); +- +- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { +- /* Message sent with no response */ +- return 0; +- } +- +- if (res.a0 == resp_mode) { +- /* Message sent with response extract the return data */ +- msg->data0 = res.a3; +- msg->data1 = res.a4; +- msg->data2 = res.a5; +- msg->data3 = res.a6; +- msg->data4 = res.a7; +- +- return 0; +- } +- +- ffa_errno = res.a2; +- return ffa_to_std_errno(ffa_errno); ++return ffa_invoke_msg_send_direct_req(uc_priv->rt.id, dst_part_id, msg, is_smc64); + } + + /** +@@ -1008,7 +921,7 @@ static int ffa_setup_and_transmit(struct udevice *dev, u32 func_id, + + mem_region->tag = args->tag; + mem_region->flags = args->flags; +- mem_region->sender_id = uc_priv->id; ++ mem_region->sender_id = uc_priv->rt.id; + + /* + * These attributes are only valid for FFA_MEM_SHARE. +@@ -1322,6 +1235,7 @@ int ffa_memory_reclaim(struct udevice *dev, u64 g_handle, u32 flags) + static int ffa_do_probe(struct udevice *dev) + { + int ret; ++ struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + ret = ffa_get_version_hdlr(dev); + if (ret) +@@ -1345,6 +1259,11 @@ static int ffa_do_probe(struct udevice *dev) + return ret; + } + ++ if (IS_ENABLED(CONFIG_ARM_FFA_RT_MODE)) { ++ ret = ffa_setup_efi_exit_boot_services_event(uc_priv); ++ if (ret) ++ return ret; ++ } + return 0; + } + +diff --git a/drivers/firmware/arm-ffa/arm-ffa.c b/drivers/firmware/arm-ffa/arm-ffa.c +index de36f5647d2..0d451c0ddc7 100644 +--- a/drivers/firmware/arm-ffa/arm-ffa.c ++++ b/drivers/firmware/arm-ffa/arm-ffa.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0+ + /* +- * Copyright 2022-2023, 2025 Arm Limited and/or its affiliates ++ * Copyright 2022-2023, 2026 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi +@@ -8,6 +8,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -28,6 +29,19 @@ void invoke_ffa_fn(ffa_value_t args, ffa_value_t *res) + arm_smccc_1_2_smc(&args, res); + } + ++/** ++ * invoke_ffa_fn_runtime() - Runtime-safe SMC wrapper ++ * @args: FF-A ABI arguments to be copied to Xn registers ++ * @res: FF-A ABI return values copied from Xn registers ++ * ++ * Calls the SMCCC SMC 1.2 helper from EFI runtime context. This wrapper ++ * is marked __ffa_runtime so it remains available after ExitBootServices(). ++ */ ++void __ffa_runtime invoke_ffa_fn_runtime(ffa_value_t *args, ffa_value_t *res) ++{ ++ arm_smccc_1_2_smc(args, res); ++} ++ + /** + * arm_ffa_discover() - perform FF-A discovery + * @dev: The Arm FF-A bus device (arm_ffa) +diff --git a/drivers/firmware/arm-ffa/ffa-emul-uclass.c b/drivers/firmware/arm-ffa/ffa-emul-uclass.c +index a630392b6d1..531ecb12f3a 100644 +--- a/drivers/firmware/arm-ffa/ffa-emul-uclass.c ++++ b/drivers/firmware/arm-ffa/ffa-emul-uclass.c +@@ -758,6 +758,18 @@ void invoke_ffa_fn(ffa_value_t args, ffa_value_t *res) + sandbox_arm_ffa_smccc_smc(&args, res); + } + ++/** ++ * invoke_ffa_fn_runtime() - Runtime-safe SMC wrapper ++ * @args: FF-A ABI arguments to be copied to Xn registers ++ * @res: FF-A ABI return data to be copied from Xn registers ++ * ++ * Calls the emulated SMC call. ++ */ ++void invoke_ffa_fn_runtime(ffa_value_t *args, ffa_value_t *res) ++{ ++ sandbox_arm_ffa_smccc_smc(args, res); ++} ++ + /** + * ffa_emul_find() - Find the FF-A emulator + * @dev: the sandbox FF-A device (sandbox-arm-ffa) +diff --git a/include/arm_ffa.h b/include/arm_ffa.h +index 1229e6e3d02..f19a9759e55 100644 +--- a/include/arm_ffa.h ++++ b/include/arm_ffa.h +@@ -1,6 +1,6 @@ + /* SPDX-License-Identifier: GPL-2.0+ */ + /* +- * Copyright 2022-2023, 2025 Arm Limited and/or its affiliates ++ * Copyright 2022-2023, 2025-2026 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi +@@ -195,21 +195,13 @@ int ffa_sync_send_receive(struct udevice *dev, u16 dst_part_id, + + /** + * ffa_msg_send_direct_req_hdlr() - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function +- * @dev: The arm_ffa bus device ++ * @dev: The FF-A bus device + * @dst_part_id: destination partition ID + * @msg: pointer to the message data preallocated by the client (in/out) + * @is_smc64: select 64-bit or 32-bit FF-A ABI + * +- * This function implements FFA_MSG_SEND_DIRECT_{REQ,RESP} +- * FF-A functions. +- * +- * FFA_MSG_SEND_DIRECT_REQ is used to send the data to the secure partition. +- * The response from the secure partition is handled by reading the +- * FFA_MSG_SEND_DIRECT_RESP arguments. +- * +- * The maximum size of the data that can be exchanged is 40 bytes which is +- * sizeof(struct ffa_send_direct_data) as defined by the FF-A specification 1.0 +- * in the section relevant to FFA_MSG_SEND_DIRECT_{REQ,RESP} ++ * This function calls the ffa_invoke_msg_send_direct_req() function which ++ * invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A functions. + * + * Return: + * +diff --git a/include/arm_ffa_priv.h b/include/arm_ffa_priv.h +index 54196199ce3..73eba942e3b 100644 +--- a/include/arm_ffa_priv.h ++++ b/include/arm_ffa_priv.h +@@ -1,6 +1,6 @@ + /* SPDX-License-Identifier: GPL-2.0+ */ + /* +- * Copyright 2022-2023, 2025 Arm Limited and/or its affiliates ++ * Copyright 2022-2023, 2025-2026 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi +@@ -202,11 +202,26 @@ struct ffa_partitions { + }; + + /** +- * struct ffa_priv - the driver private data structure ++ * struct ffa_priv_runtime - the driver's private runtime data structure + * ++ * @use_ffa_runtime: Whether FF-A runtime is available to use or not + * @fwk_version: FF-A framework version +- * @emul: FF-A sandbox emulator + * @id: u-boot endpoint ID ++ * ++ * The device private runtime data structure containing all the ++ * data read from secure world. ++ */ ++struct ffa_priv_runtime { ++ bool use_ffa_runtime; ++ u32 fwk_version; ++ u16 id; ++}; ++ ++/** ++ * struct ffa_priv - the driver private data structure ++ * ++ * @rt: Runtime data captured at boot time ++ * @emul: FF-A sandbox emulator + * @partitions: The partitions descriptors structure + * @pair: The RX/TX buffers pair + * +@@ -214,9 +229,8 @@ struct ffa_partitions { + * data read from secure world. + */ + struct ffa_priv { +- u32 fwk_version; ++ struct ffa_priv_runtime rt; + struct udevice *emul; +- u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + }; +diff --git a/include/arm_ffa_runtime.h b/include/arm_ffa_runtime.h +new file mode 100644 +index 00000000000..b6ddda68f4b +--- /dev/null ++++ b/include/arm_ffa_runtime.h +@@ -0,0 +1,189 @@ ++/* SPDX-License-Identifier: GPL-2.0+ */ ++/* ++ * Copyright 2026 Arm Limited and/or its affiliates ++ * ++ * Authors: ++ * Harsimran Singh Tungal ++ * Abdellatif El Khlifi ++ */ ++ ++#ifndef __ARM_FFA_RUNTIME_H ++#define __ARM_FFA_RUNTIME_H ++ ++#include ++#include ++#include ++#include ++ ++/** ++ * __ffa_runtime - controls whether functions are ++ * available after calling the EFI ExitBootServices service. ++ * Functions tagged with these keywords are resident (available at boot time and ++ * at runtime) ++ */ ++#if CONFIG_IS_ENABLED(ARM_FFA_RT_MODE) ++#define __ffa_runtime_data __efi_runtime_data ++#define __ffa_runtime __efi_runtime ++#else ++#define __ffa_runtime_data ++#define __ffa_runtime ++#endif ++ ++/** ++ * invoke_ffa_fn_runtime() - Runtime-safe SMC wrapper ++ * @args: FF-A ABI arguments to be copied to Xn registers ++ * @res: FF-A ABI return values copied from Xn registers ++ * ++ * Calls low level SMC implementation. This wrapper ++ * is marked __ffa_runtime so it remains available after ExitBootServices(). ++ */ ++void __ffa_runtime invoke_ffa_fn_runtime(ffa_value_t *args, ffa_value_t *res); ++ ++/** ++ * struct ffa_bus_ops_runtime - Operations for FF-A runtime ++ * @sync_send_receive: callback for the FFA_MSG_SEND_DIRECT_REQ ++ * ++ * The data structure providing all the runtime operations supported by the driver. ++ * This structure is an EFI runtime resident. ++ */ ++struct ffa_bus_ops_runtime { ++ int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg, ++ bool is_smc64); ++}; ++ ++/** ++ * ffa_enable_runtime_context() - Enable FF-A runtime context ++ * ++ * This function marks the FF-A runtime environment as ready for use by ++ * EFI runtime services. It is called when ExitBootServices() is invoked, ++ * after the FF-A bus device has successfully probed and U-Boot’s FF-A ++ * endpoint ID has been discovered and stored in the runtime private data ++ * structure. ++ * ++ * The FF-A runtime flag allows the EFI runtime layer to verify that the ++ * FF-A transport was initialized during the boot phase and that all ++ * runtime-safe FF-A operations may now be used after ExitBootServices(). ++ * ++ */ ++void ffa_enable_runtime_context(void); ++ ++/** ++ * ffa_copy_runtime_priv() - copy runtime data into resident storage ++ * @priv: pointer to the runtime private data ++ * ++ * Copy boot-time runtime data into the resident runtime storage to be used ++ * after ExitBootServices(). ++ */ ++void ffa_copy_runtime_priv(const struct ffa_priv_runtime *priv); ++ ++/** ++ * ffa_setup_efi_exit_boot_services_event() - register ExitBootServices event ++ * @priv: pointer to the FF-A private data ++ * ++ * Register an EFI event that enables the FF-A runtime context when ++ * ExitBootServices() is invoked. ++ * ++ * Return: 0 on success, -EPERM on failure to create the event. ++ */ ++#if CONFIG_IS_ENABLED(ARM_FFA_RT_MODE) ++int ffa_setup_efi_exit_boot_services_event(struct ffa_priv *priv); ++#else ++static inline int ffa_setup_efi_exit_boot_services_event(struct ffa_priv *priv) ++{ ++ return 0; ++} ++#endif ++ ++/** ++ * ffa_get_status_runtime_context() - Query FF-A runtime readiness ++ * ++ * This helper returns whether the FF-A runtime environment has been ++ * enabled during the boot phase. Runtime FF-A operations must check this ++ * flag before attempting any FF-A access, as the U-Boot driver model ++ * (DM/uclass) is no longer available after ExitBootServices(). ++ * ++ * The runtime context becomes enabled when ffa_enable_runtime_context() ++ * is called, typically after the FF-A bus device has probed and the ++ * endpoint ID has been discovered and stored in the runtime private ++ * data structure. ++ * ++ * Return: ++ * true FF-A runtime support is ready ++ * false FF-A runtime environment is not ready ++ */ ++bool __ffa_runtime ffa_get_status_runtime_context(void); ++ ++/** ++ * ffa_to_std_errno() - convert FF-A error code to standard error code ++ * @ffa_errno: Error code returned by the FF-A ABI ++ * ++ * Map the given FF-A error code as specified ++ * by the spec to a u-boot standard error code. ++ * ++ * Return: ++ * ++ * The standard error code on success. . Otherwise, failure ++ */ ++int __ffa_runtime ffa_to_std_errno(int ffa_errno); ++ ++/** ++ * ffa_sync_send_receive_runtime() - Runtime implementation of ++ * ffa_sync_send_receive() ++ * @dst_part_id: destination partition ID ++ * @msg: pointer to the message data preallocated by the client (in/out) ++ * @is_smc64: select 64-bit or 32-bit FF-A ABI ++ * ++ * Please see ffa_msg_send_direct_req_hdlr_runtime() description for more details. ++ * ++ * Return: ++ * ++ * 0 on success. Otherwise, failure ++ */ ++int __ffa_runtime ffa_sync_send_receive_runtime(u16 dst_part_id, ++ struct ffa_send_direct_data *msg, ++ bool is_smc64); ++ ++/** ++ * ffa_invoke_msg_send_direct_req() - Invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} ++ * @endpoint_id: u-boot endpoint id ++ * @dst_part_id: destination partition ID ++ * @msg: pointer to the message data preallocated by the client (in/out) ++ * @is_smc64: select 64-bit or 32-bit FF-A ABI ++ * ++ * This function invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A functions. ++ * ++ * FFA_MSG_SEND_DIRECT_REQ is used to send the data to the secure partition. ++ * The response from the secure partition is handled by reading the ++ * FFA_MSG_SEND_DIRECT_RESP arguments. ++ * ++ * The maximum size of the data that can be exchanged is 40 bytes which is ++ * sizeof(struct ffa_send_direct_data) as defined by the FF-A specification 1.0 ++ * in the section relevant to FFA_MSG_SEND_DIRECT_{REQ,RESP} ++ * ++ * Return: ++ * ++ * 0 on success. Otherwise, error on failure ++ */ ++int __ffa_runtime ffa_invoke_msg_send_direct_req(u16 endpoint_id, u16 dst_part_id, ++ struct ffa_send_direct_data *msg, ++ bool is_smc64); ++ ++/** ++ * ffa_msg_send_direct_req_hdlr_runtime() - Runtime implementation of ++ * FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function ++ * @dst_part_id: destination partition ID ++ * @msg: pointer to the message data preallocated by the client (in/out) ++ * @is_smc64: select 64-bit or 32-bit FF-A ABI ++ * ++ * This function calls the ffa_invoke_msg_send_direct_req() function which ++ * invokes FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A functions. ++ * ++ * Return: ++ * ++ * 0 on success. Otherwise, failure ++ */ ++int __ffa_runtime ffa_msg_send_direct_req_hdlr_runtime(u16 dst_part_id, ++ struct ffa_send_direct_data *msg, ++ bool is_smc64); ++ ++#endif +diff --git a/test/dm/ffa.c b/test/dm/ffa.c +index f4a6716cfd8..1240bb4141a 100644 +--- a/test/dm/ffa.c ++++ b/test/dm/ffa.c +@@ -2,7 +2,7 @@ + /* + * Functional tests for UCLASS_FFA class + * +- * Copyright 2022-2023, 2025 Arm Limited and/or its affiliates ++ * Copyright 2022-2023, 2026 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi +@@ -27,14 +27,14 @@ static int check_fwk_version(struct ffa_priv *uc_priv, struct unit_test_state *u + func_data.data0 = &fwk_version; + func_data.data0_size = sizeof(fwk_version); + ut_assertok(sandbox_query_ffa_emul_state(FFA_VERSION, &func_data)); +- ut_asserteq(uc_priv->fwk_version, fwk_version); ++ ut_asserteq(uc_priv->rt.fwk_version, fwk_version); + + return 0; + } + + static int check_endpoint_id(struct ffa_priv *uc_priv, struct unit_test_state *uts) + { +- ut_asserteq(0, uc_priv->id); ++ ut_asserteq(0, uc_priv->rt.id); + + return 0; + } diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0043-efi_loader-add-FF-A-runtime-support-in-EFI-variable-.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0043-efi_loader-add-FF-A-runtime-support-in-EFI-variable-.patch new file mode 100644 index 00000000..2f0c1d68 --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0043-efi_loader-add-FF-A-runtime-support-in-EFI-variable-.patch @@ -0,0 +1,478 @@ +From dd992d82e9958786bd8367a8b7018648d217d552 Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Thu, 20 Nov 2025 22:19:01 +0000 +Subject: [PATCH 43/53] efi_loader: add FF-A runtime support in EFI variable + TEE driver + +Enable MM variable services over FF-A after ExitBootServices + +This patch extends lib/efi_loader/efi_variable_tee.c to support FF-A +communication with the secure world during EFI runtime. It enables EFI +runtime variable access and MM communication using FF-A transport when +ExitBootServices() has already been called. + +Key changes: + ------------ + - Introduce runtime-safe implementations for MM communication, + notification, and variable access using FF-A driver. + - Introduce communication-buffer helper (get_comm_buf()) that switches + between dynamic allocation (boot phase) and the fixed FF-A shared + buffer (runtime phase). + - Mark persistent data and code with __efi_runtime and + __efi_runtime_data attributes. + - Use direct physical address mapping for shared buffers since + U-Boot operates with 1:1 physical-to-virtual mapping. + - Only per-buffer cache maintenance is performed at runtime, + as whole D-cache invalidation would violate the OS coherency model + after ExitBootServices(). + - Add runtime-phase tracking (efi_runtime_enabled). + +The change reuses the statically reserved shared buffer, replaces +allocations with __efi_runtime copies, and updates the runtime service +table so EFI variable runtime calls reach the secure partition via FF-A. + +Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/] +Signed-off-by: Harsimran Singh Tungal +--- + lib/efi_loader/efi_variable_tee.c | 331 ++++++++++++++++++++++++++++-- + 1 file changed, 319 insertions(+), 12 deletions(-) + +diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c +index 6a1fa39bb6f..e4d97dc55ab 100644 +--- a/lib/efi_loader/efi_variable_tee.c ++++ b/lib/efi_loader/efi_variable_tee.c +@@ -4,7 +4,7 @@ + * + * Copyright (C) 2019 Linaro Ltd. + * Copyright (C) 2019 Linaro Ltd. +- * Copyright 2022-2023 Arm Limited and/or its affiliates ++ * Copyright 2022-2023, 2026 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi +@@ -14,6 +14,7 @@ + + #if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) + #include ++#include + #endif + #include + #include +@@ -34,20 +35,47 @@ + #define MM_DENIED (-3) + #define MM_NO_MEMORY (-5) + ++static const int __efi_runtime_rodata mm_sp_errmap[] = { ++ [-MM_NOT_SUPPORTED] = -EINVAL, ++ [-MM_INVALID_PARAMETER] = -EPERM, ++ [-MM_DENIED] = -EACCES, ++ [-MM_NO_MEMORY] = -EBUSY, ++}; ++ + static const char *mm_sp_svc_uuid = MM_SP_UUID; +-static u16 mm_sp_id; ++static u16 __efi_runtime_data mm_sp_id; + #endif + ++static void *__efi_runtime_data ffa_shared_buf; ++static const efi_guid_t __efi_runtime_rodata mm_var_guid_runtime = ++ EFI_MM_VARIABLE_GUID; ++ + extern struct efi_var_file __efi_runtime_data *efi_var_buf; +-static efi_uintn_t max_buffer_size; /* comm + var + func + data */ +-static efi_uintn_t max_payload_size; /* func + data */ ++static efi_uintn_t __efi_runtime_data max_buffer_size; /* comm + var + func + data */ ++static efi_uintn_t __efi_runtime_data max_payload_size; /* func + data */ + static const u16 __efi_runtime_rodata pk[] = u"PK"; ++static bool __efi_runtime_data efi_runtime_enabled; + + struct mm_connection { + struct udevice *tee; + u32 session; + }; + ++/** ++ * efi_is_runtime_enabled() - Indicate whether the system is in the UEFI runtime phase ++ * ++ * This helper returns whether the firmware has transitioned into the ++ * UEFI runtime phase, meaning that ExitBootServices() has been invoked. ++ * ++ * Return: ++ * true - The system is operating in UEFI runtime mode. ++ * false - The system is still in the boot services phase. ++ */ ++static bool __efi_runtime efi_is_runtime_enabled(void) ++{ ++ return efi_runtime_enabled; ++} ++ + /** + * get_connection() - Retrieve OP-TEE session for a specific UUID. + * +@@ -169,6 +197,28 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) + } + + #if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) ++/** ++ * ffa_map_sp_event_runtime() - Map MM SP response to errno (runtime-safe) ++ * @sp_event_ret: MM SP return code from ffa_notify_mm_sp_runtime() ++ * ++ * Convert the MM SP return code into a standard U-Boot errno. This helper ++ * is marked __efi_runtime to ensure it is safe to call after ++ * ExitBootServices(). ++ * ++ * Return: 0 on success, negative errno on failure ++ */ ++static __efi_runtime int ffa_map_sp_event_runtime(int sp_event_ret) ++{ ++ int idx = -sp_event_ret; ++ ++ if (sp_event_ret == MM_SUCCESS) ++ return 0; ++ if (idx > 0 && idx < (int)ARRAY_SIZE(mm_sp_errmap) && ++ mm_sp_errmap[idx]) ++ return mm_sp_errmap[idx]; ++ return -EACCES; ++} ++ + /** + * ffa_notify_mm_sp() - Announce there is data in the shared buffer + * +@@ -225,6 +275,35 @@ static int ffa_notify_mm_sp(void) + return ret; + } + ++/** ++ * ffa_notify_mm_sp_runtime() - Runtime implementation of ++ * ffa_notify_mm_sp() ++ * ++ * Notify the MM partition in the trusted world that ++ * data is available in the shared buffer. ++ * This is a blocking call during which trusted world has exclusive access ++ * to the MM shared buffer. ++ * ++ * Return: ++ * ++ * 0 on success ++ */ ++static int __efi_runtime ffa_notify_mm_sp_runtime(void) ++{ ++ struct ffa_send_direct_data msg = {0}; ++ int ret; ++ int sp_event_ret; ++ ++ msg.data0 = CONFIG_FFA_SHARED_MM_BUF_OFFSET; ++ ++ ret = ffa_sync_send_receive_runtime(mm_sp_id, &msg, 1); ++ if (ret) ++ return ret; ++ ++ ret = ffa_map_sp_event_runtime(sp_event_ret); ++ return ret; ++} ++ + /** + * ffa_discover_mm_sp_id() - Query the MM partition ID + * +@@ -360,6 +439,116 @@ static efi_status_t ffa_mm_communicate(void *comm_buf, ulong comm_buf_size) + return efi_ret; + } + ++/** ++ * ffa_mm_communicate_runtime() - Runtime implementation of ffa_mm_communicate() ++ * @comm_buf: locally allocated communication buffer used for rx/tx ++ * @comm_buf_size: communication buffer size ++ * ++ * Issue a door bell event to notify the MM partition (SP) running in OP-TEE ++ * that there is data to read from the shared buffer. ++ * Communication with the MM SP is performed using FF-A transport. ++ * On the event, MM SP can read the data from the buffer and ++ * update the MM shared buffer with response data. ++ * The response data is copied back to the communication buffer. ++ * ++ * Return: ++ * ++ * EFI status code ++ */ ++static efi_status_t __efi_runtime ffa_mm_communicate_runtime(void *comm_buf, ++ ulong comm_buf_size) ++{ ++ ulong tx_data_size; ++ int ffa_ret; ++ efi_status_t efi_ret; ++ struct efi_mm_communicate_header *mm_hdr; ++ ++ if (!comm_buf) ++ return EFI_INVALID_PARAMETER; ++ ++ /* Discover MM partition ID at boot time */ ++ if (!mm_sp_id) ++ return EFI_UNSUPPORTED; ++ ++ mm_hdr = (struct efi_mm_communicate_header *)comm_buf; ++ tx_data_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t); ++ ++ if (comm_buf_size != tx_data_size || tx_data_size > CONFIG_FFA_SHARED_MM_BUF_SIZE) ++ return EFI_INVALID_PARAMETER; ++ ++ /* ++ * Shared buffer cache maintenance for FF-A / OP-TEE communication: ++ * ++ * NS -> S (request path): ++ * ++ * The non-secure side populates the shared buffer. If the buffer is cached ++ * in NS, the updated bytes may reside in dirty D-cache lines and not yet be ++ * visible in DDR. Since the secure world typically reads the shared buffer ++ * directly from DDR (e.g. with caches disabled / non-coherent mapping), we ++ * must clean the corresponding cache lines to the Point of Coherency (PoC) ++ * before entering secure world. ++ * ++ * S -> NS (response path): ++ * ++ * The secure world may update the same shared buffer in DDR. After returning ++ * to non-secure, any cached copies of that region in NS may be stale. We ++ * therefore invalidate the shared buffer range after the FF-A call to drop ++ * those lines and force subsequent reads to fetch the latest data from DDR. ++ * ++ * Note: Whole-cache invalidation must not be used in EFI runtime context. ++ * After ExitBootServices(), the OS owns the cache hierarchy; global invalidation ++ * could drop OS dirty lines and violate the OS coherency model. Always operate ++ * on the shared buffer range only. ++ */ ++ if (IS_ENABLED(CONFIG_ARM64)) ++ flush_dcache_range((unsigned long)comm_buf, ++ (unsigned long)((u8 *)comm_buf + ++ CONFIG_FFA_SHARED_MM_BUF_SIZE)); ++ ++ /* Announce there is data in the shared buffer */ ++ ++ ffa_ret = ffa_notify_mm_sp_runtime(); ++ ++ if (IS_ENABLED(CONFIG_ARM64)) ++ invalidate_dcache_range((unsigned long)comm_buf, ++ (unsigned long)((u8 *)comm_buf + ++ CONFIG_FFA_SHARED_MM_BUF_SIZE)); ++ ++ switch (ffa_ret) { ++ case 0: { ++ ulong rx_data_size; ++ ++ rx_data_size = ((struct efi_mm_communicate_header *)comm_buf)->message_len + ++ sizeof(efi_guid_t) + ++ sizeof(size_t); ++ ++ if (rx_data_size > comm_buf_size) { ++ efi_ret = EFI_OUT_OF_RESOURCES; ++ break; ++ } ++ ++ efi_ret = EFI_SUCCESS; ++ break; ++ } ++ case -EINVAL: ++ efi_ret = EFI_DEVICE_ERROR; ++ break; ++ case -EPERM: ++ efi_ret = EFI_INVALID_PARAMETER; ++ break; ++ case -EACCES: ++ efi_ret = EFI_ACCESS_DENIED; ++ break; ++ case -EBUSY: ++ efi_ret = EFI_OUT_OF_RESOURCES; ++ break; ++ default: ++ efi_ret = EFI_ACCESS_DENIED; ++ } ++ ++ return efi_ret; ++} ++ + /** + * get_mm_comms() - detect the available MM transport + * +@@ -386,6 +575,27 @@ static enum mm_comms_select get_mm_comms(void) + + return MM_COMMS_FFA; + } ++ ++/** ++ * get_mm_comms_runtime() - detect the available MM transport at runtime ++ * ++ * Make sure the FF-A bus is available at runtime and ready ++ * for use. ++ * ++ * Return: ++ * ++ * MM_COMMS_FFA or MM_COMMS_UNDEFINED ++ */ ++static enum mm_comms_select __efi_runtime get_mm_comms_runtime(void) ++{ ++ bool ret; ++ ++ ret = efi_is_runtime_enabled(); ++ if (!ret) ++ return MM_COMMS_UNDEFINED; ++ ++ return MM_COMMS_FFA; ++} + #endif + + /** +@@ -433,9 +643,86 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) + return var_hdr->ret_status; + } + ++/** ++ * mm_communicate_runtime() - Runtime implementation of mm_communicate() ++ * ++ * @comm_buf: locally allocated communication buffer ++ * @dsize: buffer size ++ * ++ * The SP (also called partition) can be any MM SP such as StandAlonneMM or smm-gateway. ++ * The comm_buf format is the same for both partitions. ++ * When using the u-boot OP-TEE driver, StandAlonneMM is supported. ++ * When using the u-boot FF-A driver, any MM SP is supported. ++ * ++ * Return: status code ++ */ ++static efi_status_t __efi_runtime mm_communicate_runtime(u8 *comm_buf, efi_uintn_t dsize) ++{ ++ efi_status_t ret = EFI_UNSUPPORTED; ++ struct efi_mm_communicate_header *mm_hdr; ++ struct smm_variable_communicate_header *var_hdr; ++ enum mm_comms_select mm_comms; ++ ++ dsize += MM_COMMUNICATE_HEADER_SIZE + MM_VARIABLE_COMMUNICATE_SIZE; ++ mm_hdr = (struct efi_mm_communicate_header *)comm_buf; ++ var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data; ++ ++ if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) { ++ mm_comms = get_mm_comms_runtime(); ++ if (mm_comms == MM_COMMS_FFA) ++ ret = ffa_mm_communicate_runtime(comm_buf, dsize); ++ } ++ ++ if (ret != EFI_SUCCESS) ++ return ret; ++ ++ return var_hdr->ret_status; ++} ++ ++/** ++ * get_comm_buf() - Obtain a communication buffer for MM/FF-A exchange ++ * @payload_size: size of the payload that will be appended to the ++ * MM communication header ++ * This helper returns a buffer suitable for constructing an ++ * EFI_MM_COMMUNICATE message. During the boot phase a new buffer is ++ * dynamically allocated. After ExitBootServices(), dynamic ++ * allocation is no longer permitted, and all runtime communication must ++ * use the statically reserved FF-A shared buffer. ++ * ++ * Return: ++ * Pointer to a valid communication buffer on success, ++ * NULL if allocation fails during the boot phase. ++ */ ++static __efi_runtime u8 *get_comm_buf(efi_uintn_t payload_size) ++{ ++ u8 *comm_buf; ++ ++ /* After ExitBootServices(), dynamic allocation is no longer permitted. ++ * Use the predefined FF-A shared buffer at runtime; otherwise allocate ++ * a fresh buffer during the boot phase. ++ */ ++ if (efi_is_runtime_enabled()) { ++ if (IS_ENABLED(CONFIG_ARM_FFA_RT_MODE)) { ++ comm_buf = ffa_shared_buf; ++ if (!comm_buf) ++ return NULL; ++ efi_memset_runtime(comm_buf, 0, CONFIG_FFA_SHARED_MM_BUF_SIZE); ++ } else { ++ return NULL; ++ } ++ } else { ++ comm_buf = calloc(1, MM_COMMUNICATE_HEADER_SIZE + ++ MM_VARIABLE_COMMUNICATE_SIZE + ++ payload_size); ++ if (!comm_buf) ++ return NULL; ++ } ++ return comm_buf; ++} ++ + /** + * setup_mm_hdr() - Allocate a buffer for StandAloneMM and initialize the +- * header data. ++ * header data. It is runtime safe. + * + * @dptr: pointer address of the corresponding StandAloneMM + * function +@@ -444,10 +731,9 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) + * @ret: EFI return code + * Return: buffer or NULL + */ +-static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size, +- efi_uintn_t func, efi_status_t *ret) ++static __efi_runtime u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size, ++ efi_uintn_t func, efi_status_t *ret) + { +- const efi_guid_t mm_var_guid = EFI_MM_VARIABLE_GUID; + struct efi_mm_communicate_header *mm_hdr; + struct smm_variable_communicate_header *var_hdr; + u8 *comm_buf; +@@ -465,16 +751,15 @@ static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size, + return NULL; + } + +- comm_buf = calloc(1, MM_COMMUNICATE_HEADER_SIZE + +- MM_VARIABLE_COMMUNICATE_SIZE + +- payload_size); ++ comm_buf = get_comm_buf(payload_size); + if (!comm_buf) { + *ret = EFI_OUT_OF_RESOURCES; + return NULL; + } + + mm_hdr = (struct efi_mm_communicate_header *)comm_buf; +- guidcpy(&mm_hdr->header_guid, &mm_var_guid); ++ efi_memcpy_runtime(&mm_hdr->header_guid, &mm_var_guid_runtime, ++ sizeof(mm_hdr->header_guid)); + mm_hdr->message_len = MM_VARIABLE_COMMUNICATE_SIZE + payload_size; + + var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data; +@@ -982,6 +1267,9 @@ void efi_variables_boot_exit_notify(void) + efi_get_next_variable_name_runtime; + efi_runtime_services.set_variable = efi_set_variable_runtime; + efi_update_table_header_crc32(&efi_runtime_services.hdr); ++ ++ /* Set efi_runtime_enabled as true after ExitBootServices */ ++ efi_runtime_enabled = true; + } + + /** +@@ -993,6 +1281,25 @@ efi_status_t efi_init_variables(void) + { + efi_status_t ret; + ++ if (IS_ENABLED(CONFIG_ARM_FFA_RT_MODE)) { ++ /* ++ * The FF-A shared buffer is accessed by EFI runtime services, so it must ++ * be marked as runtime memory in the EFI memory map. ++ */ ++ ffa_shared_buf = (void *)CONFIG_FFA_SHARED_MM_BUF_ADDR; ++ ret = efi_add_memory_map(CONFIG_FFA_SHARED_MM_BUF_ADDR, ++ CONFIG_FFA_SHARED_MM_BUF_SIZE, ++ EFI_RUNTIME_SERVICES_DATA); ++ if (ret != EFI_SUCCESS) { ++ log_err("EFI: failed to add FF-A shared buffer to runtime map (%lu)\n", ++ ret); ++ return ret; ++ } ++ log_info("EFI: FF-A shared buffer runtime map: addr=0x%lx size=0x%lx\n", ++ (ulong)CONFIG_FFA_SHARED_MM_BUF_ADDR, ++ (ulong)CONFIG_FFA_SHARED_MM_BUF_SIZE); ++ } ++ + /* Create a cached copy of the variables that will be enabled on ExitBootServices() */ + ret = efi_var_mem_init(); + if (ret != EFI_SUCCESS) diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0044-efi_loader-enable-EFI-runtime-SetVariable-GetVariabl.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0044-efi_loader-enable-EFI-runtime-SetVariable-GetVariabl.patch new file mode 100644 index 00000000..6a5748b8 --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0044-efi_loader-enable-EFI-runtime-SetVariable-GetVariabl.patch @@ -0,0 +1,418 @@ +From 119d43911b72f8c90b09e91b727d33d21d539c49 Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Thu, 11 Dec 2025 15:28:39 +0000 +Subject: [PATCH 44/53] efi_loader: enable EFI runtime + SetVariable()/GetVariable() using FF-A transport + +Route EFI runtime variable APIs through FF-A MM communication + +This patch implements full EFI Runtime Variable Services (GetVariable, +SetVariable, GetNextVariableName, and QueryVariableInfo) using the +FF-A/MM communication backend. Once ExitBootServices() has been invoked, +all variable operations now use the runtime-safe FF-A transport instead +of the boot-time communication paths. + +Key changes: +============ + + - Add runtime-safe variable property handling via FF-A MM SP. + - Add runtime versions of variable access and property operations. + - Replace all standard memory operations with EFI-runtime-safe variants. + +Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/] +Signed-off-by: Harsimran Singh Tungal +--- + lib/charset.c | 2 +- + lib/efi_loader/efi_variable_tee.c | 322 +++++++++++++++++++++++++++++- + 2 files changed, 318 insertions(+), 6 deletions(-) + +diff --git a/lib/charset.c b/lib/charset.c +index 182c92a50c4..738ad1352de 100644 +--- a/lib/charset.c ++++ b/lib/charset.c +@@ -407,7 +407,7 @@ size_t __efi_runtime u16_strnlen(const u16 *in, size_t count) + return i; + } + +-size_t u16_strsize(const void *in) ++size_t __efi_runtime u16_strsize(const void *in) + { + return (u16_strlen(in) + 1) * sizeof(u16); + } +diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c +index e4d97dc55ab..30687c21b8e 100644 +--- a/lib/efi_loader/efi_variable_tee.c ++++ b/lib/efi_loader/efi_variable_tee.c +@@ -859,6 +859,38 @@ out: + return ret; + } + ++static efi_status_t __efi_runtime set_property_int_runtime(const u16 *variable_name, ++ efi_uintn_t name_size, ++ const efi_guid_t *vendor, ++ struct var_check_property *var_property) ++{ ++ struct smm_variable_var_check_property *smm_property; ++ efi_uintn_t payload_size; ++ u8 *comm_buf = NULL; ++ efi_status_t ret; ++ ++ payload_size = sizeof(*smm_property) + name_size; ++ if (payload_size > max_payload_size) { ++ ret = EFI_INVALID_PARAMETER; ++ return ret; ++ } ++ comm_buf = setup_mm_hdr((void **)&smm_property, payload_size, ++ SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET, ++ &ret); ++ if (!comm_buf) ++ return ret; ++ ++ efi_memcpy_runtime(&smm_property->guid, vendor, sizeof(*vendor)); ++ smm_property->name_size = name_size; ++ efi_memcpy_runtime(&smm_property->property, var_property, ++ sizeof(smm_property->property)); ++ efi_memcpy_runtime(smm_property->name, variable_name, name_size); ++ ++ ret = mm_communicate_runtime(comm_buf, payload_size); ++ ++ return ret; ++} ++ + static efi_status_t get_property_int(const u16 *variable_name, + efi_uintn_t name_size, + const efi_guid_t *vendor, +@@ -904,6 +936,49 @@ out: + return ret; + } + ++static efi_status_t __efi_runtime get_property_int_runtime(const u16 *variable_name, ++ efi_uintn_t name_size, ++ const efi_guid_t *vendor, ++ struct var_check_property *var_property) ++{ ++ struct smm_variable_var_check_property *smm_property; ++ efi_uintn_t payload_size; ++ u8 *comm_buf = NULL; ++ efi_status_t ret; ++ ++ efi_memset_runtime(var_property, 0, sizeof(*var_property)); ++ payload_size = sizeof(*smm_property) + name_size; ++ if (payload_size > max_payload_size) { ++ ret = EFI_INVALID_PARAMETER; ++ return ret; ++ } ++ comm_buf = setup_mm_hdr((void **)&smm_property, payload_size, ++ SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET, ++ &ret); ++ if (!comm_buf) ++ return ret; ++ ++ efi_memcpy_runtime(&smm_property->guid, vendor, sizeof(smm_property->guid)); ++ smm_property->name_size = name_size; ++ efi_memcpy_runtime(smm_property->name, variable_name, name_size); ++ ++ ret = mm_communicate_runtime(comm_buf, payload_size); ++ /* ++ * Currently only R/O property is supported in StMM. ++ * Variables that are not set to R/O will not set the property in StMM ++ * and the call will return EFI_NOT_FOUND. We are setting the ++ * properties to 0x0 so checking against that is enough for the ++ * EFI_NOT_FOUND case. ++ */ ++ if (ret == EFI_NOT_FOUND) ++ ret = EFI_SUCCESS; ++ if (ret != EFI_SUCCESS) ++ return ret; ++ efi_memcpy_runtime(var_property, &smm_property->property, sizeof(*var_property)); ++ ++ return ret; ++} ++ + efi_status_t efi_get_variable_int(const u16 *variable_name, + const efi_guid_t *vendor, + u32 *attributes, efi_uintn_t *data_size, +@@ -991,6 +1066,92 @@ out: + return ret; + } + ++efi_status_t __efi_runtime efi_get_variable_runtime(u16 *variable_name, ++ const efi_guid_t *vendor, ++ u32 *attributes, ++ efi_uintn_t *data_size, ++ void *data) ++{ ++ struct var_check_property var_property; ++ struct smm_variable_access *var_acc; ++ efi_uintn_t payload_size; ++ efi_uintn_t name_size; ++ efi_uintn_t tmp_dsize; ++ u8 *comm_buf = NULL; ++ efi_status_t ret, tmp; ++ ++ if (!variable_name || !vendor || !data_size) { ++ ret = EFI_INVALID_PARAMETER; ++ return ret; ++ } ++ ++ /* Check payload size */ ++ name_size = u16_strsize(variable_name); ++ if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) { ++ ret = EFI_INVALID_PARAMETER; ++ return ret; ++ } ++ ++ /* Trim output buffer size */ ++ tmp_dsize = *data_size; ++ if (name_size + tmp_dsize > ++ max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) { ++ tmp_dsize = max_payload_size - ++ MM_VARIABLE_ACCESS_HEADER_SIZE - ++ name_size; ++ } ++ ++ /* Get communication buffer and initialize header */ ++ payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + tmp_dsize; ++ comm_buf = setup_mm_hdr((void **)&var_acc, payload_size, ++ SMM_VARIABLE_FUNCTION_GET_VARIABLE, &ret); ++ if (!comm_buf) ++ return ret; ++ ++ /* Fill in contents */ ++ efi_memcpy_runtime(&var_acc->guid, vendor, sizeof(var_acc->guid)); ++ var_acc->data_size = tmp_dsize; ++ var_acc->name_size = name_size; ++ var_acc->attr = attributes ? *attributes : 0; ++ efi_memcpy_runtime(var_acc->name, variable_name, name_size); ++ ++ /* Communicate */ ++ ret = mm_communicate_runtime(comm_buf, payload_size); ++ if (ret != EFI_SUCCESS && ret != EFI_BUFFER_TOO_SMALL) ++ return ret; ++ ++ /* Update with reported data size for trimmed case */ ++ *data_size = var_acc->data_size; ++ ++ /* Copy the data if ret is EFI_SUCCESS */ ++ if (ret == EFI_SUCCESS) { ++ if (data) ++ efi_memcpy_runtime(data, (u8 *)var_acc->name + var_acc->name_size, ++ var_acc->data_size); ++ else ++ ret = EFI_INVALID_PARAMETER; ++ } ++ ++ /* ++ * UEFI > 2.7 needs the attributes set even if the buffer is ++ * smaller ++ */ ++ if (attributes) { ++ tmp = get_property_int_runtime(variable_name, name_size, vendor, ++ &var_property); ++ if (tmp != EFI_SUCCESS) { ++ ret = tmp; ++ return ret; ++ } ++ *attributes = var_acc->attr; ++ if (var_property.property & ++ VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) ++ *attributes |= EFI_VARIABLE_READ_ONLY; ++ } ++ ++ return ret; ++} ++ + efi_status_t efi_get_next_variable_name_int(efi_uintn_t *variable_name_size, + u16 *variable_name, + efi_guid_t *guid) +@@ -1055,6 +1216,68 @@ out: + return ret; + } + ++efi_status_t __efi_runtime efi_get_next_variable_name_runtime(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; ++ u8 *comm_buf = NULL; ++ efi_status_t ret; ++ ++ if (!variable_name_size || !variable_name || !guid) { ++ ret = EFI_INVALID_PARAMETER; ++ return ret; ++ } ++ ++ out_name_size = *variable_name_size; ++ in_name_size = u16_strsize(variable_name); ++ ++ if (out_name_size < in_name_size) { ++ ret = EFI_INVALID_PARAMETER; ++ return ret; ++ } ++ ++ if (in_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) { ++ ret = EFI_INVALID_PARAMETER; ++ return ret; ++ } ++ ++ /* Trim output buffer size */ ++ if (out_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) ++ out_name_size = max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE; ++ ++ payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size; ++ comm_buf = setup_mm_hdr((void **)&var_getnext, payload_size, ++ SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME, ++ &ret); ++ if (!comm_buf) ++ return ret; ++ ++ /* Fill in contents */ ++ efi_memcpy_runtime(&var_getnext->guid, guid, sizeof(*guid)); ++ var_getnext->name_size = out_name_size; ++ efi_memcpy_runtime(var_getnext->name, variable_name, in_name_size); ++ efi_memset_runtime((u8 *)var_getnext->name + in_name_size, 0x0, ++ out_name_size - in_name_size); ++ ++ /* Communicate */ ++ ret = mm_communicate_runtime(comm_buf, payload_size); ++ if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) { ++ /* Update with reported data size for trimmed case */ ++ *variable_name_size = var_getnext->name_size; ++ } ++ if (ret != EFI_SUCCESS) ++ return ret; ++ ++ efi_memcpy_runtime(guid, &var_getnext->guid, sizeof(*guid)); ++ efi_memcpy_runtime(variable_name, var_getnext->name, var_getnext->name_size); ++ ++ return ret; ++} ++ + efi_status_t efi_set_variable_int(const u16 *variable_name, + const efi_guid_t *vendor, u32 attributes, + efi_uintn_t data_size, const void *data, +@@ -1197,11 +1420,11 @@ out: + * + * @attributes: bitmask to select variables to be + * queried +- * @maximum_variable_storage_size: maximum size of storage area for the ++ * @max_variable_storage_size: maximum size of storage area for the + * selected variable types +- * @remaining_variable_storage_size: remaining size of storage are for the ++ * @remain_variable_storage_size: remaining size of storage are for the + * selected variable types +- * @maximum_variable_size: maximum size of a variable of the ++ * @max_variable_size: maximum size of a variable of the + * selected type + * Return: status code + */ +@@ -1210,7 +1433,33 @@ efi_query_variable_info_runtime(u32 attributes, u64 *max_variable_storage_size, + u64 *remain_variable_storage_size, + u64 *max_variable_size) + { +- return EFI_UNSUPPORTED; ++ struct smm_variable_query_info *mm_query_info; ++ efi_uintn_t payload_size; ++ efi_status_t ret; ++ u8 *comm_buf; ++ ++ if (!max_variable_storage_size || ++ !remain_variable_storage_size || ++ !max_variable_size || !attributes) ++ return EFI_INVALID_PARAMETER; ++ ++ payload_size = sizeof(*mm_query_info); ++ comm_buf = setup_mm_hdr((void **)&mm_query_info, payload_size, ++ SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO, ++ &ret); ++ if (!comm_buf) ++ return ret; ++ ++ mm_query_info->attr = attributes; ++ ret = mm_communicate_runtime(comm_buf, payload_size); ++ if (ret != EFI_SUCCESS) ++ return ret; ++ *max_variable_storage_size = mm_query_info->max_variable_storage; ++ *remain_variable_storage_size = ++ mm_query_info->remaining_variable_storage; ++ *max_variable_size = mm_query_info->max_variable_size; ++ ++ return ret; + } + + /** +@@ -1228,7 +1477,70 @@ efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *guid, + u32 attributes, efi_uintn_t data_size, + const void *data) + { +- return EFI_UNSUPPORTED; ++ efi_status_t ret, mm_communicate_ret = EFI_SUCCESS; ++ struct var_check_property var_property; ++ struct smm_variable_access *var_acc; ++ efi_uintn_t payload_size; ++ efi_uintn_t name_size; ++ u8 *comm_buf = NULL; ++ bool ro; ++ ++ if (!variable_name || variable_name[0] == 0 || !guid) ++ return EFI_INVALID_PARAMETER; ++ ++ if (data_size > 0 && !data) ++ return EFI_INVALID_PARAMETER; ++ ++ /* Check payload size */ ++ name_size = u16_strsize(variable_name); ++ payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size; ++ if (payload_size > max_payload_size) ++ return EFI_INVALID_PARAMETER; ++ ++ ro = !!(attributes & EFI_VARIABLE_READ_ONLY); ++ attributes &= EFI_VARIABLE_MASK; ++ ++ ret = get_property_int_runtime(variable_name, name_size, guid, ++ &var_property); ++ if (ret != EFI_SUCCESS) ++ return ret; ++ ++ /* ++ * Allocate the buffer early, before switching to RW (if needed) ++ * so we won't need to account for any failures in reading/setting ++ * the properties, if the allocation fails ++ */ ++ comm_buf = setup_mm_hdr((void **)&var_acc, payload_size, ++ SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret); ++ if (!comm_buf) ++ return ret; ++ ++ if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) ++ return EFI_WRITE_PROTECTED; ++ ++ /* Fill in contents */ ++ efi_memcpy_runtime(&var_acc->guid, guid, sizeof(*guid)); ++ var_acc->data_size = data_size; ++ var_acc->name_size = name_size; ++ var_acc->attr = attributes; ++ efi_memcpy_runtime(var_acc->name, variable_name, name_size); ++ efi_memcpy_runtime((u8 *)var_acc->name + name_size, data, data_size); ++ ++ /* Communicate */ ++ ret = mm_communicate_runtime(comm_buf, payload_size); ++ if (ret != EFI_SUCCESS) ++ mm_communicate_ret = ret; ++ ++ if (ro && !(var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)) { ++ var_property.revision = VAR_CHECK_VARIABLE_PROPERTY_REVISION; ++ var_property.property |= VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY; ++ var_property.attributes = attributes; ++ var_property.minsize = 1; ++ var_property.maxsize = var_acc->data_size; ++ ret = set_property_int_runtime(variable_name, name_size, guid, &var_property); ++ } ++ ++ return (mm_communicate_ret == EFI_SUCCESS) ? ret : mm_communicate_ret; + } + + /** diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0045-efi_loader-move-runtime-GetVariable-helpers-to-efi_v.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0045-efi_loader-move-runtime-GetVariable-helpers-to-efi_v.patch new file mode 100644 index 00000000..f3da408a --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0045-efi_loader-move-runtime-GetVariable-helpers-to-efi_v.patch @@ -0,0 +1,103 @@ +From 5c2b1f517c97f3349108bfd9fef9aac40ba236ac Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Fri, 12 Dec 2025 10:59:47 +0000 +Subject: [PATCH 45/53] efi_loader: move runtime GetVariable() helpers to + efi_variable.c + +Consolidate runtime GetVariable helpers to avoid duplicates + +The functions efi_get_variable_runtime() and +efi_get_next_variable_name_runtime() were implemented in +efi_variable_tee.c as part of the FF-A runtime variable handling work. +However, default implementations for these same runtime helpers also +exist in efi_var_common.c. This results in duplicate symbol definitions. +To resolve the conflict and centralize the runtime API, this patch moves +the default implementations of the two runtime GetVariable() helpers +from efi_var_common.c to efi_variable.c. This ensures that: + + - only a single definition of each runtime helper exists, + - backend-specific implementations can override these cleanly, + - the EFI runtime variable service table continues to reference the + correct functions. + +No functional changes are introduced; this is a structural cleanup to +avoid build-time conflicts and consolidate EFI runtime variable support +in the appropriate file. + +Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/] +Signed-off-by: Harsimran Singh Tungal +--- + lib/efi_loader/efi_var_common.c | 24 ------------------------ + lib/efi_loader/efi_variable.c | 24 ++++++++++++++++++++++++ + 2 files changed, 24 insertions(+), 24 deletions(-) + +diff --git a/lib/efi_loader/efi_var_common.c b/lib/efi_loader/efi_var_common.c +index 4b34a58b4cf..0eaf89112e6 100644 +--- a/lib/efi_loader/efi_var_common.c ++++ b/lib/efi_loader/efi_var_common.c +@@ -172,30 +172,6 @@ efi_status_t EFIAPI efi_query_variable_info( + return EFI_EXIT(ret); + } + +-efi_status_t __efi_runtime EFIAPI +-efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *guid, +- u32 *attributes, efi_uintn_t *data_size, void *data) +-{ +- efi_status_t ret; +- +- ret = efi_get_variable_mem(variable_name, guid, attributes, data_size, +- data, NULL, EFI_VARIABLE_RUNTIME_ACCESS); +- +- /* Remove EFI_VARIABLE_READ_ONLY flag */ +- if (attributes) +- *attributes &= EFI_VARIABLE_MASK; +- +- return ret; +-} +- +-efi_status_t __efi_runtime EFIAPI +-efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size, +- u16 *variable_name, efi_guid_t *guid) +-{ +- return efi_get_next_variable_name_mem(variable_name_size, variable_name, +- guid, EFI_VARIABLE_RUNTIME_ACCESS); +-} +- + /** + * efi_set_secure_state - modify secure boot state variables + * @secure_boot: value of SecureBoot +diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c +index f3533f4def3..a688c6da58d 100644 +--- a/lib/efi_loader/efi_variable.c ++++ b/lib/efi_loader/efi_variable.c +@@ -566,6 +566,30 @@ efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *vendor, + return EFI_SUCCESS; + } + ++efi_status_t __efi_runtime EFIAPI ++efi_get_variable_runtime(u16 *variable_name, const efi_guid_t *guid, ++ u32 *attributes, efi_uintn_t *data_size, void *data) ++{ ++ efi_status_t ret; ++ ++ ret = efi_get_variable_mem(variable_name, guid, attributes, data_size, ++ data, NULL, EFI_VARIABLE_RUNTIME_ACCESS); ++ ++ /* Remove EFI_VARIABLE_READ_ONLY flag */ ++ if (attributes) ++ *attributes &= EFI_VARIABLE_MASK; ++ ++ return ret; ++} ++ ++efi_status_t __efi_runtime EFIAPI ++efi_get_next_variable_name_runtime(efi_uintn_t *variable_name_size, ++ u16 *variable_name, efi_guid_t *guid) ++{ ++ return efi_get_next_variable_name_mem(variable_name_size, variable_name, ++ guid, EFI_VARIABLE_RUNTIME_ACCESS); ++} ++ + /** + * efi_variables_boot_exit_notify() - notify ExitBootServices() is called + */ diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0046-corstone1000-enable-bootefi-selftest.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0046-corstone1000-enable-bootefi-selftest.patch new file mode 100644 index 00000000..c1dc141a --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0046-corstone1000-enable-bootefi-selftest.patch @@ -0,0 +1,38 @@ +From 4c8504fe21f91bf462486afdfe4113f8bbc9f930 Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Sun, 11 Jan 2026 20:34:13 +0000 +Subject: [PATCH 46/53] corstone1000: enable bootefi selftest + +Enable UEFI selftest command in Corstone-1000 defconfig + +Turn on CONFIG_CMD_BOOTEFI_SELFTEST in the Corstone-1000 defconfig +so the board can run the built-in UEFI self-test suite during +development and validation. + +Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/] +Signed-off-by: Harsimran Singh Tungal +--- + configs/corstone1000_defconfig | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig +index 350607892fa..8de2f9bd440 100644 +--- a/configs/corstone1000_defconfig ++++ b/configs/corstone1000_defconfig +@@ -41,6 +41,8 @@ CONFIG_SYS_PROMPT="corstone1000# " + # CONFIG_CMD_CONSOLE is not set + CONFIG_CMD_FWU_METADATA=y + CONFIG_CMD_BOOTZ=y ++# CONFIG_CMD_BOOTEFI_HELLO is not set ++CONFIG_CMD_BOOTEFI_SELFTEST=y + # CONFIG_CMD_XIMG is not set + CONFIG_CMD_NVEDIT_EFI=y + CONFIG_CMD_GPT=y +@@ -79,6 +81,7 @@ CONFIG_SYSRESET=y + CONFIG_SYSRESET_PSCI=y + CONFIG_TEE=y + CONFIG_OPTEE=y ++# CONFIG_CMD_POWEROFF is not set + CONFIG_USB=y + CONFIG_USB_ISP1760=y + CONFIG_VIRTIO_MMIO=y diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0047-efi-selftest-add-runtime-variable-tests-with-non-vol.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0047-efi-selftest-add-runtime-variable-tests-with-non-vol.patch new file mode 100644 index 00000000..b24f128e --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0047-efi-selftest-add-runtime-variable-tests-with-non-vol.patch @@ -0,0 +1,176 @@ +From 85edddc46913a19ce9e2dd0cb802a2dac60c9b9d Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Sun, 11 Jan 2026 20:36:45 +0000 +Subject: [PATCH 47/53] efi: selftest: add runtime variable tests with + non-volatile storage + +Extend runtime variable tests for persistent storage + +Previously, EFI selftesting of runtime variables was only supported +under CONFIG_EFI_RT_VOLATILE_STORE, which uses VarToFile to simulate +non-volatile variable storage. This commit adds new test cases that +exercise runtime variable operations for persistent storage. + +Features tested: +- Creation of runtime-accessible variables (set) +- Retrieval of runtime variables (get) +- Deletion using SetVariable() with size = 0 +- Append operation using EFI_VARIABLE_APPEND_WRITE + +This improves EFI compliance validation for non-volatile runtime storage +scenarios and ensures proper attribute enforcement and variable +management. + +Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/] +Signed-off-by: Harsimran Singh Tungal +--- + .../efi_selftest_variables_runtime.c | 108 +++++++++++++++++- + 1 file changed, 105 insertions(+), 3 deletions(-) + +diff --git a/lib/efi_selftest/efi_selftest_variables_runtime.c b/lib/efi_selftest/efi_selftest_variables_runtime.c +index 379c4f9c47b..7c14b9fefd4 100644 +--- a/lib/efi_selftest/efi_selftest_variables_runtime.c ++++ b/lib/efi_selftest/efi_selftest_variables_runtime.c +@@ -3,6 +3,7 @@ + * efi_selftest_variables_runtime + * + * Copyright (c) 2019 Heinrich Schuchardt ++ * Copyright (c) 2026 Arm Limited and/or its affiliates + * + * This unit test checks the runtime services for variables after + * ExitBootServices(): +@@ -40,7 +41,13 @@ static int setup(const efi_handle_t img_handle, + /** + * execute() - execute unit test + * +- * As runtime support is not implmented expect EFI_UNSUPPORTED to be returned. ++ * For EFI variables in non-volatile storage, these tests have to be executed in two phases ++ * 1. During first phase, run these tests and it creates EFI variable in persistent storage. ++ * 2. Then reboot and run the test again to verify if the variable created above is still ++ * available in non-volatile storage. If available, validate the EFI variable, append ++ * it, and validate it again. If validation is successful, delete the same variable. ++ * ++ * Return: EFI_ST_SUCCESS on success, EFI_ST_FAILURE on failure + */ + static int execute(void) + { +@@ -57,6 +64,80 @@ static int execute(void) + u64 max_storage, rem_storage, max_size; + int test_ret; + ++ /* Compare the value of EFI variable if it already exists in non volatile storage */ ++ if (!IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) { ++ len = sizeof(v) / 2; ++ ret = runtime->get_variable(u"efi_st_var0", &guid_vendor0, ++ &attr, &len, data); ++ if (ret == EFI_SUCCESS) { ++ efi_st_printf("EFI Variable efi_st_var0 found. Executing Second Phase\n"); ++ if (len != sizeof(v) / 2) { ++ efi_st_error("GetVariable failed\n"); ++ return EFI_ST_FAILURE; ++ } ++ if (memcmp(data, v, len)) { ++ efi_st_error("GetVariable failed\n"); ++ return EFI_ST_FAILURE; ++ } ++ ++ /* Append an existing variable */ ++ append_len = sizeof(v) - len; ++ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, ++ EFI_VARIABLE_BOOTSERVICE_ACCESS | ++ EFI_VARIABLE_RUNTIME_ACCESS | ++ EFI_VARIABLE_APPEND_WRITE | ++ EFI_VARIABLE_NON_VOLATILE, ++ append_len, (v + len)); ++ if (ret != EFI_SUCCESS) { ++ efi_st_error("SetVariable failed\n"); ++ return EFI_ST_FAILURE; ++ } ++ ++ len = sizeof(v); ++ ret = runtime->get_variable(u"efi_st_var0", &guid_vendor0, ++ &attr, &len, data); ++ if (ret != EFI_SUCCESS) { ++ efi_st_error("GetVariable failed\n"); ++ return EFI_ST_FAILURE; ++ } ++ ++ if (len != sizeof(v)) { ++ efi_st_error("GetVariable failed\n"); ++ return EFI_ST_FAILURE; ++ } ++ ++ if (memcmp(data, v, len)) { ++ efi_st_error("GetVariable failed\n"); ++ return EFI_ST_FAILURE; ++ } ++ ++ /* Delete it by setting the size to 0 */ ++ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, ++ EFI_VARIABLE_BOOTSERVICE_ACCESS | ++ EFI_VARIABLE_RUNTIME_ACCESS | ++ EFI_VARIABLE_NON_VOLATILE, ++ 0, NULL); ++ if (ret != EFI_SUCCESS) { ++ efi_st_error("SetVariable failed\n"); ++ return EFI_ST_FAILURE; ++ } ++ ++ ret = runtime->get_variable(u"efi_st_var0", &guid_vendor0, ++ &attr, &len, data); ++ if (ret != EFI_NOT_FOUND) { ++ efi_st_error("GetVariable failed\n"); ++ return EFI_ST_FAILURE; ++ } ++ ++ return EFI_ST_SUCCESS; ++ } else { ++ if (ret == EFI_NOT_FOUND) { ++ efi_st_printf("EFI Variable efi_st_var0 not found. " ++ "Executing First Phase\n"); ++ } ++ } ++ } ++ + memset(v2, 0x1, sizeof(v2)); + + if (IS_ENABLED(CONFIG_EFI_VARIABLE_FILE_STORE)) { +@@ -70,7 +151,7 @@ static int execute(void) + ret = runtime->query_variable_info(EFI_VARIABLE_BOOTSERVICE_ACCESS, + &max_storage, &rem_storage, + &max_size); +- if (ret != EFI_UNSUPPORTED) { ++ if (ret != EFI_ST_SUCCESS) { + efi_st_error("QueryVariableInfo failed\n"); + return EFI_ST_FAILURE; + } +@@ -286,7 +367,28 @@ static int execute(void) + return EFI_ST_FAILURE; + } + } else { +- if (ret != EFI_UNSUPPORTED) { ++ if (ret != EFI_SUCCESS) { ++ efi_st_error("SetVariable failed\n"); ++ return EFI_ST_FAILURE; ++ } ++ ++ /* Delete it by setting the size to 0 */ ++ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, ++ EFI_VARIABLE_BOOTSERVICE_ACCESS | ++ EFI_VARIABLE_RUNTIME_ACCESS, ++ 0, NULL); ++ if (ret != EFI_SUCCESS) { ++ efi_st_error("SetVariable failed\n"); ++ return EFI_ST_FAILURE; ++ } ++ ++ /* Add an 8byte aligned variable */ ++ ret = runtime->set_variable(u"efi_st_var0", &guid_vendor0, ++ EFI_VARIABLE_BOOTSERVICE_ACCESS | ++ EFI_VARIABLE_RUNTIME_ACCESS | ++ EFI_VARIABLE_NON_VOLATILE, ++ sizeof(v) / 2, v); ++ if (ret != EFI_SUCCESS) { + efi_st_error("SetVariable failed\n"); + return EFI_ST_FAILURE; + } diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0048-test-dm-add-sandbox-FF-A-runtime-transport-tests.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0048-test-dm-add-sandbox-FF-A-runtime-transport-tests.patch new file mode 100644 index 00000000..8682a181 --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0048-test-dm-add-sandbox-FF-A-runtime-transport-tests.patch @@ -0,0 +1,163 @@ +From d12b38b7af88fdb37487d5196e214c8a4ad561fc Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Wed, 14 Jan 2026 13:55:02 +0000 +Subject: [PATCH 48/53] test: dm: add sandbox FF-A runtime transport tests + +Exercise FF-A runtime helpers via sandbox DM tests + +Add driver-model unit tests that exercise the FF-A runtime helpers on +sandbox. The new tests reuse the sandbox emulator to validate both the +positive direct-request flow and failure handling, priming the emulator +state so the runtime path can be executed. + +Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/] +Signed-off-by: Harsimran Singh Tungal +--- + arch/sandbox/include/asm/sandbox_arm_ffa.h | 16 ++++- + test/dm/Makefile | 3 +- + test/dm/ffa_runtime.c | 82 ++++++++++++++++++++++ + 3 files changed, 99 insertions(+), 2 deletions(-) + create mode 100644 test/dm/ffa_runtime.c + +diff --git a/arch/sandbox/include/asm/sandbox_arm_ffa.h b/arch/sandbox/include/asm/sandbox_arm_ffa.h +index be2790f4960..a20eb159b73 100644 +--- a/arch/sandbox/include/asm/sandbox_arm_ffa.h ++++ b/arch/sandbox/include/asm/sandbox_arm_ffa.h +@@ -1,6 +1,6 @@ + /* SPDX-License-Identifier: GPL-2.0+ */ + /* +- * Copyright 2022-2023 Arm Limited and/or its affiliates ++ * Copyright 2022-2023, 2026 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi +@@ -26,6 +26,20 @@ + #define SANDBOX_SP3_ID 0x6452 + #define SANDBOX_SP4_ID 0x7814 + ++/* ++ * The sandbox FF-A emulator uses fixed, synthetic execution-context counts and ++ * property bitfields for each partition. ++ */ ++#define SANDBOX_SP1_EXEC_CTXT 0x5687 ++#define SANDBOX_SP2_EXEC_CTXT 0x9587 ++#define SANDBOX_SP3_EXEC_CTXT 0x7687 ++#define SANDBOX_SP4_EXEC_CTXT 0x1487 ++ ++#define SANDBOX_SP1_PROPERTIES 0x89325621 ++#define SANDBOX_SP2_PROPERTIES 0x45325621 ++#define SANDBOX_SP3_PROPERTIES 0x23325621 ++#define SANDBOX_SP4_PROPERTIES 0x70325621 ++ + /* Invalid service UUID (no matching SP) */ + #define SANDBOX_SERVICE3_UUID "55d532ed-0942-e699-722d-c09ca798d9cd" + +diff --git a/test/dm/Makefile b/test/dm/Makefile +index 474e77a2151..218b0b1411f 100644 +--- a/test/dm/Makefile ++++ b/test/dm/Makefile +@@ -1,7 +1,7 @@ + # SPDX-License-Identifier: GPL-2.0+ + # + # Copyright (c) 2013 Google, Inc +-# Copyright 2022-2023 Arm Limited and/or its affiliates ++# Copyright 2022-2023, 2026 Arm Limited and/or its affiliates + + # Tests for particular subsystems - when enabling driver model for a new + # subsystem you must add sandbox tests here. +@@ -95,6 +95,7 @@ obj-$(CONFIG_ACPI_PMC) += pmc.o + obj-$(CONFIG_DM_PMIC) += pmic.o + obj-$(CONFIG_DM_PWM) += pwm.o + obj-$(CONFIG_ARM_FFA_TRANSPORT) += ffa.o ++obj-$(CONFIG_ARM_FFA_TRANSPORT) += ffa_runtime.o + obj-$(CONFIG_QFW) += qfw.o + obj-$(CONFIG_RAM) += ram.o + obj-y += regmap.o +diff --git a/test/dm/ffa_runtime.c b/test/dm/ffa_runtime.c +new file mode 100644 +index 00000000000..4ba859fa314 +--- /dev/null ++++ b/test/dm/ffa_runtime.c +@@ -0,0 +1,82 @@ ++// SPDX-License-Identifier: GPL-2.0+ ++/* ++ * Functional tests for FF-A runtime helpers ++ * ++ * Copyright 2026 Arm Limited and/or its affiliates ++ * ++ * Authors: ++ * Harsimran Singh Tungal ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int ffa_runtime_get_sp_id(struct unit_test_state *uts, u16 *sp_id) ++{ ++ struct ffa_partition_desc *descs; ++ u32 count; ++ struct udevice *dev; ++ const char *svc_uuid = SANDBOX_SERVICE1_UUID; ++ ++ ut_assertok(uclass_first_device_err(UCLASS_FFA, &dev)); ++ ut_assertok(ffa_partition_info_get(dev, svc_uuid, &count, &descs)); ++ ut_assert(count > 0); ++ ++ *sp_id = descs[0].info.id; ++ return 0; ++} ++ ++static int dm_test_ffa_runtime_ack(struct unit_test_state *uts) ++{ ++ struct ffa_send_direct_data msg = {0}; ++ u16 sp_id; ++ u8 cnt; ++ ++ ut_assertok(ffa_runtime_get_sp_id(uts, &sp_id)); ++ ++ ffa_copy_runtime_priv(&(struct ffa_priv_runtime){ ++ .id = NS_PHYS_ENDPOINT_ID, ++ }); ++ ffa_enable_runtime_context(); ++ ++ /* Ensure runtime context is available before attempting runtime-only paths */ ++ ut_assert(ffa_get_status_runtime_context()); ++ ++ /* Runtime messaging should reuse the sandbox emulator and return 0xff pattern */ ++ ut_assertok(ffa_sync_send_receive_runtime(sp_id, &msg, true)); ++ for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); cnt++) ++ ut_asserteq_64(-1UL, ((u64 *)&msg)[cnt]); ++ ++ /* Validate FF-A error to errno translation helpers */ ++ ut_asserteq(-EINVAL, ffa_to_std_errno(-INVALID_PARAMETERS)); ++ ut_asserteq(-EOPNOTSUPP, ffa_to_std_errno(-NOT_SUPPORTED)); ++ ++ return 0; ++} ++DM_TEST(dm_test_ffa_runtime_ack, UTF_SCAN_FDT | UTF_CONSOLE); ++ ++static int dm_test_ffa_runtime_nack(struct unit_test_state *uts) ++{ ++ struct ffa_send_direct_data msg = {0}; ++ u16 sp_id; ++ ++ ut_assertok(ffa_runtime_get_sp_id(uts, &sp_id)); ++ ++ ffa_copy_runtime_priv(&(struct ffa_priv_runtime){ ++ .id = NS_PHYS_ENDPOINT_ID, ++ }); ++ ffa_enable_runtime_context(); ++ ++ /* Ensure runtime context is available before attempting runtime-only paths */ ++ ut_assert(ffa_get_status_runtime_context()); ++ ++ /* Invalid partition IDs must be rejected and mapped to -EINVAL */ ++ ut_asserteq(-EINVAL, ffa_sync_send_receive_runtime(0, &msg, true)); ++ ++ return 0; ++} ++DM_TEST(dm_test_ffa_runtime_nack, UTF_SCAN_FDT | UTF_CONSOLE); diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0049-sandbox-ffa-share-synthetic-partition-metadata-via-m.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0049-sandbox-ffa-share-synthetic-partition-metadata-via-m.patch new file mode 100644 index 00000000..f807be49 --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0049-sandbox-ffa-share-synthetic-partition-metadata-via-m.patch @@ -0,0 +1,96 @@ +From 6e707fe6a0120a3a6aa7954a250293162a441b21 Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Thu, 15 Jan 2026 10:57:48 +0000 +Subject: [PATCH 49/53] sandbox: ffa: share synthetic partition metadata via + macros + +Reuse sandbox FF-A partition constants in emulator tests + +Allow the sandbox FF-A emulator test to use execution-context and property +constants defined in sandbox_arm_ffa.h + +Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/] +Signed-off-by: Harsimran Singh Tungal +--- + drivers/firmware/arm-ffa/ffa-emul-uclass.c | 36 ++++++++++++++++------ + 1 file changed, 26 insertions(+), 10 deletions(-) + +diff --git a/drivers/firmware/arm-ffa/ffa-emul-uclass.c b/drivers/firmware/arm-ffa/ffa-emul-uclass.c +index 531ecb12f3a..33d38d37ddb 100644 +--- a/drivers/firmware/arm-ffa/ffa-emul-uclass.c ++++ b/drivers/firmware/arm-ffa/ffa-emul-uclass.c +@@ -1,6 +1,6 @@ + // SPDX-License-Identifier: GPL-2.0+ + /* +- * Copyright 2022-2023, 2025 Arm Limited and/or its affiliates ++ * Copyright 2022-2023, 2025-2026 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi +@@ -22,41 +22,57 @@ DECLARE_GLOBAL_DATA_PTR; + /* The partitions (SPs) table */ + static struct ffa_partition_desc sandbox_partitions[SANDBOX_PARTITIONS_CNT] = { + { +- .info = { .id = SANDBOX_SP1_ID, .exec_ctxt = 0x5687, .properties = 0x89325621 }, ++ .info = { ++ .id = SANDBOX_SP1_ID, ++ .exec_ctxt = SANDBOX_SP1_EXEC_CTXT, ++ .properties = SANDBOX_SP1_PROPERTIES, ++ }, + .sp_uuid = { + .a1 = SANDBOX_SERVICE1_UUID_A1, + .a2 = SANDBOX_SERVICE1_UUID_A2, + .a3 = SANDBOX_SERVICE1_UUID_A3, + .a4 = SANDBOX_SERVICE1_UUID_A4, +- } ++ }, + }, + { +- .info = { .id = SANDBOX_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 }, ++ .info = { ++ .id = SANDBOX_SP2_ID, ++ .exec_ctxt = SANDBOX_SP2_EXEC_CTXT, ++ .properties = SANDBOX_SP2_PROPERTIES, ++ }, + .sp_uuid = { + .a1 = SANDBOX_SERVICE2_UUID_A1, + .a2 = SANDBOX_SERVICE2_UUID_A2, + .a3 = SANDBOX_SERVICE2_UUID_A3, + .a4 = SANDBOX_SERVICE2_UUID_A4, +- } ++ }, + }, + { +- .info = { .id = SANDBOX_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 }, ++ .info = { ++ .id = SANDBOX_SP3_ID, ++ .exec_ctxt = SANDBOX_SP3_EXEC_CTXT, ++ .properties = SANDBOX_SP3_PROPERTIES, ++ }, + .sp_uuid = { + .a1 = SANDBOX_SERVICE1_UUID_A1, + .a2 = SANDBOX_SERVICE1_UUID_A2, + .a3 = SANDBOX_SERVICE1_UUID_A3, + .a4 = SANDBOX_SERVICE1_UUID_A4, +- } ++ }, + }, + { +- .info = { .id = SANDBOX_SP4_ID, .exec_ctxt = 0x1487, .properties = 0x70325621 }, ++ .info = { ++ .id = SANDBOX_SP4_ID, ++ .exec_ctxt = SANDBOX_SP4_EXEC_CTXT, ++ .properties = SANDBOX_SP4_PROPERTIES, ++ }, + .sp_uuid = { + .a1 = SANDBOX_SERVICE2_UUID_A1, + .a2 = SANDBOX_SERVICE2_UUID_A2, + .a3 = SANDBOX_SERVICE2_UUID_A3, + .a4 = SANDBOX_SERVICE2_UUID_A4, +- } +- } ++ }, ++ }, + + }; + diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0050-doc-arm64-document-FF-A-runtime-path-for-EFI-variabl.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0050-doc-arm64-document-FF-A-runtime-path-for-EFI-variabl.patch new file mode 100644 index 00000000..f4107244 --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0050-doc-arm64-document-FF-A-runtime-path-for-EFI-variabl.patch @@ -0,0 +1,176 @@ +From a4d871a16513e86290aaa2304f0195e533200937 Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Wed, 25 Feb 2026 14:40:19 +0000 +Subject: [PATCH 50/53] doc: arm64: document FF-A runtime path for EFI + variables + +Document how EFI runtime variables use FF-A MM communication + +Describe the FF-A runtime layer on arm64 and how EFI variable +runtime services use the FF-A transport and shared MM buffer. +Also clarify armffa command scope and link to the FF-A doc. + +Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/] +Signed-off-by: Harsimran Singh Tungal +--- + doc/arch/arm64.ffa.rst | 92 +++++++++++++++++++++++++++++++++++++--- + doc/usage/cmd/armffa.rst | 11 +++++ + 2 files changed, 96 insertions(+), 7 deletions(-) + +diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst +index d2c4fb49f79..53218cd9c96 100644 +--- a/doc/arch/arm64.ffa.rst ++++ b/doc/arch/arm64.ffa.rst +@@ -15,10 +15,10 @@ application in S-EL0, or a Trusted OS in S-EL1. + The U-Boot FF-A support (the bus) implements the interfaces to communicate + with partitions in the Secure world aka Secure partitions (SPs). + +-The FF-A support specifically focuses on communicating with SPs that +-isolate portions of EFI runtime services that must run in a protected +-environment which is inaccessible by the Host OS or Hypervisor. +-Examples of such services are set/get variables. ++U-Boot's FF-A bus support exposes an optional transport that EFI runtime ++services can use to communicate with Secure Partitions. Through this ++interface, EFI services (such as variable access) can request or exchange ++data with the Secure World using FF-A. + + The FF-A support uses the SMC ABIs defined by the FF-A specification to: + +@@ -26,13 +26,13 @@ The FF-A support uses the SMC ABIs defined by the FF-A specification to: + - Access an SP's service through communication protocols + e.g. EFI MM communication protocol + +-At this stage of development only EFI boot-time services are supported. +-Runtime support will be added in future developments. +- + The U-Boot FF-A support provides the following parts: + + - A Uclass driver providing generic FF-A methods. + - An Arm FF-A device driver providing Arm-specific methods and reusing the Uclass methods. ++- An optional runtime FF-A transport (toggled via CONFIG_ARM_FFA_RT_MODE) that is ++ resident after ExitBootServices() and enables EFI runtime variable services ++ via FF-A helpers and a shared MM communication buffer. + - A sandbox emulator for Arm FF-A, emulates the FF-A side of the Secure World and provides + FF-A ABIs inspection methods. + - An FF-A sandbox device driver for FF-A communication with the emulated Secure World. +@@ -69,6 +69,12 @@ CONFIG_ARM_FFA_TRANSPORT + When using an Arm 64-bit platform, the Arm FF-A driver will be used. + When using sandbox, the sandbox FF-A emulator and FF-A sandbox driver will be used. + ++CONFIG_ARM_FFA_RT_MODE ++ Enables the FF-A runtime transport. This option depends on ++ CONFIG_ARM_FFA_TRANSPORT and CONFIG_EFI_LOADER, and is enabled by default. ++ When enabled, a minimal set of FF-A operations remains available after ++ ExitBootServices(). ++ + FF-A ABIs under the hood + ------------------------ + +@@ -158,6 +164,77 @@ they want to use (32-bit vs 64-bit). Selecting the protocol means using + the 32-bit or 64-bit version of FFA_MSG_SEND_DIRECT_{REQ, RESP}. + The calling convention between U-Boot and the secure world stays the same: SMC32. + ++FF-A runtime support for EFI variables ++-------------------------------------- ++ ++U-Boot provides an FF-A runtime transport to keep a minimal set of FF-A operations ++available after ExitBootServices(). This runtime transport is enabled with ++CONFIG_ARM_FFA_RT_MODE. The runtime helpers and data are marked with ++``__efi_runtime`` / ``__efi_runtime_data`` through the ++``__ffa_runtime`` / ``__ffa_runtime_data`` aliases in ++``include/arm_ffa_runtime.h``. ++ ++Runtime context is enabled at ExitBootServices(), and the following prerequisites ++are prepared during FF-A bus probe: ++ ++- The U-Boot FF-A endpoint ID is discovered at boot time with FFA_ID_GET and ++ stored in the runtime private data. ++- An ExitBootServices() event is registered by the runtime transport to enable ++ the runtime context with ffa_enable_runtime_context() when EFI transitions ++ to runtime. ++ ++At runtime, the driver model is no longer available. Runtime users should ++use the runtime interface ffa_sync_send_receive_runtime(), which internally ++verifies the runtime context before issuing FF-A calls. ++ ++EFI variable services over FF-A ++------------------------------- ++ ++When CONFIG_EFI_MM_COMM_TEE and CONFIG_ARM_FFA_TRANSPORT are enabled, U-Boot ++routes EFI variable services to an MM Secure Partition (SP) over FF-A. ++The MM SP is discovered at boot time using FFA_PARTITION_INFO_GET and its ++partition ID is cached in runtime data. ++ ++The data path uses a shared MM communication buffer and a door-bell style ++direct message: ++ ++- The communication buffer is located at CONFIG_FFA_SHARED_MM_BUF_ADDR and ++ sized by CONFIG_FFA_SHARED_MM_BUF_SIZE. ++- CONFIG_FFA_SHARED_MM_BUF_OFFSET is sent in the FF-A direct message payload ++ to indicate where the data begins. ++- The buffer is filled by memcpy(), the cache is flushed before notifying the ++ MM SP, and later the buffer is reused directly with only the shared-buffer ++ range invalidated (invalidate_dcache_range()) to avoid whole-cache ++ invalidation. ++ ++After ExitBootServices(), EFI SetVariable()/GetVariable() call paths use the ++runtime MM communication helpers: ++ ++- get_comm_buf() switches to the static shared buffer ++- mm_communicate_runtime() selects FF-A transport when the runtime context ++ is enabled ++- ffa_mm_communicate_runtime() issues FFA_MSG_SEND_DIRECT_{REQ,RESP} through ++ ffa_sync_send_receive_runtime() ++ ++Configuration notes ++------------------- ++ ++Enable FF-A transport and MM communication: ++ ++- CONFIG_ARM_FFA_TRANSPORT ++- CONFIG_ARM_FFA_RT_MODE ++- CONFIG_EFI_MM_COMM_TEE ++ ++CONFIG_ARM_FFA_RT_MODE is enabled by default. It turns on MM communication for ++EFI runtime and may be disabled when EFI services over FF-A are not required ++after ExitBootServices(). ++ ++Configure the shared MM communication buffer: ++ ++- CONFIG_FFA_SHARED_MM_BUF_ADDR ++- CONFIG_FFA_SHARED_MM_BUF_SIZE ++- CONFIG_FFA_SHARED_MM_BUF_OFFSET ++ + Requirements for user drivers + ----------------------------- + +@@ -260,6 +337,7 @@ For example, when using FF-A with Corstone-1000, debug logs enabled, the output + Contributors + ------------ + * Abdellatif El Khlifi ++ * Harsimran Singh Tungal + + .. _`FF-A v1.0 specification`: https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e + .. _`SMC Calling Convention v1.2 specification`: https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6 +diff --git a/doc/usage/cmd/armffa.rst b/doc/usage/cmd/armffa.rst +index 4f41e3393fd..5dcc3047a45 100644 +--- a/doc/usage/cmd/armffa.rst ++++ b/doc/usage/cmd/armffa.rst +@@ -39,6 +39,17 @@ The command also allows to gather secure partitions information and ping these + + The command is also helpful in testing the communication with secure partitions. + ++Notes ++----- ++ ++armffa is a boot-time test command. It relies on the FF-A driver model device ++(UCLASS_FFA) and is not intended for EFI runtime use after ++ExitBootServices(). ++ ++armffa does not provide EFI variable operations. For more information about ++using EFI runtime SetVariable() and GetVariable() over FF-A please see ++:doc:`../../arch/arm64.ffa`. ++ + Example + ------- + diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0051-doc-bootefi-note-two-phase-runtime-variables-selftes.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0051-doc-bootefi-note-two-phase-runtime-variables-selftes.patch new file mode 100644 index 00000000..0b5d0348 --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0051-doc-bootefi-note-two-phase-runtime-variables-selftes.patch @@ -0,0 +1,47 @@ +From d595e579413e8bcf1f65be25f3da340abb5d2c2b Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Wed, 1 Apr 2026 13:37:35 +0100 +Subject: [PATCH 51/53] doc: bootefi: note two-phase runtime variables selftest + +Explain how the runtime variable selftest runs in two phases + +Document that the "variables at runtime" selftest runs in two +phases when CONFIG_EFI_RT_VOLATILE_STORE is not enabled, and +show how to select it with efi_selftest. + +Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/] +Signed-off-by: Harsimran Singh Tungal +--- + doc/usage/cmd/bootefi.rst | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/doc/usage/cmd/bootefi.rst b/doc/usage/cmd/bootefi.rst +index 7c5448586b7..91aebf1a4b5 100644 +--- a/doc/usage/cmd/bootefi.rst ++++ b/doc/usage/cmd/bootefi.rst +@@ -160,6 +160,16 @@ environment variable to match one of the listed identifiers + Some of the tests execute the ExitBootServices() UEFI boot service and will not + return to the command line but require a board reset. + ++The test *variables at runtime* runs in two phases when ++CONFIG_EFI_RT_VOLATILE_STORE is not enabled. Run it once to create a ++runtime-accessible variable in non-volatile storage, reboot, then run it ++again to validate, append, and delete that variable. ++ ++:: ++ ++ => setenv efi_selftest 'variables at runtime' ++ => bootefi selftest ++ + Configuration + ------------- + +@@ -167,6 +177,8 @@ To use the *bootefi* command you must specify CONFIG_CMD_BOOTEFI=y. + The *bootefi bootmgr* sub-command requries CMD_BOOTEFI_BOOTMGR=y. + The *bootefi hello* sub-command requries CMD_BOOTEFI_HELLO=y. + The *bootefi selftest* sub-command depends on CMD_BOOTEFI_SELFTEST=y. ++The *variables at runtime* selftest runs in two phases when ++CONFIG\_EFI\_RT\_VOLATILE\_STORE is not enabled. + + See also + -------- diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0052-efi_loader-align-FF-A-cache-maintenance-with-runtime.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0052-efi_loader-align-FF-A-cache-maintenance-with-runtime.patch new file mode 100644 index 00000000..3f7a0d2f --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0052-efi_loader-align-FF-A-cache-maintenance-with-runtime.patch @@ -0,0 +1,75 @@ +From 00b68d1c3dd2d182545bcacc668dbb2f637c94c1 Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Fri, 10 Apr 2026 13:50:25 +0100 +Subject: [PATCH 52/53] efi_loader: align FF-A cache maintenance with runtime + path + +Match boot-time FF-A cache handling to runtime behavior + +The boot-time FF-A MM communication path used invalidate_dcache_all() +after copying the message into the shared buffer. This differs from the +runtime path, which performs range-based maintenance to avoid global cache +operations. + +Update ffa_mm_communicate() to use the same pattern as the runtime helper: +clean the shared buffer range before the SMC and invalidate the same range +after the response. This keeps boot-time and runtime behavior consistent +and avoids whole-cache invalidation. + +Upstream-Status: Submitted [cover letter: https://lore.kernel.org/u-boot/20260424173151.371134-1-harsimransingh.tungal@arm.com/] +Signed-off-by: Abdellatif El Khlifi +Signed-off-by: Harsimran Singh Tungal +--- + lib/efi_loader/efi_variable_tee.c | 33 ++++++++++++++++++++++++------- + 1 file changed, 26 insertions(+), 7 deletions(-) + +diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c +index 30687c21b8e..df509a435b1 100644 +--- a/lib/efi_loader/efi_variable_tee.c ++++ b/lib/efi_loader/efi_variable_tee.c +@@ -389,19 +389,38 @@ static efi_status_t ffa_mm_communicate(void *comm_buf, ulong comm_buf_size) + memcpy(virt_shared_buf, comm_buf, tx_data_size); + + /* +- * The secure world might have cache disabled for +- * the device region used for shared buffer (which is the case for Optee). +- * In this case, the secure world reads the data from DRAM. +- * Let's flush the cache so the DRAM is updated with the latest data. ++ * Shared buffer cache maintenance for FF-A / OP-TEE communication: ++ * ++ * NS -> S (request path): ++ * ++ * The non-secure side populates the shared buffer. If the buffer is cached ++ * in NS, the updated bytes may reside in dirty D-cache lines and not yet be ++ * visible in DDR. Since the secure world typically reads the shared buffer ++ * directly from DDR (e.g. with caches disabled / non-coherent mapping), we ++ * must clean the corresponding cache lines to the Point of Coherency (PoC) ++ * before entering secure world. ++ * ++ * S -> NS (response path): ++ * ++ * The secure world may update the same shared buffer in DDR. After returning ++ * to non-secure, any cached copies of that region in NS may be stale. We ++ * therefore invalidate the shared buffer range after the FF-A call to drop ++ * those lines and force subsequent reads to fetch the latest data from DDR. + */ +-#ifdef CONFIG_ARM64 +- invalidate_dcache_all(); +-#endif ++ if (IS_ENABLED(CONFIG_ARM64)) ++ flush_dcache_range((unsigned long)virt_shared_buf, ++ (unsigned long)virt_shared_buf + ++ CONFIG_FFA_SHARED_MM_BUF_SIZE); + + /* Announce there is data in the shared buffer */ + + ffa_ret = ffa_notify_mm_sp(); + ++ if (IS_ENABLED(CONFIG_ARM64)) ++ invalidate_dcache_range((unsigned long)virt_shared_buf, ++ (unsigned long)virt_shared_buf + ++ CONFIG_FFA_SHARED_MM_BUF_SIZE); ++ + switch (ffa_ret) { + case 0: { + ulong rx_data_size; diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0053-efi_loader-fix-AllocatePages-overlap-status.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0053-efi_loader-fix-AllocatePages-overlap-status.patch new file mode 100644 index 00000000..5f71526f --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0053-efi_loader-fix-AllocatePages-overlap-status.patch @@ -0,0 +1,50 @@ +From 1be3bcbc0c7efc36b8fbac7ef0f8c8375dc6770d Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Sat, 18 Apr 2026 14:26:28 +0100 +Subject: [PATCH 53/53] efi_loader: fix AllocatePages overlap status +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Return EFI_NOT_FOUND for EFI_ALLOCATE_ADDRESS overlap + +When efi_allocate_pages() is called with EFI_ALLOCATE_ADDRESS, UEFI +expects EFI_NOT_FOUND if the requested address range is already +allocated or unavailable. U-Boot currently returns +EFI_OUT_OF_RESOURCES when efi_update_memory_map() detects an overlap +after a successful lmb_alloc_mem(), which does not match +EFI_ALLOCATE_ADDRESS semantics. + +Return EFI_NOT_FOUND for EFI_ALLOCATE_ADDRESS requests that fail due +to an overlapping EFI memory descriptor, while keeping +EFI_OUT_OF_RESOURCES for other allocation types. + +The UEFI specification [1] specifies that +EFI_BOOT_SERVICES.AllocatePages must return EFI_NOT_FOUND when the +requested address range is unavailable or already allocated; +EFI_OUT_OF_RESOURCES applies to non‑address‑specific allocation +failures. + +[1] https://uefi.org/specs/UEFI/2.10_A/07_Services_Boot_Services.html + +Upstream-Status: Submitted [https://lore.kernel.org/u-boot/20260427150530.3156000-1-harsimransingh.tungal@arm.com/] +Signed-off-by: Harsimran Singh Tungal +--- + lib/efi_loader/efi_memory.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/lib/efi_loader/efi_memory.c b/lib/efi_loader/efi_memory.c +index b77c2f980cc..63cf1a7277f 100644 +--- a/lib/efi_loader/efi_memory.c ++++ b/lib/efi_loader/efi_memory.c +@@ -510,7 +510,9 @@ efi_status_t efi_allocate_pages(enum efi_allocate_type type, + /* Map would overlap, bail out */ + lmb_free(addr, (u64)pages << EFI_PAGE_SHIFT, flags); + unmap_sysmem((void *)(uintptr_t)efi_addr); +- return EFI_OUT_OF_RESOURCES; ++ if (type == EFI_ALLOCATE_ADDRESS) ++ return EFI_NOT_FOUND; ++ return EFI_OUT_OF_RESOURCES; + } + + *memory = efi_addr; diff --git a/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0054-corstone1000-a320-enable-bootefi-selftest.patch b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0054-corstone1000-a320-enable-bootefi-selftest.patch new file mode 100644 index 00000000..f53e2ff2 --- /dev/null +++ b/meta-arm-bsp/recipes-bsp/u-boot/u-boot/corstone1000/0054-corstone1000-a320-enable-bootefi-selftest.patch @@ -0,0 +1,37 @@ +From 0da9650a3ed86e63c6a9621d7a9195eb3a75a233 Mon Sep 17 00:00:00 2001 +From: Harsimran Singh Tungal +Date: Fri, 17 Apr 2026 13:41:07 +0100 +Subject: [PATCH] corstone1000-a320: enable bootefi selftest + +Turn on CONFIG_CMD_BOOTEFI_SELFTEST in the Corstone-1000 defconfig +so the board can run the built-in UEFI self-test suite during +development and validation. + +Upstream-Status: Pending [Will be submitted upstream with the +pending Cortex-A320 related U-Boot changes] +Signed-off-by: Harsimran Singh Tungal +--- + configs/corstone1000-a320_defconfig | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/configs/corstone1000-a320_defconfig b/configs/corstone1000-a320_defconfig +index d0ae1e745db..c4dcd1790cc 100644 +--- a/configs/corstone1000-a320_defconfig ++++ b/configs/corstone1000-a320_defconfig +@@ -41,6 +41,8 @@ CONFIG_SYS_PROMPT="corstone1000# " + # CONFIG_CMD_CONSOLE is not set + CONFIG_CMD_FWU_METADATA=y + CONFIG_CMD_BOOTZ=y ++# CONFIG_CMD_BOOTEFI_HELLO is not set ++CONFIG_CMD_BOOTEFI_SELFTEST=y + # CONFIG_CMD_XIMG is not set + CONFIG_CMD_NVEDIT_EFI=y + CONFIG_CMD_GPT=y +@@ -76,6 +78,7 @@ CONFIG_SYSRESET=y + CONFIG_SYSRESET_PSCI=y + CONFIG_TEE=y + CONFIG_OPTEE=y ++# CONFIG_CMD_POWEROFF is not set + CONFIG_USB=y + CONFIG_USB_ISP1760=y + CONFIG_VIRTIO_MMIO=y