All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service
@ 2023-01-26 13:21 Masahisa Kojima
  2023-01-26 13:21 ` [RFC PATCH 1/2] efi: expose efivar generic ops register function Masahisa Kojima
                   ` (2 more replies)
  0 siblings, 3 replies; 21+ messages in thread
From: Masahisa Kojima @ 2023-01-26 13:21 UTC (permalink / raw)
  To: Ard Biesheuvel, Jens Wiklander, linux-kernel, op-tee
  Cc: Ilias Apalodimas, Johan Hovold, Masahisa Kojima

This RFC series introduces the op-tee based EFI Runtime Variable
Service.

The eMMC device is typically owned by the non-secure world(linux in
this case). There is an existing solution utilizing eMMC RPMB partition
for EFI Variables, it is implemented by interacting with
OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
and tee-supplicant. The last piece is the tee-based variable access
driver to interact with OP-TEE and StandaloneMM.

Masahisa Kojima (2):
  efi: expose efivar generic ops register function
  tee: Add op-tee helper functions for variable access

 drivers/firmware/efi/efi.c           |  12 +
 drivers/tee/optee/Kconfig            |  10 +
 drivers/tee/optee/Makefile           |   1 +
 drivers/tee/optee/mm_communication.h | 249 +++++++++++
 drivers/tee/optee/optee_private.h    |   5 +-
 drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
 drivers/tee/tee_core.c               |  23 ++
 include/linux/efi.h                  |   4 +
 include/linux/tee_drv.h              |  23 ++
 9 files changed, 924 insertions(+), 1 deletion(-)
 create mode 100644 drivers/tee/optee/mm_communication.h
 create mode 100644 drivers/tee/optee/optee_stmm_efi.c

-- 
2.30.2


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [RFC PATCH 1/2] efi: expose efivar generic ops register function
  2023-01-26 13:21 [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service Masahisa Kojima
@ 2023-01-26 13:21 ` Masahisa Kojima
  2023-01-26 13:21   ` Masahisa Kojima
  2023-02-02 12:05 ` [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service Sumit Garg
  2 siblings, 0 replies; 21+ messages in thread
From: Masahisa Kojima @ 2023-01-26 13:21 UTC (permalink / raw)
  To: Ard Biesheuvel, Jens Wiklander, linux-kernel, op-tee
  Cc: Ilias Apalodimas, Johan Hovold, Masahisa Kojima, linux-efi

This is a preparation for supporting efivar operations
provided by other than efi subsystem.
Both register and unregister functions are exposed
so that non-efi subsystem can revert the efi generic
operation.

Co-developed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
 drivers/firmware/efi/efi.c | 12 ++++++++++++
 include/linux/efi.h        |  3 +++
 2 files changed, 15 insertions(+)

diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 52146f95d58e..4e576b62c170 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -227,6 +227,18 @@ static void generic_ops_unregister(void)
 	efivars_unregister(&generic_efivars);
 }
 
+void efivars_generic_ops_register(void)
+{
+	generic_ops_register();
+}
+EXPORT_SYMBOL_GPL(efivars_generic_ops_register);
+
+void efivars_generic_ops_unregister(void)
+{
+	generic_ops_unregister();
+}
+EXPORT_SYMBOL_GPL(efivars_generic_ops_unregister);
+
 #ifdef CONFIG_EFI_CUSTOM_SSDT_OVERLAYS
 #define EFIVAR_SSDT_NAME_MAX	16UL
 static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX] __initdata;
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 1a1adc8d3ba3..5e301c00e9b0 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -1332,4 +1332,7 @@ bool efi_config_table_is_usable(const efi_guid_t *guid, unsigned long table)
 	return xen_efi_config_table_is_usable(guid, table);
 }
 
+void efivars_generic_ops_register(void);
+void efivars_generic_ops_unregister(void);
+
 #endif /* _LINUX_EFI_H */
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC PATCH 2/2] tee: Add op-tee helper functions for variable access
  2023-01-26 13:21 [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service Masahisa Kojima
@ 2023-01-26 13:21   ` Masahisa Kojima
  2023-01-26 13:21   ` Masahisa Kojima
  2023-02-02 12:05 ` [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service Sumit Garg
  2 siblings, 0 replies; 21+ messages in thread
From: Masahisa Kojima @ 2023-01-26 13:21 UTC (permalink / raw)
  To: Ard Biesheuvel, Jens Wiklander, linux-kernel, op-tee
  Cc: Ilias Apalodimas, Johan Hovold, Masahisa Kojima, Sumit Garg,
	Maxime Coquelin, Alexandre Torgue, linux-stm32, linux-arm-kernel,
	linux-efi

When the flash is not owned by the non-secure world, accessing the EFI
variables is straightforward and done via EFI Runtime Variable Services.
In this case, critical variables for system integrity and security
are normally stored in the dedicated secure storage and only accessible
from the secure world.

On the other hand, the small embedded devices don't have the special
dedicated secure storage. The eMMC device with an RPMB partition is
becoming more common, we can use an RPMB partition to store the
EFI Variables.

The eMMC device is typically owned by the non-secure world(linux in
this case). There is an existing solution utilizing eMMC RPMB partition
for EFI Variables, it is implemented by interacting with
OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
and tee-supplicant. The last piece is the tee-based variable access
driver to interact with OP-TEE and StandaloneMM.

So let's add the kernel functions needed.

This feature is implemented as a kernel module.
StMM PTA has TA_FLAG_DEVICE_ENUM_SUPP flag when registered to OP-TEE
so that this optee_stmm_efi module is probed after tee-supplicant starts,
since "SetVariable" EFI Runtime Variable Service requires to
interact with tee-supplicant.

Co-developed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
 drivers/tee/optee/Kconfig            |  10 +
 drivers/tee/optee/Makefile           |   1 +
 drivers/tee/optee/mm_communication.h | 249 +++++++++++
 drivers/tee/optee/optee_private.h    |   5 +-
 drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
 drivers/tee/tee_core.c               |  23 ++
 include/linux/efi.h                  |   1 +
 include/linux/tee_drv.h              |  23 ++
 8 files changed, 909 insertions(+), 1 deletion(-)
 create mode 100644 drivers/tee/optee/mm_communication.h
 create mode 100644 drivers/tee/optee/optee_stmm_efi.c

diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
index f121c224e682..a0b699977e51 100644
--- a/drivers/tee/optee/Kconfig
+++ b/drivers/tee/optee/Kconfig
@@ -7,3 +7,13 @@ config OPTEE
 	help
 	  This implements the OP-TEE Trusted Execution Environment (TEE)
 	  driver.
+
+config EFI_STMM_OPTEE
+	tristate "OP-TEE based EFI runtime variable service driver"
+	depends on OPTEE
+	help
+	  This  driver provides support for OP-TEE based EFI runtime
+	  variable service driver.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called optee_stmm_efi.
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
index a6eff388d300..9acb0b2de8fe 100644
--- a/drivers/tee/optee/Makefile
+++ b/drivers/tee/optee/Makefile
@@ -8,6 +8,7 @@ optee-objs += supp.o
 optee-objs += device.o
 optee-objs += smc_abi.o
 optee-objs += ffa_abi.o
+obj-$(CONFIG_EFI_STMM_OPTEE) += optee_stmm_efi.o
 
 # for tracing framework to find optee_trace.h
 CFLAGS_smc_abi.o := -I$(src)
diff --git a/drivers/tee/optee/mm_communication.h b/drivers/tee/optee/mm_communication.h
new file mode 100644
index 000000000000..23e6672991ef
--- /dev/null
+++ b/drivers/tee/optee/mm_communication.h
@@ -0,0 +1,249 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ *  Headers for EFI variable service via StandAloneMM, EDK2 application running
+ *  in OP-TEE
+ *
+ *  Copyright (c) 2017, Intel Corporation. All rights reserved.
+ *  Copyright (C) 2020 Linaro Ltd.
+ */
+
+#ifndef _MM_COMMUNICATION_H_
+#define _MM_COMMUNICATION_H_
+
+/*
+ * Interface to the pseudo Trusted Application (TA), which provides a
+ * communication channel with the Standalone MM (Management Mode)
+ * Secure Partition running at Secure-EL0
+ */
+
+#define PTA_STMM_CMDID_COMMUNICATE 0
+
+/* OP-TEE is using big endian GUIDs while UEFI uses little endian ones */
+#define PTA_STMM_UUID \
+	UUID_INIT(0xed32d533, 0x99e6, 0x4209, \
+		  0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
+
+#define EFI_MM_VARIABLE_GUID \
+	EFI_GUID(0xed32d533, 0x99e6, 0x4209, \
+		 0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
+
+/* Defined in EDK2 MdePkg/Include/Protocol/MmCommunication.h */
+
+/**
+ * struct efi_mm_communicate_header - Header used for SMM variable communication
+
+ * @header_guid:  header use for disambiguation of content
+ * @message_len:  length of the message. Does not include the size of the
+ *                header
+ * @data:         payload of the message
+ *
+ * Defined in EDK2 as EFI_MM_COMMUNICATE_HEADER.
+ * To avoid confusion in interpreting frames, the communication buffer should
+ * always begin with efi_mm_communicate_header.
+ */
+struct efi_mm_communicate_header {
+	efi_guid_t header_guid;
+	size_t     message_len;
+	u8         data[];
+} __packed;
+
+#define MM_COMMUNICATE_HEADER_SIZE \
+	(sizeof(struct efi_mm_communicate_header))
+
+/* Defined in EDK2 ArmPkg/Include/IndustryStandard/ArmMmSvc.h */
+
+/* SPM return error codes */
+#define ARM_SVC_SPM_RET_SUCCESS               0
+#define ARM_SVC_SPM_RET_NOT_SUPPORTED        -1
+#define ARM_SVC_SPM_RET_INVALID_PARAMS       -2
+#define ARM_SVC_SPM_RET_DENIED               -3
+#define ARM_SVC_SPM_RET_NO_MEMORY            -5
+
+/* Defined in EDK2 MdeModulePkg/Include/Guid/SmmVariableCommon.h */
+
+#define SMM_VARIABLE_FUNCTION_GET_VARIABLE  1
+/*
+ * The payload for this function is
+ * SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME.
+ */
+#define SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME  2
+/*
+ * The payload for this function is SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
+ */
+#define SMM_VARIABLE_FUNCTION_SET_VARIABLE  3
+/*
+ * The payload for this function is
+ * SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO.
+ */
+#define SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO  4
+/*
+ * It is a notify event, no extra payload for this function.
+ */
+#define SMM_VARIABLE_FUNCTION_READY_TO_BOOT  5
+/*
+ * It is a notify event, no extra payload for this function.
+ */
+#define SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE  6
+/*
+ * The payload for this function is VARIABLE_INFO_ENTRY.
+ * The GUID in EFI_SMM_COMMUNICATE_HEADER is gEfiSmmVariableProtocolGuid.
+ */
+#define SMM_VARIABLE_FUNCTION_GET_STATISTICS  7
+/*
+ * The payload for this function is SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
+ */
+#define SMM_VARIABLE_FUNCTION_LOCK_VARIABLE   8
+
+#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET  9
+
+#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET  10
+
+#define SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE  11
+/*
+ * The payload for this function is
+ * SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT
+ */
+#define SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT 12
+
+#define SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE  13
+/*
+ * The payload for this function is
+ * SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO
+ */
+#define SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO  14
+
+/**
+ * struct smm_variable_communicate_header - Used for SMM variable communication
+
+ * @function:     function to call in Smm.
+ * @ret_status:   return status
+ * @data:         payload
+ *
+ * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_HEADER.
+ */
+struct smm_variable_communicate_header {
+	size_t  function;
+	efi_status_t ret_status;
+	u8 data[];
+};
+
+#define MM_VARIABLE_COMMUNICATE_SIZE \
+	(sizeof(struct smm_variable_communicate_header))
+
+/**
+ * struct smm_variable_access - Used to communicate with StMM by
+ *                              SetVariable and GetVariable.
+
+ * @guid:         vendor GUID
+ * @data_size:    size of EFI variable data
+ * @name_size:    size of EFI name
+ * @attr:         attributes
+ * @name:         variable name
+ *
+ * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
+ *
+ */
+struct smm_variable_access {
+	efi_guid_t  guid;
+	size_t data_size;
+	size_t name_size;
+	u32 attr;
+	u16 name[];
+};
+
+#define MM_VARIABLE_ACCESS_HEADER_SIZE \
+	(sizeof(struct smm_variable_access))
+/**
+ * struct smm_variable_payload_size - Used to get the max allowed
+ *                                    payload used in StMM.
+ *
+ * @size:  size to fill in
+ *
+ * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE.
+ *
+ */
+struct smm_variable_payload_size {
+	size_t size;
+};
+
+/**
+ * struct smm_variable_getnext - Used to communicate with StMM for
+ *                               GetNextVariableName.
+ *
+ * @guid:       vendor GUID
+ * @name_size:  size of the name of the variable
+ * @name:       variable name
+ *
+ * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME.
+ */
+struct smm_variable_getnext {
+	efi_guid_t  guid;
+	size_t name_size;
+	u16         name[];
+};
+
+#define MM_VARIABLE_GET_NEXT_HEADER_SIZE \
+	(sizeof(struct smm_variable_getnext))
+
+/**
+ * struct smm_variable_query_info - Used to communicate with StMM for
+ *                                  QueryVariableInfo.
+ *
+ * @max_variable_storage:        max available storage
+ * @remaining_variable_storage:  remaining available storage
+ * @max_variable_size:           max variable supported size
+ * @attr:                        attributes to query storage for
+ *
+ * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO.
+ */
+struct smm_variable_query_info {
+	u64 max_variable_storage;
+	u64 remaining_variable_storage;
+	u64 max_variable_size;
+	u32 attr;
+};
+
+#define VAR_CHECK_VARIABLE_PROPERTY_REVISION 0x0001
+#define VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY BIT(0)
+/**
+ * struct var_check_property - Used to store variable properties in StMM
+ *
+ * @revision:   magic revision number for variable property checking
+ * @property:   properties mask for the variable used in StMM.
+ *              Currently RO flag is supported
+ * @attributes: variable attributes used in StMM checking when properties
+ *              for a variable are enabled
+ * @minsize:    minimum allowed size for variable payload checked against
+ *              smm_variable_access->datasize in StMM
+ * @maxsize:    maximum allowed size for variable payload checked against
+ *              smm_variable_access->datasize in StMM
+ *
+ * Defined in EDK2 as VAR_CHECK_VARIABLE_PROPERTY.
+ */
+struct var_check_property {
+	u16 revision;
+	u16 property;
+	u32 attributes;
+	size_t minsize;
+	size_t maxsize;
+};
+
+/**
+ * struct smm_variable_var_check_property - Used to communicate variable
+ *                                          properties with StMM
+ *
+ * @guid:       vendor GUID
+ * @name_size:  size of EFI name
+ * @property:   variable properties struct
+ * @name:       variable name
+ *
+ * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY.
+ */
+struct smm_variable_var_check_property {
+	efi_guid_t guid;
+	size_t name_size;
+	struct var_check_property property;
+	u16 name[];
+};
+
+#endif /* _MM_COMMUNICATION_H_ */
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index 04ae58892608..10af910e0dce 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -12,6 +12,7 @@
 #include <linux/tee_drv.h>
 #include <linux/types.h>
 #include "optee_msg.h"
+#include "optee_private.h"
 
 #define DRIVER_NAME "optee"
 
@@ -19,6 +20,7 @@
 
 /* Some Global Platform error codes used in this driver */
 #define TEEC_SUCCESS			0x00000000
+#define TEEC_ERROR_EXCESS_DATA		0xFFFF0004
 #define TEEC_ERROR_BAD_PARAMETERS	0xFFFF0006
 #define TEEC_ERROR_NOT_SUPPORTED	0xFFFF000A
 #define TEEC_ERROR_COMMUNICATION	0xFFFF000E
@@ -250,7 +252,6 @@ void optee_pool_op_free_helper(struct tee_shm_pool *pool, struct tee_shm *shm,
 			       int (*shm_unregister)(struct tee_context *ctx,
 						     struct tee_shm *shm));
 
-
 void optee_remove_common(struct optee *optee);
 int optee_open(struct tee_context *ctx, bool cap_memref_null);
 void optee_release(struct tee_context *ctx);
@@ -322,4 +323,6 @@ void optee_smc_abi_unregister(void);
 int optee_ffa_abi_register(void);
 void optee_ffa_abi_unregister(void);
 
+int optee_efivar_ops_init(struct tee_context *ctx);
+
 #endif /*OPTEE_PRIVATE_H*/
diff --git a/drivers/tee/optee/optee_stmm_efi.c b/drivers/tee/optee/optee_stmm_efi.c
new file mode 100644
index 000000000000..6dcf1eb4b96c
--- /dev/null
+++ b/drivers/tee/optee/optee_stmm_efi.c
@@ -0,0 +1,598 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  EFI variable service via OP-TEE
+ *
+ *  Copyright (C) 2022 Linaro
+ */
+
+#include <linux/efi.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/tee.h>
+#include <linux/tee_drv.h>
+#include <linux/ucs2_string.h>
+#include "mm_communication.h"
+#include "optee_private.h"
+
+static struct efivars optee_efivars;
+static struct efivar_operations optee_ops;
+
+static size_t max_buffer_size; /* comm + var + func + data */
+static size_t max_payload_size; /* func + data */
+
+struct mm_connection {
+	struct tee_context *ctx;
+	u32 session;
+};
+
+/* UUID of the stmm PTA */
+static const struct tee_client_device_id optee_stmm_efi_id_table[] = {
+	{PTA_STMM_UUID},
+	{}
+};
+
+static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
+{
+	if (ver->impl_id == TEE_IMPL_ID_OPTEE)
+		return 1;
+	else
+		return 0;
+}
+
+/**
+ * get_connection() - Retrieve OP-TEE session for a specific UUID.
+ *
+ * @conn:   session buffer to fill
+ * Return:  status code
+ */
+static int get_connection(struct mm_connection *conn)
+{
+	struct tee_context *ctx = NULL;
+	struct tee_ioctl_open_session_arg arg;
+	int rc;
+
+	memset(&arg, 0, sizeof(arg));
+
+	/* Open context with OP-TEE driver */
+	ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
+	if (IS_ERR(ctx))
+		return -ENODEV;
+
+	export_uuid(arg.uuid, &optee_stmm_efi_id_table[0].uuid);
+	rc = tee_client_open_session(ctx, &arg, NULL);
+	if (!rc) {
+		conn->ctx = ctx;
+		conn->session = arg.session;
+	}
+
+	return rc;
+}
+
+/**
+ * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE
+ *
+ * @comm_buf:		locally allocated communcation buffer
+ * @dsize:		buffer size
+ * Return:		status code
+ */
+static efi_status_t optee_mm_communicate(void *comm_buf, size_t dsize)
+{
+	size_t buf_size;
+	efi_status_t ret;
+	struct efi_mm_communicate_header *mm_hdr;
+	struct mm_connection conn = { NULL, 0 };
+	struct tee_ioctl_invoke_arg arg;
+	struct tee_param param[4];
+	struct tee_shm *shm = NULL;
+	int rc;
+
+	if (!comm_buf)
+		return EFI_INVALID_PARAMETER;
+
+	mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
+	buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
+
+	if (dsize != buf_size)
+		return EFI_INVALID_PARAMETER;
+
+	rc = get_connection(&conn);
+	if (rc) {
+		pr_err("Unable to open OP-TEE session (err=%d)\n", rc);
+		return EFI_UNSUPPORTED;
+	}
+
+	shm = tee_shm_register_kernel_buf(conn.ctx, comm_buf, buf_size);
+	if (IS_ERR(shm)) {
+		pr_err("Unable to register shared memory\n");
+		tee_client_close_session(conn.ctx, conn.session);
+		return EFI_UNSUPPORTED;
+	}
+
+	memset(&arg, 0, sizeof(arg));
+	arg.func = PTA_STMM_CMDID_COMMUNICATE;
+	arg.session = conn.session;
+	arg.num_params = 4;
+
+	memset(param, 0, sizeof(param));
+	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
+	param[0].u.memref.size = buf_size;
+	param[0].u.memref.shm = shm;
+	param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
+	param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+	param[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+
+	rc = tee_client_invoke_func(conn.ctx, &arg, param);
+	tee_shm_free(shm);
+	/* even if close session fails the session will be invalidaded */
+	tee_client_close_session(conn.ctx, conn.session);
+	if (rc)
+		return EFI_DEVICE_ERROR;
+	if (arg.ret == TEEC_ERROR_EXCESS_DATA)
+		pr_err("Variable payload too large\n");
+	if (arg.ret != TEEC_SUCCESS)
+		return EFI_DEVICE_ERROR;
+
+	switch (param[1].u.value.a) {
+	case ARM_SVC_SPM_RET_SUCCESS:
+		ret = EFI_SUCCESS;
+		break;
+
+	case ARM_SVC_SPM_RET_INVALID_PARAMS:
+		ret = EFI_INVALID_PARAMETER;
+		break;
+
+	case ARM_SVC_SPM_RET_DENIED:
+		ret = EFI_ACCESS_DENIED;
+		break;
+
+	case ARM_SVC_SPM_RET_NO_MEMORY:
+		ret = EFI_OUT_OF_RESOURCES;
+		break;
+
+	default:
+		ret = EFI_ACCESS_DENIED;
+	}
+
+	return ret;
+}
+
+/**
+ * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
+ * it to OP-TEE
+ *
+ * @comm_buf:		locally allocated communcation buffer
+ * @dsize:		buffer size
+ * Return:		status code
+ */
+static efi_status_t mm_communicate(u8 *comm_buf, size_t dsize)
+{
+	efi_status_t ret;
+	struct efi_mm_communicate_header *mm_hdr;
+	struct smm_variable_communicate_header *var_hdr;
+
+	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;
+
+	ret = optee_mm_communicate(comm_buf, dsize);
+	if (ret != EFI_SUCCESS) {
+		pr_err("%s failed!\n", __func__);
+		return ret;
+	}
+
+	return var_hdr->ret_status;
+}
+
+/**
+ * setup_mm_hdr() -	Allocate a buffer for StandAloneMM and initialize the
+ *			header data.
+ *
+ * @dptr:		pointer address of the corresponding StandAloneMM
+ *			function
+ * @payload_size:	buffer size
+ * @func:		standAloneMM function number
+ * @ret:		EFI return code
+ * Return:		buffer or NULL
+ */
+static u8 *setup_mm_hdr(void **dptr, size_t payload_size, size_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;
+
+	/* In the init function we initialize max_buffer_size with
+	 * get_max_payload(). So skip the test if max_buffer_size is initialized
+	 * StandAloneMM will perform similar checks and drop the buffer if it's
+	 * too long
+	 */
+	if (max_buffer_size &&
+	    max_buffer_size < (MM_COMMUNICATE_HEADER_SIZE +
+			       MM_VARIABLE_COMMUNICATE_SIZE + payload_size)) {
+		*ret = EFI_INVALID_PARAMETER;
+		return NULL;
+	}
+
+	comm_buf = kzalloc(MM_COMMUNICATE_HEADER_SIZE +
+				   MM_VARIABLE_COMMUNICATE_SIZE + payload_size,
+			   GFP_KERNEL);
+	if (!comm_buf) {
+		*ret = EFI_OUT_OF_RESOURCES;
+		return NULL;
+	}
+
+	mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
+	memcpy(&mm_hdr->header_guid, &mm_var_guid, 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;
+	var_hdr->function = func;
+	if (dptr)
+		*dptr = var_hdr->data;
+	*ret = EFI_SUCCESS;
+
+	return comm_buf;
+}
+
+/**
+ * get_max_payload() - Get variable payload size from StandAloneMM.
+ *
+ * @size:    size of the variable in storage
+ * Return:   status code
+ */
+static efi_status_t get_max_payload(size_t *size)
+{
+	struct smm_variable_payload_size *var_payload = NULL;
+	size_t payload_size;
+	u8 *comm_buf = NULL;
+	efi_status_t ret;
+
+	if (!size) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	payload_size = sizeof(*var_payload);
+	comm_buf = setup_mm_hdr((void **)&var_payload, payload_size,
+				SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, &ret);
+	if (!comm_buf)
+		goto out;
+
+	ret = mm_communicate(comm_buf, payload_size);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	/* Make sure the buffer is big enough for storing variables */
+	if (var_payload->size < MM_VARIABLE_ACCESS_HEADER_SIZE + 0x20) {
+		ret = EFI_DEVICE_ERROR;
+		goto out;
+	}
+	*size = var_payload->size;
+	/*
+	 * There seems to be a bug in EDK2 miscalculating the boundaries and
+	 * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
+	 * it up here to ensure backwards compatibility with older versions
+	 * (cf. StandaloneMmPkg/Drivers/StandaloneMmCpu/AArch64/EventHandle.c.
+	 * sizeof (EFI_MM_COMMUNICATE_HEADER) instead the size minus the
+	 * flexible array member).
+	 *
+	 * size is guaranteed to be > 2 due to checks on the beginning.
+	 */
+	*size -= 2;
+out:
+	kfree(comm_buf);
+	return ret;
+}
+
+static efi_status_t get_property_int(u16 *name, size_t name_size,
+				     const efi_guid_t *vendor,
+				     struct var_check_property *var_property)
+{
+	struct smm_variable_var_check_property *smm_property;
+	size_t payload_size;
+	u8 *comm_buf = NULL;
+	efi_status_t ret;
+
+	memset(var_property, 0, sizeof(*var_property));
+	payload_size = sizeof(*smm_property) + name_size;
+	if (payload_size > max_payload_size) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+	comm_buf = setup_mm_hdr(
+		(void **)&smm_property, payload_size,
+		SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET, &ret);
+	if (!comm_buf)
+		goto out;
+
+	memcpy(&smm_property->guid, vendor, sizeof(smm_property->guid));
+	smm_property->name_size = name_size;
+	memcpy(smm_property->name, name, name_size);
+
+	ret = mm_communicate(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)
+		goto out;
+	memcpy(var_property, &smm_property->property, sizeof(*var_property));
+
+out:
+	kfree(comm_buf);
+	return ret;
+}
+
+static efi_status_t optee_get_variable(u16 *name, efi_guid_t *vendor,
+				       u32 *attributes,
+				       unsigned long *data_size, void *data)
+{
+	struct var_check_property var_property;
+	struct smm_variable_access *var_acc;
+	size_t payload_size;
+	size_t name_size;
+	size_t tmp_dsize;
+	u8 *comm_buf = NULL;
+	efi_status_t ret;
+
+	if (!name || !vendor || !data_size) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
+	if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	/* 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)
+		goto out;
+
+	/* Fill in contents */
+	memcpy(&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;
+	memcpy(var_acc->name, name, name_size);
+
+	/* Communicate */
+	ret = mm_communicate(comm_buf, payload_size);
+	if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL)
+		/* Update with reported data size for trimmed case */
+		*data_size = var_acc->data_size;
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	ret = get_property_int(name, name_size, vendor, &var_property);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	if (attributes)
+		*attributes = var_acc->attr;
+
+	if (data)
+		memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
+		       var_acc->data_size);
+	else
+		ret = EFI_INVALID_PARAMETER;
+
+out:
+	kfree(comm_buf);
+	return ret;
+}
+
+static efi_status_t optee_get_next_variable(unsigned long *name_size,
+					    efi_char16_t *name,
+					    efi_guid_t *guid)
+{
+	struct smm_variable_getnext *var_getnext;
+	size_t payload_size;
+	size_t out_name_size;
+	size_t in_name_size;
+	u8 *comm_buf = NULL;
+	efi_status_t ret;
+
+	if (!name_size || !name || !guid) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	out_name_size = *name_size;
+	in_name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
+
+	if (out_name_size < in_name_size) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	if (in_name_size >
+	    max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	/* 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)
+		goto out;
+
+	/* Fill in contents */
+	memcpy(&var_getnext->guid, guid, sizeof(var_getnext->guid));
+	var_getnext->name_size = out_name_size;
+	memcpy(var_getnext->name, name, in_name_size);
+	memset((u8 *)var_getnext->name + in_name_size, 0x0,
+	       out_name_size - in_name_size);
+
+	/* Communicate */
+	ret = mm_communicate(comm_buf, payload_size);
+	if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
+		/* Update with reported data size for trimmed case */
+		*name_size = var_getnext->name_size;
+	}
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	memcpy(guid, &var_getnext->guid, sizeof(*guid));
+	memcpy(name, var_getnext->name, var_getnext->name_size);
+
+out:
+	kfree(comm_buf);
+	return ret;
+}
+
+static efi_status_t optee_set_variable(efi_char16_t *name, efi_guid_t *vendor,
+				       u32 attributes, unsigned long data_size,
+				       void *data)
+{
+	efi_status_t ret;
+	struct var_check_property var_property;
+	struct smm_variable_access *var_acc;
+	size_t payload_size;
+	size_t name_size;
+	u8 *comm_buf = NULL;
+
+	if (!name || name[0] == 0 || !vendor) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+	if (data_size > 0 && !data) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+	/* Check payload size */
+	name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
+	payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
+	if (payload_size > max_payload_size) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	/*
+	 * 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)
+		goto out;
+
+	/*
+	 * The API has the ability to override RO flags. If no RO check was
+	 * requested switch the variable to RW for the duration of this call
+	 */
+	ret = get_property_int(name, name_size, vendor, &var_property);
+	if (ret != EFI_SUCCESS) {
+		pr_err("Getting variable property failed\n");
+		goto out;
+	}
+
+	if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) {
+		ret = EFI_WRITE_PROTECTED;
+		goto out;
+	}
+
+	/* Fill in contents */
+	memcpy(&var_acc->guid, vendor, sizeof(var_acc->guid));
+	var_acc->data_size = data_size;
+	var_acc->name_size = name_size;
+	var_acc->attr = attributes;
+	memcpy(var_acc->name, name, name_size);
+	memcpy((u8 *)var_acc->name + name_size, data, data_size);
+
+
+	/* Communicate */
+	ret = mm_communicate(comm_buf, payload_size);
+	pr_info("Set Variable %s %d %lx\n", __FILE__, __LINE__, ret);
+out:
+	kfree(comm_buf);
+	return ret;
+}
+
+static int optee_stmm_efi_probe(struct device *dev)
+{
+	efi_status_t ret;
+
+	ret = get_max_payload(&max_payload_size);
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	max_buffer_size = MM_COMMUNICATE_HEADER_SIZE +
+			  MM_VARIABLE_COMMUNICATE_SIZE +
+			  max_payload_size;
+
+	optee_ops.get_variable = optee_get_variable;
+	optee_ops.get_next_variable = optee_get_next_variable;
+	optee_ops.set_variable = optee_set_variable;
+	/* TODO: support nonblocking variant */
+	optee_ops.set_variable_nonblocking = NULL;
+	/* set NULL, always return EFI_SUCCESS by efi_query_variable_store() */
+	optee_ops.query_variable_store = NULL;
+
+	tee_register_efivar_ops(&optee_efivars, &optee_ops);
+
+	return 0;
+}
+
+static int optee_stmm_efi_remove(struct device *dev)
+{
+	tee_unregister_efivar_ops(&optee_efivars);
+
+	return 0;
+}
+
+MODULE_DEVICE_TABLE(tee, optee_stmm_efi_id_table);
+
+static struct tee_client_driver optee_stmm_efi_driver = {
+	.id_table	= optee_stmm_efi_id_table,
+	.driver		= {
+		.name		= "optee-stmm-efi",
+		.bus		= &tee_bus_type,
+		.probe		= optee_stmm_efi_probe,
+		.remove		= optee_stmm_efi_remove,
+	},
+};
+
+static int __init optee_stmm_efi_mod_init(void)
+{
+	return driver_register(&optee_stmm_efi_driver.driver);
+}
+
+static void __exit optee_stmm_efi_mod_exit(void)
+{
+	driver_unregister(&optee_stmm_efi_driver.driver);
+}
+
+module_init(optee_stmm_efi_mod_init);
+module_exit(optee_stmm_efi_mod_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ilias Apalodimas <ilias.apalodimas@linaro.org>");
+MODULE_AUTHOR("Masahisa Kojima <masahisa.kojima@linaro.org>");
+MODULE_DESCRIPTION("OP-TEE based EFI runtime variable service driver");
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index 98da206cd761..ac46274844a3 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -7,6 +7,7 @@
 
 #include <linux/cdev.h>
 #include <linux/cred.h>
+#include <linux/efi.h>
 #include <linux/fs.h>
 #include <linux/idr.h>
 #include <linux/module.h>
@@ -1263,6 +1264,28 @@ static void __exit tee_exit(void)
 	tee_class = NULL;
 }
 
+void tee_register_efivar_ops(struct efivars *tee_efivars,
+			     struct efivar_operations *ops)
+{
+	/*
+	 * If the firmware EFI runtime services support SetVariable(),
+	 * tee-based EFI variable services are not used.
+	 */
+	if (!efivar_supports_writes()) {
+		pr_info("Use tee-based EFI runtime variable services\n");
+		efivars_generic_ops_unregister();
+		efivars_register(tee_efivars, ops);
+	}
+}
+EXPORT_SYMBOL_GPL(tee_register_efivar_ops);
+
+void tee_unregister_efivar_ops(struct efivars *tee_efivars)
+{
+	efivars_unregister(tee_efivars);
+	efivars_generic_ops_register();
+}
+EXPORT_SYMBOL_GPL(tee_unregister_efivar_ops);
+
 subsys_initcall(tee_init);
 module_exit(tee_exit);
 
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 5e301c00e9b0..14d4aa83ce60 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -39,6 +39,7 @@
 #define EFI_WRITE_PROTECTED	( 8 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_OUT_OF_RESOURCES	( 9 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_NOT_FOUND		(14 | (1UL << (BITS_PER_LONG-1)))
+#define EFI_ACCESS_DENIED	(15 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_TIMEOUT		(18 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_ABORTED		(21 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_SECURITY_VIOLATION	(26 | (1UL << (BITS_PER_LONG-1)))
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index 17eb1c5205d3..def4ea6212ee 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -7,6 +7,7 @@
 #define __TEE_DRV_H
 
 #include <linux/device.h>
+#include <linux/efi.h>
 #include <linux/idr.h>
 #include <linux/kref.h>
 #include <linux/list.h>
@@ -507,4 +508,26 @@ struct tee_context *teedev_open(struct tee_device *teedev);
  */
 void teedev_close_context(struct tee_context *ctx);
 
+/**
+ * tee_register_efivar_ops() - register the efivar ops
+ * @tee_efivars:	pointer to efivars structure
+ * @ops:		pointer to contain the efivar operation
+ *
+ * This function registers the tee-based efivar operation as an
+ * EFI Runtime Service.
+ *
+ */
+void tee_register_efivar_ops(struct efivars *tee_efivars,
+			     struct efivar_operations *ops);
+
+/**
+ * tee_unregister_efivar_ops() - unregister the efivar ops
+ * @tee_efivars:	pointer to efivars structure
+ *
+ * This function unregisters the tee-based efivar operation
+ * and reverts to the generic operation.
+ *
+ */
+void tee_unregister_efivar_ops(struct efivars *tee_efivars);
+
 #endif /*__TEE_DRV_H*/
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC PATCH 2/2] tee: Add op-tee helper functions for variable access
@ 2023-01-26 13:21   ` Masahisa Kojima
  0 siblings, 0 replies; 21+ messages in thread
From: Masahisa Kojima @ 2023-01-26 13:21 UTC (permalink / raw)
  To: Ard Biesheuvel, Jens Wiklander, linux-kernel, op-tee
  Cc: Ilias Apalodimas, Johan Hovold, Masahisa Kojima, Sumit Garg,
	Maxime Coquelin, Alexandre Torgue, linux-stm32, linux-arm-kernel,
	linux-efi

When the flash is not owned by the non-secure world, accessing the EFI
variables is straightforward and done via EFI Runtime Variable Services.
In this case, critical variables for system integrity and security
are normally stored in the dedicated secure storage and only accessible
from the secure world.

On the other hand, the small embedded devices don't have the special
dedicated secure storage. The eMMC device with an RPMB partition is
becoming more common, we can use an RPMB partition to store the
EFI Variables.

The eMMC device is typically owned by the non-secure world(linux in
this case). There is an existing solution utilizing eMMC RPMB partition
for EFI Variables, it is implemented by interacting with
OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
and tee-supplicant. The last piece is the tee-based variable access
driver to interact with OP-TEE and StandaloneMM.

So let's add the kernel functions needed.

This feature is implemented as a kernel module.
StMM PTA has TA_FLAG_DEVICE_ENUM_SUPP flag when registered to OP-TEE
so that this optee_stmm_efi module is probed after tee-supplicant starts,
since "SetVariable" EFI Runtime Variable Service requires to
interact with tee-supplicant.

Co-developed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
---
 drivers/tee/optee/Kconfig            |  10 +
 drivers/tee/optee/Makefile           |   1 +
 drivers/tee/optee/mm_communication.h | 249 +++++++++++
 drivers/tee/optee/optee_private.h    |   5 +-
 drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
 drivers/tee/tee_core.c               |  23 ++
 include/linux/efi.h                  |   1 +
 include/linux/tee_drv.h              |  23 ++
 8 files changed, 909 insertions(+), 1 deletion(-)
 create mode 100644 drivers/tee/optee/mm_communication.h
 create mode 100644 drivers/tee/optee/optee_stmm_efi.c

diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
index f121c224e682..a0b699977e51 100644
--- a/drivers/tee/optee/Kconfig
+++ b/drivers/tee/optee/Kconfig
@@ -7,3 +7,13 @@ config OPTEE
 	help
 	  This implements the OP-TEE Trusted Execution Environment (TEE)
 	  driver.
+
+config EFI_STMM_OPTEE
+	tristate "OP-TEE based EFI runtime variable service driver"
+	depends on OPTEE
+	help
+	  This  driver provides support for OP-TEE based EFI runtime
+	  variable service driver.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called optee_stmm_efi.
diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
index a6eff388d300..9acb0b2de8fe 100644
--- a/drivers/tee/optee/Makefile
+++ b/drivers/tee/optee/Makefile
@@ -8,6 +8,7 @@ optee-objs += supp.o
 optee-objs += device.o
 optee-objs += smc_abi.o
 optee-objs += ffa_abi.o
+obj-$(CONFIG_EFI_STMM_OPTEE) += optee_stmm_efi.o
 
 # for tracing framework to find optee_trace.h
 CFLAGS_smc_abi.o := -I$(src)
diff --git a/drivers/tee/optee/mm_communication.h b/drivers/tee/optee/mm_communication.h
new file mode 100644
index 000000000000..23e6672991ef
--- /dev/null
+++ b/drivers/tee/optee/mm_communication.h
@@ -0,0 +1,249 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ *  Headers for EFI variable service via StandAloneMM, EDK2 application running
+ *  in OP-TEE
+ *
+ *  Copyright (c) 2017, Intel Corporation. All rights reserved.
+ *  Copyright (C) 2020 Linaro Ltd.
+ */
+
+#ifndef _MM_COMMUNICATION_H_
+#define _MM_COMMUNICATION_H_
+
+/*
+ * Interface to the pseudo Trusted Application (TA), which provides a
+ * communication channel with the Standalone MM (Management Mode)
+ * Secure Partition running at Secure-EL0
+ */
+
+#define PTA_STMM_CMDID_COMMUNICATE 0
+
+/* OP-TEE is using big endian GUIDs while UEFI uses little endian ones */
+#define PTA_STMM_UUID \
+	UUID_INIT(0xed32d533, 0x99e6, 0x4209, \
+		  0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
+
+#define EFI_MM_VARIABLE_GUID \
+	EFI_GUID(0xed32d533, 0x99e6, 0x4209, \
+		 0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
+
+/* Defined in EDK2 MdePkg/Include/Protocol/MmCommunication.h */
+
+/**
+ * struct efi_mm_communicate_header - Header used for SMM variable communication
+
+ * @header_guid:  header use for disambiguation of content
+ * @message_len:  length of the message. Does not include the size of the
+ *                header
+ * @data:         payload of the message
+ *
+ * Defined in EDK2 as EFI_MM_COMMUNICATE_HEADER.
+ * To avoid confusion in interpreting frames, the communication buffer should
+ * always begin with efi_mm_communicate_header.
+ */
+struct efi_mm_communicate_header {
+	efi_guid_t header_guid;
+	size_t     message_len;
+	u8         data[];
+} __packed;
+
+#define MM_COMMUNICATE_HEADER_SIZE \
+	(sizeof(struct efi_mm_communicate_header))
+
+/* Defined in EDK2 ArmPkg/Include/IndustryStandard/ArmMmSvc.h */
+
+/* SPM return error codes */
+#define ARM_SVC_SPM_RET_SUCCESS               0
+#define ARM_SVC_SPM_RET_NOT_SUPPORTED        -1
+#define ARM_SVC_SPM_RET_INVALID_PARAMS       -2
+#define ARM_SVC_SPM_RET_DENIED               -3
+#define ARM_SVC_SPM_RET_NO_MEMORY            -5
+
+/* Defined in EDK2 MdeModulePkg/Include/Guid/SmmVariableCommon.h */
+
+#define SMM_VARIABLE_FUNCTION_GET_VARIABLE  1
+/*
+ * The payload for this function is
+ * SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME.
+ */
+#define SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME  2
+/*
+ * The payload for this function is SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
+ */
+#define SMM_VARIABLE_FUNCTION_SET_VARIABLE  3
+/*
+ * The payload for this function is
+ * SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO.
+ */
+#define SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO  4
+/*
+ * It is a notify event, no extra payload for this function.
+ */
+#define SMM_VARIABLE_FUNCTION_READY_TO_BOOT  5
+/*
+ * It is a notify event, no extra payload for this function.
+ */
+#define SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE  6
+/*
+ * The payload for this function is VARIABLE_INFO_ENTRY.
+ * The GUID in EFI_SMM_COMMUNICATE_HEADER is gEfiSmmVariableProtocolGuid.
+ */
+#define SMM_VARIABLE_FUNCTION_GET_STATISTICS  7
+/*
+ * The payload for this function is SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
+ */
+#define SMM_VARIABLE_FUNCTION_LOCK_VARIABLE   8
+
+#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET  9
+
+#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET  10
+
+#define SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE  11
+/*
+ * The payload for this function is
+ * SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT
+ */
+#define SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT 12
+
+#define SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE  13
+/*
+ * The payload for this function is
+ * SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO
+ */
+#define SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO  14
+
+/**
+ * struct smm_variable_communicate_header - Used for SMM variable communication
+
+ * @function:     function to call in Smm.
+ * @ret_status:   return status
+ * @data:         payload
+ *
+ * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_HEADER.
+ */
+struct smm_variable_communicate_header {
+	size_t  function;
+	efi_status_t ret_status;
+	u8 data[];
+};
+
+#define MM_VARIABLE_COMMUNICATE_SIZE \
+	(sizeof(struct smm_variable_communicate_header))
+
+/**
+ * struct smm_variable_access - Used to communicate with StMM by
+ *                              SetVariable and GetVariable.
+
+ * @guid:         vendor GUID
+ * @data_size:    size of EFI variable data
+ * @name_size:    size of EFI name
+ * @attr:         attributes
+ * @name:         variable name
+ *
+ * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
+ *
+ */
+struct smm_variable_access {
+	efi_guid_t  guid;
+	size_t data_size;
+	size_t name_size;
+	u32 attr;
+	u16 name[];
+};
+
+#define MM_VARIABLE_ACCESS_HEADER_SIZE \
+	(sizeof(struct smm_variable_access))
+/**
+ * struct smm_variable_payload_size - Used to get the max allowed
+ *                                    payload used in StMM.
+ *
+ * @size:  size to fill in
+ *
+ * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE.
+ *
+ */
+struct smm_variable_payload_size {
+	size_t size;
+};
+
+/**
+ * struct smm_variable_getnext - Used to communicate with StMM for
+ *                               GetNextVariableName.
+ *
+ * @guid:       vendor GUID
+ * @name_size:  size of the name of the variable
+ * @name:       variable name
+ *
+ * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME.
+ */
+struct smm_variable_getnext {
+	efi_guid_t  guid;
+	size_t name_size;
+	u16         name[];
+};
+
+#define MM_VARIABLE_GET_NEXT_HEADER_SIZE \
+	(sizeof(struct smm_variable_getnext))
+
+/**
+ * struct smm_variable_query_info - Used to communicate with StMM for
+ *                                  QueryVariableInfo.
+ *
+ * @max_variable_storage:        max available storage
+ * @remaining_variable_storage:  remaining available storage
+ * @max_variable_size:           max variable supported size
+ * @attr:                        attributes to query storage for
+ *
+ * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO.
+ */
+struct smm_variable_query_info {
+	u64 max_variable_storage;
+	u64 remaining_variable_storage;
+	u64 max_variable_size;
+	u32 attr;
+};
+
+#define VAR_CHECK_VARIABLE_PROPERTY_REVISION 0x0001
+#define VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY BIT(0)
+/**
+ * struct var_check_property - Used to store variable properties in StMM
+ *
+ * @revision:   magic revision number for variable property checking
+ * @property:   properties mask for the variable used in StMM.
+ *              Currently RO flag is supported
+ * @attributes: variable attributes used in StMM checking when properties
+ *              for a variable are enabled
+ * @minsize:    minimum allowed size for variable payload checked against
+ *              smm_variable_access->datasize in StMM
+ * @maxsize:    maximum allowed size for variable payload checked against
+ *              smm_variable_access->datasize in StMM
+ *
+ * Defined in EDK2 as VAR_CHECK_VARIABLE_PROPERTY.
+ */
+struct var_check_property {
+	u16 revision;
+	u16 property;
+	u32 attributes;
+	size_t minsize;
+	size_t maxsize;
+};
+
+/**
+ * struct smm_variable_var_check_property - Used to communicate variable
+ *                                          properties with StMM
+ *
+ * @guid:       vendor GUID
+ * @name_size:  size of EFI name
+ * @property:   variable properties struct
+ * @name:       variable name
+ *
+ * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY.
+ */
+struct smm_variable_var_check_property {
+	efi_guid_t guid;
+	size_t name_size;
+	struct var_check_property property;
+	u16 name[];
+};
+
+#endif /* _MM_COMMUNICATION_H_ */
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index 04ae58892608..10af910e0dce 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -12,6 +12,7 @@
 #include <linux/tee_drv.h>
 #include <linux/types.h>
 #include "optee_msg.h"
+#include "optee_private.h"
 
 #define DRIVER_NAME "optee"
 
@@ -19,6 +20,7 @@
 
 /* Some Global Platform error codes used in this driver */
 #define TEEC_SUCCESS			0x00000000
+#define TEEC_ERROR_EXCESS_DATA		0xFFFF0004
 #define TEEC_ERROR_BAD_PARAMETERS	0xFFFF0006
 #define TEEC_ERROR_NOT_SUPPORTED	0xFFFF000A
 #define TEEC_ERROR_COMMUNICATION	0xFFFF000E
@@ -250,7 +252,6 @@ void optee_pool_op_free_helper(struct tee_shm_pool *pool, struct tee_shm *shm,
 			       int (*shm_unregister)(struct tee_context *ctx,
 						     struct tee_shm *shm));
 
-
 void optee_remove_common(struct optee *optee);
 int optee_open(struct tee_context *ctx, bool cap_memref_null);
 void optee_release(struct tee_context *ctx);
@@ -322,4 +323,6 @@ void optee_smc_abi_unregister(void);
 int optee_ffa_abi_register(void);
 void optee_ffa_abi_unregister(void);
 
+int optee_efivar_ops_init(struct tee_context *ctx);
+
 #endif /*OPTEE_PRIVATE_H*/
diff --git a/drivers/tee/optee/optee_stmm_efi.c b/drivers/tee/optee/optee_stmm_efi.c
new file mode 100644
index 000000000000..6dcf1eb4b96c
--- /dev/null
+++ b/drivers/tee/optee/optee_stmm_efi.c
@@ -0,0 +1,598 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  EFI variable service via OP-TEE
+ *
+ *  Copyright (C) 2022 Linaro
+ */
+
+#include <linux/efi.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/tee.h>
+#include <linux/tee_drv.h>
+#include <linux/ucs2_string.h>
+#include "mm_communication.h"
+#include "optee_private.h"
+
+static struct efivars optee_efivars;
+static struct efivar_operations optee_ops;
+
+static size_t max_buffer_size; /* comm + var + func + data */
+static size_t max_payload_size; /* func + data */
+
+struct mm_connection {
+	struct tee_context *ctx;
+	u32 session;
+};
+
+/* UUID of the stmm PTA */
+static const struct tee_client_device_id optee_stmm_efi_id_table[] = {
+	{PTA_STMM_UUID},
+	{}
+};
+
+static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
+{
+	if (ver->impl_id == TEE_IMPL_ID_OPTEE)
+		return 1;
+	else
+		return 0;
+}
+
+/**
+ * get_connection() - Retrieve OP-TEE session for a specific UUID.
+ *
+ * @conn:   session buffer to fill
+ * Return:  status code
+ */
+static int get_connection(struct mm_connection *conn)
+{
+	struct tee_context *ctx = NULL;
+	struct tee_ioctl_open_session_arg arg;
+	int rc;
+
+	memset(&arg, 0, sizeof(arg));
+
+	/* Open context with OP-TEE driver */
+	ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
+	if (IS_ERR(ctx))
+		return -ENODEV;
+
+	export_uuid(arg.uuid, &optee_stmm_efi_id_table[0].uuid);
+	rc = tee_client_open_session(ctx, &arg, NULL);
+	if (!rc) {
+		conn->ctx = ctx;
+		conn->session = arg.session;
+	}
+
+	return rc;
+}
+
+/**
+ * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE
+ *
+ * @comm_buf:		locally allocated communcation buffer
+ * @dsize:		buffer size
+ * Return:		status code
+ */
+static efi_status_t optee_mm_communicate(void *comm_buf, size_t dsize)
+{
+	size_t buf_size;
+	efi_status_t ret;
+	struct efi_mm_communicate_header *mm_hdr;
+	struct mm_connection conn = { NULL, 0 };
+	struct tee_ioctl_invoke_arg arg;
+	struct tee_param param[4];
+	struct tee_shm *shm = NULL;
+	int rc;
+
+	if (!comm_buf)
+		return EFI_INVALID_PARAMETER;
+
+	mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
+	buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
+
+	if (dsize != buf_size)
+		return EFI_INVALID_PARAMETER;
+
+	rc = get_connection(&conn);
+	if (rc) {
+		pr_err("Unable to open OP-TEE session (err=%d)\n", rc);
+		return EFI_UNSUPPORTED;
+	}
+
+	shm = tee_shm_register_kernel_buf(conn.ctx, comm_buf, buf_size);
+	if (IS_ERR(shm)) {
+		pr_err("Unable to register shared memory\n");
+		tee_client_close_session(conn.ctx, conn.session);
+		return EFI_UNSUPPORTED;
+	}
+
+	memset(&arg, 0, sizeof(arg));
+	arg.func = PTA_STMM_CMDID_COMMUNICATE;
+	arg.session = conn.session;
+	arg.num_params = 4;
+
+	memset(param, 0, sizeof(param));
+	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
+	param[0].u.memref.size = buf_size;
+	param[0].u.memref.shm = shm;
+	param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
+	param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+	param[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
+
+	rc = tee_client_invoke_func(conn.ctx, &arg, param);
+	tee_shm_free(shm);
+	/* even if close session fails the session will be invalidaded */
+	tee_client_close_session(conn.ctx, conn.session);
+	if (rc)
+		return EFI_DEVICE_ERROR;
+	if (arg.ret == TEEC_ERROR_EXCESS_DATA)
+		pr_err("Variable payload too large\n");
+	if (arg.ret != TEEC_SUCCESS)
+		return EFI_DEVICE_ERROR;
+
+	switch (param[1].u.value.a) {
+	case ARM_SVC_SPM_RET_SUCCESS:
+		ret = EFI_SUCCESS;
+		break;
+
+	case ARM_SVC_SPM_RET_INVALID_PARAMS:
+		ret = EFI_INVALID_PARAMETER;
+		break;
+
+	case ARM_SVC_SPM_RET_DENIED:
+		ret = EFI_ACCESS_DENIED;
+		break;
+
+	case ARM_SVC_SPM_RET_NO_MEMORY:
+		ret = EFI_OUT_OF_RESOURCES;
+		break;
+
+	default:
+		ret = EFI_ACCESS_DENIED;
+	}
+
+	return ret;
+}
+
+/**
+ * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
+ * it to OP-TEE
+ *
+ * @comm_buf:		locally allocated communcation buffer
+ * @dsize:		buffer size
+ * Return:		status code
+ */
+static efi_status_t mm_communicate(u8 *comm_buf, size_t dsize)
+{
+	efi_status_t ret;
+	struct efi_mm_communicate_header *mm_hdr;
+	struct smm_variable_communicate_header *var_hdr;
+
+	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;
+
+	ret = optee_mm_communicate(comm_buf, dsize);
+	if (ret != EFI_SUCCESS) {
+		pr_err("%s failed!\n", __func__);
+		return ret;
+	}
+
+	return var_hdr->ret_status;
+}
+
+/**
+ * setup_mm_hdr() -	Allocate a buffer for StandAloneMM and initialize the
+ *			header data.
+ *
+ * @dptr:		pointer address of the corresponding StandAloneMM
+ *			function
+ * @payload_size:	buffer size
+ * @func:		standAloneMM function number
+ * @ret:		EFI return code
+ * Return:		buffer or NULL
+ */
+static u8 *setup_mm_hdr(void **dptr, size_t payload_size, size_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;
+
+	/* In the init function we initialize max_buffer_size with
+	 * get_max_payload(). So skip the test if max_buffer_size is initialized
+	 * StandAloneMM will perform similar checks and drop the buffer if it's
+	 * too long
+	 */
+	if (max_buffer_size &&
+	    max_buffer_size < (MM_COMMUNICATE_HEADER_SIZE +
+			       MM_VARIABLE_COMMUNICATE_SIZE + payload_size)) {
+		*ret = EFI_INVALID_PARAMETER;
+		return NULL;
+	}
+
+	comm_buf = kzalloc(MM_COMMUNICATE_HEADER_SIZE +
+				   MM_VARIABLE_COMMUNICATE_SIZE + payload_size,
+			   GFP_KERNEL);
+	if (!comm_buf) {
+		*ret = EFI_OUT_OF_RESOURCES;
+		return NULL;
+	}
+
+	mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
+	memcpy(&mm_hdr->header_guid, &mm_var_guid, 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;
+	var_hdr->function = func;
+	if (dptr)
+		*dptr = var_hdr->data;
+	*ret = EFI_SUCCESS;
+
+	return comm_buf;
+}
+
+/**
+ * get_max_payload() - Get variable payload size from StandAloneMM.
+ *
+ * @size:    size of the variable in storage
+ * Return:   status code
+ */
+static efi_status_t get_max_payload(size_t *size)
+{
+	struct smm_variable_payload_size *var_payload = NULL;
+	size_t payload_size;
+	u8 *comm_buf = NULL;
+	efi_status_t ret;
+
+	if (!size) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	payload_size = sizeof(*var_payload);
+	comm_buf = setup_mm_hdr((void **)&var_payload, payload_size,
+				SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, &ret);
+	if (!comm_buf)
+		goto out;
+
+	ret = mm_communicate(comm_buf, payload_size);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	/* Make sure the buffer is big enough for storing variables */
+	if (var_payload->size < MM_VARIABLE_ACCESS_HEADER_SIZE + 0x20) {
+		ret = EFI_DEVICE_ERROR;
+		goto out;
+	}
+	*size = var_payload->size;
+	/*
+	 * There seems to be a bug in EDK2 miscalculating the boundaries and
+	 * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
+	 * it up here to ensure backwards compatibility with older versions
+	 * (cf. StandaloneMmPkg/Drivers/StandaloneMmCpu/AArch64/EventHandle.c.
+	 * sizeof (EFI_MM_COMMUNICATE_HEADER) instead the size minus the
+	 * flexible array member).
+	 *
+	 * size is guaranteed to be > 2 due to checks on the beginning.
+	 */
+	*size -= 2;
+out:
+	kfree(comm_buf);
+	return ret;
+}
+
+static efi_status_t get_property_int(u16 *name, size_t name_size,
+				     const efi_guid_t *vendor,
+				     struct var_check_property *var_property)
+{
+	struct smm_variable_var_check_property *smm_property;
+	size_t payload_size;
+	u8 *comm_buf = NULL;
+	efi_status_t ret;
+
+	memset(var_property, 0, sizeof(*var_property));
+	payload_size = sizeof(*smm_property) + name_size;
+	if (payload_size > max_payload_size) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+	comm_buf = setup_mm_hdr(
+		(void **)&smm_property, payload_size,
+		SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET, &ret);
+	if (!comm_buf)
+		goto out;
+
+	memcpy(&smm_property->guid, vendor, sizeof(smm_property->guid));
+	smm_property->name_size = name_size;
+	memcpy(smm_property->name, name, name_size);
+
+	ret = mm_communicate(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)
+		goto out;
+	memcpy(var_property, &smm_property->property, sizeof(*var_property));
+
+out:
+	kfree(comm_buf);
+	return ret;
+}
+
+static efi_status_t optee_get_variable(u16 *name, efi_guid_t *vendor,
+				       u32 *attributes,
+				       unsigned long *data_size, void *data)
+{
+	struct var_check_property var_property;
+	struct smm_variable_access *var_acc;
+	size_t payload_size;
+	size_t name_size;
+	size_t tmp_dsize;
+	u8 *comm_buf = NULL;
+	efi_status_t ret;
+
+	if (!name || !vendor || !data_size) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
+	if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	/* 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)
+		goto out;
+
+	/* Fill in contents */
+	memcpy(&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;
+	memcpy(var_acc->name, name, name_size);
+
+	/* Communicate */
+	ret = mm_communicate(comm_buf, payload_size);
+	if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL)
+		/* Update with reported data size for trimmed case */
+		*data_size = var_acc->data_size;
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	ret = get_property_int(name, name_size, vendor, &var_property);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	if (attributes)
+		*attributes = var_acc->attr;
+
+	if (data)
+		memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
+		       var_acc->data_size);
+	else
+		ret = EFI_INVALID_PARAMETER;
+
+out:
+	kfree(comm_buf);
+	return ret;
+}
+
+static efi_status_t optee_get_next_variable(unsigned long *name_size,
+					    efi_char16_t *name,
+					    efi_guid_t *guid)
+{
+	struct smm_variable_getnext *var_getnext;
+	size_t payload_size;
+	size_t out_name_size;
+	size_t in_name_size;
+	u8 *comm_buf = NULL;
+	efi_status_t ret;
+
+	if (!name_size || !name || !guid) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	out_name_size = *name_size;
+	in_name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
+
+	if (out_name_size < in_name_size) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	if (in_name_size >
+	    max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	/* 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)
+		goto out;
+
+	/* Fill in contents */
+	memcpy(&var_getnext->guid, guid, sizeof(var_getnext->guid));
+	var_getnext->name_size = out_name_size;
+	memcpy(var_getnext->name, name, in_name_size);
+	memset((u8 *)var_getnext->name + in_name_size, 0x0,
+	       out_name_size - in_name_size);
+
+	/* Communicate */
+	ret = mm_communicate(comm_buf, payload_size);
+	if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
+		/* Update with reported data size for trimmed case */
+		*name_size = var_getnext->name_size;
+	}
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	memcpy(guid, &var_getnext->guid, sizeof(*guid));
+	memcpy(name, var_getnext->name, var_getnext->name_size);
+
+out:
+	kfree(comm_buf);
+	return ret;
+}
+
+static efi_status_t optee_set_variable(efi_char16_t *name, efi_guid_t *vendor,
+				       u32 attributes, unsigned long data_size,
+				       void *data)
+{
+	efi_status_t ret;
+	struct var_check_property var_property;
+	struct smm_variable_access *var_acc;
+	size_t payload_size;
+	size_t name_size;
+	u8 *comm_buf = NULL;
+
+	if (!name || name[0] == 0 || !vendor) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+	if (data_size > 0 && !data) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+	/* Check payload size */
+	name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
+	payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
+	if (payload_size > max_payload_size) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+
+	/*
+	 * 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)
+		goto out;
+
+	/*
+	 * The API has the ability to override RO flags. If no RO check was
+	 * requested switch the variable to RW for the duration of this call
+	 */
+	ret = get_property_int(name, name_size, vendor, &var_property);
+	if (ret != EFI_SUCCESS) {
+		pr_err("Getting variable property failed\n");
+		goto out;
+	}
+
+	if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) {
+		ret = EFI_WRITE_PROTECTED;
+		goto out;
+	}
+
+	/* Fill in contents */
+	memcpy(&var_acc->guid, vendor, sizeof(var_acc->guid));
+	var_acc->data_size = data_size;
+	var_acc->name_size = name_size;
+	var_acc->attr = attributes;
+	memcpy(var_acc->name, name, name_size);
+	memcpy((u8 *)var_acc->name + name_size, data, data_size);
+
+
+	/* Communicate */
+	ret = mm_communicate(comm_buf, payload_size);
+	pr_info("Set Variable %s %d %lx\n", __FILE__, __LINE__, ret);
+out:
+	kfree(comm_buf);
+	return ret;
+}
+
+static int optee_stmm_efi_probe(struct device *dev)
+{
+	efi_status_t ret;
+
+	ret = get_max_payload(&max_payload_size);
+	if (ret != EFI_SUCCESS)
+		return ret;
+
+	max_buffer_size = MM_COMMUNICATE_HEADER_SIZE +
+			  MM_VARIABLE_COMMUNICATE_SIZE +
+			  max_payload_size;
+
+	optee_ops.get_variable = optee_get_variable;
+	optee_ops.get_next_variable = optee_get_next_variable;
+	optee_ops.set_variable = optee_set_variable;
+	/* TODO: support nonblocking variant */
+	optee_ops.set_variable_nonblocking = NULL;
+	/* set NULL, always return EFI_SUCCESS by efi_query_variable_store() */
+	optee_ops.query_variable_store = NULL;
+
+	tee_register_efivar_ops(&optee_efivars, &optee_ops);
+
+	return 0;
+}
+
+static int optee_stmm_efi_remove(struct device *dev)
+{
+	tee_unregister_efivar_ops(&optee_efivars);
+
+	return 0;
+}
+
+MODULE_DEVICE_TABLE(tee, optee_stmm_efi_id_table);
+
+static struct tee_client_driver optee_stmm_efi_driver = {
+	.id_table	= optee_stmm_efi_id_table,
+	.driver		= {
+		.name		= "optee-stmm-efi",
+		.bus		= &tee_bus_type,
+		.probe		= optee_stmm_efi_probe,
+		.remove		= optee_stmm_efi_remove,
+	},
+};
+
+static int __init optee_stmm_efi_mod_init(void)
+{
+	return driver_register(&optee_stmm_efi_driver.driver);
+}
+
+static void __exit optee_stmm_efi_mod_exit(void)
+{
+	driver_unregister(&optee_stmm_efi_driver.driver);
+}
+
+module_init(optee_stmm_efi_mod_init);
+module_exit(optee_stmm_efi_mod_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ilias Apalodimas <ilias.apalodimas@linaro.org>");
+MODULE_AUTHOR("Masahisa Kojima <masahisa.kojima@linaro.org>");
+MODULE_DESCRIPTION("OP-TEE based EFI runtime variable service driver");
diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
index 98da206cd761..ac46274844a3 100644
--- a/drivers/tee/tee_core.c
+++ b/drivers/tee/tee_core.c
@@ -7,6 +7,7 @@
 
 #include <linux/cdev.h>
 #include <linux/cred.h>
+#include <linux/efi.h>
 #include <linux/fs.h>
 #include <linux/idr.h>
 #include <linux/module.h>
@@ -1263,6 +1264,28 @@ static void __exit tee_exit(void)
 	tee_class = NULL;
 }
 
+void tee_register_efivar_ops(struct efivars *tee_efivars,
+			     struct efivar_operations *ops)
+{
+	/*
+	 * If the firmware EFI runtime services support SetVariable(),
+	 * tee-based EFI variable services are not used.
+	 */
+	if (!efivar_supports_writes()) {
+		pr_info("Use tee-based EFI runtime variable services\n");
+		efivars_generic_ops_unregister();
+		efivars_register(tee_efivars, ops);
+	}
+}
+EXPORT_SYMBOL_GPL(tee_register_efivar_ops);
+
+void tee_unregister_efivar_ops(struct efivars *tee_efivars)
+{
+	efivars_unregister(tee_efivars);
+	efivars_generic_ops_register();
+}
+EXPORT_SYMBOL_GPL(tee_unregister_efivar_ops);
+
 subsys_initcall(tee_init);
 module_exit(tee_exit);
 
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 5e301c00e9b0..14d4aa83ce60 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -39,6 +39,7 @@
 #define EFI_WRITE_PROTECTED	( 8 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_OUT_OF_RESOURCES	( 9 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_NOT_FOUND		(14 | (1UL << (BITS_PER_LONG-1)))
+#define EFI_ACCESS_DENIED	(15 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_TIMEOUT		(18 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_ABORTED		(21 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_SECURITY_VIOLATION	(26 | (1UL << (BITS_PER_LONG-1)))
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index 17eb1c5205d3..def4ea6212ee 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -7,6 +7,7 @@
 #define __TEE_DRV_H
 
 #include <linux/device.h>
+#include <linux/efi.h>
 #include <linux/idr.h>
 #include <linux/kref.h>
 #include <linux/list.h>
@@ -507,4 +508,26 @@ struct tee_context *teedev_open(struct tee_device *teedev);
  */
 void teedev_close_context(struct tee_context *ctx);
 
+/**
+ * tee_register_efivar_ops() - register the efivar ops
+ * @tee_efivars:	pointer to efivars structure
+ * @ops:		pointer to contain the efivar operation
+ *
+ * This function registers the tee-based efivar operation as an
+ * EFI Runtime Service.
+ *
+ */
+void tee_register_efivar_ops(struct efivars *tee_efivars,
+			     struct efivar_operations *ops);
+
+/**
+ * tee_unregister_efivar_ops() - unregister the efivar ops
+ * @tee_efivars:	pointer to efivars structure
+ *
+ * This function unregisters the tee-based efivar operation
+ * and reverts to the generic operation.
+ *
+ */
+void tee_unregister_efivar_ops(struct efivars *tee_efivars);
+
 #endif /*__TEE_DRV_H*/
-- 
2.30.2


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/2] tee: Add op-tee helper functions for variable access
  2023-01-26 13:21   ` Masahisa Kojima
  (?)
@ 2023-01-29  2:08   ` kernel test robot
  -1 siblings, 0 replies; 21+ messages in thread
From: kernel test robot @ 2023-01-29  2:08 UTC (permalink / raw)
  To: Masahisa Kojima; +Cc: oe-kbuild-all

Hi Masahisa,

[FYI, it's a private test report for your RFC patch.]
[auto build test ERROR on efi/next]
[also build test ERROR on next-20230127]
[cannot apply to linus/master v6.2-rc5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Masahisa-Kojima/efi-expose-efivar-generic-ops-register-function/20230128-170022
base:   https://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git next
patch link:    https://lore.kernel.org/r/20230126132120.1661-3-masahisa.kojima%40linaro.org
patch subject: [RFC PATCH 2/2] tee: Add op-tee helper functions for variable access
config: arm-randconfig-r046-20230129 (https://download.01.org/0day-ci/archive/20230129/202301290936.Tla4Ic8d-lkp@intel.com/config)
compiler: arm-linux-gnueabi-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/2ff56d7a5b6fa1214c4ef504b921b52d387e0e3b
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Masahisa-Kojima/efi-expose-efivar-generic-ops-register-function/20230128-170022
        git checkout 2ff56d7a5b6fa1214c4ef504b921b52d387e0e3b
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arm olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=arm SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   arm-linux-gnueabi-ld: drivers/tee/tee_core.o: in function `tee_register_efivar_ops':
>> tee_core.c:(.text+0x1122): undefined reference to `efivar_supports_writes'
>> arm-linux-gnueabi-ld: tee_core.c:(.text+0x1132): undefined reference to `efivars_generic_ops_unregister'
   arm-linux-gnueabi-ld: drivers/tee/tee_core.o: in function `tee_unregister_efivar_ops':
>> tee_core.c:(.text+0x114e): undefined reference to `efivars_unregister'
   arm-linux-gnueabi-ld: drivers/tee/tee_core.o: in function `tee_register_efivar_ops':
>> tee_core.c:(.text+0x113e): undefined reference to `efivars_register'
   arm-linux-gnueabi-ld: drivers/tee/tee_core.o: in function `tee_unregister_efivar_ops':
>> tee_core.c:(.text+0x1156): undefined reference to `efivars_generic_ops_register'

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/2] tee: Add op-tee helper functions for variable access
  2023-01-26 13:21   ` Masahisa Kojima
  (?)
  (?)
@ 2023-01-29  3:41   ` kernel test robot
  -1 siblings, 0 replies; 21+ messages in thread
From: kernel test robot @ 2023-01-29  3:41 UTC (permalink / raw)
  To: Masahisa Kojima; +Cc: oe-kbuild-all

Hi Masahisa,

[FYI, it's a private test report for your RFC patch.]
[auto build test ERROR on efi/next]
[also build test ERROR on next-20230127]
[cannot apply to linus/master v6.2-rc5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Masahisa-Kojima/efi-expose-efivar-generic-ops-register-function/20230128-170022
base:   https://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git next
patch link:    https://lore.kernel.org/r/20230126132120.1661-3-masahisa.kojima%40linaro.org
patch subject: [RFC PATCH 2/2] tee: Add op-tee helper functions for variable access
config: powerpc-randconfig-c042-20230129 (https://download.01.org/0day-ci/archive/20230129/202301291136.9ddQFcPV-lkp@intel.com/config)
compiler: powerpc-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/2ff56d7a5b6fa1214c4ef504b921b52d387e0e3b
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Masahisa-Kojima/efi-expose-efivar-generic-ops-register-function/20230128-170022
        git checkout 2ff56d7a5b6fa1214c4ef504b921b52d387e0e3b
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=powerpc olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=powerpc SHELL=/bin/bash

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   powerpc-linux-ld: drivers/tee/tee_core.o: in function `tee_register_efivar_ops':
   tee_core.c:(.text+0x22c0): undefined reference to `efivar_supports_writes'
>> powerpc-linux-ld: tee_core.c:(.text+0x22e4): undefined reference to `efivars_generic_ops_unregister'
>> powerpc-linux-ld: tee_core.c:(.text+0x2304): undefined reference to `efivars_register'
   powerpc-linux-ld: drivers/tee/tee_core.o: in function `tee_unregister_efivar_ops':
   tee_core.c:(.text+0x2328): undefined reference to `efivars_unregister'
>> powerpc-linux-ld: tee_core.c:(.text+0x2338): undefined reference to `efivars_generic_ops_register'

Kconfig warnings: (for reference only)
   WARNING: unmet direct dependencies detected for HOTPLUG_CPU
   Depends on [n]: SMP [=y] && (PPC_PSERIES [=n] || PPC_PMAC [=n] || PPC_POWERNV [=n] || FSL_SOC_BOOKE [=n])
   Selected by [y]:
   - PM_SLEEP_SMP [=y] && SMP [=y] && (ARCH_SUSPEND_POSSIBLE [=y] || ARCH_HIBERNATION_POSSIBLE [=y]) && PM_SLEEP [=y]

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service
  2023-01-26 13:21 [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service Masahisa Kojima
  2023-01-26 13:21 ` [RFC PATCH 1/2] efi: expose efivar generic ops register function Masahisa Kojima
  2023-01-26 13:21   ` Masahisa Kojima
@ 2023-02-02 12:05 ` Sumit Garg
  2023-02-02 13:19   ` Ilias Apalodimas
  2023-02-03  8:29   ` Jens Wiklander
  2 siblings, 2 replies; 21+ messages in thread
From: Sumit Garg @ 2023-02-02 12:05 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ard Biesheuvel, Jens Wiklander, linux-kernel, op-tee,
	Ilias Apalodimas, Johan Hovold

Hi Masahisa,

On Thu, 26 Jan 2023 at 18:52, Masahisa Kojima
<masahisa.kojima@linaro.org> wrote:
>
> This RFC series introduces the op-tee based EFI Runtime Variable
> Service.
>
> The eMMC device is typically owned by the non-secure world(linux in
> this case). There is an existing solution utilizing eMMC RPMB partition
> for EFI Variables, it is implemented by interacting with
> OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
> and tee-supplicant. The last piece is the tee-based variable access
> driver to interact with OP-TEE and StandaloneMM.
>

After an overall look at the APIs, following are some initial comments:
- Is there any reason to have the edk2 specific StandaloneMM stack in
Linux to communicate with OP-TEE pseudo TA?
- I think the OP-TEE pseudo TA should be able to expose a rather
generic invoke commands such as:
     TEE_EFI_GET_VARIABLE
     TEE_EFI_GET_NEXT_VARIABLE
     TEE_EFI_SET_VARIABLE
  So it should no longer be tied to StMM stack and other TEE
implementations can re-use the abstracted interface to communicate
with its corresponding secure storage TA.

-Sumit

> Masahisa Kojima (2):
>   efi: expose efivar generic ops register function
>   tee: Add op-tee helper functions for variable access
>
>  drivers/firmware/efi/efi.c           |  12 +
>  drivers/tee/optee/Kconfig            |  10 +
>  drivers/tee/optee/Makefile           |   1 +
>  drivers/tee/optee/mm_communication.h | 249 +++++++++++
>  drivers/tee/optee/optee_private.h    |   5 +-
>  drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
>  drivers/tee/tee_core.c               |  23 ++
>  include/linux/efi.h                  |   4 +
>  include/linux/tee_drv.h              |  23 ++
>  9 files changed, 924 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/tee/optee/mm_communication.h
>  create mode 100644 drivers/tee/optee/optee_stmm_efi.c
>
> --
> 2.30.2
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service
  2023-02-02 12:05 ` [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service Sumit Garg
@ 2023-02-02 13:19   ` Ilias Apalodimas
  2023-02-03  8:29   ` Jens Wiklander
  1 sibling, 0 replies; 21+ messages in thread
From: Ilias Apalodimas @ 2023-02-02 13:19 UTC (permalink / raw)
  To: Sumit Garg
  Cc: Masahisa Kojima, Ard Biesheuvel, Jens Wiklander, linux-kernel,
	op-tee, Johan Hovold

On Thu, Feb 02, 2023 at 05:35:49PM +0530, Sumit Garg wrote:
> Hi Masahisa,
>
> On Thu, 26 Jan 2023 at 18:52, Masahisa Kojima
> <masahisa.kojima@linaro.org> wrote:
> >
> > This RFC series introduces the op-tee based EFI Runtime Variable
> > Service.
> >
> > The eMMC device is typically owned by the non-secure world(linux in
> > this case). There is an existing solution utilizing eMMC RPMB partition
> > for EFI Variables, it is implemented by interacting with
> > OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
> > and tee-supplicant. The last piece is the tee-based variable access
> > driver to interact with OP-TEE and StandaloneMM.
> >
>
> After an overall look at the APIs, following are some initial comments:
> - Is there any reason to have the edk2 specific StandaloneMM stack in
> Linux to communicate with OP-TEE pseudo TA?

The problem is StMM is not a pseudo-TA and pretending to be one
is not easy.  It looks like one, but due to the internal ABI, it's compiled
and launched differently within OP-TEE.  We could still add this but...

> - I think the OP-TEE pseudo TA should be able to expose a rather
> generic invoke commands such as:
>      TEE_EFI_GET_VARIABLE
>      TEE_EFI_GET_NEXT_VARIABLE
>      TEE_EFI_SET_VARIABLE
>   So it should no longer be tied to StMM stack and other TEE
> implementations can re-use the abstracted interface to communicate
> with its corresponding secure storage TA.

I talked about this with Jens, but there's an assumption here that every
TEE from that point onward will implement this.  But without a standard
describing this, I don't see how we can convince others.  The current code
puts the responsibility back to the tee/core subsystem, so any vendor can
provide his own callbacks

Regards
/Ilias
>
> -Sumit
>
> > Masahisa Kojima (2):
> >   efi: expose efivar generic ops register function
> >   tee: Add op-tee helper functions for variable access
> >
> >  drivers/firmware/efi/efi.c           |  12 +
> >  drivers/tee/optee/Kconfig            |  10 +
> >  drivers/tee/optee/Makefile           |   1 +
> >  drivers/tee/optee/mm_communication.h | 249 +++++++++++
> >  drivers/tee/optee/optee_private.h    |   5 +-
> >  drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
> >  drivers/tee/tee_core.c               |  23 ++
> >  include/linux/efi.h                  |   4 +
> >  include/linux/tee_drv.h              |  23 ++
> >  9 files changed, 924 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/tee/optee/mm_communication.h
> >  create mode 100644 drivers/tee/optee/optee_stmm_efi.c
> >
> > --
> > 2.30.2
> >

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service
  2023-02-02 12:05 ` [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service Sumit Garg
  2023-02-02 13:19   ` Ilias Apalodimas
@ 2023-02-03  8:29   ` Jens Wiklander
  2023-02-03  9:33     ` Sumit Garg
  1 sibling, 1 reply; 21+ messages in thread
From: Jens Wiklander @ 2023-02-03  8:29 UTC (permalink / raw)
  To: Sumit Garg
  Cc: Masahisa Kojima, Ard Biesheuvel, linux-kernel, op-tee,
	Ilias Apalodimas, Johan Hovold

Hi Sumit,

On Thu, Feb 02, 2023 at 05:35:49PM +0530, Sumit Garg wrote:
> Hi Masahisa,
> 
> On Thu, 26 Jan 2023 at 18:52, Masahisa Kojima
> <masahisa.kojima@linaro.org> wrote:
> >
> > This RFC series introduces the op-tee based EFI Runtime Variable
> > Service.
> >
> > The eMMC device is typically owned by the non-secure world(linux in
> > this case). There is an existing solution utilizing eMMC RPMB partition
> > for EFI Variables, it is implemented by interacting with
> > OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
> > and tee-supplicant. The last piece is the tee-based variable access
> > driver to interact with OP-TEE and StandaloneMM.
> >
> 
> After an overall look at the APIs, following are some initial comments:
> - Is there any reason to have the edk2 specific StandaloneMM stack in
> Linux to communicate with OP-TEE pseudo TA?
> - I think the OP-TEE pseudo TA should be able to expose a rather
> generic invoke commands such as:
>      TEE_EFI_GET_VARIABLE
>      TEE_EFI_GET_NEXT_VARIABLE
>      TEE_EFI_SET_VARIABLE
>   So it should no longer be tied to StMM stack and other TEE
> implementations can re-use the abstracted interface to communicate
> with its corresponding secure storage TA.

In the current setup we have the following layers in the kernel:
1. efivar_operations
2. MM
3. PTA_STMM
4. OP-TEE MSG

and in the secure world:
S1. internal to StMM
S2. MM interface to StMM
S3. PTA_STMM
S4. OP-TEE MSG

If I understand you correctly you'd like to see this instead:
Kernel:
1. efivar_operations
2. PTA_EFIVAR
4. OP-TEE MSG

Since we still have the MM interface with StMM we'd have this in the secure
world:
S1. internal to StMM
S2. MM interface to StMM
S3. PTA_EFIVAR
S4. OP-TEE MSG

At S3 we'd have to convert between EFIVAR and MM messages. The
difference is that we're moving the EFIVAR <-> MM conversion from the
non-secure world into the secure world. We're still using OP-TEE
specific communication at the fourth layer. So we're only moving problem
around, I'd rather avoid growing the OP-TEE part in the secure world.

Cheers,
Jens

> 
> -Sumit
> 
> > Masahisa Kojima (2):
> >   efi: expose efivar generic ops register function
> >   tee: Add op-tee helper functions for variable access
> >
> >  drivers/firmware/efi/efi.c           |  12 +
> >  drivers/tee/optee/Kconfig            |  10 +
> >  drivers/tee/optee/Makefile           |   1 +
> >  drivers/tee/optee/mm_communication.h | 249 +++++++++++
> >  drivers/tee/optee/optee_private.h    |   5 +-
> >  drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
> >  drivers/tee/tee_core.c               |  23 ++
> >  include/linux/efi.h                  |   4 +
> >  include/linux/tee_drv.h              |  23 ++
> >  9 files changed, 924 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/tee/optee/mm_communication.h
> >  create mode 100644 drivers/tee/optee/optee_stmm_efi.c
> >
> > --
> > 2.30.2
> >

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/2] tee: Add op-tee helper functions for variable access
  2023-01-26 13:21   ` Masahisa Kojima
@ 2023-02-03  9:30     ` Jens Wiklander
  -1 siblings, 0 replies; 21+ messages in thread
From: Jens Wiklander @ 2023-02-03  9:30 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ard Biesheuvel, linux-kernel, op-tee, Ilias Apalodimas,
	Johan Hovold, Sumit Garg, Maxime Coquelin, Alexandre Torgue,
	linux-stm32, linux-arm-kernel, linux-efi

On Thu, Jan 26, 2023 at 10:21:19PM +0900, Masahisa Kojima wrote:
> When the flash is not owned by the non-secure world, accessing the EFI
> variables is straightforward and done via EFI Runtime Variable Services.
> In this case, critical variables for system integrity and security
> are normally stored in the dedicated secure storage and only accessible
> from the secure world.
> 
> On the other hand, the small embedded devices don't have the special
> dedicated secure storage. The eMMC device with an RPMB partition is
> becoming more common, we can use an RPMB partition to store the
> EFI Variables.
> 
> The eMMC device is typically owned by the non-secure world(linux in
> this case). There is an existing solution utilizing eMMC RPMB partition
> for EFI Variables, it is implemented by interacting with
> OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
> and tee-supplicant. The last piece is the tee-based variable access
> driver to interact with OP-TEE and StandaloneMM.
> 
> So let's add the kernel functions needed.
> 
> This feature is implemented as a kernel module.
> StMM PTA has TA_FLAG_DEVICE_ENUM_SUPP flag when registered to OP-TEE
> so that this optee_stmm_efi module is probed after tee-supplicant starts,
> since "SetVariable" EFI Runtime Variable Service requires to
> interact with tee-supplicant.
> 
> Co-developed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
>  drivers/tee/optee/Kconfig            |  10 +
>  drivers/tee/optee/Makefile           |   1 +
>  drivers/tee/optee/mm_communication.h | 249 +++++++++++
>  drivers/tee/optee/optee_private.h    |   5 +-
>  drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
>  drivers/tee/tee_core.c               |  23 ++
>  include/linux/efi.h                  |   1 +
>  include/linux/tee_drv.h              |  23 ++
>  8 files changed, 909 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/tee/optee/mm_communication.h
>  create mode 100644 drivers/tee/optee/optee_stmm_efi.c


Please split this patch into three patches, one for drivers/tee, one
for the driver/tee/optee, and one for include/linux/efi.h.

> 
> diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
> index f121c224e682..a0b699977e51 100644
> --- a/drivers/tee/optee/Kconfig
> +++ b/drivers/tee/optee/Kconfig
> @@ -7,3 +7,13 @@ config OPTEE
>  	help
>  	  This implements the OP-TEE Trusted Execution Environment (TEE)
>  	  driver.
> +
> +config EFI_STMM_OPTEE

Should this be OPTEE_EFI_STMM?

> +	tristate "OP-TEE based EFI runtime variable service driver"
> +	depends on OPTEE
> +	help
> +	  This  driver provides support for OP-TEE based EFI runtime
> +	  variable service driver.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called optee_stmm_efi.
> diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
> index a6eff388d300..9acb0b2de8fe 100644
> --- a/drivers/tee/optee/Makefile
> +++ b/drivers/tee/optee/Makefile
> @@ -8,6 +8,7 @@ optee-objs += supp.o
>  optee-objs += device.o
>  optee-objs += smc_abi.o
>  optee-objs += ffa_abi.o
> +obj-$(CONFIG_EFI_STMM_OPTEE) += optee_stmm_efi.o
>  
>  # for tracing framework to find optee_trace.h
>  CFLAGS_smc_abi.o := -I$(src)
> diff --git a/drivers/tee/optee/mm_communication.h b/drivers/tee/optee/mm_communication.h
> new file mode 100644
> index 000000000000..23e6672991ef
> --- /dev/null
> +++ b/drivers/tee/optee/mm_communication.h
> @@ -0,0 +1,249 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + *  Headers for EFI variable service via StandAloneMM, EDK2 application running
> + *  in OP-TEE
> + *
> + *  Copyright (c) 2017, Intel Corporation. All rights reserved.
> + *  Copyright (C) 2020 Linaro Ltd.
> + */
> +
> +#ifndef _MM_COMMUNICATION_H_
> +#define _MM_COMMUNICATION_H_
> +
> +/*
> + * Interface to the pseudo Trusted Application (TA), which provides a
> + * communication channel with the Standalone MM (Management Mode)
> + * Secure Partition running at Secure-EL0
> + */
> +
> +#define PTA_STMM_CMDID_COMMUNICATE 0

PTA_STMM_CMD_COMMUNICATE

> +
> +/* OP-TEE is using big endian GUIDs while UEFI uses little endian ones */
> +#define PTA_STMM_UUID \
> +	UUID_INIT(0xed32d533, 0x99e6, 0x4209, \
> +		  0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
> +
> +#define EFI_MM_VARIABLE_GUID \
> +	EFI_GUID(0xed32d533, 0x99e6, 0x4209, \
> +		 0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
> +
> +/* Defined in EDK2 MdePkg/Include/Protocol/MmCommunication.h */
> +
> +/**
> + * struct efi_mm_communicate_header - Header used for SMM variable communication
> +
> + * @header_guid:  header use for disambiguation of content
> + * @message_len:  length of the message. Does not include the size of the
> + *                header
> + * @data:         payload of the message
> + *
> + * Defined in EDK2 as EFI_MM_COMMUNICATE_HEADER.
> + * To avoid confusion in interpreting frames, the communication buffer should
> + * always begin with efi_mm_communicate_header.
> + */
> +struct efi_mm_communicate_header {
> +	efi_guid_t header_guid;
> +	size_t     message_len;
> +	u8         data[];
> +} __packed;
> +
> +#define MM_COMMUNICATE_HEADER_SIZE \
> +	(sizeof(struct efi_mm_communicate_header))
> +
> +/* Defined in EDK2 ArmPkg/Include/IndustryStandard/ArmMmSvc.h */
> +
> +/* SPM return error codes */
> +#define ARM_SVC_SPM_RET_SUCCESS               0
> +#define ARM_SVC_SPM_RET_NOT_SUPPORTED        -1
> +#define ARM_SVC_SPM_RET_INVALID_PARAMS       -2
> +#define ARM_SVC_SPM_RET_DENIED               -3
> +#define ARM_SVC_SPM_RET_NO_MEMORY            -5
> +
> +/* Defined in EDK2 MdeModulePkg/Include/Guid/SmmVariableCommon.h */
> +
> +#define SMM_VARIABLE_FUNCTION_GET_VARIABLE  1
> +/*
> + * The payload for this function is
> + * SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME.
> + */
> +#define SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME  2
> +/*
> + * The payload for this function is SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
> + */
> +#define SMM_VARIABLE_FUNCTION_SET_VARIABLE  3
> +/*
> + * The payload for this function is
> + * SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO.
> + */
> +#define SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO  4
> +/*
> + * It is a notify event, no extra payload for this function.
> + */
> +#define SMM_VARIABLE_FUNCTION_READY_TO_BOOT  5
> +/*
> + * It is a notify event, no extra payload for this function.
> + */
> +#define SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE  6
> +/*
> + * The payload for this function is VARIABLE_INFO_ENTRY.
> + * The GUID in EFI_SMM_COMMUNICATE_HEADER is gEfiSmmVariableProtocolGuid.
> + */
> +#define SMM_VARIABLE_FUNCTION_GET_STATISTICS  7
> +/*
> + * The payload for this function is SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
> + */
> +#define SMM_VARIABLE_FUNCTION_LOCK_VARIABLE   8
> +
> +#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET  9
> +
> +#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET  10
> +
> +#define SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE  11
> +/*
> + * The payload for this function is
> + * SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT
> + */
> +#define SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT 12
> +
> +#define SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE  13
> +/*
> + * The payload for this function is
> + * SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO
> + */
> +#define SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO  14
> +
> +/**
> + * struct smm_variable_communicate_header - Used for SMM variable communication
> +
> + * @function:     function to call in Smm.
> + * @ret_status:   return status
> + * @data:         payload
> + *
> + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_HEADER.
> + */
> +struct smm_variable_communicate_header {
> +	size_t  function;
> +	efi_status_t ret_status;
> +	u8 data[];
> +};
> +
> +#define MM_VARIABLE_COMMUNICATE_SIZE \
> +	(sizeof(struct smm_variable_communicate_header))
> +
> +/**
> + * struct smm_variable_access - Used to communicate with StMM by
> + *                              SetVariable and GetVariable.
> +
> + * @guid:         vendor GUID
> + * @data_size:    size of EFI variable data
> + * @name_size:    size of EFI name
> + * @attr:         attributes
> + * @name:         variable name
> + *
> + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
> + *
> + */
> +struct smm_variable_access {
> +	efi_guid_t  guid;
> +	size_t data_size;
> +	size_t name_size;
> +	u32 attr;
> +	u16 name[];
> +};
> +
> +#define MM_VARIABLE_ACCESS_HEADER_SIZE \
> +	(sizeof(struct smm_variable_access))
> +/**
> + * struct smm_variable_payload_size - Used to get the max allowed
> + *                                    payload used in StMM.
> + *
> + * @size:  size to fill in
> + *
> + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE.
> + *
> + */
> +struct smm_variable_payload_size {
> +	size_t size;
> +};
> +
> +/**
> + * struct smm_variable_getnext - Used to communicate with StMM for
> + *                               GetNextVariableName.
> + *
> + * @guid:       vendor GUID
> + * @name_size:  size of the name of the variable
> + * @name:       variable name
> + *
> + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME.
> + */
> +struct smm_variable_getnext {
> +	efi_guid_t  guid;
> +	size_t name_size;
> +	u16         name[];
> +};
> +
> +#define MM_VARIABLE_GET_NEXT_HEADER_SIZE \
> +	(sizeof(struct smm_variable_getnext))
> +
> +/**
> + * struct smm_variable_query_info - Used to communicate with StMM for
> + *                                  QueryVariableInfo.
> + *
> + * @max_variable_storage:        max available storage
> + * @remaining_variable_storage:  remaining available storage
> + * @max_variable_size:           max variable supported size
> + * @attr:                        attributes to query storage for
> + *
> + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO.
> + */
> +struct smm_variable_query_info {
> +	u64 max_variable_storage;
> +	u64 remaining_variable_storage;
> +	u64 max_variable_size;
> +	u32 attr;
> +};
> +
> +#define VAR_CHECK_VARIABLE_PROPERTY_REVISION 0x0001
> +#define VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY BIT(0)
> +/**
> + * struct var_check_property - Used to store variable properties in StMM
> + *
> + * @revision:   magic revision number for variable property checking
> + * @property:   properties mask for the variable used in StMM.
> + *              Currently RO flag is supported
> + * @attributes: variable attributes used in StMM checking when properties
> + *              for a variable are enabled
> + * @minsize:    minimum allowed size for variable payload checked against
> + *              smm_variable_access->datasize in StMM
> + * @maxsize:    maximum allowed size for variable payload checked against
> + *              smm_variable_access->datasize in StMM
> + *
> + * Defined in EDK2 as VAR_CHECK_VARIABLE_PROPERTY.
> + */
> +struct var_check_property {
> +	u16 revision;
> +	u16 property;
> +	u32 attributes;
> +	size_t minsize;
> +	size_t maxsize;
> +};
> +
> +/**
> + * struct smm_variable_var_check_property - Used to communicate variable
> + *                                          properties with StMM
> + *
> + * @guid:       vendor GUID
> + * @name_size:  size of EFI name
> + * @property:   variable properties struct
> + * @name:       variable name
> + *
> + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY.
> + */
> +struct smm_variable_var_check_property {
> +	efi_guid_t guid;
> +	size_t name_size;
> +	struct var_check_property property;
> +	u16 name[];
> +};
> +
> +#endif /* _MM_COMMUNICATION_H_ */
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index 04ae58892608..10af910e0dce 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -12,6 +12,7 @@
>  #include <linux/tee_drv.h>
>  #include <linux/types.h>
>  #include "optee_msg.h"
> +#include "optee_private.h"

This is "optee_private.h", no need to include it.

>  
>  #define DRIVER_NAME "optee"
>  
> @@ -19,6 +20,7 @@
>  
>  /* Some Global Platform error codes used in this driver */
>  #define TEEC_SUCCESS			0x00000000
> +#define TEEC_ERROR_EXCESS_DATA		0xFFFF0004
>  #define TEEC_ERROR_BAD_PARAMETERS	0xFFFF0006
>  #define TEEC_ERROR_NOT_SUPPORTED	0xFFFF000A
>  #define TEEC_ERROR_COMMUNICATION	0xFFFF000E
> @@ -250,7 +252,6 @@ void optee_pool_op_free_helper(struct tee_shm_pool *pool, struct tee_shm *shm,
>  			       int (*shm_unregister)(struct tee_context *ctx,
>  						     struct tee_shm *shm));
>  
> -
>  void optee_remove_common(struct optee *optee);
>  int optee_open(struct tee_context *ctx, bool cap_memref_null);
>  void optee_release(struct tee_context *ctx);
> @@ -322,4 +323,6 @@ void optee_smc_abi_unregister(void);
>  int optee_ffa_abi_register(void);
>  void optee_ffa_abi_unregister(void);
>  
> +int optee_efivar_ops_init(struct tee_context *ctx);

This isn't used or implemented as far as I can tell.

> +
>  #endif /*OPTEE_PRIVATE_H*/
> diff --git a/drivers/tee/optee/optee_stmm_efi.c b/drivers/tee/optee/optee_stmm_efi.c
> new file mode 100644
> index 000000000000..6dcf1eb4b96c
> --- /dev/null
> +++ b/drivers/tee/optee/optee_stmm_efi.c
> @@ -0,0 +1,598 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + *  EFI variable service via OP-TEE
> + *
> + *  Copyright (C) 2022 Linaro
> + */
> +
> +#include <linux/efi.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/tee.h>
> +#include <linux/tee_drv.h>
> +#include <linux/ucs2_string.h>
> +#include "mm_communication.h"
> +#include "optee_private.h"
> +
> +static struct efivars optee_efivars;
> +static struct efivar_operations optee_ops;

optee_efi_ops instead? To avoid being confused with the other optee_ops.

> +
> +static size_t max_buffer_size; /* comm + var + func + data */
> +static size_t max_payload_size; /* func + data */
> +
> +struct mm_connection {
> +	struct tee_context *ctx;
> +	u32 session;
> +};
> +
> +/* UUID of the stmm PTA */
> +static const struct tee_client_device_id optee_stmm_efi_id_table[] = {
> +	{PTA_STMM_UUID},
> +	{}
> +};
> +
> +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
> +{
> +	if (ver->impl_id == TEE_IMPL_ID_OPTEE)
> +		return 1;
> +	else
> +		return 0;
> +}
> +
> +/**
> + * get_connection() - Retrieve OP-TEE session for a specific UUID.
> + *
> + * @conn:   session buffer to fill
> + * Return:  status code
> + */
> +static int get_connection(struct mm_connection *conn)
> +{
> +	struct tee_context *ctx = NULL;
> +	struct tee_ioctl_open_session_arg arg;
> +	int rc;
> +
> +	memset(&arg, 0, sizeof(arg));
> +
> +	/* Open context with OP-TEE driver */
> +	ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);

tee_client_close_context() is missing in the error paths.

> +	if (IS_ERR(ctx))
> +		return -ENODEV;
> +
> +	export_uuid(arg.uuid, &optee_stmm_efi_id_table[0].uuid);
> +	rc = tee_client_open_session(ctx, &arg, NULL);
> +	if (!rc) {
> +		conn->ctx = ctx;
> +		conn->session = arg.session;
> +	}
> +
> +	return rc;
> +}
> +
> +/**
> + * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE
> + *
> + * @comm_buf:		locally allocated communcation buffer
> + * @dsize:		buffer size
> + * Return:		status code
> + */
> +static efi_status_t optee_mm_communicate(void *comm_buf, size_t dsize)
> +{
> +	size_t buf_size;
> +	efi_status_t ret;
> +	struct efi_mm_communicate_header *mm_hdr;
> +	struct mm_connection conn = { NULL, 0 };
> +	struct tee_ioctl_invoke_arg arg;
> +	struct tee_param param[4];
> +	struct tee_shm *shm = NULL;
> +	int rc;
> +
> +	if (!comm_buf)
> +		return EFI_INVALID_PARAMETER;
> +
> +	mm_hdr = (struct efi_mm_communicate_header *)comm_buf;

No need to cast.

> +	buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
> +
> +	if (dsize != buf_size)
> +		return EFI_INVALID_PARAMETER;
> +
> +	rc = get_connection(&conn);
> +	if (rc) {
> +		pr_err("Unable to open OP-TEE session (err=%d)\n", rc);

Perhaps better to print from get_connection() to be able to distinguish
between errors from tee_client_open_context() and
tee_client_open_session().

> +		return EFI_UNSUPPORTED;
> +	}
> +
> +	shm = tee_shm_register_kernel_buf(conn.ctx, comm_buf, buf_size);
> +	if (IS_ERR(shm)) {
> +		pr_err("Unable to register shared memory\n");
> +		tee_client_close_session(conn.ctx, conn.session);
> +		return EFI_UNSUPPORTED;
> +	}
> +
> +	memset(&arg, 0, sizeof(arg));
> +	arg.func = PTA_STMM_CMDID_COMMUNICATE;
> +	arg.session = conn.session;
> +	arg.num_params = 4;
> +
> +	memset(param, 0, sizeof(param));
> +	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
> +	param[0].u.memref.size = buf_size;
> +	param[0].u.memref.shm = shm;
> +	param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
> +	param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
> +	param[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
> +
> +	rc = tee_client_invoke_func(conn.ctx, &arg, param);
> +	tee_shm_free(shm);
> +	/* even if close session fails the session will be invalidaded */
> +	tee_client_close_session(conn.ctx, conn.session);
> +	if (rc)
> +		return EFI_DEVICE_ERROR;
> +	if (arg.ret == TEEC_ERROR_EXCESS_DATA)
> +		pr_err("Variable payload too large\n");
> +	if (arg.ret != TEEC_SUCCESS)
> +		return EFI_DEVICE_ERROR;
> +
> +	switch (param[1].u.value.a) {
> +	case ARM_SVC_SPM_RET_SUCCESS:
> +		ret = EFI_SUCCESS;
> +		break;
> +
> +	case ARM_SVC_SPM_RET_INVALID_PARAMS:
> +		ret = EFI_INVALID_PARAMETER;
> +		break;
> +
> +	case ARM_SVC_SPM_RET_DENIED:
> +		ret = EFI_ACCESS_DENIED;
> +		break;
> +
> +	case ARM_SVC_SPM_RET_NO_MEMORY:
> +		ret = EFI_OUT_OF_RESOURCES;
> +		break;
> +
> +	default:
> +		ret = EFI_ACCESS_DENIED;
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
> + * it to OP-TEE
> + *
> + * @comm_buf:		locally allocated communcation buffer
> + * @dsize:		buffer size
> + * Return:		status code
> + */
> +static efi_status_t mm_communicate(u8 *comm_buf, size_t dsize)
> +{
> +	efi_status_t ret;
> +	struct efi_mm_communicate_header *mm_hdr;
> +	struct smm_variable_communicate_header *var_hdr;
> +
> +	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;
> +
> +	ret = optee_mm_communicate(comm_buf, dsize);
> +	if (ret != EFI_SUCCESS) {
> +		pr_err("%s failed!\n", __func__);
> +		return ret;
> +	}
> +
> +	return var_hdr->ret_status;
> +}
> +
> +/**
> + * setup_mm_hdr() -	Allocate a buffer for StandAloneMM and initialize the
> + *			header data.
> + *
> + * @dptr:		pointer address of the corresponding StandAloneMM
> + *			function
> + * @payload_size:	buffer size
> + * @func:		standAloneMM function number
> + * @ret:		EFI return code
> + * Return:		buffer or NULL
> + */
> +static u8 *setup_mm_hdr(void **dptr, size_t payload_size, size_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;
> +
> +	/* In the init function we initialize max_buffer_size with
> +	 * get_max_payload(). So skip the test if max_buffer_size is initialized
> +	 * StandAloneMM will perform similar checks and drop the buffer if it's
> +	 * too long
> +	 */
> +	if (max_buffer_size &&
> +	    max_buffer_size < (MM_COMMUNICATE_HEADER_SIZE +
> +			       MM_VARIABLE_COMMUNICATE_SIZE + payload_size)) {
> +		*ret = EFI_INVALID_PARAMETER;
> +		return NULL;
> +	}
> +
> +	comm_buf = kzalloc(MM_COMMUNICATE_HEADER_SIZE +
> +				   MM_VARIABLE_COMMUNICATE_SIZE + payload_size,
> +			   GFP_KERNEL);
> +	if (!comm_buf) {
> +		*ret = EFI_OUT_OF_RESOURCES;
> +		return NULL;
> +	}
> +
> +	mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
> +	memcpy(&mm_hdr->header_guid, &mm_var_guid, 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;
> +	var_hdr->function = func;
> +	if (dptr)
> +		*dptr = var_hdr->data;
> +	*ret = EFI_SUCCESS;
> +
> +	return comm_buf;
> +}
> +
> +/**
> + * get_max_payload() - Get variable payload size from StandAloneMM.
> + *
> + * @size:    size of the variable in storage
> + * Return:   status code
> + */
> +static efi_status_t get_max_payload(size_t *size)
> +{
> +	struct smm_variable_payload_size *var_payload = NULL;
> +	size_t payload_size;
> +	u8 *comm_buf = NULL;
> +	efi_status_t ret;
> +
> +	if (!size) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	payload_size = sizeof(*var_payload);
> +	comm_buf = setup_mm_hdr((void **)&var_payload, payload_size,
> +				SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, &ret);
> +	if (!comm_buf)
> +		goto out;
> +
> +	ret = mm_communicate(comm_buf, payload_size);
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	/* Make sure the buffer is big enough for storing variables */
> +	if (var_payload->size < MM_VARIABLE_ACCESS_HEADER_SIZE + 0x20) {
> +		ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	}
> +	*size = var_payload->size;
> +	/*
> +	 * There seems to be a bug in EDK2 miscalculating the boundaries and
> +	 * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
> +	 * it up here to ensure backwards compatibility with older versions
> +	 * (cf. StandaloneMmPkg/Drivers/StandaloneMmCpu/AArch64/EventHandle.c.
> +	 * sizeof (EFI_MM_COMMUNICATE_HEADER) instead the size minus the
> +	 * flexible array member).
> +	 *
> +	 * size is guaranteed to be > 2 due to checks on the beginning.
> +	 */
> +	*size -= 2;
> +out:
> +	kfree(comm_buf);
> +	return ret;
> +}
> +
> +static efi_status_t get_property_int(u16 *name, size_t name_size,
> +				     const efi_guid_t *vendor,
> +				     struct var_check_property *var_property)
> +{
> +	struct smm_variable_var_check_property *smm_property;
> +	size_t payload_size;
> +	u8 *comm_buf = NULL;
> +	efi_status_t ret;
> +
> +	memset(var_property, 0, sizeof(*var_property));
> +	payload_size = sizeof(*smm_property) + name_size;
> +	if (payload_size > max_payload_size) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +	comm_buf = setup_mm_hdr(
> +		(void **)&smm_property, payload_size,
> +		SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET, &ret);
> +	if (!comm_buf)
> +		goto out;
> +
> +	memcpy(&smm_property->guid, vendor, sizeof(smm_property->guid));
> +	smm_property->name_size = name_size;
> +	memcpy(smm_property->name, name, name_size);
> +
> +	ret = mm_communicate(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)
> +		goto out;
> +	memcpy(var_property, &smm_property->property, sizeof(*var_property));
> +
> +out:
> +	kfree(comm_buf);
> +	return ret;
> +}
> +
> +static efi_status_t optee_get_variable(u16 *name, efi_guid_t *vendor,
> +				       u32 *attributes,
> +				       unsigned long *data_size, void *data)
> +{
> +	struct var_check_property var_property;
> +	struct smm_variable_access *var_acc;
> +	size_t payload_size;
> +	size_t name_size;
> +	size_t tmp_dsize;
> +	u8 *comm_buf = NULL;
> +	efi_status_t ret;
> +
> +	if (!name || !vendor || !data_size) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
> +	if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	/* 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)
> +		goto out;
> +
> +	/* Fill in contents */
> +	memcpy(&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;
> +	memcpy(var_acc->name, name, name_size);
> +
> +	/* Communicate */
> +	ret = mm_communicate(comm_buf, payload_size);
> +	if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL)
> +		/* Update with reported data size for trimmed case */
> +		*data_size = var_acc->data_size;
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	ret = get_property_int(name, name_size, vendor, &var_property);
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	if (attributes)
> +		*attributes = var_acc->attr;
> +
> +	if (data)
> +		memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
> +		       var_acc->data_size);
> +	else
> +		ret = EFI_INVALID_PARAMETER;
> +
> +out:
> +	kfree(comm_buf);
> +	return ret;
> +}
> +
> +static efi_status_t optee_get_next_variable(unsigned long *name_size,
> +					    efi_char16_t *name,
> +					    efi_guid_t *guid)
> +{
> +	struct smm_variable_getnext *var_getnext;
> +	size_t payload_size;
> +	size_t out_name_size;
> +	size_t in_name_size;
> +	u8 *comm_buf = NULL;
> +	efi_status_t ret;
> +
> +	if (!name_size || !name || !guid) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	out_name_size = *name_size;
> +	in_name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
> +
> +	if (out_name_size < in_name_size) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	if (in_name_size >
> +	    max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	/* 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)
> +		goto out;
> +
> +	/* Fill in contents */
> +	memcpy(&var_getnext->guid, guid, sizeof(var_getnext->guid));
> +	var_getnext->name_size = out_name_size;
> +	memcpy(var_getnext->name, name, in_name_size);
> +	memset((u8 *)var_getnext->name + in_name_size, 0x0,
> +	       out_name_size - in_name_size);
> +
> +	/* Communicate */
> +	ret = mm_communicate(comm_buf, payload_size);
> +	if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
> +		/* Update with reported data size for trimmed case */
> +		*name_size = var_getnext->name_size;
> +	}
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	memcpy(guid, &var_getnext->guid, sizeof(*guid));
> +	memcpy(name, var_getnext->name, var_getnext->name_size);
> +
> +out:
> +	kfree(comm_buf);
> +	return ret;
> +}
> +
> +static efi_status_t optee_set_variable(efi_char16_t *name, efi_guid_t *vendor,
> +				       u32 attributes, unsigned long data_size,
> +				       void *data)
> +{
> +	efi_status_t ret;
> +	struct var_check_property var_property;
> +	struct smm_variable_access *var_acc;
> +	size_t payload_size;
> +	size_t name_size;
> +	u8 *comm_buf = NULL;
> +
> +	if (!name || name[0] == 0 || !vendor) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +	if (data_size > 0 && !data) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +	/* Check payload size */
> +	name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
> +	payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
> +	if (payload_size > max_payload_size) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	/*
> +	 * 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)
> +		goto out;
> +
> +	/*
> +	 * The API has the ability to override RO flags. If no RO check was
> +	 * requested switch the variable to RW for the duration of this call
> +	 */
> +	ret = get_property_int(name, name_size, vendor, &var_property);
> +	if (ret != EFI_SUCCESS) {
> +		pr_err("Getting variable property failed\n");
> +		goto out;
> +	}
> +
> +	if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) {
> +		ret = EFI_WRITE_PROTECTED;
> +		goto out;
> +	}
> +
> +	/* Fill in contents */
> +	memcpy(&var_acc->guid, vendor, sizeof(var_acc->guid));
> +	var_acc->data_size = data_size;
> +	var_acc->name_size = name_size;
> +	var_acc->attr = attributes;
> +	memcpy(var_acc->name, name, name_size);
> +	memcpy((u8 *)var_acc->name + name_size, data, data_size);
> +
> +
> +	/* Communicate */
> +	ret = mm_communicate(comm_buf, payload_size);
> +	pr_info("Set Variable %s %d %lx\n", __FILE__, __LINE__, ret);
> +out:
> +	kfree(comm_buf);
> +	return ret;
> +}
> +
> +static int optee_stmm_efi_probe(struct device *dev)
> +{
> +	efi_status_t ret;
> +
> +	ret = get_max_payload(&max_payload_size);
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	max_buffer_size = MM_COMMUNICATE_HEADER_SIZE +
> +			  MM_VARIABLE_COMMUNICATE_SIZE +
> +			  max_payload_size;
> +
> +	optee_ops.get_variable = optee_get_variable;
> +	optee_ops.get_next_variable = optee_get_next_variable;
> +	optee_ops.set_variable = optee_set_variable;
> +	/* TODO: support nonblocking variant */
> +	optee_ops.set_variable_nonblocking = NULL;
> +	/* set NULL, always return EFI_SUCCESS by efi_query_variable_store() */
> +	optee_ops.query_variable_store = NULL;

Why aren't you using static initialization instead?

Thanks,
Jens

> +
> +	tee_register_efivar_ops(&optee_efivars, &optee_ops);
> +
> +	return 0;
> +}
> +
> +static int optee_stmm_efi_remove(struct device *dev)
> +{
> +	tee_unregister_efivar_ops(&optee_efivars);
> +
> +	return 0;
> +}
> +
> +MODULE_DEVICE_TABLE(tee, optee_stmm_efi_id_table);
> +
> +static struct tee_client_driver optee_stmm_efi_driver = {
> +	.id_table	= optee_stmm_efi_id_table,
> +	.driver		= {
> +		.name		= "optee-stmm-efi",
> +		.bus		= &tee_bus_type,
> +		.probe		= optee_stmm_efi_probe,
> +		.remove		= optee_stmm_efi_remove,
> +	},
> +};
> +
> +static int __init optee_stmm_efi_mod_init(void)
> +{
> +	return driver_register(&optee_stmm_efi_driver.driver);
> +}
> +
> +static void __exit optee_stmm_efi_mod_exit(void)
> +{
> +	driver_unregister(&optee_stmm_efi_driver.driver);
> +}
> +
> +module_init(optee_stmm_efi_mod_init);
> +module_exit(optee_stmm_efi_mod_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Ilias Apalodimas <ilias.apalodimas@linaro.org>");
> +MODULE_AUTHOR("Masahisa Kojima <masahisa.kojima@linaro.org>");
> +MODULE_DESCRIPTION("OP-TEE based EFI runtime variable service driver");
> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> index 98da206cd761..ac46274844a3 100644
> --- a/drivers/tee/tee_core.c
> +++ b/drivers/tee/tee_core.c
> @@ -7,6 +7,7 @@
>  
>  #include <linux/cdev.h>
>  #include <linux/cred.h>
> +#include <linux/efi.h>
>  #include <linux/fs.h>
>  #include <linux/idr.h>
>  #include <linux/module.h>
> @@ -1263,6 +1264,28 @@ static void __exit tee_exit(void)
>  	tee_class = NULL;
>  }
>  
> +void tee_register_efivar_ops(struct efivars *tee_efivars,
> +			     struct efivar_operations *ops)
> +{
> +	/*
> +	 * If the firmware EFI runtime services support SetVariable(),
> +	 * tee-based EFI variable services are not used.
> +	 */
> +	if (!efivar_supports_writes()) {
> +		pr_info("Use tee-based EFI runtime variable services\n");
> +		efivars_generic_ops_unregister();
> +		efivars_register(tee_efivars, ops);
> +	}
> +}
> +EXPORT_SYMBOL_GPL(tee_register_efivar_ops);
> +
> +void tee_unregister_efivar_ops(struct efivars *tee_efivars)
> +{
> +	efivars_unregister(tee_efivars);
> +	efivars_generic_ops_register();
> +}
> +EXPORT_SYMBOL_GPL(tee_unregister_efivar_ops);
> +
>  subsys_initcall(tee_init);
>  module_exit(tee_exit);
>  
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 5e301c00e9b0..14d4aa83ce60 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -39,6 +39,7 @@
>  #define EFI_WRITE_PROTECTED	( 8 | (1UL << (BITS_PER_LONG-1)))
>  #define EFI_OUT_OF_RESOURCES	( 9 | (1UL << (BITS_PER_LONG-1)))
>  #define EFI_NOT_FOUND		(14 | (1UL << (BITS_PER_LONG-1)))
> +#define EFI_ACCESS_DENIED	(15 | (1UL << (BITS_PER_LONG-1)))
>  #define EFI_TIMEOUT		(18 | (1UL << (BITS_PER_LONG-1)))
>  #define EFI_ABORTED		(21 | (1UL << (BITS_PER_LONG-1)))
>  #define EFI_SECURITY_VIOLATION	(26 | (1UL << (BITS_PER_LONG-1)))
> diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
> index 17eb1c5205d3..def4ea6212ee 100644
> --- a/include/linux/tee_drv.h
> +++ b/include/linux/tee_drv.h
> @@ -7,6 +7,7 @@
>  #define __TEE_DRV_H
>  
>  #include <linux/device.h>
> +#include <linux/efi.h>
>  #include <linux/idr.h>
>  #include <linux/kref.h>
>  #include <linux/list.h>
> @@ -507,4 +508,26 @@ struct tee_context *teedev_open(struct tee_device *teedev);
>   */
>  void teedev_close_context(struct tee_context *ctx);
>  
> +/**
> + * tee_register_efivar_ops() - register the efivar ops
> + * @tee_efivars:	pointer to efivars structure
> + * @ops:		pointer to contain the efivar operation
> + *
> + * This function registers the tee-based efivar operation as an
> + * EFI Runtime Service.
> + *
> + */
> +void tee_register_efivar_ops(struct efivars *tee_efivars,
> +			     struct efivar_operations *ops);
> +
> +/**
> + * tee_unregister_efivar_ops() - unregister the efivar ops
> + * @tee_efivars:	pointer to efivars structure
> + *
> + * This function unregisters the tee-based efivar operation
> + * and reverts to the generic operation.
> + *
> + */
> +void tee_unregister_efivar_ops(struct efivars *tee_efivars);
> +
>  #endif /*__TEE_DRV_H*/
> -- 
> 2.30.2
> 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/2] tee: Add op-tee helper functions for variable access
@ 2023-02-03  9:30     ` Jens Wiklander
  0 siblings, 0 replies; 21+ messages in thread
From: Jens Wiklander @ 2023-02-03  9:30 UTC (permalink / raw)
  To: Masahisa Kojima
  Cc: Ard Biesheuvel, linux-kernel, op-tee, Ilias Apalodimas,
	Johan Hovold, Sumit Garg, Maxime Coquelin, Alexandre Torgue,
	linux-stm32, linux-arm-kernel, linux-efi

On Thu, Jan 26, 2023 at 10:21:19PM +0900, Masahisa Kojima wrote:
> When the flash is not owned by the non-secure world, accessing the EFI
> variables is straightforward and done via EFI Runtime Variable Services.
> In this case, critical variables for system integrity and security
> are normally stored in the dedicated secure storage and only accessible
> from the secure world.
> 
> On the other hand, the small embedded devices don't have the special
> dedicated secure storage. The eMMC device with an RPMB partition is
> becoming more common, we can use an RPMB partition to store the
> EFI Variables.
> 
> The eMMC device is typically owned by the non-secure world(linux in
> this case). There is an existing solution utilizing eMMC RPMB partition
> for EFI Variables, it is implemented by interacting with
> OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
> and tee-supplicant. The last piece is the tee-based variable access
> driver to interact with OP-TEE and StandaloneMM.
> 
> So let's add the kernel functions needed.
> 
> This feature is implemented as a kernel module.
> StMM PTA has TA_FLAG_DEVICE_ENUM_SUPP flag when registered to OP-TEE
> so that this optee_stmm_efi module is probed after tee-supplicant starts,
> since "SetVariable" EFI Runtime Variable Service requires to
> interact with tee-supplicant.
> 
> Co-developed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> ---
>  drivers/tee/optee/Kconfig            |  10 +
>  drivers/tee/optee/Makefile           |   1 +
>  drivers/tee/optee/mm_communication.h | 249 +++++++++++
>  drivers/tee/optee/optee_private.h    |   5 +-
>  drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
>  drivers/tee/tee_core.c               |  23 ++
>  include/linux/efi.h                  |   1 +
>  include/linux/tee_drv.h              |  23 ++
>  8 files changed, 909 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/tee/optee/mm_communication.h
>  create mode 100644 drivers/tee/optee/optee_stmm_efi.c


Please split this patch into three patches, one for drivers/tee, one
for the driver/tee/optee, and one for include/linux/efi.h.

> 
> diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
> index f121c224e682..a0b699977e51 100644
> --- a/drivers/tee/optee/Kconfig
> +++ b/drivers/tee/optee/Kconfig
> @@ -7,3 +7,13 @@ config OPTEE
>  	help
>  	  This implements the OP-TEE Trusted Execution Environment (TEE)
>  	  driver.
> +
> +config EFI_STMM_OPTEE

Should this be OPTEE_EFI_STMM?

> +	tristate "OP-TEE based EFI runtime variable service driver"
> +	depends on OPTEE
> +	help
> +	  This  driver provides support for OP-TEE based EFI runtime
> +	  variable service driver.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called optee_stmm_efi.
> diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
> index a6eff388d300..9acb0b2de8fe 100644
> --- a/drivers/tee/optee/Makefile
> +++ b/drivers/tee/optee/Makefile
> @@ -8,6 +8,7 @@ optee-objs += supp.o
>  optee-objs += device.o
>  optee-objs += smc_abi.o
>  optee-objs += ffa_abi.o
> +obj-$(CONFIG_EFI_STMM_OPTEE) += optee_stmm_efi.o
>  
>  # for tracing framework to find optee_trace.h
>  CFLAGS_smc_abi.o := -I$(src)
> diff --git a/drivers/tee/optee/mm_communication.h b/drivers/tee/optee/mm_communication.h
> new file mode 100644
> index 000000000000..23e6672991ef
> --- /dev/null
> +++ b/drivers/tee/optee/mm_communication.h
> @@ -0,0 +1,249 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + *  Headers for EFI variable service via StandAloneMM, EDK2 application running
> + *  in OP-TEE
> + *
> + *  Copyright (c) 2017, Intel Corporation. All rights reserved.
> + *  Copyright (C) 2020 Linaro Ltd.
> + */
> +
> +#ifndef _MM_COMMUNICATION_H_
> +#define _MM_COMMUNICATION_H_
> +
> +/*
> + * Interface to the pseudo Trusted Application (TA), which provides a
> + * communication channel with the Standalone MM (Management Mode)
> + * Secure Partition running at Secure-EL0
> + */
> +
> +#define PTA_STMM_CMDID_COMMUNICATE 0

PTA_STMM_CMD_COMMUNICATE

> +
> +/* OP-TEE is using big endian GUIDs while UEFI uses little endian ones */
> +#define PTA_STMM_UUID \
> +	UUID_INIT(0xed32d533, 0x99e6, 0x4209, \
> +		  0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
> +
> +#define EFI_MM_VARIABLE_GUID \
> +	EFI_GUID(0xed32d533, 0x99e6, 0x4209, \
> +		 0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
> +
> +/* Defined in EDK2 MdePkg/Include/Protocol/MmCommunication.h */
> +
> +/**
> + * struct efi_mm_communicate_header - Header used for SMM variable communication
> +
> + * @header_guid:  header use for disambiguation of content
> + * @message_len:  length of the message. Does not include the size of the
> + *                header
> + * @data:         payload of the message
> + *
> + * Defined in EDK2 as EFI_MM_COMMUNICATE_HEADER.
> + * To avoid confusion in interpreting frames, the communication buffer should
> + * always begin with efi_mm_communicate_header.
> + */
> +struct efi_mm_communicate_header {
> +	efi_guid_t header_guid;
> +	size_t     message_len;
> +	u8         data[];
> +} __packed;
> +
> +#define MM_COMMUNICATE_HEADER_SIZE \
> +	(sizeof(struct efi_mm_communicate_header))
> +
> +/* Defined in EDK2 ArmPkg/Include/IndustryStandard/ArmMmSvc.h */
> +
> +/* SPM return error codes */
> +#define ARM_SVC_SPM_RET_SUCCESS               0
> +#define ARM_SVC_SPM_RET_NOT_SUPPORTED        -1
> +#define ARM_SVC_SPM_RET_INVALID_PARAMS       -2
> +#define ARM_SVC_SPM_RET_DENIED               -3
> +#define ARM_SVC_SPM_RET_NO_MEMORY            -5
> +
> +/* Defined in EDK2 MdeModulePkg/Include/Guid/SmmVariableCommon.h */
> +
> +#define SMM_VARIABLE_FUNCTION_GET_VARIABLE  1
> +/*
> + * The payload for this function is
> + * SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME.
> + */
> +#define SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME  2
> +/*
> + * The payload for this function is SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
> + */
> +#define SMM_VARIABLE_FUNCTION_SET_VARIABLE  3
> +/*
> + * The payload for this function is
> + * SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO.
> + */
> +#define SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO  4
> +/*
> + * It is a notify event, no extra payload for this function.
> + */
> +#define SMM_VARIABLE_FUNCTION_READY_TO_BOOT  5
> +/*
> + * It is a notify event, no extra payload for this function.
> + */
> +#define SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE  6
> +/*
> + * The payload for this function is VARIABLE_INFO_ENTRY.
> + * The GUID in EFI_SMM_COMMUNICATE_HEADER is gEfiSmmVariableProtocolGuid.
> + */
> +#define SMM_VARIABLE_FUNCTION_GET_STATISTICS  7
> +/*
> + * The payload for this function is SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
> + */
> +#define SMM_VARIABLE_FUNCTION_LOCK_VARIABLE   8
> +
> +#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET  9
> +
> +#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET  10
> +
> +#define SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE  11
> +/*
> + * The payload for this function is
> + * SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT
> + */
> +#define SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT 12
> +
> +#define SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE  13
> +/*
> + * The payload for this function is
> + * SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO
> + */
> +#define SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO  14
> +
> +/**
> + * struct smm_variable_communicate_header - Used for SMM variable communication
> +
> + * @function:     function to call in Smm.
> + * @ret_status:   return status
> + * @data:         payload
> + *
> + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_HEADER.
> + */
> +struct smm_variable_communicate_header {
> +	size_t  function;
> +	efi_status_t ret_status;
> +	u8 data[];
> +};
> +
> +#define MM_VARIABLE_COMMUNICATE_SIZE \
> +	(sizeof(struct smm_variable_communicate_header))
> +
> +/**
> + * struct smm_variable_access - Used to communicate with StMM by
> + *                              SetVariable and GetVariable.
> +
> + * @guid:         vendor GUID
> + * @data_size:    size of EFI variable data
> + * @name_size:    size of EFI name
> + * @attr:         attributes
> + * @name:         variable name
> + *
> + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
> + *
> + */
> +struct smm_variable_access {
> +	efi_guid_t  guid;
> +	size_t data_size;
> +	size_t name_size;
> +	u32 attr;
> +	u16 name[];
> +};
> +
> +#define MM_VARIABLE_ACCESS_HEADER_SIZE \
> +	(sizeof(struct smm_variable_access))
> +/**
> + * struct smm_variable_payload_size - Used to get the max allowed
> + *                                    payload used in StMM.
> + *
> + * @size:  size to fill in
> + *
> + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE.
> + *
> + */
> +struct smm_variable_payload_size {
> +	size_t size;
> +};
> +
> +/**
> + * struct smm_variable_getnext - Used to communicate with StMM for
> + *                               GetNextVariableName.
> + *
> + * @guid:       vendor GUID
> + * @name_size:  size of the name of the variable
> + * @name:       variable name
> + *
> + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME.
> + */
> +struct smm_variable_getnext {
> +	efi_guid_t  guid;
> +	size_t name_size;
> +	u16         name[];
> +};
> +
> +#define MM_VARIABLE_GET_NEXT_HEADER_SIZE \
> +	(sizeof(struct smm_variable_getnext))
> +
> +/**
> + * struct smm_variable_query_info - Used to communicate with StMM for
> + *                                  QueryVariableInfo.
> + *
> + * @max_variable_storage:        max available storage
> + * @remaining_variable_storage:  remaining available storage
> + * @max_variable_size:           max variable supported size
> + * @attr:                        attributes to query storage for
> + *
> + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO.
> + */
> +struct smm_variable_query_info {
> +	u64 max_variable_storage;
> +	u64 remaining_variable_storage;
> +	u64 max_variable_size;
> +	u32 attr;
> +};
> +
> +#define VAR_CHECK_VARIABLE_PROPERTY_REVISION 0x0001
> +#define VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY BIT(0)
> +/**
> + * struct var_check_property - Used to store variable properties in StMM
> + *
> + * @revision:   magic revision number for variable property checking
> + * @property:   properties mask for the variable used in StMM.
> + *              Currently RO flag is supported
> + * @attributes: variable attributes used in StMM checking when properties
> + *              for a variable are enabled
> + * @minsize:    minimum allowed size for variable payload checked against
> + *              smm_variable_access->datasize in StMM
> + * @maxsize:    maximum allowed size for variable payload checked against
> + *              smm_variable_access->datasize in StMM
> + *
> + * Defined in EDK2 as VAR_CHECK_VARIABLE_PROPERTY.
> + */
> +struct var_check_property {
> +	u16 revision;
> +	u16 property;
> +	u32 attributes;
> +	size_t minsize;
> +	size_t maxsize;
> +};
> +
> +/**
> + * struct smm_variable_var_check_property - Used to communicate variable
> + *                                          properties with StMM
> + *
> + * @guid:       vendor GUID
> + * @name_size:  size of EFI name
> + * @property:   variable properties struct
> + * @name:       variable name
> + *
> + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY.
> + */
> +struct smm_variable_var_check_property {
> +	efi_guid_t guid;
> +	size_t name_size;
> +	struct var_check_property property;
> +	u16 name[];
> +};
> +
> +#endif /* _MM_COMMUNICATION_H_ */
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index 04ae58892608..10af910e0dce 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -12,6 +12,7 @@
>  #include <linux/tee_drv.h>
>  #include <linux/types.h>
>  #include "optee_msg.h"
> +#include "optee_private.h"

This is "optee_private.h", no need to include it.

>  
>  #define DRIVER_NAME "optee"
>  
> @@ -19,6 +20,7 @@
>  
>  /* Some Global Platform error codes used in this driver */
>  #define TEEC_SUCCESS			0x00000000
> +#define TEEC_ERROR_EXCESS_DATA		0xFFFF0004
>  #define TEEC_ERROR_BAD_PARAMETERS	0xFFFF0006
>  #define TEEC_ERROR_NOT_SUPPORTED	0xFFFF000A
>  #define TEEC_ERROR_COMMUNICATION	0xFFFF000E
> @@ -250,7 +252,6 @@ void optee_pool_op_free_helper(struct tee_shm_pool *pool, struct tee_shm *shm,
>  			       int (*shm_unregister)(struct tee_context *ctx,
>  						     struct tee_shm *shm));
>  
> -
>  void optee_remove_common(struct optee *optee);
>  int optee_open(struct tee_context *ctx, bool cap_memref_null);
>  void optee_release(struct tee_context *ctx);
> @@ -322,4 +323,6 @@ void optee_smc_abi_unregister(void);
>  int optee_ffa_abi_register(void);
>  void optee_ffa_abi_unregister(void);
>  
> +int optee_efivar_ops_init(struct tee_context *ctx);

This isn't used or implemented as far as I can tell.

> +
>  #endif /*OPTEE_PRIVATE_H*/
> diff --git a/drivers/tee/optee/optee_stmm_efi.c b/drivers/tee/optee/optee_stmm_efi.c
> new file mode 100644
> index 000000000000..6dcf1eb4b96c
> --- /dev/null
> +++ b/drivers/tee/optee/optee_stmm_efi.c
> @@ -0,0 +1,598 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + *  EFI variable service via OP-TEE
> + *
> + *  Copyright (C) 2022 Linaro
> + */
> +
> +#include <linux/efi.h>
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/tee.h>
> +#include <linux/tee_drv.h>
> +#include <linux/ucs2_string.h>
> +#include "mm_communication.h"
> +#include "optee_private.h"
> +
> +static struct efivars optee_efivars;
> +static struct efivar_operations optee_ops;

optee_efi_ops instead? To avoid being confused with the other optee_ops.

> +
> +static size_t max_buffer_size; /* comm + var + func + data */
> +static size_t max_payload_size; /* func + data */
> +
> +struct mm_connection {
> +	struct tee_context *ctx;
> +	u32 session;
> +};
> +
> +/* UUID of the stmm PTA */
> +static const struct tee_client_device_id optee_stmm_efi_id_table[] = {
> +	{PTA_STMM_UUID},
> +	{}
> +};
> +
> +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
> +{
> +	if (ver->impl_id == TEE_IMPL_ID_OPTEE)
> +		return 1;
> +	else
> +		return 0;
> +}
> +
> +/**
> + * get_connection() - Retrieve OP-TEE session for a specific UUID.
> + *
> + * @conn:   session buffer to fill
> + * Return:  status code
> + */
> +static int get_connection(struct mm_connection *conn)
> +{
> +	struct tee_context *ctx = NULL;
> +	struct tee_ioctl_open_session_arg arg;
> +	int rc;
> +
> +	memset(&arg, 0, sizeof(arg));
> +
> +	/* Open context with OP-TEE driver */
> +	ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);

tee_client_close_context() is missing in the error paths.

> +	if (IS_ERR(ctx))
> +		return -ENODEV;
> +
> +	export_uuid(arg.uuid, &optee_stmm_efi_id_table[0].uuid);
> +	rc = tee_client_open_session(ctx, &arg, NULL);
> +	if (!rc) {
> +		conn->ctx = ctx;
> +		conn->session = arg.session;
> +	}
> +
> +	return rc;
> +}
> +
> +/**
> + * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE
> + *
> + * @comm_buf:		locally allocated communcation buffer
> + * @dsize:		buffer size
> + * Return:		status code
> + */
> +static efi_status_t optee_mm_communicate(void *comm_buf, size_t dsize)
> +{
> +	size_t buf_size;
> +	efi_status_t ret;
> +	struct efi_mm_communicate_header *mm_hdr;
> +	struct mm_connection conn = { NULL, 0 };
> +	struct tee_ioctl_invoke_arg arg;
> +	struct tee_param param[4];
> +	struct tee_shm *shm = NULL;
> +	int rc;
> +
> +	if (!comm_buf)
> +		return EFI_INVALID_PARAMETER;
> +
> +	mm_hdr = (struct efi_mm_communicate_header *)comm_buf;

No need to cast.

> +	buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
> +
> +	if (dsize != buf_size)
> +		return EFI_INVALID_PARAMETER;
> +
> +	rc = get_connection(&conn);
> +	if (rc) {
> +		pr_err("Unable to open OP-TEE session (err=%d)\n", rc);

Perhaps better to print from get_connection() to be able to distinguish
between errors from tee_client_open_context() and
tee_client_open_session().

> +		return EFI_UNSUPPORTED;
> +	}
> +
> +	shm = tee_shm_register_kernel_buf(conn.ctx, comm_buf, buf_size);
> +	if (IS_ERR(shm)) {
> +		pr_err("Unable to register shared memory\n");
> +		tee_client_close_session(conn.ctx, conn.session);
> +		return EFI_UNSUPPORTED;
> +	}
> +
> +	memset(&arg, 0, sizeof(arg));
> +	arg.func = PTA_STMM_CMDID_COMMUNICATE;
> +	arg.session = conn.session;
> +	arg.num_params = 4;
> +
> +	memset(param, 0, sizeof(param));
> +	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
> +	param[0].u.memref.size = buf_size;
> +	param[0].u.memref.shm = shm;
> +	param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
> +	param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
> +	param[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
> +
> +	rc = tee_client_invoke_func(conn.ctx, &arg, param);
> +	tee_shm_free(shm);
> +	/* even if close session fails the session will be invalidaded */
> +	tee_client_close_session(conn.ctx, conn.session);
> +	if (rc)
> +		return EFI_DEVICE_ERROR;
> +	if (arg.ret == TEEC_ERROR_EXCESS_DATA)
> +		pr_err("Variable payload too large\n");
> +	if (arg.ret != TEEC_SUCCESS)
> +		return EFI_DEVICE_ERROR;
> +
> +	switch (param[1].u.value.a) {
> +	case ARM_SVC_SPM_RET_SUCCESS:
> +		ret = EFI_SUCCESS;
> +		break;
> +
> +	case ARM_SVC_SPM_RET_INVALID_PARAMS:
> +		ret = EFI_INVALID_PARAMETER;
> +		break;
> +
> +	case ARM_SVC_SPM_RET_DENIED:
> +		ret = EFI_ACCESS_DENIED;
> +		break;
> +
> +	case ARM_SVC_SPM_RET_NO_MEMORY:
> +		ret = EFI_OUT_OF_RESOURCES;
> +		break;
> +
> +	default:
> +		ret = EFI_ACCESS_DENIED;
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
> + * it to OP-TEE
> + *
> + * @comm_buf:		locally allocated communcation buffer
> + * @dsize:		buffer size
> + * Return:		status code
> + */
> +static efi_status_t mm_communicate(u8 *comm_buf, size_t dsize)
> +{
> +	efi_status_t ret;
> +	struct efi_mm_communicate_header *mm_hdr;
> +	struct smm_variable_communicate_header *var_hdr;
> +
> +	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;
> +
> +	ret = optee_mm_communicate(comm_buf, dsize);
> +	if (ret != EFI_SUCCESS) {
> +		pr_err("%s failed!\n", __func__);
> +		return ret;
> +	}
> +
> +	return var_hdr->ret_status;
> +}
> +
> +/**
> + * setup_mm_hdr() -	Allocate a buffer for StandAloneMM and initialize the
> + *			header data.
> + *
> + * @dptr:		pointer address of the corresponding StandAloneMM
> + *			function
> + * @payload_size:	buffer size
> + * @func:		standAloneMM function number
> + * @ret:		EFI return code
> + * Return:		buffer or NULL
> + */
> +static u8 *setup_mm_hdr(void **dptr, size_t payload_size, size_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;
> +
> +	/* In the init function we initialize max_buffer_size with
> +	 * get_max_payload(). So skip the test if max_buffer_size is initialized
> +	 * StandAloneMM will perform similar checks and drop the buffer if it's
> +	 * too long
> +	 */
> +	if (max_buffer_size &&
> +	    max_buffer_size < (MM_COMMUNICATE_HEADER_SIZE +
> +			       MM_VARIABLE_COMMUNICATE_SIZE + payload_size)) {
> +		*ret = EFI_INVALID_PARAMETER;
> +		return NULL;
> +	}
> +
> +	comm_buf = kzalloc(MM_COMMUNICATE_HEADER_SIZE +
> +				   MM_VARIABLE_COMMUNICATE_SIZE + payload_size,
> +			   GFP_KERNEL);
> +	if (!comm_buf) {
> +		*ret = EFI_OUT_OF_RESOURCES;
> +		return NULL;
> +	}
> +
> +	mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
> +	memcpy(&mm_hdr->header_guid, &mm_var_guid, 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;
> +	var_hdr->function = func;
> +	if (dptr)
> +		*dptr = var_hdr->data;
> +	*ret = EFI_SUCCESS;
> +
> +	return comm_buf;
> +}
> +
> +/**
> + * get_max_payload() - Get variable payload size from StandAloneMM.
> + *
> + * @size:    size of the variable in storage
> + * Return:   status code
> + */
> +static efi_status_t get_max_payload(size_t *size)
> +{
> +	struct smm_variable_payload_size *var_payload = NULL;
> +	size_t payload_size;
> +	u8 *comm_buf = NULL;
> +	efi_status_t ret;
> +
> +	if (!size) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	payload_size = sizeof(*var_payload);
> +	comm_buf = setup_mm_hdr((void **)&var_payload, payload_size,
> +				SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, &ret);
> +	if (!comm_buf)
> +		goto out;
> +
> +	ret = mm_communicate(comm_buf, payload_size);
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	/* Make sure the buffer is big enough for storing variables */
> +	if (var_payload->size < MM_VARIABLE_ACCESS_HEADER_SIZE + 0x20) {
> +		ret = EFI_DEVICE_ERROR;
> +		goto out;
> +	}
> +	*size = var_payload->size;
> +	/*
> +	 * There seems to be a bug in EDK2 miscalculating the boundaries and
> +	 * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
> +	 * it up here to ensure backwards compatibility with older versions
> +	 * (cf. StandaloneMmPkg/Drivers/StandaloneMmCpu/AArch64/EventHandle.c.
> +	 * sizeof (EFI_MM_COMMUNICATE_HEADER) instead the size minus the
> +	 * flexible array member).
> +	 *
> +	 * size is guaranteed to be > 2 due to checks on the beginning.
> +	 */
> +	*size -= 2;
> +out:
> +	kfree(comm_buf);
> +	return ret;
> +}
> +
> +static efi_status_t get_property_int(u16 *name, size_t name_size,
> +				     const efi_guid_t *vendor,
> +				     struct var_check_property *var_property)
> +{
> +	struct smm_variable_var_check_property *smm_property;
> +	size_t payload_size;
> +	u8 *comm_buf = NULL;
> +	efi_status_t ret;
> +
> +	memset(var_property, 0, sizeof(*var_property));
> +	payload_size = sizeof(*smm_property) + name_size;
> +	if (payload_size > max_payload_size) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +	comm_buf = setup_mm_hdr(
> +		(void **)&smm_property, payload_size,
> +		SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET, &ret);
> +	if (!comm_buf)
> +		goto out;
> +
> +	memcpy(&smm_property->guid, vendor, sizeof(smm_property->guid));
> +	smm_property->name_size = name_size;
> +	memcpy(smm_property->name, name, name_size);
> +
> +	ret = mm_communicate(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)
> +		goto out;
> +	memcpy(var_property, &smm_property->property, sizeof(*var_property));
> +
> +out:
> +	kfree(comm_buf);
> +	return ret;
> +}
> +
> +static efi_status_t optee_get_variable(u16 *name, efi_guid_t *vendor,
> +				       u32 *attributes,
> +				       unsigned long *data_size, void *data)
> +{
> +	struct var_check_property var_property;
> +	struct smm_variable_access *var_acc;
> +	size_t payload_size;
> +	size_t name_size;
> +	size_t tmp_dsize;
> +	u8 *comm_buf = NULL;
> +	efi_status_t ret;
> +
> +	if (!name || !vendor || !data_size) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
> +	if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	/* 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)
> +		goto out;
> +
> +	/* Fill in contents */
> +	memcpy(&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;
> +	memcpy(var_acc->name, name, name_size);
> +
> +	/* Communicate */
> +	ret = mm_communicate(comm_buf, payload_size);
> +	if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL)
> +		/* Update with reported data size for trimmed case */
> +		*data_size = var_acc->data_size;
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	ret = get_property_int(name, name_size, vendor, &var_property);
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	if (attributes)
> +		*attributes = var_acc->attr;
> +
> +	if (data)
> +		memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
> +		       var_acc->data_size);
> +	else
> +		ret = EFI_INVALID_PARAMETER;
> +
> +out:
> +	kfree(comm_buf);
> +	return ret;
> +}
> +
> +static efi_status_t optee_get_next_variable(unsigned long *name_size,
> +					    efi_char16_t *name,
> +					    efi_guid_t *guid)
> +{
> +	struct smm_variable_getnext *var_getnext;
> +	size_t payload_size;
> +	size_t out_name_size;
> +	size_t in_name_size;
> +	u8 *comm_buf = NULL;
> +	efi_status_t ret;
> +
> +	if (!name_size || !name || !guid) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	out_name_size = *name_size;
> +	in_name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
> +
> +	if (out_name_size < in_name_size) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	if (in_name_size >
> +	    max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	/* 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)
> +		goto out;
> +
> +	/* Fill in contents */
> +	memcpy(&var_getnext->guid, guid, sizeof(var_getnext->guid));
> +	var_getnext->name_size = out_name_size;
> +	memcpy(var_getnext->name, name, in_name_size);
> +	memset((u8 *)var_getnext->name + in_name_size, 0x0,
> +	       out_name_size - in_name_size);
> +
> +	/* Communicate */
> +	ret = mm_communicate(comm_buf, payload_size);
> +	if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
> +		/* Update with reported data size for trimmed case */
> +		*name_size = var_getnext->name_size;
> +	}
> +	if (ret != EFI_SUCCESS)
> +		goto out;
> +
> +	memcpy(guid, &var_getnext->guid, sizeof(*guid));
> +	memcpy(name, var_getnext->name, var_getnext->name_size);
> +
> +out:
> +	kfree(comm_buf);
> +	return ret;
> +}
> +
> +static efi_status_t optee_set_variable(efi_char16_t *name, efi_guid_t *vendor,
> +				       u32 attributes, unsigned long data_size,
> +				       void *data)
> +{
> +	efi_status_t ret;
> +	struct var_check_property var_property;
> +	struct smm_variable_access *var_acc;
> +	size_t payload_size;
> +	size_t name_size;
> +	u8 *comm_buf = NULL;
> +
> +	if (!name || name[0] == 0 || !vendor) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +	if (data_size > 0 && !data) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +	/* Check payload size */
> +	name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
> +	payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
> +	if (payload_size > max_payload_size) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	/*
> +	 * 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)
> +		goto out;
> +
> +	/*
> +	 * The API has the ability to override RO flags. If no RO check was
> +	 * requested switch the variable to RW for the duration of this call
> +	 */
> +	ret = get_property_int(name, name_size, vendor, &var_property);
> +	if (ret != EFI_SUCCESS) {
> +		pr_err("Getting variable property failed\n");
> +		goto out;
> +	}
> +
> +	if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) {
> +		ret = EFI_WRITE_PROTECTED;
> +		goto out;
> +	}
> +
> +	/* Fill in contents */
> +	memcpy(&var_acc->guid, vendor, sizeof(var_acc->guid));
> +	var_acc->data_size = data_size;
> +	var_acc->name_size = name_size;
> +	var_acc->attr = attributes;
> +	memcpy(var_acc->name, name, name_size);
> +	memcpy((u8 *)var_acc->name + name_size, data, data_size);
> +
> +
> +	/* Communicate */
> +	ret = mm_communicate(comm_buf, payload_size);
> +	pr_info("Set Variable %s %d %lx\n", __FILE__, __LINE__, ret);
> +out:
> +	kfree(comm_buf);
> +	return ret;
> +}
> +
> +static int optee_stmm_efi_probe(struct device *dev)
> +{
> +	efi_status_t ret;
> +
> +	ret = get_max_payload(&max_payload_size);
> +	if (ret != EFI_SUCCESS)
> +		return ret;
> +
> +	max_buffer_size = MM_COMMUNICATE_HEADER_SIZE +
> +			  MM_VARIABLE_COMMUNICATE_SIZE +
> +			  max_payload_size;
> +
> +	optee_ops.get_variable = optee_get_variable;
> +	optee_ops.get_next_variable = optee_get_next_variable;
> +	optee_ops.set_variable = optee_set_variable;
> +	/* TODO: support nonblocking variant */
> +	optee_ops.set_variable_nonblocking = NULL;
> +	/* set NULL, always return EFI_SUCCESS by efi_query_variable_store() */
> +	optee_ops.query_variable_store = NULL;

Why aren't you using static initialization instead?

Thanks,
Jens

> +
> +	tee_register_efivar_ops(&optee_efivars, &optee_ops);
> +
> +	return 0;
> +}
> +
> +static int optee_stmm_efi_remove(struct device *dev)
> +{
> +	tee_unregister_efivar_ops(&optee_efivars);
> +
> +	return 0;
> +}
> +
> +MODULE_DEVICE_TABLE(tee, optee_stmm_efi_id_table);
> +
> +static struct tee_client_driver optee_stmm_efi_driver = {
> +	.id_table	= optee_stmm_efi_id_table,
> +	.driver		= {
> +		.name		= "optee-stmm-efi",
> +		.bus		= &tee_bus_type,
> +		.probe		= optee_stmm_efi_probe,
> +		.remove		= optee_stmm_efi_remove,
> +	},
> +};
> +
> +static int __init optee_stmm_efi_mod_init(void)
> +{
> +	return driver_register(&optee_stmm_efi_driver.driver);
> +}
> +
> +static void __exit optee_stmm_efi_mod_exit(void)
> +{
> +	driver_unregister(&optee_stmm_efi_driver.driver);
> +}
> +
> +module_init(optee_stmm_efi_mod_init);
> +module_exit(optee_stmm_efi_mod_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Ilias Apalodimas <ilias.apalodimas@linaro.org>");
> +MODULE_AUTHOR("Masahisa Kojima <masahisa.kojima@linaro.org>");
> +MODULE_DESCRIPTION("OP-TEE based EFI runtime variable service driver");
> diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> index 98da206cd761..ac46274844a3 100644
> --- a/drivers/tee/tee_core.c
> +++ b/drivers/tee/tee_core.c
> @@ -7,6 +7,7 @@
>  
>  #include <linux/cdev.h>
>  #include <linux/cred.h>
> +#include <linux/efi.h>
>  #include <linux/fs.h>
>  #include <linux/idr.h>
>  #include <linux/module.h>
> @@ -1263,6 +1264,28 @@ static void __exit tee_exit(void)
>  	tee_class = NULL;
>  }
>  
> +void tee_register_efivar_ops(struct efivars *tee_efivars,
> +			     struct efivar_operations *ops)
> +{
> +	/*
> +	 * If the firmware EFI runtime services support SetVariable(),
> +	 * tee-based EFI variable services are not used.
> +	 */
> +	if (!efivar_supports_writes()) {
> +		pr_info("Use tee-based EFI runtime variable services\n");
> +		efivars_generic_ops_unregister();
> +		efivars_register(tee_efivars, ops);
> +	}
> +}
> +EXPORT_SYMBOL_GPL(tee_register_efivar_ops);
> +
> +void tee_unregister_efivar_ops(struct efivars *tee_efivars)
> +{
> +	efivars_unregister(tee_efivars);
> +	efivars_generic_ops_register();
> +}
> +EXPORT_SYMBOL_GPL(tee_unregister_efivar_ops);
> +
>  subsys_initcall(tee_init);
>  module_exit(tee_exit);
>  
> diff --git a/include/linux/efi.h b/include/linux/efi.h
> index 5e301c00e9b0..14d4aa83ce60 100644
> --- a/include/linux/efi.h
> +++ b/include/linux/efi.h
> @@ -39,6 +39,7 @@
>  #define EFI_WRITE_PROTECTED	( 8 | (1UL << (BITS_PER_LONG-1)))
>  #define EFI_OUT_OF_RESOURCES	( 9 | (1UL << (BITS_PER_LONG-1)))
>  #define EFI_NOT_FOUND		(14 | (1UL << (BITS_PER_LONG-1)))
> +#define EFI_ACCESS_DENIED	(15 | (1UL << (BITS_PER_LONG-1)))
>  #define EFI_TIMEOUT		(18 | (1UL << (BITS_PER_LONG-1)))
>  #define EFI_ABORTED		(21 | (1UL << (BITS_PER_LONG-1)))
>  #define EFI_SECURITY_VIOLATION	(26 | (1UL << (BITS_PER_LONG-1)))
> diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
> index 17eb1c5205d3..def4ea6212ee 100644
> --- a/include/linux/tee_drv.h
> +++ b/include/linux/tee_drv.h
> @@ -7,6 +7,7 @@
>  #define __TEE_DRV_H
>  
>  #include <linux/device.h>
> +#include <linux/efi.h>
>  #include <linux/idr.h>
>  #include <linux/kref.h>
>  #include <linux/list.h>
> @@ -507,4 +508,26 @@ struct tee_context *teedev_open(struct tee_device *teedev);
>   */
>  void teedev_close_context(struct tee_context *ctx);
>  
> +/**
> + * tee_register_efivar_ops() - register the efivar ops
> + * @tee_efivars:	pointer to efivars structure
> + * @ops:		pointer to contain the efivar operation
> + *
> + * This function registers the tee-based efivar operation as an
> + * EFI Runtime Service.
> + *
> + */
> +void tee_register_efivar_ops(struct efivars *tee_efivars,
> +			     struct efivar_operations *ops);
> +
> +/**
> + * tee_unregister_efivar_ops() - unregister the efivar ops
> + * @tee_efivars:	pointer to efivars structure
> + *
> + * This function unregisters the tee-based efivar operation
> + * and reverts to the generic operation.
> + *
> + */
> +void tee_unregister_efivar_ops(struct efivars *tee_efivars);
> +
>  #endif /*__TEE_DRV_H*/
> -- 
> 2.30.2
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service
  2023-02-03  8:29   ` Jens Wiklander
@ 2023-02-03  9:33     ` Sumit Garg
  2023-02-03 10:55       ` Jens Wiklander
  0 siblings, 1 reply; 21+ messages in thread
From: Sumit Garg @ 2023-02-03  9:33 UTC (permalink / raw)
  To: Jens Wiklander
  Cc: Masahisa Kojima, Ard Biesheuvel, linux-kernel, op-tee,
	Ilias Apalodimas, Johan Hovold

Hi Jens,

On Fri, 3 Feb 2023 at 13:59, Jens Wiklander <jens.wiklander@linaro.org> wrote:
>
> Hi Sumit,
>
> On Thu, Feb 02, 2023 at 05:35:49PM +0530, Sumit Garg wrote:
> > Hi Masahisa,
> >
> > On Thu, 26 Jan 2023 at 18:52, Masahisa Kojima
> > <masahisa.kojima@linaro.org> wrote:
> > >
> > > This RFC series introduces the op-tee based EFI Runtime Variable
> > > Service.
> > >
> > > The eMMC device is typically owned by the non-secure world(linux in
> > > this case). There is an existing solution utilizing eMMC RPMB partition
> > > for EFI Variables, it is implemented by interacting with
> > > OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
> > > and tee-supplicant. The last piece is the tee-based variable access
> > > driver to interact with OP-TEE and StandaloneMM.
> > >
> >
> > After an overall look at the APIs, following are some initial comments:
> > - Is there any reason to have the edk2 specific StandaloneMM stack in
> > Linux to communicate with OP-TEE pseudo TA?
> > - I think the OP-TEE pseudo TA should be able to expose a rather
> > generic invoke commands such as:
> >      TEE_EFI_GET_VARIABLE
> >      TEE_EFI_GET_NEXT_VARIABLE
> >      TEE_EFI_SET_VARIABLE
> >   So it should no longer be tied to StMM stack and other TEE
> > implementations can re-use the abstracted interface to communicate
> > with its corresponding secure storage TA.
>
> In the current setup we have the following layers in the kernel:
> 1. efivar_operations
> 2. MM
> 3. PTA_STMM
> 4. OP-TEE MSG
>
> and in the secure world:
> S1. internal to StMM
> S2. MM interface to StMM
> S3. PTA_STMM
> S4. OP-TEE MSG
>
> If I understand you correctly you'd like to see this instead:
> Kernel:
> 1. efivar_operations
> 2. PTA_EFIVAR
> 4. OP-TEE MSG
>
> Since we still have the MM interface with StMM we'd have this in the secure
> world:
> S1. internal to StMM
> S2. MM interface to StMM
> S3. PTA_EFIVAR
> S4. OP-TEE MSG
>
> At S3 we'd have to convert between EFIVAR and MM messages. The
> difference is that we're moving the EFIVAR <-> MM conversion from the
> non-secure world into the secure world. We're still using OP-TEE
> specific communication at the fourth layer. So we're only moving problem
> around, I'd rather avoid growing the OP-TEE part in the secure world.
>

If you look carefully, we are essentially defining an ABI towards the
secure world. The approach in this patch-set adds the MM interface as
a redundant ABI layer which makes it complex to maintain. Now think
about if every TEE implementation would propose such a complex ABI. It
looks like a maintenance nightmare to me.

The concerns you are highlighting about OP-TEE size, I think those are
implementation details which can be simplified later but once you have
defined an ABI then you are stuck with its maintainability.

-Sumit

> Cheers,
> Jens
>
> >
> > -Sumit
> >
> > > Masahisa Kojima (2):
> > >   efi: expose efivar generic ops register function
> > >   tee: Add op-tee helper functions for variable access
> > >
> > >  drivers/firmware/efi/efi.c           |  12 +
> > >  drivers/tee/optee/Kconfig            |  10 +
> > >  drivers/tee/optee/Makefile           |   1 +
> > >  drivers/tee/optee/mm_communication.h | 249 +++++++++++
> > >  drivers/tee/optee/optee_private.h    |   5 +-
> > >  drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
> > >  drivers/tee/tee_core.c               |  23 ++
> > >  include/linux/efi.h                  |   4 +
> > >  include/linux/tee_drv.h              |  23 ++
> > >  9 files changed, 924 insertions(+), 1 deletion(-)
> > >  create mode 100644 drivers/tee/optee/mm_communication.h
> > >  create mode 100644 drivers/tee/optee/optee_stmm_efi.c
> > >
> > > --
> > > 2.30.2
> > >

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service
  2023-02-03  9:33     ` Sumit Garg
@ 2023-02-03 10:55       ` Jens Wiklander
  2023-02-06  6:44         ` Sumit Garg
  0 siblings, 1 reply; 21+ messages in thread
From: Jens Wiklander @ 2023-02-03 10:55 UTC (permalink / raw)
  To: Sumit Garg
  Cc: Masahisa Kojima, Ard Biesheuvel, linux-kernel, op-tee,
	Ilias Apalodimas, Johan Hovold

On Fri, Feb 03, 2023 at 03:03:34PM +0530, Sumit Garg wrote:
> Hi Jens,
> 
> On Fri, 3 Feb 2023 at 13:59, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> >
> > Hi Sumit,
> >
> > On Thu, Feb 02, 2023 at 05:35:49PM +0530, Sumit Garg wrote:
> > > Hi Masahisa,
> > >
> > > On Thu, 26 Jan 2023 at 18:52, Masahisa Kojima
> > > <masahisa.kojima@linaro.org> wrote:
> > > >
> > > > This RFC series introduces the op-tee based EFI Runtime Variable
> > > > Service.
> > > >
> > > > The eMMC device is typically owned by the non-secure world(linux in
> > > > this case). There is an existing solution utilizing eMMC RPMB partition
> > > > for EFI Variables, it is implemented by interacting with
> > > > OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
> > > > and tee-supplicant. The last piece is the tee-based variable access
> > > > driver to interact with OP-TEE and StandaloneMM.
> > > >
> > >
> > > After an overall look at the APIs, following are some initial comments:
> > > - Is there any reason to have the edk2 specific StandaloneMM stack in
> > > Linux to communicate with OP-TEE pseudo TA?
> > > - I think the OP-TEE pseudo TA should be able to expose a rather
> > > generic invoke commands such as:
> > >      TEE_EFI_GET_VARIABLE
> > >      TEE_EFI_GET_NEXT_VARIABLE
> > >      TEE_EFI_SET_VARIABLE
> > >   So it should no longer be tied to StMM stack and other TEE
> > > implementations can re-use the abstracted interface to communicate
> > > with its corresponding secure storage TA.
> >
> > In the current setup we have the following layers in the kernel:
> > 1. efivar_operations
> > 2. MM
> > 3. PTA_STMM
> > 4. OP-TEE MSG
> >
> > and in the secure world:
> > S1. internal to StMM
> > S2. MM interface to StMM
> > S3. PTA_STMM
> > S4. OP-TEE MSG
> >
> > If I understand you correctly you'd like to see this instead:
> > Kernel:
> > 1. efivar_operations
> > 2. PTA_EFIVAR
> > 4. OP-TEE MSG
> >
> > Since we still have the MM interface with StMM we'd have this in the secure
> > world:
> > S1. internal to StMM
> > S2. MM interface to StMM
> > S3. PTA_EFIVAR
> > S4. OP-TEE MSG
> >
> > At S3 we'd have to convert between EFIVAR and MM messages. The
> > difference is that we're moving the EFIVAR <-> MM conversion from the
> > non-secure world into the secure world. We're still using OP-TEE
> > specific communication at the fourth layer. So we're only moving problem
> > around, I'd rather avoid growing the OP-TEE part in the secure world.
> >
> 
> If you look carefully, we are essentially defining an ABI towards the
> secure world. The approach in this patch-set adds the MM interface as
> a redundant ABI layer which makes it complex to maintain. Now think
> about if every TEE implementation would propose such a complex ABI. It
> looks like a maintenance nightmare to me.
> 
> The concerns you are highlighting about OP-TEE size, I think those are
> implementation details which can be simplified later but once you have
> defined an ABI then you are stuck with its maintainability.

You have a point, but keep in mind that it's StMM that matters here.
StMM uses the MM protocol. It was originially using raw SMCs as a
conduit, but with the need for OP-TEE accessing RPMB that's not usable.
So instead we use OP-TEE MSG as a conduit. Seen from that perspective
we're only resuing something established instead of inventing something
new.

Cheers,
Jens

> 
> -Sumit
> 
> > Cheers,
> > Jens
> >
> > >
> > > -Sumit
> > >
> > > > Masahisa Kojima (2):
> > > >   efi: expose efivar generic ops register function
> > > >   tee: Add op-tee helper functions for variable access
> > > >
> > > >  drivers/firmware/efi/efi.c           |  12 +
> > > >  drivers/tee/optee/Kconfig            |  10 +
> > > >  drivers/tee/optee/Makefile           |   1 +
> > > >  drivers/tee/optee/mm_communication.h | 249 +++++++++++
> > > >  drivers/tee/optee/optee_private.h    |   5 +-
> > > >  drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
> > > >  drivers/tee/tee_core.c               |  23 ++
> > > >  include/linux/efi.h                  |   4 +
> > > >  include/linux/tee_drv.h              |  23 ++
> > > >  9 files changed, 924 insertions(+), 1 deletion(-)
> > > >  create mode 100644 drivers/tee/optee/mm_communication.h
> > > >  create mode 100644 drivers/tee/optee/optee_stmm_efi.c
> > > >
> > > > --
> > > > 2.30.2
> > > >

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/2] tee: Add op-tee helper functions for variable access
  2023-02-03  9:30     ` Jens Wiklander
@ 2023-02-06  6:08       ` Masahisa Kojima
  -1 siblings, 0 replies; 21+ messages in thread
From: Masahisa Kojima @ 2023-02-06  6:08 UTC (permalink / raw)
  To: Jens Wiklander
  Cc: Ard Biesheuvel, linux-kernel, op-tee, Ilias Apalodimas,
	Johan Hovold, Sumit Garg, Maxime Coquelin, Alexandre Torgue,
	linux-stm32, linux-arm-kernel, linux-efi

() Hi Jens,

On Fri, 3 Feb 2023 at 18:30, Jens Wiklander <jens.wiklander@linaro.org> wrote:
>
> On Thu, Jan 26, 2023 at 10:21:19PM +0900, Masahisa Kojima wrote:
> > When the flash is not owned by the non-secure world, accessing the EFI
> > variables is straightforward and done via EFI Runtime Variable Services.
> > In this case, critical variables for system integrity and security
> > are normally stored in the dedicated secure storage and only accessible
> > from the secure world.
> >
> > On the other hand, the small embedded devices don't have the special
> > dedicated secure storage. The eMMC device with an RPMB partition is
> > becoming more common, we can use an RPMB partition to store the
> > EFI Variables.
> >
> > The eMMC device is typically owned by the non-secure world(linux in
> > this case). There is an existing solution utilizing eMMC RPMB partition
> > for EFI Variables, it is implemented by interacting with
> > OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
> > and tee-supplicant. The last piece is the tee-based variable access
> > driver to interact with OP-TEE and StandaloneMM.
> >
> > So let's add the kernel functions needed.
> >
> > This feature is implemented as a kernel module.
> > StMM PTA has TA_FLAG_DEVICE_ENUM_SUPP flag when registered to OP-TEE
> > so that this optee_stmm_efi module is probed after tee-supplicant starts,
> > since "SetVariable" EFI Runtime Variable Service requires to
> > interact with tee-supplicant.
> >
> > Co-developed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> > Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > ---
> >  drivers/tee/optee/Kconfig            |  10 +
> >  drivers/tee/optee/Makefile           |   1 +
> >  drivers/tee/optee/mm_communication.h | 249 +++++++++++
> >  drivers/tee/optee/optee_private.h    |   5 +-
> >  drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
> >  drivers/tee/tee_core.c               |  23 ++
> >  include/linux/efi.h                  |   1 +
> >  include/linux/tee_drv.h              |  23 ++
> >  8 files changed, 909 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/tee/optee/mm_communication.h
> >  create mode 100644 drivers/tee/optee/optee_stmm_efi.c
>
>
> Please split this patch into three patches, one for drivers/tee, one
> for the driver/tee/optee, and one for include/linux/efi.h.

OK.

>
> >
> > diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
> > index f121c224e682..a0b699977e51 100644
> > --- a/drivers/tee/optee/Kconfig
> > +++ b/drivers/tee/optee/Kconfig
> > @@ -7,3 +7,13 @@ config OPTEE
> >       help
> >         This implements the OP-TEE Trusted Execution Environment (TEE)
> >         driver.
> > +
> > +config EFI_STMM_OPTEE
>
> Should this be OPTEE_EFI_STMM?

OK.

>
> > +     tristate "OP-TEE based EFI runtime variable service driver"
> > +     depends on OPTEE
> > +     help
> > +       This  driver provides support for OP-TEE based EFI runtime
> > +       variable service driver.
> > +
> > +       To compile this driver as a module, choose M here: the module
> > +       will be called optee_stmm_efi.
> > diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
> > index a6eff388d300..9acb0b2de8fe 100644
> > --- a/drivers/tee/optee/Makefile
> > +++ b/drivers/tee/optee/Makefile
> > @@ -8,6 +8,7 @@ optee-objs += supp.o
> >  optee-objs += device.o
> >  optee-objs += smc_abi.o
> >  optee-objs += ffa_abi.o
> > +obj-$(CONFIG_EFI_STMM_OPTEE) += optee_stmm_efi.o
> >
> >  # for tracing framework to find optee_trace.h
> >  CFLAGS_smc_abi.o := -I$(src)
> > diff --git a/drivers/tee/optee/mm_communication.h b/drivers/tee/optee/mm_communication.h
> > new file mode 100644
> > index 000000000000..23e6672991ef
> > --- /dev/null
> > +++ b/drivers/tee/optee/mm_communication.h
> > @@ -0,0 +1,249 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/*
> > + *  Headers for EFI variable service via StandAloneMM, EDK2 application running
> > + *  in OP-TEE
> > + *
> > + *  Copyright (c) 2017, Intel Corporation. All rights reserved.
> > + *  Copyright (C) 2020 Linaro Ltd.
> > + */
> > +
> > +#ifndef _MM_COMMUNICATION_H_
> > +#define _MM_COMMUNICATION_H_
> > +
> > +/*
> > + * Interface to the pseudo Trusted Application (TA), which provides a
> > + * communication channel with the Standalone MM (Management Mode)
> > + * Secure Partition running at Secure-EL0
> > + */
> > +
> > +#define PTA_STMM_CMDID_COMMUNICATE 0
>
> PTA_STMM_CMD_COMMUNICATE

OK.

>
> > +
> > +/* OP-TEE is using big endian GUIDs while UEFI uses little endian ones */
> > +#define PTA_STMM_UUID \
> > +     UUID_INIT(0xed32d533, 0x99e6, 0x4209, \
> > +               0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
> > +
> > +#define EFI_MM_VARIABLE_GUID \
> > +     EFI_GUID(0xed32d533, 0x99e6, 0x4209, \
> > +              0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
> > +
> > +/* Defined in EDK2 MdePkg/Include/Protocol/MmCommunication.h */
> > +
> > +/**
> > + * struct efi_mm_communicate_header - Header used for SMM variable communication
> > +
> > + * @header_guid:  header use for disambiguation of content
> > + * @message_len:  length of the message. Does not include the size of the
> > + *                header
> > + * @data:         payload of the message
> > + *
> > + * Defined in EDK2 as EFI_MM_COMMUNICATE_HEADER.
> > + * To avoid confusion in interpreting frames, the communication buffer should
> > + * always begin with efi_mm_communicate_header.
> > + */
> > +struct efi_mm_communicate_header {
> > +     efi_guid_t header_guid;
> > +     size_t     message_len;
> > +     u8         data[];
> > +} __packed;
> > +
> > +#define MM_COMMUNICATE_HEADER_SIZE \
> > +     (sizeof(struct efi_mm_communicate_header))
> > +
> > +/* Defined in EDK2 ArmPkg/Include/IndustryStandard/ArmMmSvc.h */
> > +
> > +/* SPM return error codes */
> > +#define ARM_SVC_SPM_RET_SUCCESS               0
> > +#define ARM_SVC_SPM_RET_NOT_SUPPORTED        -1
> > +#define ARM_SVC_SPM_RET_INVALID_PARAMS       -2
> > +#define ARM_SVC_SPM_RET_DENIED               -3
> > +#define ARM_SVC_SPM_RET_NO_MEMORY            -5
> > +
> > +/* Defined in EDK2 MdeModulePkg/Include/Guid/SmmVariableCommon.h */
> > +
> > +#define SMM_VARIABLE_FUNCTION_GET_VARIABLE  1
> > +/*
> > + * The payload for this function is
> > + * SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME.
> > + */
> > +#define SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME  2
> > +/*
> > + * The payload for this function is SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
> > + */
> > +#define SMM_VARIABLE_FUNCTION_SET_VARIABLE  3
> > +/*
> > + * The payload for this function is
> > + * SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO.
> > + */
> > +#define SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO  4
> > +/*
> > + * It is a notify event, no extra payload for this function.
> > + */
> > +#define SMM_VARIABLE_FUNCTION_READY_TO_BOOT  5
> > +/*
> > + * It is a notify event, no extra payload for this function.
> > + */
> > +#define SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE  6
> > +/*
> > + * The payload for this function is VARIABLE_INFO_ENTRY.
> > + * The GUID in EFI_SMM_COMMUNICATE_HEADER is gEfiSmmVariableProtocolGuid.
> > + */
> > +#define SMM_VARIABLE_FUNCTION_GET_STATISTICS  7
> > +/*
> > + * The payload for this function is SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
> > + */
> > +#define SMM_VARIABLE_FUNCTION_LOCK_VARIABLE   8
> > +
> > +#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET  9
> > +
> > +#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET  10
> > +
> > +#define SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE  11
> > +/*
> > + * The payload for this function is
> > + * SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT
> > + */
> > +#define SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT 12
> > +
> > +#define SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE  13
> > +/*
> > + * The payload for this function is
> > + * SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO
> > + */
> > +#define SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO  14
> > +
> > +/**
> > + * struct smm_variable_communicate_header - Used for SMM variable communication
> > +
> > + * @function:     function to call in Smm.
> > + * @ret_status:   return status
> > + * @data:         payload
> > + *
> > + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_HEADER.
> > + */
> > +struct smm_variable_communicate_header {
> > +     size_t  function;
> > +     efi_status_t ret_status;
> > +     u8 data[];
> > +};
> > +
> > +#define MM_VARIABLE_COMMUNICATE_SIZE \
> > +     (sizeof(struct smm_variable_communicate_header))
> > +
> > +/**
> > + * struct smm_variable_access - Used to communicate with StMM by
> > + *                              SetVariable and GetVariable.
> > +
> > + * @guid:         vendor GUID
> > + * @data_size:    size of EFI variable data
> > + * @name_size:    size of EFI name
> > + * @attr:         attributes
> > + * @name:         variable name
> > + *
> > + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
> > + *
> > + */
> > +struct smm_variable_access {
> > +     efi_guid_t  guid;
> > +     size_t data_size;
> > +     size_t name_size;
> > +     u32 attr;
> > +     u16 name[];
> > +};
> > +
> > +#define MM_VARIABLE_ACCESS_HEADER_SIZE \
> > +     (sizeof(struct smm_variable_access))
> > +/**
> > + * struct smm_variable_payload_size - Used to get the max allowed
> > + *                                    payload used in StMM.
> > + *
> > + * @size:  size to fill in
> > + *
> > + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE.
> > + *
> > + */
> > +struct smm_variable_payload_size {
> > +     size_t size;
> > +};
> > +
> > +/**
> > + * struct smm_variable_getnext - Used to communicate with StMM for
> > + *                               GetNextVariableName.
> > + *
> > + * @guid:       vendor GUID
> > + * @name_size:  size of the name of the variable
> > + * @name:       variable name
> > + *
> > + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME.
> > + */
> > +struct smm_variable_getnext {
> > +     efi_guid_t  guid;
> > +     size_t name_size;
> > +     u16         name[];
> > +};
> > +
> > +#define MM_VARIABLE_GET_NEXT_HEADER_SIZE \
> > +     (sizeof(struct smm_variable_getnext))
> > +
> > +/**
> > + * struct smm_variable_query_info - Used to communicate with StMM for
> > + *                                  QueryVariableInfo.
> > + *
> > + * @max_variable_storage:        max available storage
> > + * @remaining_variable_storage:  remaining available storage
> > + * @max_variable_size:           max variable supported size
> > + * @attr:                        attributes to query storage for
> > + *
> > + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO.
> > + */
> > +struct smm_variable_query_info {
> > +     u64 max_variable_storage;
> > +     u64 remaining_variable_storage;
> > +     u64 max_variable_size;
> > +     u32 attr;
> > +};
> > +
> > +#define VAR_CHECK_VARIABLE_PROPERTY_REVISION 0x0001
> > +#define VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY BIT(0)
> > +/**
> > + * struct var_check_property - Used to store variable properties in StMM
> > + *
> > + * @revision:   magic revision number for variable property checking
> > + * @property:   properties mask for the variable used in StMM.
> > + *              Currently RO flag is supported
> > + * @attributes: variable attributes used in StMM checking when properties
> > + *              for a variable are enabled
> > + * @minsize:    minimum allowed size for variable payload checked against
> > + *              smm_variable_access->datasize in StMM
> > + * @maxsize:    maximum allowed size for variable payload checked against
> > + *              smm_variable_access->datasize in StMM
> > + *
> > + * Defined in EDK2 as VAR_CHECK_VARIABLE_PROPERTY.
> > + */
> > +struct var_check_property {
> > +     u16 revision;
> > +     u16 property;
> > +     u32 attributes;
> > +     size_t minsize;
> > +     size_t maxsize;
> > +};
> > +
> > +/**
> > + * struct smm_variable_var_check_property - Used to communicate variable
> > + *                                          properties with StMM
> > + *
> > + * @guid:       vendor GUID
> > + * @name_size:  size of EFI name
> > + * @property:   variable properties struct
> > + * @name:       variable name
> > + *
> > + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY.
> > + */
> > +struct smm_variable_var_check_property {
> > +     efi_guid_t guid;
> > +     size_t name_size;
> > +     struct var_check_property property;
> > +     u16 name[];
> > +};
> > +
> > +#endif /* _MM_COMMUNICATION_H_ */
> > diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> > index 04ae58892608..10af910e0dce 100644
> > --- a/drivers/tee/optee/optee_private.h
> > +++ b/drivers/tee/optee/optee_private.h
> > @@ -12,6 +12,7 @@
> >  #include <linux/tee_drv.h>
> >  #include <linux/types.h>
> >  #include "optee_msg.h"
> > +#include "optee_private.h"
>
> This is "optee_private.h", no need to include it.

I added this line by mistake, remove this line.

>
> >
> >  #define DRIVER_NAME "optee"
> >
> > @@ -19,6 +20,7 @@
> >
> >  /* Some Global Platform error codes used in this driver */
> >  #define TEEC_SUCCESS                 0x00000000
> > +#define TEEC_ERROR_EXCESS_DATA               0xFFFF0004
> >  #define TEEC_ERROR_BAD_PARAMETERS    0xFFFF0006
> >  #define TEEC_ERROR_NOT_SUPPORTED     0xFFFF000A
> >  #define TEEC_ERROR_COMMUNICATION     0xFFFF000E
> > @@ -250,7 +252,6 @@ void optee_pool_op_free_helper(struct tee_shm_pool *pool, struct tee_shm *shm,
> >                              int (*shm_unregister)(struct tee_context *ctx,
> >                                                    struct tee_shm *shm));
> >
> > -
> >  void optee_remove_common(struct optee *optee);
> >  int optee_open(struct tee_context *ctx, bool cap_memref_null);
> >  void optee_release(struct tee_context *ctx);
> > @@ -322,4 +323,6 @@ void optee_smc_abi_unregister(void);
> >  int optee_ffa_abi_register(void);
> >  void optee_ffa_abi_unregister(void);
> >
> > +int optee_efivar_ops_init(struct tee_context *ctx);
>
> This isn't used or implemented as far as I can tell.

This is the previous version of code, I will remove it.

>
> > +
> >  #endif /*OPTEE_PRIVATE_H*/
> > diff --git a/drivers/tee/optee/optee_stmm_efi.c b/drivers/tee/optee/optee_stmm_efi.c
> > new file mode 100644
> > index 000000000000..6dcf1eb4b96c
> > --- /dev/null
> > +++ b/drivers/tee/optee/optee_stmm_efi.c
> > @@ -0,0 +1,598 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + *  EFI variable service via OP-TEE
> > + *
> > + *  Copyright (C) 2022 Linaro
> > + */
> > +
> > +#include <linux/efi.h>
> > +#include <linux/kernel.h>
> > +#include <linux/slab.h>
> > +#include <linux/tee.h>
> > +#include <linux/tee_drv.h>
> > +#include <linux/ucs2_string.h>
> > +#include "mm_communication.h"
> > +#include "optee_private.h"
> > +
> > +static struct efivars optee_efivars;
> > +static struct efivar_operations optee_ops;
>
> optee_efi_ops instead? To avoid being confused with the other optee_ops.
>
> > +
> > +static size_t max_buffer_size; /* comm + var + func + data */
> > +static size_t max_payload_size; /* func + data */
> > +
> > +struct mm_connection {
> > +     struct tee_context *ctx;
> > +     u32 session;
> > +};
> > +
> > +/* UUID of the stmm PTA */
> > +static const struct tee_client_device_id optee_stmm_efi_id_table[] = {
> > +     {PTA_STMM_UUID},
> > +     {}
> > +};
> > +
> > +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
> > +{
> > +     if (ver->impl_id == TEE_IMPL_ID_OPTEE)
> > +             return 1;
> > +     else
> > +             return 0;
> > +}
> > +
> > +/**
> > + * get_connection() - Retrieve OP-TEE session for a specific UUID.
> > + *
> > + * @conn:   session buffer to fill
> > + * Return:  status code
> > + */
> > +static int get_connection(struct mm_connection *conn)
> > +{
> > +     struct tee_context *ctx = NULL;
> > +     struct tee_ioctl_open_session_arg arg;
> > +     int rc;
> > +
> > +     memset(&arg, 0, sizeof(arg));
> > +
> > +     /* Open context with OP-TEE driver */
> > +     ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
>
> tee_client_close_context() is missing in the error paths.

Yes, I will add tee_client_close_context in the error paths.

>
> > +     if (IS_ERR(ctx))
> > +             return -ENODEV;
> > +
> > +     export_uuid(arg.uuid, &optee_stmm_efi_id_table[0].uuid);
> > +     rc = tee_client_open_session(ctx, &arg, NULL);
> > +     if (!rc) {
> > +             conn->ctx = ctx;
> > +             conn->session = arg.session;
> > +     }
> > +
> > +     return rc;
> > +}
> > +
> > +/**
> > + * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE
> > + *
> > + * @comm_buf:                locally allocated communcation buffer
> > + * @dsize:           buffer size
> > + * Return:           status code
> > + */
> > +static efi_status_t optee_mm_communicate(void *comm_buf, size_t dsize)
> > +{
> > +     size_t buf_size;
> > +     efi_status_t ret;
> > +     struct efi_mm_communicate_header *mm_hdr;
> > +     struct mm_connection conn = { NULL, 0 };
> > +     struct tee_ioctl_invoke_arg arg;
> > +     struct tee_param param[4];
> > +     struct tee_shm *shm = NULL;
> > +     int rc;
> > +
> > +     if (!comm_buf)
> > +             return EFI_INVALID_PARAMETER;
> > +
> > +     mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
>
> No need to cast.

OK.

>
> > +     buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
> > +
> > +     if (dsize != buf_size)
> > +             return EFI_INVALID_PARAMETER;
> > +
> > +     rc = get_connection(&conn);
> > +     if (rc) {
> > +             pr_err("Unable to open OP-TEE session (err=%d)\n", rc);
>
> Perhaps better to print from get_connection() to be able to distinguish
> between errors from tee_client_open_context() and
> tee_client_open_session().

OK, I will update.

>
> > +             return EFI_UNSUPPORTED;
> > +     }
> > +
> > +     shm = tee_shm_register_kernel_buf(conn.ctx, comm_buf, buf_size);
> > +     if (IS_ERR(shm)) {
> > +             pr_err("Unable to register shared memory\n");
> > +             tee_client_close_session(conn.ctx, conn.session);
> > +             return EFI_UNSUPPORTED;
> > +     }
> > +
> > +     memset(&arg, 0, sizeof(arg));
> > +     arg.func = PTA_STMM_CMDID_COMMUNICATE;
> > +     arg.session = conn.session;
> > +     arg.num_params = 4;
> > +
> > +     memset(param, 0, sizeof(param));
> > +     param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
> > +     param[0].u.memref.size = buf_size;
> > +     param[0].u.memref.shm = shm;
> > +     param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
> > +     param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
> > +     param[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
> > +
> > +     rc = tee_client_invoke_func(conn.ctx, &arg, param);
> > +     tee_shm_free(shm);
> > +     /* even if close session fails the session will be invalidaded */
> > +     tee_client_close_session(conn.ctx, conn.session);
> > +     if (rc)
> > +             return EFI_DEVICE_ERROR;
> > +     if (arg.ret == TEEC_ERROR_EXCESS_DATA)
> > +             pr_err("Variable payload too large\n");
> > +     if (arg.ret != TEEC_SUCCESS)
> > +             return EFI_DEVICE_ERROR;
> > +
> > +     switch (param[1].u.value.a) {
> > +     case ARM_SVC_SPM_RET_SUCCESS:
> > +             ret = EFI_SUCCESS;
> > +             break;
> > +
> > +     case ARM_SVC_SPM_RET_INVALID_PARAMS:
> > +             ret = EFI_INVALID_PARAMETER;
> > +             break;
> > +
> > +     case ARM_SVC_SPM_RET_DENIED:
> > +             ret = EFI_ACCESS_DENIED;
> > +             break;
> > +
> > +     case ARM_SVC_SPM_RET_NO_MEMORY:
> > +             ret = EFI_OUT_OF_RESOURCES;
> > +             break;
> > +
> > +     default:
> > +             ret = EFI_ACCESS_DENIED;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +/**
> > + * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
> > + * it to OP-TEE
> > + *
> > + * @comm_buf:                locally allocated communcation buffer
> > + * @dsize:           buffer size
> > + * Return:           status code
> > + */
> > +static efi_status_t mm_communicate(u8 *comm_buf, size_t dsize)
> > +{
> > +     efi_status_t ret;
> > +     struct efi_mm_communicate_header *mm_hdr;
> > +     struct smm_variable_communicate_header *var_hdr;
> > +
> > +     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;
> > +
> > +     ret = optee_mm_communicate(comm_buf, dsize);
> > +     if (ret != EFI_SUCCESS) {
> > +             pr_err("%s failed!\n", __func__);
> > +             return ret;
> > +     }
> > +
> > +     return var_hdr->ret_status;
> > +}
> > +
> > +/**
> > + * setup_mm_hdr() -  Allocate a buffer for StandAloneMM and initialize the
> > + *                   header data.
> > + *
> > + * @dptr:            pointer address of the corresponding StandAloneMM
> > + *                   function
> > + * @payload_size:    buffer size
> > + * @func:            standAloneMM function number
> > + * @ret:             EFI return code
> > + * Return:           buffer or NULL
> > + */
> > +static u8 *setup_mm_hdr(void **dptr, size_t payload_size, size_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;
> > +
> > +     /* In the init function we initialize max_buffer_size with
> > +      * get_max_payload(). So skip the test if max_buffer_size is initialized
> > +      * StandAloneMM will perform similar checks and drop the buffer if it's
> > +      * too long
> > +      */
> > +     if (max_buffer_size &&
> > +         max_buffer_size < (MM_COMMUNICATE_HEADER_SIZE +
> > +                            MM_VARIABLE_COMMUNICATE_SIZE + payload_size)) {
> > +             *ret = EFI_INVALID_PARAMETER;
> > +             return NULL;
> > +     }
> > +
> > +     comm_buf = kzalloc(MM_COMMUNICATE_HEADER_SIZE +
> > +                                MM_VARIABLE_COMMUNICATE_SIZE + payload_size,
> > +                        GFP_KERNEL);
> > +     if (!comm_buf) {
> > +             *ret = EFI_OUT_OF_RESOURCES;
> > +             return NULL;
> > +     }
> > +
> > +     mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
> > +     memcpy(&mm_hdr->header_guid, &mm_var_guid, 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;
> > +     var_hdr->function = func;
> > +     if (dptr)
> > +             *dptr = var_hdr->data;
> > +     *ret = EFI_SUCCESS;
> > +
> > +     return comm_buf;
> > +}
> > +
> > +/**
> > + * get_max_payload() - Get variable payload size from StandAloneMM.
> > + *
> > + * @size:    size of the variable in storage
> > + * Return:   status code
> > + */
> > +static efi_status_t get_max_payload(size_t *size)
> > +{
> > +     struct smm_variable_payload_size *var_payload = NULL;
> > +     size_t payload_size;
> > +     u8 *comm_buf = NULL;
> > +     efi_status_t ret;
> > +
> > +     if (!size) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     payload_size = sizeof(*var_payload);
> > +     comm_buf = setup_mm_hdr((void **)&var_payload, payload_size,
> > +                             SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, &ret);
> > +     if (!comm_buf)
> > +             goto out;
> > +
> > +     ret = mm_communicate(comm_buf, payload_size);
> > +     if (ret != EFI_SUCCESS)
> > +             goto out;
> > +
> > +     /* Make sure the buffer is big enough for storing variables */
> > +     if (var_payload->size < MM_VARIABLE_ACCESS_HEADER_SIZE + 0x20) {
> > +             ret = EFI_DEVICE_ERROR;
> > +             goto out;
> > +     }
> > +     *size = var_payload->size;
> > +     /*
> > +      * There seems to be a bug in EDK2 miscalculating the boundaries and
> > +      * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
> > +      * it up here to ensure backwards compatibility with older versions
> > +      * (cf. StandaloneMmPkg/Drivers/StandaloneMmCpu/AArch64/EventHandle.c.
> > +      * sizeof (EFI_MM_COMMUNICATE_HEADER) instead the size minus the
> > +      * flexible array member).
> > +      *
> > +      * size is guaranteed to be > 2 due to checks on the beginning.
> > +      */
> > +     *size -= 2;
> > +out:
> > +     kfree(comm_buf);
> > +     return ret;
> > +}
> > +
> > +static efi_status_t get_property_int(u16 *name, size_t name_size,
> > +                                  const efi_guid_t *vendor,
> > +                                  struct var_check_property *var_property)
> > +{
> > +     struct smm_variable_var_check_property *smm_property;
> > +     size_t payload_size;
> > +     u8 *comm_buf = NULL;
> > +     efi_status_t ret;
> > +
> > +     memset(var_property, 0, sizeof(*var_property));
> > +     payload_size = sizeof(*smm_property) + name_size;
> > +     if (payload_size > max_payload_size) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +     comm_buf = setup_mm_hdr(
> > +             (void **)&smm_property, payload_size,
> > +             SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET, &ret);
> > +     if (!comm_buf)
> > +             goto out;
> > +
> > +     memcpy(&smm_property->guid, vendor, sizeof(smm_property->guid));
> > +     smm_property->name_size = name_size;
> > +     memcpy(smm_property->name, name, name_size);
> > +
> > +     ret = mm_communicate(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)
> > +             goto out;
> > +     memcpy(var_property, &smm_property->property, sizeof(*var_property));
> > +
> > +out:
> > +     kfree(comm_buf);
> > +     return ret;
> > +}
> > +
> > +static efi_status_t optee_get_variable(u16 *name, efi_guid_t *vendor,
> > +                                    u32 *attributes,
> > +                                    unsigned long *data_size, void *data)
> > +{
> > +     struct var_check_property var_property;
> > +     struct smm_variable_access *var_acc;
> > +     size_t payload_size;
> > +     size_t name_size;
> > +     size_t tmp_dsize;
> > +     u8 *comm_buf = NULL;
> > +     efi_status_t ret;
> > +
> > +     if (!name || !vendor || !data_size) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
> > +     if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     /* 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)
> > +             goto out;
> > +
> > +     /* Fill in contents */
> > +     memcpy(&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;
> > +     memcpy(var_acc->name, name, name_size);
> > +
> > +     /* Communicate */
> > +     ret = mm_communicate(comm_buf, payload_size);
> > +     if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL)
> > +             /* Update with reported data size for trimmed case */
> > +             *data_size = var_acc->data_size;
> > +     if (ret != EFI_SUCCESS)
> > +             goto out;
> > +
> > +     ret = get_property_int(name, name_size, vendor, &var_property);
> > +     if (ret != EFI_SUCCESS)
> > +             goto out;
> > +
> > +     if (attributes)
> > +             *attributes = var_acc->attr;
> > +
> > +     if (data)
> > +             memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
> > +                    var_acc->data_size);
> > +     else
> > +             ret = EFI_INVALID_PARAMETER;
> > +
> > +out:
> > +     kfree(comm_buf);
> > +     return ret;
> > +}
> > +
> > +static efi_status_t optee_get_next_variable(unsigned long *name_size,
> > +                                         efi_char16_t *name,
> > +                                         efi_guid_t *guid)
> > +{
> > +     struct smm_variable_getnext *var_getnext;
> > +     size_t payload_size;
> > +     size_t out_name_size;
> > +     size_t in_name_size;
> > +     u8 *comm_buf = NULL;
> > +     efi_status_t ret;
> > +
> > +     if (!name_size || !name || !guid) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     out_name_size = *name_size;
> > +     in_name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
> > +
> > +     if (out_name_size < in_name_size) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     if (in_name_size >
> > +         max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     /* 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)
> > +             goto out;
> > +
> > +     /* Fill in contents */
> > +     memcpy(&var_getnext->guid, guid, sizeof(var_getnext->guid));
> > +     var_getnext->name_size = out_name_size;
> > +     memcpy(var_getnext->name, name, in_name_size);
> > +     memset((u8 *)var_getnext->name + in_name_size, 0x0,
> > +            out_name_size - in_name_size);
> > +
> > +     /* Communicate */
> > +     ret = mm_communicate(comm_buf, payload_size);
> > +     if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
> > +             /* Update with reported data size for trimmed case */
> > +             *name_size = var_getnext->name_size;
> > +     }
> > +     if (ret != EFI_SUCCESS)
> > +             goto out;
> > +
> > +     memcpy(guid, &var_getnext->guid, sizeof(*guid));
> > +     memcpy(name, var_getnext->name, var_getnext->name_size);
> > +
> > +out:
> > +     kfree(comm_buf);
> > +     return ret;
> > +}
> > +
> > +static efi_status_t optee_set_variable(efi_char16_t *name, efi_guid_t *vendor,
> > +                                    u32 attributes, unsigned long data_size,
> > +                                    void *data)
> > +{
> > +     efi_status_t ret;
> > +     struct var_check_property var_property;
> > +     struct smm_variable_access *var_acc;
> > +     size_t payload_size;
> > +     size_t name_size;
> > +     u8 *comm_buf = NULL;
> > +
> > +     if (!name || name[0] == 0 || !vendor) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +     if (data_size > 0 && !data) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +     /* Check payload size */
> > +     name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
> > +     payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
> > +     if (payload_size > max_payload_size) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     /*
> > +      * 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)
> > +             goto out;
> > +
> > +     /*
> > +      * The API has the ability to override RO flags. If no RO check was
> > +      * requested switch the variable to RW for the duration of this call
> > +      */
> > +     ret = get_property_int(name, name_size, vendor, &var_property);
> > +     if (ret != EFI_SUCCESS) {
> > +             pr_err("Getting variable property failed\n");
> > +             goto out;
> > +     }
> > +
> > +     if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) {
> > +             ret = EFI_WRITE_PROTECTED;
> > +             goto out;
> > +     }
> > +
> > +     /* Fill in contents */
> > +     memcpy(&var_acc->guid, vendor, sizeof(var_acc->guid));
> > +     var_acc->data_size = data_size;
> > +     var_acc->name_size = name_size;
> > +     var_acc->attr = attributes;
> > +     memcpy(var_acc->name, name, name_size);
> > +     memcpy((u8 *)var_acc->name + name_size, data, data_size);
> > +
> > +
> > +     /* Communicate */
> > +     ret = mm_communicate(comm_buf, payload_size);
> > +     pr_info("Set Variable %s %d %lx\n", __FILE__, __LINE__, ret);
> > +out:
> > +     kfree(comm_buf);
> > +     return ret;
> > +}
> > +
> > +static int optee_stmm_efi_probe(struct device *dev)
> > +{
> > +     efi_status_t ret;
> > +
> > +     ret = get_max_payload(&max_payload_size);
> > +     if (ret != EFI_SUCCESS)
> > +             return ret;
> > +
> > +     max_buffer_size = MM_COMMUNICATE_HEADER_SIZE +
> > +                       MM_VARIABLE_COMMUNICATE_SIZE +
> > +                       max_payload_size;
> > +
> > +     optee_ops.get_variable = optee_get_variable;
> > +     optee_ops.get_next_variable = optee_get_next_variable;
> > +     optee_ops.set_variable = optee_set_variable;
> > +     /* TODO: support nonblocking variant */
> > +     optee_ops.set_variable_nonblocking = NULL;
> > +     /* set NULL, always return EFI_SUCCESS by efi_query_variable_store() */
> > +     optee_ops.query_variable_store = NULL;
>
> Why aren't you using static initialization instead?

On second thought, I think we can implement QueryVariableInfo(),
so it is better to set StMM based QueryVariableInfo() rather than set
efi_query_variable_store().

Thank you for your review.

Regards,
Masahisa Kojima



>
> Thanks,
> Jens
>
> > +
> > +     tee_register_efivar_ops(&optee_efivars, &optee_ops);
> > +
> > +     return 0;
> > +}
> > +
> > +static int optee_stmm_efi_remove(struct device *dev)
> > +{
> > +     tee_unregister_efivar_ops(&optee_efivars);
> > +
> > +     return 0;
> > +}
> > +
> > +MODULE_DEVICE_TABLE(tee, optee_stmm_efi_id_table);
> > +
> > +static struct tee_client_driver optee_stmm_efi_driver = {
> > +     .id_table       = optee_stmm_efi_id_table,
> > +     .driver         = {
> > +             .name           = "optee-stmm-efi",
> > +             .bus            = &tee_bus_type,
> > +             .probe          = optee_stmm_efi_probe,
> > +             .remove         = optee_stmm_efi_remove,
> > +     },
> > +};
> > +
> > +static int __init optee_stmm_efi_mod_init(void)
> > +{
> > +     return driver_register(&optee_stmm_efi_driver.driver);
> > +}
> > +
> > +static void __exit optee_stmm_efi_mod_exit(void)
> > +{
> > +     driver_unregister(&optee_stmm_efi_driver.driver);
> > +}
> > +
> > +module_init(optee_stmm_efi_mod_init);
> > +module_exit(optee_stmm_efi_mod_exit);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_AUTHOR("Ilias Apalodimas <ilias.apalodimas@linaro.org>");
> > +MODULE_AUTHOR("Masahisa Kojima <masahisa.kojima@linaro.org>");
> > +MODULE_DESCRIPTION("OP-TEE based EFI runtime variable service driver");
> > diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> > index 98da206cd761..ac46274844a3 100644
> > --- a/drivers/tee/tee_core.c
> > +++ b/drivers/tee/tee_core.c
> > @@ -7,6 +7,7 @@
> >
> >  #include <linux/cdev.h>
> >  #include <linux/cred.h>
> > +#include <linux/efi.h>
> >  #include <linux/fs.h>
> >  #include <linux/idr.h>
> >  #include <linux/module.h>
> > @@ -1263,6 +1264,28 @@ static void __exit tee_exit(void)
> >       tee_class = NULL;
> >  }
> >
> > +void tee_register_efivar_ops(struct efivars *tee_efivars,
> > +                          struct efivar_operations *ops)
> > +{
> > +     /*
> > +      * If the firmware EFI runtime services support SetVariable(),
> > +      * tee-based EFI variable services are not used.
> > +      */
> > +     if (!efivar_supports_writes()) {
> > +             pr_info("Use tee-based EFI runtime variable services\n");
> > +             efivars_generic_ops_unregister();
> > +             efivars_register(tee_efivars, ops);
> > +     }
> > +}
> > +EXPORT_SYMBOL_GPL(tee_register_efivar_ops);
> > +
> > +void tee_unregister_efivar_ops(struct efivars *tee_efivars)
> > +{
> > +     efivars_unregister(tee_efivars);
> > +     efivars_generic_ops_register();
> > +}
> > +EXPORT_SYMBOL_GPL(tee_unregister_efivar_ops);
> > +
> >  subsys_initcall(tee_init);
> >  module_exit(tee_exit);
> >
> > diff --git a/include/linux/efi.h b/include/linux/efi.h
> > index 5e301c00e9b0..14d4aa83ce60 100644
> > --- a/include/linux/efi.h
> > +++ b/include/linux/efi.h
> > @@ -39,6 +39,7 @@
> >  #define EFI_WRITE_PROTECTED  ( 8 | (1UL << (BITS_PER_LONG-1)))
> >  #define EFI_OUT_OF_RESOURCES ( 9 | (1UL << (BITS_PER_LONG-1)))
> >  #define EFI_NOT_FOUND                (14 | (1UL << (BITS_PER_LONG-1)))
> > +#define EFI_ACCESS_DENIED    (15 | (1UL << (BITS_PER_LONG-1)))
> >  #define EFI_TIMEOUT          (18 | (1UL << (BITS_PER_LONG-1)))
> >  #define EFI_ABORTED          (21 | (1UL << (BITS_PER_LONG-1)))
> >  #define EFI_SECURITY_VIOLATION       (26 | (1UL << (BITS_PER_LONG-1)))
> > diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
> > index 17eb1c5205d3..def4ea6212ee 100644
> > --- a/include/linux/tee_drv.h
> > +++ b/include/linux/tee_drv.h
> > @@ -7,6 +7,7 @@
> >  #define __TEE_DRV_H
> >
> >  #include <linux/device.h>
> > +#include <linux/efi.h>
> >  #include <linux/idr.h>
> >  #include <linux/kref.h>
> >  #include <linux/list.h>
> > @@ -507,4 +508,26 @@ struct tee_context *teedev_open(struct tee_device *teedev);
> >   */
> >  void teedev_close_context(struct tee_context *ctx);
> >
> > +/**
> > + * tee_register_efivar_ops() - register the efivar ops
> > + * @tee_efivars:     pointer to efivars structure
> > + * @ops:             pointer to contain the efivar operation
> > + *
> > + * This function registers the tee-based efivar operation as an
> > + * EFI Runtime Service.
> > + *
> > + */
> > +void tee_register_efivar_ops(struct efivars *tee_efivars,
> > +                          struct efivar_operations *ops);
> > +
> > +/**
> > + * tee_unregister_efivar_ops() - unregister the efivar ops
> > + * @tee_efivars:     pointer to efivars structure
> > + *
> > + * This function unregisters the tee-based efivar operation
> > + * and reverts to the generic operation.
> > + *
> > + */
> > +void tee_unregister_efivar_ops(struct efivars *tee_efivars);
> > +
> >  #endif /*__TEE_DRV_H*/
> > --
> > 2.30.2
> >

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 2/2] tee: Add op-tee helper functions for variable access
@ 2023-02-06  6:08       ` Masahisa Kojima
  0 siblings, 0 replies; 21+ messages in thread
From: Masahisa Kojima @ 2023-02-06  6:08 UTC (permalink / raw)
  To: Jens Wiklander
  Cc: Ard Biesheuvel, linux-kernel, op-tee, Ilias Apalodimas,
	Johan Hovold, Sumit Garg, Maxime Coquelin, Alexandre Torgue,
	linux-stm32, linux-arm-kernel, linux-efi

() Hi Jens,

On Fri, 3 Feb 2023 at 18:30, Jens Wiklander <jens.wiklander@linaro.org> wrote:
>
> On Thu, Jan 26, 2023 at 10:21:19PM +0900, Masahisa Kojima wrote:
> > When the flash is not owned by the non-secure world, accessing the EFI
> > variables is straightforward and done via EFI Runtime Variable Services.
> > In this case, critical variables for system integrity and security
> > are normally stored in the dedicated secure storage and only accessible
> > from the secure world.
> >
> > On the other hand, the small embedded devices don't have the special
> > dedicated secure storage. The eMMC device with an RPMB partition is
> > becoming more common, we can use an RPMB partition to store the
> > EFI Variables.
> >
> > The eMMC device is typically owned by the non-secure world(linux in
> > this case). There is an existing solution utilizing eMMC RPMB partition
> > for EFI Variables, it is implemented by interacting with
> > OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
> > and tee-supplicant. The last piece is the tee-based variable access
> > driver to interact with OP-TEE and StandaloneMM.
> >
> > So let's add the kernel functions needed.
> >
> > This feature is implemented as a kernel module.
> > StMM PTA has TA_FLAG_DEVICE_ENUM_SUPP flag when registered to OP-TEE
> > so that this optee_stmm_efi module is probed after tee-supplicant starts,
> > since "SetVariable" EFI Runtime Variable Service requires to
> > interact with tee-supplicant.
> >
> > Co-developed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> > Signed-off-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
> > Signed-off-by: Masahisa Kojima <masahisa.kojima@linaro.org>
> > ---
> >  drivers/tee/optee/Kconfig            |  10 +
> >  drivers/tee/optee/Makefile           |   1 +
> >  drivers/tee/optee/mm_communication.h | 249 +++++++++++
> >  drivers/tee/optee/optee_private.h    |   5 +-
> >  drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
> >  drivers/tee/tee_core.c               |  23 ++
> >  include/linux/efi.h                  |   1 +
> >  include/linux/tee_drv.h              |  23 ++
> >  8 files changed, 909 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/tee/optee/mm_communication.h
> >  create mode 100644 drivers/tee/optee/optee_stmm_efi.c
>
>
> Please split this patch into three patches, one for drivers/tee, one
> for the driver/tee/optee, and one for include/linux/efi.h.

OK.

>
> >
> > diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig
> > index f121c224e682..a0b699977e51 100644
> > --- a/drivers/tee/optee/Kconfig
> > +++ b/drivers/tee/optee/Kconfig
> > @@ -7,3 +7,13 @@ config OPTEE
> >       help
> >         This implements the OP-TEE Trusted Execution Environment (TEE)
> >         driver.
> > +
> > +config EFI_STMM_OPTEE
>
> Should this be OPTEE_EFI_STMM?

OK.

>
> > +     tristate "OP-TEE based EFI runtime variable service driver"
> > +     depends on OPTEE
> > +     help
> > +       This  driver provides support for OP-TEE based EFI runtime
> > +       variable service driver.
> > +
> > +       To compile this driver as a module, choose M here: the module
> > +       will be called optee_stmm_efi.
> > diff --git a/drivers/tee/optee/Makefile b/drivers/tee/optee/Makefile
> > index a6eff388d300..9acb0b2de8fe 100644
> > --- a/drivers/tee/optee/Makefile
> > +++ b/drivers/tee/optee/Makefile
> > @@ -8,6 +8,7 @@ optee-objs += supp.o
> >  optee-objs += device.o
> >  optee-objs += smc_abi.o
> >  optee-objs += ffa_abi.o
> > +obj-$(CONFIG_EFI_STMM_OPTEE) += optee_stmm_efi.o
> >
> >  # for tracing framework to find optee_trace.h
> >  CFLAGS_smc_abi.o := -I$(src)
> > diff --git a/drivers/tee/optee/mm_communication.h b/drivers/tee/optee/mm_communication.h
> > new file mode 100644
> > index 000000000000..23e6672991ef
> > --- /dev/null
> > +++ b/drivers/tee/optee/mm_communication.h
> > @@ -0,0 +1,249 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/*
> > + *  Headers for EFI variable service via StandAloneMM, EDK2 application running
> > + *  in OP-TEE
> > + *
> > + *  Copyright (c) 2017, Intel Corporation. All rights reserved.
> > + *  Copyright (C) 2020 Linaro Ltd.
> > + */
> > +
> > +#ifndef _MM_COMMUNICATION_H_
> > +#define _MM_COMMUNICATION_H_
> > +
> > +/*
> > + * Interface to the pseudo Trusted Application (TA), which provides a
> > + * communication channel with the Standalone MM (Management Mode)
> > + * Secure Partition running at Secure-EL0
> > + */
> > +
> > +#define PTA_STMM_CMDID_COMMUNICATE 0
>
> PTA_STMM_CMD_COMMUNICATE

OK.

>
> > +
> > +/* OP-TEE is using big endian GUIDs while UEFI uses little endian ones */
> > +#define PTA_STMM_UUID \
> > +     UUID_INIT(0xed32d533, 0x99e6, 0x4209, \
> > +               0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
> > +
> > +#define EFI_MM_VARIABLE_GUID \
> > +     EFI_GUID(0xed32d533, 0x99e6, 0x4209, \
> > +              0x9c, 0xc0, 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7)
> > +
> > +/* Defined in EDK2 MdePkg/Include/Protocol/MmCommunication.h */
> > +
> > +/**
> > + * struct efi_mm_communicate_header - Header used for SMM variable communication
> > +
> > + * @header_guid:  header use for disambiguation of content
> > + * @message_len:  length of the message. Does not include the size of the
> > + *                header
> > + * @data:         payload of the message
> > + *
> > + * Defined in EDK2 as EFI_MM_COMMUNICATE_HEADER.
> > + * To avoid confusion in interpreting frames, the communication buffer should
> > + * always begin with efi_mm_communicate_header.
> > + */
> > +struct efi_mm_communicate_header {
> > +     efi_guid_t header_guid;
> > +     size_t     message_len;
> > +     u8         data[];
> > +} __packed;
> > +
> > +#define MM_COMMUNICATE_HEADER_SIZE \
> > +     (sizeof(struct efi_mm_communicate_header))
> > +
> > +/* Defined in EDK2 ArmPkg/Include/IndustryStandard/ArmMmSvc.h */
> > +
> > +/* SPM return error codes */
> > +#define ARM_SVC_SPM_RET_SUCCESS               0
> > +#define ARM_SVC_SPM_RET_NOT_SUPPORTED        -1
> > +#define ARM_SVC_SPM_RET_INVALID_PARAMS       -2
> > +#define ARM_SVC_SPM_RET_DENIED               -3
> > +#define ARM_SVC_SPM_RET_NO_MEMORY            -5
> > +
> > +/* Defined in EDK2 MdeModulePkg/Include/Guid/SmmVariableCommon.h */
> > +
> > +#define SMM_VARIABLE_FUNCTION_GET_VARIABLE  1
> > +/*
> > + * The payload for this function is
> > + * SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME.
> > + */
> > +#define SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME  2
> > +/*
> > + * The payload for this function is SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
> > + */
> > +#define SMM_VARIABLE_FUNCTION_SET_VARIABLE  3
> > +/*
> > + * The payload for this function is
> > + * SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO.
> > + */
> > +#define SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO  4
> > +/*
> > + * It is a notify event, no extra payload for this function.
> > + */
> > +#define SMM_VARIABLE_FUNCTION_READY_TO_BOOT  5
> > +/*
> > + * It is a notify event, no extra payload for this function.
> > + */
> > +#define SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE  6
> > +/*
> > + * The payload for this function is VARIABLE_INFO_ENTRY.
> > + * The GUID in EFI_SMM_COMMUNICATE_HEADER is gEfiSmmVariableProtocolGuid.
> > + */
> > +#define SMM_VARIABLE_FUNCTION_GET_STATISTICS  7
> > +/*
> > + * The payload for this function is SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
> > + */
> > +#define SMM_VARIABLE_FUNCTION_LOCK_VARIABLE   8
> > +
> > +#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET  9
> > +
> > +#define SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET  10
> > +
> > +#define SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE  11
> > +/*
> > + * The payload for this function is
> > + * SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT
> > + */
> > +#define SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT 12
> > +
> > +#define SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE  13
> > +/*
> > + * The payload for this function is
> > + * SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO
> > + */
> > +#define SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO  14
> > +
> > +/**
> > + * struct smm_variable_communicate_header - Used for SMM variable communication
> > +
> > + * @function:     function to call in Smm.
> > + * @ret_status:   return status
> > + * @data:         payload
> > + *
> > + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_HEADER.
> > + */
> > +struct smm_variable_communicate_header {
> > +     size_t  function;
> > +     efi_status_t ret_status;
> > +     u8 data[];
> > +};
> > +
> > +#define MM_VARIABLE_COMMUNICATE_SIZE \
> > +     (sizeof(struct smm_variable_communicate_header))
> > +
> > +/**
> > + * struct smm_variable_access - Used to communicate with StMM by
> > + *                              SetVariable and GetVariable.
> > +
> > + * @guid:         vendor GUID
> > + * @data_size:    size of EFI variable data
> > + * @name_size:    size of EFI name
> > + * @attr:         attributes
> > + * @name:         variable name
> > + *
> > + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
> > + *
> > + */
> > +struct smm_variable_access {
> > +     efi_guid_t  guid;
> > +     size_t data_size;
> > +     size_t name_size;
> > +     u32 attr;
> > +     u16 name[];
> > +};
> > +
> > +#define MM_VARIABLE_ACCESS_HEADER_SIZE \
> > +     (sizeof(struct smm_variable_access))
> > +/**
> > + * struct smm_variable_payload_size - Used to get the max allowed
> > + *                                    payload used in StMM.
> > + *
> > + * @size:  size to fill in
> > + *
> > + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE.
> > + *
> > + */
> > +struct smm_variable_payload_size {
> > +     size_t size;
> > +};
> > +
> > +/**
> > + * struct smm_variable_getnext - Used to communicate with StMM for
> > + *                               GetNextVariableName.
> > + *
> > + * @guid:       vendor GUID
> > + * @name_size:  size of the name of the variable
> > + * @name:       variable name
> > + *
> > + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME.
> > + */
> > +struct smm_variable_getnext {
> > +     efi_guid_t  guid;
> > +     size_t name_size;
> > +     u16         name[];
> > +};
> > +
> > +#define MM_VARIABLE_GET_NEXT_HEADER_SIZE \
> > +     (sizeof(struct smm_variable_getnext))
> > +
> > +/**
> > + * struct smm_variable_query_info - Used to communicate with StMM for
> > + *                                  QueryVariableInfo.
> > + *
> > + * @max_variable_storage:        max available storage
> > + * @remaining_variable_storage:  remaining available storage
> > + * @max_variable_size:           max variable supported size
> > + * @attr:                        attributes to query storage for
> > + *
> > + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO.
> > + */
> > +struct smm_variable_query_info {
> > +     u64 max_variable_storage;
> > +     u64 remaining_variable_storage;
> > +     u64 max_variable_size;
> > +     u32 attr;
> > +};
> > +
> > +#define VAR_CHECK_VARIABLE_PROPERTY_REVISION 0x0001
> > +#define VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY BIT(0)
> > +/**
> > + * struct var_check_property - Used to store variable properties in StMM
> > + *
> > + * @revision:   magic revision number for variable property checking
> > + * @property:   properties mask for the variable used in StMM.
> > + *              Currently RO flag is supported
> > + * @attributes: variable attributes used in StMM checking when properties
> > + *              for a variable are enabled
> > + * @minsize:    minimum allowed size for variable payload checked against
> > + *              smm_variable_access->datasize in StMM
> > + * @maxsize:    maximum allowed size for variable payload checked against
> > + *              smm_variable_access->datasize in StMM
> > + *
> > + * Defined in EDK2 as VAR_CHECK_VARIABLE_PROPERTY.
> > + */
> > +struct var_check_property {
> > +     u16 revision;
> > +     u16 property;
> > +     u32 attributes;
> > +     size_t minsize;
> > +     size_t maxsize;
> > +};
> > +
> > +/**
> > + * struct smm_variable_var_check_property - Used to communicate variable
> > + *                                          properties with StMM
> > + *
> > + * @guid:       vendor GUID
> > + * @name_size:  size of EFI name
> > + * @property:   variable properties struct
> > + * @name:       variable name
> > + *
> > + * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY.
> > + */
> > +struct smm_variable_var_check_property {
> > +     efi_guid_t guid;
> > +     size_t name_size;
> > +     struct var_check_property property;
> > +     u16 name[];
> > +};
> > +
> > +#endif /* _MM_COMMUNICATION_H_ */
> > diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> > index 04ae58892608..10af910e0dce 100644
> > --- a/drivers/tee/optee/optee_private.h
> > +++ b/drivers/tee/optee/optee_private.h
> > @@ -12,6 +12,7 @@
> >  #include <linux/tee_drv.h>
> >  #include <linux/types.h>
> >  #include "optee_msg.h"
> > +#include "optee_private.h"
>
> This is "optee_private.h", no need to include it.

I added this line by mistake, remove this line.

>
> >
> >  #define DRIVER_NAME "optee"
> >
> > @@ -19,6 +20,7 @@
> >
> >  /* Some Global Platform error codes used in this driver */
> >  #define TEEC_SUCCESS                 0x00000000
> > +#define TEEC_ERROR_EXCESS_DATA               0xFFFF0004
> >  #define TEEC_ERROR_BAD_PARAMETERS    0xFFFF0006
> >  #define TEEC_ERROR_NOT_SUPPORTED     0xFFFF000A
> >  #define TEEC_ERROR_COMMUNICATION     0xFFFF000E
> > @@ -250,7 +252,6 @@ void optee_pool_op_free_helper(struct tee_shm_pool *pool, struct tee_shm *shm,
> >                              int (*shm_unregister)(struct tee_context *ctx,
> >                                                    struct tee_shm *shm));
> >
> > -
> >  void optee_remove_common(struct optee *optee);
> >  int optee_open(struct tee_context *ctx, bool cap_memref_null);
> >  void optee_release(struct tee_context *ctx);
> > @@ -322,4 +323,6 @@ void optee_smc_abi_unregister(void);
> >  int optee_ffa_abi_register(void);
> >  void optee_ffa_abi_unregister(void);
> >
> > +int optee_efivar_ops_init(struct tee_context *ctx);
>
> This isn't used or implemented as far as I can tell.

This is the previous version of code, I will remove it.

>
> > +
> >  #endif /*OPTEE_PRIVATE_H*/
> > diff --git a/drivers/tee/optee/optee_stmm_efi.c b/drivers/tee/optee/optee_stmm_efi.c
> > new file mode 100644
> > index 000000000000..6dcf1eb4b96c
> > --- /dev/null
> > +++ b/drivers/tee/optee/optee_stmm_efi.c
> > @@ -0,0 +1,598 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + *  EFI variable service via OP-TEE
> > + *
> > + *  Copyright (C) 2022 Linaro
> > + */
> > +
> > +#include <linux/efi.h>
> > +#include <linux/kernel.h>
> > +#include <linux/slab.h>
> > +#include <linux/tee.h>
> > +#include <linux/tee_drv.h>
> > +#include <linux/ucs2_string.h>
> > +#include "mm_communication.h"
> > +#include "optee_private.h"
> > +
> > +static struct efivars optee_efivars;
> > +static struct efivar_operations optee_ops;
>
> optee_efi_ops instead? To avoid being confused with the other optee_ops.
>
> > +
> > +static size_t max_buffer_size; /* comm + var + func + data */
> > +static size_t max_payload_size; /* func + data */
> > +
> > +struct mm_connection {
> > +     struct tee_context *ctx;
> > +     u32 session;
> > +};
> > +
> > +/* UUID of the stmm PTA */
> > +static const struct tee_client_device_id optee_stmm_efi_id_table[] = {
> > +     {PTA_STMM_UUID},
> > +     {}
> > +};
> > +
> > +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
> > +{
> > +     if (ver->impl_id == TEE_IMPL_ID_OPTEE)
> > +             return 1;
> > +     else
> > +             return 0;
> > +}
> > +
> > +/**
> > + * get_connection() - Retrieve OP-TEE session for a specific UUID.
> > + *
> > + * @conn:   session buffer to fill
> > + * Return:  status code
> > + */
> > +static int get_connection(struct mm_connection *conn)
> > +{
> > +     struct tee_context *ctx = NULL;
> > +     struct tee_ioctl_open_session_arg arg;
> > +     int rc;
> > +
> > +     memset(&arg, 0, sizeof(arg));
> > +
> > +     /* Open context with OP-TEE driver */
> > +     ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
>
> tee_client_close_context() is missing in the error paths.

Yes, I will add tee_client_close_context in the error paths.

>
> > +     if (IS_ERR(ctx))
> > +             return -ENODEV;
> > +
> > +     export_uuid(arg.uuid, &optee_stmm_efi_id_table[0].uuid);
> > +     rc = tee_client_open_session(ctx, &arg, NULL);
> > +     if (!rc) {
> > +             conn->ctx = ctx;
> > +             conn->session = arg.session;
> > +     }
> > +
> > +     return rc;
> > +}
> > +
> > +/**
> > + * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE
> > + *
> > + * @comm_buf:                locally allocated communcation buffer
> > + * @dsize:           buffer size
> > + * Return:           status code
> > + */
> > +static efi_status_t optee_mm_communicate(void *comm_buf, size_t dsize)
> > +{
> > +     size_t buf_size;
> > +     efi_status_t ret;
> > +     struct efi_mm_communicate_header *mm_hdr;
> > +     struct mm_connection conn = { NULL, 0 };
> > +     struct tee_ioctl_invoke_arg arg;
> > +     struct tee_param param[4];
> > +     struct tee_shm *shm = NULL;
> > +     int rc;
> > +
> > +     if (!comm_buf)
> > +             return EFI_INVALID_PARAMETER;
> > +
> > +     mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
>
> No need to cast.

OK.

>
> > +     buf_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t);
> > +
> > +     if (dsize != buf_size)
> > +             return EFI_INVALID_PARAMETER;
> > +
> > +     rc = get_connection(&conn);
> > +     if (rc) {
> > +             pr_err("Unable to open OP-TEE session (err=%d)\n", rc);
>
> Perhaps better to print from get_connection() to be able to distinguish
> between errors from tee_client_open_context() and
> tee_client_open_session().

OK, I will update.

>
> > +             return EFI_UNSUPPORTED;
> > +     }
> > +
> > +     shm = tee_shm_register_kernel_buf(conn.ctx, comm_buf, buf_size);
> > +     if (IS_ERR(shm)) {
> > +             pr_err("Unable to register shared memory\n");
> > +             tee_client_close_session(conn.ctx, conn.session);
> > +             return EFI_UNSUPPORTED;
> > +     }
> > +
> > +     memset(&arg, 0, sizeof(arg));
> > +     arg.func = PTA_STMM_CMDID_COMMUNICATE;
> > +     arg.session = conn.session;
> > +     arg.num_params = 4;
> > +
> > +     memset(param, 0, sizeof(param));
> > +     param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
> > +     param[0].u.memref.size = buf_size;
> > +     param[0].u.memref.shm = shm;
> > +     param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
> > +     param[2].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
> > +     param[3].attr = TEE_IOCTL_PARAM_ATTR_TYPE_NONE;
> > +
> > +     rc = tee_client_invoke_func(conn.ctx, &arg, param);
> > +     tee_shm_free(shm);
> > +     /* even if close session fails the session will be invalidaded */
> > +     tee_client_close_session(conn.ctx, conn.session);
> > +     if (rc)
> > +             return EFI_DEVICE_ERROR;
> > +     if (arg.ret == TEEC_ERROR_EXCESS_DATA)
> > +             pr_err("Variable payload too large\n");
> > +     if (arg.ret != TEEC_SUCCESS)
> > +             return EFI_DEVICE_ERROR;
> > +
> > +     switch (param[1].u.value.a) {
> > +     case ARM_SVC_SPM_RET_SUCCESS:
> > +             ret = EFI_SUCCESS;
> > +             break;
> > +
> > +     case ARM_SVC_SPM_RET_INVALID_PARAMS:
> > +             ret = EFI_INVALID_PARAMETER;
> > +             break;
> > +
> > +     case ARM_SVC_SPM_RET_DENIED:
> > +             ret = EFI_ACCESS_DENIED;
> > +             break;
> > +
> > +     case ARM_SVC_SPM_RET_NO_MEMORY:
> > +             ret = EFI_OUT_OF_RESOURCES;
> > +             break;
> > +
> > +     default:
> > +             ret = EFI_ACCESS_DENIED;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +/**
> > + * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
> > + * it to OP-TEE
> > + *
> > + * @comm_buf:                locally allocated communcation buffer
> > + * @dsize:           buffer size
> > + * Return:           status code
> > + */
> > +static efi_status_t mm_communicate(u8 *comm_buf, size_t dsize)
> > +{
> > +     efi_status_t ret;
> > +     struct efi_mm_communicate_header *mm_hdr;
> > +     struct smm_variable_communicate_header *var_hdr;
> > +
> > +     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;
> > +
> > +     ret = optee_mm_communicate(comm_buf, dsize);
> > +     if (ret != EFI_SUCCESS) {
> > +             pr_err("%s failed!\n", __func__);
> > +             return ret;
> > +     }
> > +
> > +     return var_hdr->ret_status;
> > +}
> > +
> > +/**
> > + * setup_mm_hdr() -  Allocate a buffer for StandAloneMM and initialize the
> > + *                   header data.
> > + *
> > + * @dptr:            pointer address of the corresponding StandAloneMM
> > + *                   function
> > + * @payload_size:    buffer size
> > + * @func:            standAloneMM function number
> > + * @ret:             EFI return code
> > + * Return:           buffer or NULL
> > + */
> > +static u8 *setup_mm_hdr(void **dptr, size_t payload_size, size_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;
> > +
> > +     /* In the init function we initialize max_buffer_size with
> > +      * get_max_payload(). So skip the test if max_buffer_size is initialized
> > +      * StandAloneMM will perform similar checks and drop the buffer if it's
> > +      * too long
> > +      */
> > +     if (max_buffer_size &&
> > +         max_buffer_size < (MM_COMMUNICATE_HEADER_SIZE +
> > +                            MM_VARIABLE_COMMUNICATE_SIZE + payload_size)) {
> > +             *ret = EFI_INVALID_PARAMETER;
> > +             return NULL;
> > +     }
> > +
> > +     comm_buf = kzalloc(MM_COMMUNICATE_HEADER_SIZE +
> > +                                MM_VARIABLE_COMMUNICATE_SIZE + payload_size,
> > +                        GFP_KERNEL);
> > +     if (!comm_buf) {
> > +             *ret = EFI_OUT_OF_RESOURCES;
> > +             return NULL;
> > +     }
> > +
> > +     mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
> > +     memcpy(&mm_hdr->header_guid, &mm_var_guid, 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;
> > +     var_hdr->function = func;
> > +     if (dptr)
> > +             *dptr = var_hdr->data;
> > +     *ret = EFI_SUCCESS;
> > +
> > +     return comm_buf;
> > +}
> > +
> > +/**
> > + * get_max_payload() - Get variable payload size from StandAloneMM.
> > + *
> > + * @size:    size of the variable in storage
> > + * Return:   status code
> > + */
> > +static efi_status_t get_max_payload(size_t *size)
> > +{
> > +     struct smm_variable_payload_size *var_payload = NULL;
> > +     size_t payload_size;
> > +     u8 *comm_buf = NULL;
> > +     efi_status_t ret;
> > +
> > +     if (!size) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     payload_size = sizeof(*var_payload);
> > +     comm_buf = setup_mm_hdr((void **)&var_payload, payload_size,
> > +                             SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE, &ret);
> > +     if (!comm_buf)
> > +             goto out;
> > +
> > +     ret = mm_communicate(comm_buf, payload_size);
> > +     if (ret != EFI_SUCCESS)
> > +             goto out;
> > +
> > +     /* Make sure the buffer is big enough for storing variables */
> > +     if (var_payload->size < MM_VARIABLE_ACCESS_HEADER_SIZE + 0x20) {
> > +             ret = EFI_DEVICE_ERROR;
> > +             goto out;
> > +     }
> > +     *size = var_payload->size;
> > +     /*
> > +      * There seems to be a bug in EDK2 miscalculating the boundaries and
> > +      * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
> > +      * it up here to ensure backwards compatibility with older versions
> > +      * (cf. StandaloneMmPkg/Drivers/StandaloneMmCpu/AArch64/EventHandle.c.
> > +      * sizeof (EFI_MM_COMMUNICATE_HEADER) instead the size minus the
> > +      * flexible array member).
> > +      *
> > +      * size is guaranteed to be > 2 due to checks on the beginning.
> > +      */
> > +     *size -= 2;
> > +out:
> > +     kfree(comm_buf);
> > +     return ret;
> > +}
> > +
> > +static efi_status_t get_property_int(u16 *name, size_t name_size,
> > +                                  const efi_guid_t *vendor,
> > +                                  struct var_check_property *var_property)
> > +{
> > +     struct smm_variable_var_check_property *smm_property;
> > +     size_t payload_size;
> > +     u8 *comm_buf = NULL;
> > +     efi_status_t ret;
> > +
> > +     memset(var_property, 0, sizeof(*var_property));
> > +     payload_size = sizeof(*smm_property) + name_size;
> > +     if (payload_size > max_payload_size) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +     comm_buf = setup_mm_hdr(
> > +             (void **)&smm_property, payload_size,
> > +             SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET, &ret);
> > +     if (!comm_buf)
> > +             goto out;
> > +
> > +     memcpy(&smm_property->guid, vendor, sizeof(smm_property->guid));
> > +     smm_property->name_size = name_size;
> > +     memcpy(smm_property->name, name, name_size);
> > +
> > +     ret = mm_communicate(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)
> > +             goto out;
> > +     memcpy(var_property, &smm_property->property, sizeof(*var_property));
> > +
> > +out:
> > +     kfree(comm_buf);
> > +     return ret;
> > +}
> > +
> > +static efi_status_t optee_get_variable(u16 *name, efi_guid_t *vendor,
> > +                                    u32 *attributes,
> > +                                    unsigned long *data_size, void *data)
> > +{
> > +     struct var_check_property var_property;
> > +     struct smm_variable_access *var_acc;
> > +     size_t payload_size;
> > +     size_t name_size;
> > +     size_t tmp_dsize;
> > +     u8 *comm_buf = NULL;
> > +     efi_status_t ret;
> > +
> > +     if (!name || !vendor || !data_size) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
> > +     if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     /* 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)
> > +             goto out;
> > +
> > +     /* Fill in contents */
> > +     memcpy(&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;
> > +     memcpy(var_acc->name, name, name_size);
> > +
> > +     /* Communicate */
> > +     ret = mm_communicate(comm_buf, payload_size);
> > +     if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL)
> > +             /* Update with reported data size for trimmed case */
> > +             *data_size = var_acc->data_size;
> > +     if (ret != EFI_SUCCESS)
> > +             goto out;
> > +
> > +     ret = get_property_int(name, name_size, vendor, &var_property);
> > +     if (ret != EFI_SUCCESS)
> > +             goto out;
> > +
> > +     if (attributes)
> > +             *attributes = var_acc->attr;
> > +
> > +     if (data)
> > +             memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
> > +                    var_acc->data_size);
> > +     else
> > +             ret = EFI_INVALID_PARAMETER;
> > +
> > +out:
> > +     kfree(comm_buf);
> > +     return ret;
> > +}
> > +
> > +static efi_status_t optee_get_next_variable(unsigned long *name_size,
> > +                                         efi_char16_t *name,
> > +                                         efi_guid_t *guid)
> > +{
> > +     struct smm_variable_getnext *var_getnext;
> > +     size_t payload_size;
> > +     size_t out_name_size;
> > +     size_t in_name_size;
> > +     u8 *comm_buf = NULL;
> > +     efi_status_t ret;
> > +
> > +     if (!name_size || !name || !guid) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     out_name_size = *name_size;
> > +     in_name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
> > +
> > +     if (out_name_size < in_name_size) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     if (in_name_size >
> > +         max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     /* 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)
> > +             goto out;
> > +
> > +     /* Fill in contents */
> > +     memcpy(&var_getnext->guid, guid, sizeof(var_getnext->guid));
> > +     var_getnext->name_size = out_name_size;
> > +     memcpy(var_getnext->name, name, in_name_size);
> > +     memset((u8 *)var_getnext->name + in_name_size, 0x0,
> > +            out_name_size - in_name_size);
> > +
> > +     /* Communicate */
> > +     ret = mm_communicate(comm_buf, payload_size);
> > +     if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
> > +             /* Update with reported data size for trimmed case */
> > +             *name_size = var_getnext->name_size;
> > +     }
> > +     if (ret != EFI_SUCCESS)
> > +             goto out;
> > +
> > +     memcpy(guid, &var_getnext->guid, sizeof(*guid));
> > +     memcpy(name, var_getnext->name, var_getnext->name_size);
> > +
> > +out:
> > +     kfree(comm_buf);
> > +     return ret;
> > +}
> > +
> > +static efi_status_t optee_set_variable(efi_char16_t *name, efi_guid_t *vendor,
> > +                                    u32 attributes, unsigned long data_size,
> > +                                    void *data)
> > +{
> > +     efi_status_t ret;
> > +     struct var_check_property var_property;
> > +     struct smm_variable_access *var_acc;
> > +     size_t payload_size;
> > +     size_t name_size;
> > +     u8 *comm_buf = NULL;
> > +
> > +     if (!name || name[0] == 0 || !vendor) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +     if (data_size > 0 && !data) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +     /* Check payload size */
> > +     name_size = (ucs2_strnlen(name, EFI_VAR_NAME_LEN) + 1) * sizeof(u16);
> > +     payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
> > +     if (payload_size > max_payload_size) {
> > +             ret = EFI_INVALID_PARAMETER;
> > +             goto out;
> > +     }
> > +
> > +     /*
> > +      * 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)
> > +             goto out;
> > +
> > +     /*
> > +      * The API has the ability to override RO flags. If no RO check was
> > +      * requested switch the variable to RW for the duration of this call
> > +      */
> > +     ret = get_property_int(name, name_size, vendor, &var_property);
> > +     if (ret != EFI_SUCCESS) {
> > +             pr_err("Getting variable property failed\n");
> > +             goto out;
> > +     }
> > +
> > +     if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) {
> > +             ret = EFI_WRITE_PROTECTED;
> > +             goto out;
> > +     }
> > +
> > +     /* Fill in contents */
> > +     memcpy(&var_acc->guid, vendor, sizeof(var_acc->guid));
> > +     var_acc->data_size = data_size;
> > +     var_acc->name_size = name_size;
> > +     var_acc->attr = attributes;
> > +     memcpy(var_acc->name, name, name_size);
> > +     memcpy((u8 *)var_acc->name + name_size, data, data_size);
> > +
> > +
> > +     /* Communicate */
> > +     ret = mm_communicate(comm_buf, payload_size);
> > +     pr_info("Set Variable %s %d %lx\n", __FILE__, __LINE__, ret);
> > +out:
> > +     kfree(comm_buf);
> > +     return ret;
> > +}
> > +
> > +static int optee_stmm_efi_probe(struct device *dev)
> > +{
> > +     efi_status_t ret;
> > +
> > +     ret = get_max_payload(&max_payload_size);
> > +     if (ret != EFI_SUCCESS)
> > +             return ret;
> > +
> > +     max_buffer_size = MM_COMMUNICATE_HEADER_SIZE +
> > +                       MM_VARIABLE_COMMUNICATE_SIZE +
> > +                       max_payload_size;
> > +
> > +     optee_ops.get_variable = optee_get_variable;
> > +     optee_ops.get_next_variable = optee_get_next_variable;
> > +     optee_ops.set_variable = optee_set_variable;
> > +     /* TODO: support nonblocking variant */
> > +     optee_ops.set_variable_nonblocking = NULL;
> > +     /* set NULL, always return EFI_SUCCESS by efi_query_variable_store() */
> > +     optee_ops.query_variable_store = NULL;
>
> Why aren't you using static initialization instead?

On second thought, I think we can implement QueryVariableInfo(),
so it is better to set StMM based QueryVariableInfo() rather than set
efi_query_variable_store().

Thank you for your review.

Regards,
Masahisa Kojima



>
> Thanks,
> Jens
>
> > +
> > +     tee_register_efivar_ops(&optee_efivars, &optee_ops);
> > +
> > +     return 0;
> > +}
> > +
> > +static int optee_stmm_efi_remove(struct device *dev)
> > +{
> > +     tee_unregister_efivar_ops(&optee_efivars);
> > +
> > +     return 0;
> > +}
> > +
> > +MODULE_DEVICE_TABLE(tee, optee_stmm_efi_id_table);
> > +
> > +static struct tee_client_driver optee_stmm_efi_driver = {
> > +     .id_table       = optee_stmm_efi_id_table,
> > +     .driver         = {
> > +             .name           = "optee-stmm-efi",
> > +             .bus            = &tee_bus_type,
> > +             .probe          = optee_stmm_efi_probe,
> > +             .remove         = optee_stmm_efi_remove,
> > +     },
> > +};
> > +
> > +static int __init optee_stmm_efi_mod_init(void)
> > +{
> > +     return driver_register(&optee_stmm_efi_driver.driver);
> > +}
> > +
> > +static void __exit optee_stmm_efi_mod_exit(void)
> > +{
> > +     driver_unregister(&optee_stmm_efi_driver.driver);
> > +}
> > +
> > +module_init(optee_stmm_efi_mod_init);
> > +module_exit(optee_stmm_efi_mod_exit);
> > +
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_AUTHOR("Ilias Apalodimas <ilias.apalodimas@linaro.org>");
> > +MODULE_AUTHOR("Masahisa Kojima <masahisa.kojima@linaro.org>");
> > +MODULE_DESCRIPTION("OP-TEE based EFI runtime variable service driver");
> > diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c
> > index 98da206cd761..ac46274844a3 100644
> > --- a/drivers/tee/tee_core.c
> > +++ b/drivers/tee/tee_core.c
> > @@ -7,6 +7,7 @@
> >
> >  #include <linux/cdev.h>
> >  #include <linux/cred.h>
> > +#include <linux/efi.h>
> >  #include <linux/fs.h>
> >  #include <linux/idr.h>
> >  #include <linux/module.h>
> > @@ -1263,6 +1264,28 @@ static void __exit tee_exit(void)
> >       tee_class = NULL;
> >  }
> >
> > +void tee_register_efivar_ops(struct efivars *tee_efivars,
> > +                          struct efivar_operations *ops)
> > +{
> > +     /*
> > +      * If the firmware EFI runtime services support SetVariable(),
> > +      * tee-based EFI variable services are not used.
> > +      */
> > +     if (!efivar_supports_writes()) {
> > +             pr_info("Use tee-based EFI runtime variable services\n");
> > +             efivars_generic_ops_unregister();
> > +             efivars_register(tee_efivars, ops);
> > +     }
> > +}
> > +EXPORT_SYMBOL_GPL(tee_register_efivar_ops);
> > +
> > +void tee_unregister_efivar_ops(struct efivars *tee_efivars)
> > +{
> > +     efivars_unregister(tee_efivars);
> > +     efivars_generic_ops_register();
> > +}
> > +EXPORT_SYMBOL_GPL(tee_unregister_efivar_ops);
> > +
> >  subsys_initcall(tee_init);
> >  module_exit(tee_exit);
> >
> > diff --git a/include/linux/efi.h b/include/linux/efi.h
> > index 5e301c00e9b0..14d4aa83ce60 100644
> > --- a/include/linux/efi.h
> > +++ b/include/linux/efi.h
> > @@ -39,6 +39,7 @@
> >  #define EFI_WRITE_PROTECTED  ( 8 | (1UL << (BITS_PER_LONG-1)))
> >  #define EFI_OUT_OF_RESOURCES ( 9 | (1UL << (BITS_PER_LONG-1)))
> >  #define EFI_NOT_FOUND                (14 | (1UL << (BITS_PER_LONG-1)))
> > +#define EFI_ACCESS_DENIED    (15 | (1UL << (BITS_PER_LONG-1)))
> >  #define EFI_TIMEOUT          (18 | (1UL << (BITS_PER_LONG-1)))
> >  #define EFI_ABORTED          (21 | (1UL << (BITS_PER_LONG-1)))
> >  #define EFI_SECURITY_VIOLATION       (26 | (1UL << (BITS_PER_LONG-1)))
> > diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
> > index 17eb1c5205d3..def4ea6212ee 100644
> > --- a/include/linux/tee_drv.h
> > +++ b/include/linux/tee_drv.h
> > @@ -7,6 +7,7 @@
> >  #define __TEE_DRV_H
> >
> >  #include <linux/device.h>
> > +#include <linux/efi.h>
> >  #include <linux/idr.h>
> >  #include <linux/kref.h>
> >  #include <linux/list.h>
> > @@ -507,4 +508,26 @@ struct tee_context *teedev_open(struct tee_device *teedev);
> >   */
> >  void teedev_close_context(struct tee_context *ctx);
> >
> > +/**
> > + * tee_register_efivar_ops() - register the efivar ops
> > + * @tee_efivars:     pointer to efivars structure
> > + * @ops:             pointer to contain the efivar operation
> > + *
> > + * This function registers the tee-based efivar operation as an
> > + * EFI Runtime Service.
> > + *
> > + */
> > +void tee_register_efivar_ops(struct efivars *tee_efivars,
> > +                          struct efivar_operations *ops);
> > +
> > +/**
> > + * tee_unregister_efivar_ops() - unregister the efivar ops
> > + * @tee_efivars:     pointer to efivars structure
> > + *
> > + * This function unregisters the tee-based efivar operation
> > + * and reverts to the generic operation.
> > + *
> > + */
> > +void tee_unregister_efivar_ops(struct efivars *tee_efivars);
> > +
> >  #endif /*__TEE_DRV_H*/
> > --
> > 2.30.2
> >

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service
  2023-02-03 10:55       ` Jens Wiklander
@ 2023-02-06  6:44         ` Sumit Garg
  2023-02-06  7:47           ` Jens Wiklander
                             ` (2 more replies)
  0 siblings, 3 replies; 21+ messages in thread
From: Sumit Garg @ 2023-02-06  6:44 UTC (permalink / raw)
  To: Jens Wiklander, Ard Biesheuvel
  Cc: Masahisa Kojima, linux-kernel, op-tee, Ilias Apalodimas, Johan Hovold

On Fri, 3 Feb 2023 at 16:25, Jens Wiklander <jens.wiklander@linaro.org> wrote:
>
> On Fri, Feb 03, 2023 at 03:03:34PM +0530, Sumit Garg wrote:
> > Hi Jens,
> >
> > On Fri, 3 Feb 2023 at 13:59, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> > >
> > > Hi Sumit,
> > >
> > > On Thu, Feb 02, 2023 at 05:35:49PM +0530, Sumit Garg wrote:
> > > > Hi Masahisa,
> > > >
> > > > On Thu, 26 Jan 2023 at 18:52, Masahisa Kojima
> > > > <masahisa.kojima@linaro.org> wrote:
> > > > >
> > > > > This RFC series introduces the op-tee based EFI Runtime Variable
> > > > > Service.
> > > > >
> > > > > The eMMC device is typically owned by the non-secure world(linux in
> > > > > this case). There is an existing solution utilizing eMMC RPMB partition
> > > > > for EFI Variables, it is implemented by interacting with
> > > > > OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
> > > > > and tee-supplicant. The last piece is the tee-based variable access
> > > > > driver to interact with OP-TEE and StandaloneMM.
> > > > >
> > > >
> > > > After an overall look at the APIs, following are some initial comments:
> > > > - Is there any reason to have the edk2 specific StandaloneMM stack in
> > > > Linux to communicate with OP-TEE pseudo TA?
> > > > - I think the OP-TEE pseudo TA should be able to expose a rather
> > > > generic invoke commands such as:
> > > >      TEE_EFI_GET_VARIABLE
> > > >      TEE_EFI_GET_NEXT_VARIABLE
> > > >      TEE_EFI_SET_VARIABLE
> > > >   So it should no longer be tied to StMM stack and other TEE
> > > > implementations can re-use the abstracted interface to communicate
> > > > with its corresponding secure storage TA.
> > >
> > > In the current setup we have the following layers in the kernel:
> > > 1. efivar_operations
> > > 2. MM
> > > 3. PTA_STMM
> > > 4. OP-TEE MSG
> > >
> > > and in the secure world:
> > > S1. internal to StMM
> > > S2. MM interface to StMM
> > > S3. PTA_STMM
> > > S4. OP-TEE MSG
> > >
> > > If I understand you correctly you'd like to see this instead:
> > > Kernel:
> > > 1. efivar_operations
> > > 2. PTA_EFIVAR
> > > 4. OP-TEE MSG
> > >
> > > Since we still have the MM interface with StMM we'd have this in the secure
> > > world:
> > > S1. internal to StMM
> > > S2. MM interface to StMM
> > > S3. PTA_EFIVAR
> > > S4. OP-TEE MSG
> > >
> > > At S3 we'd have to convert between EFIVAR and MM messages. The
> > > difference is that we're moving the EFIVAR <-> MM conversion from the
> > > non-secure world into the secure world. We're still using OP-TEE
> > > specific communication at the fourth layer. So we're only moving problem
> > > around, I'd rather avoid growing the OP-TEE part in the secure world.
> > >
> >
> > If you look carefully, we are essentially defining an ABI towards the
> > secure world. The approach in this patch-set adds the MM interface as
> > a redundant ABI layer which makes it complex to maintain. Now think
> > about if every TEE implementation would propose such a complex ABI. It
> > looks like a maintenance nightmare to me.
> >
> > The concerns you are highlighting about OP-TEE size, I think those are
> > implementation details which can be simplified later but once you have
> > defined an ABI then you are stuck with its maintainability.
>
> You have a point, but keep in mind that it's StMM that matters here.
> StMM uses the MM protocol. It was originially using raw SMCs as a
> conduit, but with the need for OP-TEE accessing RPMB that's not usable.
> So instead we use OP-TEE MSG as a conduit. Seen from that perspective
> we're only resuing something established instead of inventing something
> new.

Aren't we already adding PTA_STMM?

Isn't the StMM specific to Arm as you already mentioned it was
designed to specifically use raw SMCs? So if in future AMD TEE wants
to implement EFI services, can we suggest they reuse the MM interface?

I am not sure why we need to redirect EFI variables via MM interface
communication buffers rather than directly using the TEE shared memory
approach.

Ard,

Since you have better insights into how EFI runtime services have to
be implemented, can you share your opinion here? It may be something I
am missing here.

-Sumit

>
> Cheers,
> Jens
>
> >
> > -Sumit
> >
> > > Cheers,
> > > Jens
> > >
> > > >
> > > > -Sumit
> > > >
> > > > > Masahisa Kojima (2):
> > > > >   efi: expose efivar generic ops register function
> > > > >   tee: Add op-tee helper functions for variable access
> > > > >
> > > > >  drivers/firmware/efi/efi.c           |  12 +
> > > > >  drivers/tee/optee/Kconfig            |  10 +
> > > > >  drivers/tee/optee/Makefile           |   1 +
> > > > >  drivers/tee/optee/mm_communication.h | 249 +++++++++++
> > > > >  drivers/tee/optee/optee_private.h    |   5 +-
> > > > >  drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
> > > > >  drivers/tee/tee_core.c               |  23 ++
> > > > >  include/linux/efi.h                  |   4 +
> > > > >  include/linux/tee_drv.h              |  23 ++
> > > > >  9 files changed, 924 insertions(+), 1 deletion(-)
> > > > >  create mode 100644 drivers/tee/optee/mm_communication.h
> > > > >  create mode 100644 drivers/tee/optee/optee_stmm_efi.c
> > > > >
> > > > > --
> > > > > 2.30.2
> > > > >

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service
  2023-02-06  6:44         ` Sumit Garg
@ 2023-02-06  7:47           ` Jens Wiklander
  2023-02-06  9:22           ` Ard Biesheuvel
  2023-02-06  9:31           ` Ilias Apalodimas
  2 siblings, 0 replies; 21+ messages in thread
From: Jens Wiklander @ 2023-02-06  7:47 UTC (permalink / raw)
  To: Sumit Garg
  Cc: Ard Biesheuvel, Masahisa Kojima, linux-kernel, op-tee,
	Ilias Apalodimas, Johan Hovold

On Mon, Feb 6, 2023 at 7:44 AM Sumit Garg <sumit.garg@linaro.org> wrote:
>
> On Fri, 3 Feb 2023 at 16:25, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> >
> > On Fri, Feb 03, 2023 at 03:03:34PM +0530, Sumit Garg wrote:
> > > Hi Jens,
> > >
> > > On Fri, 3 Feb 2023 at 13:59, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> > > >
> > > > Hi Sumit,
> > > >
> > > > On Thu, Feb 02, 2023 at 05:35:49PM +0530, Sumit Garg wrote:
> > > > > Hi Masahisa,
> > > > >
> > > > > On Thu, 26 Jan 2023 at 18:52, Masahisa Kojima
> > > > > <masahisa.kojima@linaro.org> wrote:
> > > > > >
> > > > > > This RFC series introduces the op-tee based EFI Runtime Variable
> > > > > > Service.
> > > > > >
> > > > > > The eMMC device is typically owned by the non-secure world(linux in
> > > > > > this case). There is an existing solution utilizing eMMC RPMB partition
> > > > > > for EFI Variables, it is implemented by interacting with
> > > > > > OP-TEE, StandaloneMM(as EFI Variable Service Pseudo TA), eMMC driver
> > > > > > and tee-supplicant. The last piece is the tee-based variable access
> > > > > > driver to interact with OP-TEE and StandaloneMM.
> > > > > >
> > > > >
> > > > > After an overall look at the APIs, following are some initial comments:
> > > > > - Is there any reason to have the edk2 specific StandaloneMM stack in
> > > > > Linux to communicate with OP-TEE pseudo TA?
> > > > > - I think the OP-TEE pseudo TA should be able to expose a rather
> > > > > generic invoke commands such as:
> > > > >      TEE_EFI_GET_VARIABLE
> > > > >      TEE_EFI_GET_NEXT_VARIABLE
> > > > >      TEE_EFI_SET_VARIABLE
> > > > >   So it should no longer be tied to StMM stack and other TEE
> > > > > implementations can re-use the abstracted interface to communicate
> > > > > with its corresponding secure storage TA.
> > > >
> > > > In the current setup we have the following layers in the kernel:
> > > > 1. efivar_operations
> > > > 2. MM
> > > > 3. PTA_STMM
> > > > 4. OP-TEE MSG
> > > >
> > > > and in the secure world:
> > > > S1. internal to StMM
> > > > S2. MM interface to StMM
> > > > S3. PTA_STMM
> > > > S4. OP-TEE MSG
> > > >
> > > > If I understand you correctly you'd like to see this instead:
> > > > Kernel:
> > > > 1. efivar_operations
> > > > 2. PTA_EFIVAR
> > > > 4. OP-TEE MSG
> > > >
> > > > Since we still have the MM interface with StMM we'd have this in the secure
> > > > world:
> > > > S1. internal to StMM
> > > > S2. MM interface to StMM
> > > > S3. PTA_EFIVAR
> > > > S4. OP-TEE MSG
> > > >
> > > > At S3 we'd have to convert between EFIVAR and MM messages. The
> > > > difference is that we're moving the EFIVAR <-> MM conversion from the
> > > > non-secure world into the secure world. We're still using OP-TEE
> > > > specific communication at the fourth layer. So we're only moving problem
> > > > around, I'd rather avoid growing the OP-TEE part in the secure world.
> > > >
> > >
> > > If you look carefully, we are essentially defining an ABI towards the
> > > secure world. The approach in this patch-set adds the MM interface as
> > > a redundant ABI layer which makes it complex to maintain. Now think
> > > about if every TEE implementation would propose such a complex ABI. It
> > > looks like a maintenance nightmare to me.
> > >
> > > The concerns you are highlighting about OP-TEE size, I think those are
> > > implementation details which can be simplified later but once you have
> > > defined an ABI then you are stuck with its maintainability.
> >
> > You have a point, but keep in mind that it's StMM that matters here.
> > StMM uses the MM protocol. It was originially using raw SMCs as a
> > conduit, but with the need for OP-TEE accessing RPMB that's not usable.
> > So instead we use OP-TEE MSG as a conduit. Seen from that perspective
> > we're only resuing something established instead of inventing something
> > new.
>
> Aren't we already adding PTA_STMM?

Yes, something is need to recieve those messages and forward the MM
stuff to secure user space.

>
> Isn't the StMM specific to Arm as you already mentioned it was
> designed to specifically use raw SMCs? So if in future AMD TEE wants
> to implement EFI services, can we suggest they reuse the MM interface?

I wouldn't suggest anything until I understood that problem better.

>
> I am not sure why we need to redirect EFI variables via MM interface
> communication buffers rather than directly using the TEE shared memory
> approach.

I allways assumed that was done in order to keep the changes in StMM
at a mininum compared to non-TEE configurations.

Cheers,
Jens

>
> Ard,
>
> Since you have better insights into how EFI runtime services have to
> be implemented, can you share your opinion here? It may be something I
> am missing here.
>
> -Sumit
>
> >
> > Cheers,
> > Jens
> >
> > >
> > > -Sumit
> > >
> > > > Cheers,
> > > > Jens
> > > >
> > > > >
> > > > > -Sumit
> > > > >
> > > > > > Masahisa Kojima (2):
> > > > > >   efi: expose efivar generic ops register function
> > > > > >   tee: Add op-tee helper functions for variable access
> > > > > >
> > > > > >  drivers/firmware/efi/efi.c           |  12 +
> > > > > >  drivers/tee/optee/Kconfig            |  10 +
> > > > > >  drivers/tee/optee/Makefile           |   1 +
> > > > > >  drivers/tee/optee/mm_communication.h | 249 +++++++++++
> > > > > >  drivers/tee/optee/optee_private.h    |   5 +-
> > > > > >  drivers/tee/optee/optee_stmm_efi.c   | 598 +++++++++++++++++++++++++++
> > > > > >  drivers/tee/tee_core.c               |  23 ++
> > > > > >  include/linux/efi.h                  |   4 +
> > > > > >  include/linux/tee_drv.h              |  23 ++
> > > > > >  9 files changed, 924 insertions(+), 1 deletion(-)
> > > > > >  create mode 100644 drivers/tee/optee/mm_communication.h
> > > > > >  create mode 100644 drivers/tee/optee/optee_stmm_efi.c
> > > > > >
> > > > > > --
> > > > > > 2.30.2
> > > > > >

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service
  2023-02-06  6:44         ` Sumit Garg
  2023-02-06  7:47           ` Jens Wiklander
@ 2023-02-06  9:22           ` Ard Biesheuvel
  2023-02-06 11:11             ` Sumit Garg
  2023-02-06  9:31           ` Ilias Apalodimas
  2 siblings, 1 reply; 21+ messages in thread
From: Ard Biesheuvel @ 2023-02-06  9:22 UTC (permalink / raw)
  To: Sumit Garg
  Cc: Jens Wiklander, Masahisa Kojima, linux-kernel, op-tee,
	Ilias Apalodimas, Johan Hovold

On Mon, 6 Feb 2023 at 07:44, Sumit Garg <sumit.garg@linaro.org> wrote:
>
> On Fri, 3 Feb 2023 at 16:25, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> >
..
> > StMM uses the MM protocol. It was originially using raw SMCs as a
> > conduit, but with the need for OP-TEE accessing RPMB that's not usable.
> > So instead we use OP-TEE MSG as a conduit. Seen from that perspective
> > we're only resuing something established instead of inventing something
> > new.
>
> Aren't we already adding PTA_STMM?
>
> Isn't the StMM specific to Arm as you already mentioned it was
> designed to specifically use raw SMCs? So if in future AMD TEE wants
> to implement EFI services, can we suggest they reuse the MM interface?
>
> I am not sure why we need to redirect EFI variables via MM interface
> communication buffers rather than directly using the TEE shared memory
> approach.
>
> Ard,
>
> Since you have better insights into how EFI runtime services have to
> be implemented, can you share your opinion here? It may be something I
> am missing here.
>

Hello Sumit,

I'm not sure I understand what you are asking me here. Allow me to
reiterate, apologies if I am stating the obvious:

The EFI spec describes how the OS should expose the EFI runtime
services, but this is difficult to implement when access to the
underlying storage requires arbitration between accesses by the OS
itself and accesses made by the firmware.

On systems where this issue is absent, the EFI runtime service
implementation for the variable services are very thin wrappers around
calls into standalone MM, which are not standardized, but are also not
ARM specific (standalone MM is being used on other architectures as
well, and 'classic' SMM uses the same protocol but dispatches the call
into the secure/SMM world in a different way)

On systems where arbitration is needed, the standalone MM payload
needs to call back up into the OS to request access to the flash
storage. OP-TEE is a suitable vehicle for this, as it already does the
same thing for other reasons, and is already set up to dispatch SMC
calls that are taken to S-EL1.

All of his is uncharted territory as far as the EFI spec is concerned,
as it occurs inside the StMM pseudo-API call, which itself normally
occurs inside the EFI runtime service call. Given that we cannot use
the latter (as the firmware does not provide a working get/setvariable
at OS runtime [0]), we need to provide some plumbing to call the StMM
pseudo-API directly, and expose it as an alternative efivars
implementation. (Note that this should mean that other implementations
of the StMM pseudo-API that do not require this arbitration may
potentially be accessed in the same way, although I don't see a reason
to use it like that.)

If I am understanding you correctly, your question is whether OP-TEE
should expose the StMM pseudo-API in the usual way as well, and make
the OS rely on the usual discovery mechanisms etc to bind to it?

If that is indeed your question: what purpose would that serve,
exactly? In principle, the OS piece that implements efivars on top of
the StMM pseudo-API should not be specific to any TEE implementation
or API, and I think the fact that OP-TEE is the provider in this case
is an implementation detail.

If you feel that OP-TEE should not expose this interface directly to
begin with, and rely on some marshalling layer to expose the StMM
pseudo-API on top of its ordinary exposed API, that is really an
OP-TEE internal matter (which I think is what you discussed with Jens
up in the thread?) Since StMM calls are defined in terms of SMC
instructions + arguments, this would require more code inside OP-TEE
to translate access to an API that it already exposes directly as
well, so I'm not sure what the gain would be.

The thing to remember is that, even though the wrappers are very thin,
it is fundamentally the StMM API that is being exposed, not the EFI
runtime services API, and so whether or not a use case may materialize
that wants to expose a different API via efivars is out of scope here,
even if they are roughly shaped like get/setvariable.



-- 
Ard.


[0] It is permitted for implementations of, e.g., Get/SetTime or
ResetSystem to use Get/SetVariable internally, and this is quite
clearly broken if the EFI variable services cannot be used. However,
firmware implementations would presumably avoid that situation.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service
  2023-02-06  6:44         ` Sumit Garg
  2023-02-06  7:47           ` Jens Wiklander
  2023-02-06  9:22           ` Ard Biesheuvel
@ 2023-02-06  9:31           ` Ilias Apalodimas
  2 siblings, 0 replies; 21+ messages in thread
From: Ilias Apalodimas @ 2023-02-06  9:31 UTC (permalink / raw)
  To: Sumit Garg
  Cc: Jens Wiklander, Ard Biesheuvel, Masahisa Kojima, linux-kernel,
	op-tee, Johan Hovold

On Mon, Feb 06, 2023 at 12:14:12PM +0530, Sumit Garg wrote:
> On Fri, 3 Feb 2023 at 16:25, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> >
> > > > > with its corresponding secure storage TA.
> > > >
> > > > In the current setup we have the following layers in the kernel:
> > > > 1. efivar_operations
> > > > 2. MM
> > > > 3. PTA_STMM
> > > > 4. OP-TEE MSG
> > > >
> > > > and in the secure world:
> > > > S1. internal to StMM
> > > > S2. MM interface to StMM
> > > > S3. PTA_STMM
> > > > S4. OP-TEE MSG
> > > >
> > > > If I understand you correctly you'd like to see this instead:
> > > > Kernel:
> > > > 1. efivar_operations
> > > > 2. PTA_EFIVAR
> > > > 4. OP-TEE MSG
> > > >
> > > > Since we still have the MM interface with StMM we'd have this in the secure
> > > > world:
> > > > S1. internal to StMM
> > > > S2. MM interface to StMM
> > > > S3. PTA_EFIVAR
> > > > S4. OP-TEE MSG
> > > >
> > > > At S3 we'd have to convert between EFIVAR and MM messages. The
> > > > difference is that we're moving the EFIVAR <-> MM conversion from the
> > > > non-secure world into the secure world. We're still using OP-TEE
> > > > specific communication at the fourth layer. So we're only moving problem
> > > > around, I'd rather avoid growing the OP-TEE part in the secure world.
> > > >
> > >
> > > If you look carefully, we are essentially defining an ABI towards the
> > > secure world. The approach in this patch-set adds the MM interface as
> > > a redundant ABI layer which makes it complex to maintain. Now think
> > > about if every TEE implementation would propose such a complex ABI. It
> > > looks like a maintenance nightmare to me.
> > >
> > > The concerns you are highlighting about OP-TEE size, I think those are
> > > implementation details which can be simplified later but once you have
> > > defined an ABI then you are stuck with its maintainability.
> >
> > You have a point, but keep in mind that it's StMM that matters here.
> > StMM uses the MM protocol. It was originially using raw SMCs as a
> > conduit, but with the need for OP-TEE accessing RPMB that's not usable.
> > So instead we use OP-TEE MSG as a conduit. Seen from that perspective
> > we're only resuing something established instead of inventing something
> > new.
> 
> Aren't we already adding PTA_STMM?

There's a sequence diagram that might help here [0].
The StMM PTA,  is responsible for wrapping the buffer it received from the
NS-world into an MM buffer.

> 
> Isn't the StMM specific to Arm as you already mentioned it was
> designed to specifically use raw SMCs? So if in future AMD TEE wants
> to implement EFI services, can we suggest they reuse the MM interface?

The MM interface is not exposed as an ABI to the non-secure world.  From
a Linux point of view, it's still a normal SMC invoke command towards OP-TEE.
What's 'special' and part of the ABI, is that the driver prepares the buffer
in a way StMM understands.  Then it gets handed over to OP-TEE, which
encapsulates it in an MM buffer and sends it to StMM.

As Jens already said, you asking to move the 'special stmm buffer' creation 
into OP-TEE instead of having the linux driver responsible for it.  That way we
can define an API for other TEEs, which will make the linux driver simpler.
We would ofc need to define some kind of versioning or service ID in that
API, so every TEE knows what it's supposed to call afterwards (iow an
internal TEE identifier in case we end up with multiple backends handling
EFI variables even in the same TEE). 

The proposal definitely makes sense,  but we are adding complexity and
knowledge of EFI to the secure world.  Someone still has to prepare a
buffer the way StMM understands it.

> 
> I am not sure why we need to redirect EFI variables via MM interface
> communication buffers rather than directly using the TEE shared memory
> approach.
> 
> Ard,
> 
> Since you have better insights into how EFI runtime services have to
> be implemented, can you share your opinion here? It may be something I
> am missing here.
> 
> -Sumit
> 

[0] https://apalos.github.io/Protected%20UEFI%20variables%20with%20U-Boot.html#Protected%20UEFI%20variables%20with%20U-Boot

Cheers
/Ilias

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service
  2023-02-06  9:22           ` Ard Biesheuvel
@ 2023-02-06 11:11             ` Sumit Garg
  2023-02-20  5:01               ` Masahisa Kojima
  0 siblings, 1 reply; 21+ messages in thread
From: Sumit Garg @ 2023-02-06 11:11 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: Jens Wiklander, Masahisa Kojima, linux-kernel, op-tee,
	Ilias Apalodimas, Johan Hovold

Thanks Ard for the detailed background information.

On Mon, 6 Feb 2023 at 14:52, Ard Biesheuvel <ardb@kernel.org> wrote:
>
> On Mon, 6 Feb 2023 at 07:44, Sumit Garg <sumit.garg@linaro.org> wrote:
> >
> > On Fri, 3 Feb 2023 at 16:25, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> > >
> ..
> > > StMM uses the MM protocol. It was originially using raw SMCs as a
> > > conduit, but with the need for OP-TEE accessing RPMB that's not usable.
> > > So instead we use OP-TEE MSG as a conduit. Seen from that perspective
> > > we're only resuing something established instead of inventing something
> > > new.
> >
> > Aren't we already adding PTA_STMM?
> >
> > Isn't the StMM specific to Arm as you already mentioned it was
> > designed to specifically use raw SMCs? So if in future AMD TEE wants
> > to implement EFI services, can we suggest they reuse the MM interface?
> >
> > I am not sure why we need to redirect EFI variables via MM interface
> > communication buffers rather than directly using the TEE shared memory
> > approach.
> >
> > Ard,
> >
> > Since you have better insights into how EFI runtime services have to
> > be implemented, can you share your opinion here? It may be something I
> > am missing here.
> >
>
> Hello Sumit,
>
> I'm not sure I understand what you are asking me here. Allow me to
> reiterate, apologies if I am stating the obvious:
>
> The EFI spec describes how the OS should expose the EFI runtime
> services, but this is difficult to implement when access to the
> underlying storage requires arbitration between accesses by the OS
> itself and accesses made by the firmware.

Agree.

>
> On systems where this issue is absent, the EFI runtime service
> implementation for the variable services are very thin wrappers around
> calls into standalone MM, which are not standardized, but are also not
> ARM specific (standalone MM is being used on other architectures as
> well, and 'classic' SMM uses the same protocol but dispatches the call
> into the secure/SMM world in a different way)
>

Thanks for the clarification. So wouldn't it be better to have the
standalone MM API reside here: drivers/firmware/efi/ and it should be
exposed instead of efivars ops? As you mention below that there is
nothing OP-TEE specific in there.

> On systems where arbitration is needed, the standalone MM payload
> needs to call back up into the OS to request access to the flash
> storage. OP-TEE is a suitable vehicle for this, as it already does the
> same thing for other reasons, and is already set up to dispatch SMC
> calls that are taken to S-EL1.

Agree.

>
> All of his is uncharted territory as far as the EFI spec is concerned,
> as it occurs inside the StMM pseudo-API call, which itself normally
> occurs inside the EFI runtime service call. Given that we cannot use
> the latter (as the firmware does not provide a working get/setvariable
> at OS runtime [0]), we need to provide some plumbing to call the StMM
> pseudo-API directly, and expose it as an alternative efivars
> implementation. (Note that this should mean that other implementations
> of the StMM pseudo-API that do not require this arbitration may
> potentially be accessed in the same way, although I don't see a reason
> to use it like that.)
>
> If I am understanding you correctly, your question is whether OP-TEE
> should expose the StMM pseudo-API in the usual way as well, and make
> the OS rely on the usual discovery mechanisms etc to bind to it?

No, I am trying to understand and generalize how an EFI runtime
service ABI would look like among Linux kernel and a TEE. As you may
be aware there are multiple TEE implementations and OP-TEE is one of
them. So we should try to have a generic TEE client driver [1] rather
than every other TEE implementation coming up with its own driver.

>
> If that is indeed your question: what purpose would that serve,
> exactly?

> In principle, the OS piece that implements efivars on top of
> the StMM pseudo-API should not be specific to any TEE implementation
> or API, and I think the fact that OP-TEE is the provider in this case
> is an implementation detail.

Yeah as I said above we should abstract the StMM pieces out of an
OP-TEE driver and then the driver can be a generic TEE client driver
which is just providing the underline vehicle (invoke commands and
StMM buffer passing) as you described above.

>
> If you feel that OP-TEE should not expose this interface directly to
> begin with, and rely on some marshalling layer to expose the StMM
> pseudo-API on top of its ordinary exposed API, that is really an
> OP-TEE internal matter (which I think is what you discussed with Jens
> up in the thread?) Since StMM calls are defined in terms of SMC
> instructions + arguments, this would require more code inside OP-TEE
> to translate access to an API that it already exposes directly as
> well, so I'm not sure what the gain would be.

No I am not against OP-TEE exposing StMM stuff but rather the StMM
stuff (buffer allocation etc.) being bundled into OP-TEE client
driver.

>
> The thing to remember is that, even though the wrappers are very thin,
> it is fundamentally the StMM API that is being exposed, not the EFI
> runtime services API, and so whether or not a use case may materialize
> that wants to expose a different API via efivars is out of scope here,
> even if they are roughly shaped like get/setvariable.
>

Okay I get your point. If we want the StMM API to be exposed then I
think the EFI subsystem is the suitable place for that.

[1] Although there can be minor differences allowed based on TEE
implementation ID. You can consider it analogous to how we use
multiple DT compatibles for a generic platform driver.

-Sumit

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service
  2023-02-06 11:11             ` Sumit Garg
@ 2023-02-20  5:01               ` Masahisa Kojima
  0 siblings, 0 replies; 21+ messages in thread
From: Masahisa Kojima @ 2023-02-20  5:01 UTC (permalink / raw)
  To: Sumit Garg
  Cc: Ard Biesheuvel, Jens Wiklander, linux-kernel, op-tee,
	Ilias Apalodimas, Johan Hovold

On Mon, 6 Feb 2023 at 20:12, Sumit Garg <sumit.garg@linaro.org> wrote:
>
> Thanks Ard for the detailed background information.
>
> On Mon, 6 Feb 2023 at 14:52, Ard Biesheuvel <ardb@kernel.org> wrote:
> >
> > On Mon, 6 Feb 2023 at 07:44, Sumit Garg <sumit.garg@linaro.org> wrote:
> > >
> > > On Fri, 3 Feb 2023 at 16:25, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> > > >
> > ..
> > > > StMM uses the MM protocol. It was originially using raw SMCs as a
> > > > conduit, but with the need for OP-TEE accessing RPMB that's not usable.
> > > > So instead we use OP-TEE MSG as a conduit. Seen from that perspective
> > > > we're only resuing something established instead of inventing something
> > > > new.
> > >
> > > Aren't we already adding PTA_STMM?
> > >
> > > Isn't the StMM specific to Arm as you already mentioned it was
> > > designed to specifically use raw SMCs? So if in future AMD TEE wants
> > > to implement EFI services, can we suggest they reuse the MM interface?
> > >
> > > I am not sure why we need to redirect EFI variables via MM interface
> > > communication buffers rather than directly using the TEE shared memory
> > > approach.
> > >
> > > Ard,
> > >
> > > Since you have better insights into how EFI runtime services have to
> > > be implemented, can you share your opinion here? It may be something I
> > > am missing here.
> > >
> >
> > Hello Sumit,
> >
> > I'm not sure I understand what you are asking me here. Allow me to
> > reiterate, apologies if I am stating the obvious:
> >
> > The EFI spec describes how the OS should expose the EFI runtime
> > services, but this is difficult to implement when access to the
> > underlying storage requires arbitration between accesses by the OS
> > itself and accesses made by the firmware.
>
> Agree.
>
> >
> > On systems where this issue is absent, the EFI runtime service
> > implementation for the variable services are very thin wrappers around
> > calls into standalone MM, which are not standardized, but are also not
> > ARM specific (standalone MM is being used on other architectures as
> > well, and 'classic' SMM uses the same protocol but dispatches the call
> > into the secure/SMM world in a different way)
> >
>
> Thanks for the clarification. So wouldn't it be better to have the
> standalone MM API reside here: drivers/firmware/efi/ and it should be
> exposed instead of efivars ops? As you mention below that there is
> nothing OP-TEE specific in there.
>
> > On systems where arbitration is needed, the standalone MM payload
> > needs to call back up into the OS to request access to the flash
> > storage. OP-TEE is a suitable vehicle for this, as it already does the
> > same thing for other reasons, and is already set up to dispatch SMC
> > calls that are taken to S-EL1.
>
> Agree.
>
> >
> > All of his is uncharted territory as far as the EFI spec is concerned,
> > as it occurs inside the StMM pseudo-API call, which itself normally
> > occurs inside the EFI runtime service call. Given that we cannot use
> > the latter (as the firmware does not provide a working get/setvariable
> > at OS runtime [0]), we need to provide some plumbing to call the StMM
> > pseudo-API directly, and expose it as an alternative efivars
> > implementation. (Note that this should mean that other implementations
> > of the StMM pseudo-API that do not require this arbitration may
> > potentially be accessed in the same way, although I don't see a reason
> > to use it like that.)
> >
> > If I am understanding you correctly, your question is whether OP-TEE
> > should expose the StMM pseudo-API in the usual way as well, and make
> > the OS rely on the usual discovery mechanisms etc to bind to it?
>
> No, I am trying to understand and generalize how an EFI runtime
> service ABI would look like among Linux kernel and a TEE. As you may
> be aware there are multiple TEE implementations and OP-TEE is one of
> them. So we should try to have a generic TEE client driver [1] rather
> than every other TEE implementation coming up with its own driver.
>
> >
> > If that is indeed your question: what purpose would that serve,
> > exactly?
>
> > In principle, the OS piece that implements efivars on top of
> > the StMM pseudo-API should not be specific to any TEE implementation
> > or API, and I think the fact that OP-TEE is the provider in this case
> > is an implementation detail.
>
> Yeah as I said above we should abstract the StMM pieces out of an
> OP-TEE driver and then the driver can be a generic TEE client driver
> which is just providing the underline vehicle (invoke commands and
> StMM buffer passing) as you described above.
>
> >
> > If you feel that OP-TEE should not expose this interface directly to
> > begin with, and rely on some marshalling layer to expose the StMM
> > pseudo-API on top of its ordinary exposed API, that is really an
> > OP-TEE internal matter (which I think is what you discussed with Jens
> > up in the thread?) Since StMM calls are defined in terms of SMC
> > instructions + arguments, this would require more code inside OP-TEE
> > to translate access to an API that it already exposes directly as
> > well, so I'm not sure what the gain would be.
>
> No I am not against OP-TEE exposing StMM stuff but rather the StMM
> stuff (buffer allocation etc.) being bundled into OP-TEE client
> driver.
>
> >
> > The thing to remember is that, even though the wrappers are very thin,
> > it is fundamentally the StMM API that is being exposed, not the EFI
> > runtime services API, and so whether or not a use case may materialize
> > that wants to expose a different API via efivars is out of scope here,
> > even if they are roughly shaped like get/setvariable.
> >
>
> Okay I get your point. If we want the StMM API to be exposed then I
> think the EFI subsystem is the suitable place for that.

Thank you for your comments.
In the next version, I move the StMM code under drivers/firmware/efi/stmm,
then 'optee' prefix is changed to 'tee' because StMM code does not contain
OP-TEE specific code.

Regards,
Masahisa Kojima

>
> [1] Although there can be minor differences allowed based on TEE
> implementation ID. You can consider it analogous to how we use
> multiple DT compatibles for a generic platform driver.
>
> -Sumit

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2023-02-20  5:01 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-26 13:21 [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service Masahisa Kojima
2023-01-26 13:21 ` [RFC PATCH 1/2] efi: expose efivar generic ops register function Masahisa Kojima
2023-01-26 13:21 ` [RFC PATCH 2/2] tee: Add op-tee helper functions for variable access Masahisa Kojima
2023-01-26 13:21   ` Masahisa Kojima
2023-01-29  2:08   ` kernel test robot
2023-01-29  3:41   ` kernel test robot
2023-02-03  9:30   ` Jens Wiklander
2023-02-03  9:30     ` Jens Wiklander
2023-02-06  6:08     ` Masahisa Kojima
2023-02-06  6:08       ` Masahisa Kojima
2023-02-02 12:05 ` [RFC PATCH 0/2] introduce op-tee based EFI Runtime Variable Service Sumit Garg
2023-02-02 13:19   ` Ilias Apalodimas
2023-02-03  8:29   ` Jens Wiklander
2023-02-03  9:33     ` Sumit Garg
2023-02-03 10:55       ` Jens Wiklander
2023-02-06  6:44         ` Sumit Garg
2023-02-06  7:47           ` Jens Wiklander
2023-02-06  9:22           ` Ard Biesheuvel
2023-02-06 11:11             ` Sumit Garg
2023-02-20  5:01               ` Masahisa Kojima
2023-02-06  9:31           ` Ilias Apalodimas

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.