ANDROID: trusty: Create API for sharing trusty info

Android Trusty-Driver and Trusty-Kernel need a shared-memory
based data-structure for exchanging information in order to
influence each other for improved performance. An API is
required for the trusty-driver to register the presence
of the shared-memory block with the trusty-kernel, after
the trusty-driver has established it during probe().
Likewise, an API is also required to unregister the
shared-memory and release resources during trusty-driver
remove().

Bug: 251903627
Test: shpriotest
Change-Id: I7670dcfd711f5cbeed32650dc13eac2cedc8e5db
Signed-off-by: Sudhin Mishra <[email protected]>
diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile
index 2cf1cfc..42424ad 100644
--- a/drivers/trusty/Makefile
+++ b/drivers/trusty/Makefile
@@ -4,7 +4,7 @@
 #
 
 obj-$(CONFIG_TRUSTY)		+= trusty-core.o
-trusty-core-objs		+= trusty.o trusty-mem.o
+trusty-core-objs		+= trusty.o trusty-mem.o trusty-sched-share.o
 trusty-core-$(CONFIG_ARM)	+= trusty-smc-arm.o
 trusty-core-$(CONFIG_ARM64)	+= trusty-smc-arm64.o
 obj-$(CONFIG_TRUSTY_IRQ)	+= trusty-irq.o
diff --git a/drivers/trusty/trusty-sched-share-api.h b/drivers/trusty/trusty-sched-share-api.h
new file mode 100644
index 0000000..e2726e6
--- /dev/null
+++ b/drivers/trusty/trusty-sched-share-api.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2022 Google, Inc.
+ *
+ * This header file contains the definitions of APIs, used for the
+ * registration/unregistration of shared-memory used for the
+ * exchange of info between the Linux Trusty-Driver and the Trusty-Kernel.
+ */
+#ifndef _TRUSTY_SCHED_SHARE_API_H_
+#define _TRUSTY_SCHED_SHARE_API_H_
+
+#include <linux/device.h>
+
+struct trusty_sched_share_state;
+
+struct trusty_sched_share_state *trusty_register_sched_share(struct device *device);
+void trusty_unregister_sched_share(struct trusty_sched_share_state *sched_share_state);
+
+#endif /* _TRUSTY_SCHED_SHARE_API_H_ */
diff --git a/drivers/trusty/trusty-sched-share.c b/drivers/trusty/trusty-sched-share.c
new file mode 100644
index 0000000..0072739
--- /dev/null
+++ b/drivers/trusty/trusty-sched-share.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Google, Inc.
+ *
+ * This trusty-driver module contains the SMC API for the trusty-driver to
+ * communicate with the trusty-kernel for shared memory
+ * registration/unregistration.
+ */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/scatterlist.h>
+#include <linux/trusty/trusty.h>
+#include "trusty-sched-share.h"
+
+/**
+ * struct trusty_sched_share_state - Trusty share resources state local to Trusty-Driver
+ * @dev: ptr to the trusty-device instance
+ * @sg: ptr to the scatter-gather list used for shared-memory buffers
+ * @sched_shared_mem_id: trusty-priority shared-memory id
+ * @sched_shared_vm: vm ptr to the shared-memory block
+ * @mem_size: size of trusty shared-memory block in bytes
+ * @buf_size: page-aligned size of trusty shared-memory buffer in bytes
+ * @num_pages: number of pages containing the allocated shared-memory buffer
+ */
+struct trusty_sched_share_state {
+	struct device *dev;
+	struct scatterlist *sg;
+	trusty_shared_mem_id_t sched_shared_mem_id;
+	char *sched_shared_vm;
+	u32 mem_size;
+	u32 buf_size;
+	u32 num_pages;
+};
+
+static int
+trusty_sched_share_resources_allocate(struct trusty_sched_share_state *share_state)
+{
+	struct scatterlist *sg;
+	struct trusty_sched_shared *shared;
+	unsigned char *mem;
+	trusty_shared_mem_id_t mem_id;
+	int result = 0;
+	int i;
+
+	share_state->mem_size = sizeof(struct trusty_sched_shared) +
+				nr_cpu_ids * sizeof(struct trusty_percpu_data);
+	share_state->num_pages =
+		round_up(share_state->mem_size, PAGE_SIZE) / PAGE_SIZE;
+	share_state->buf_size = share_state->num_pages * PAGE_SIZE;
+
+	dev_dbg(share_state->dev,
+		"%s: mem_size=%d,  num_pages=%d,  buf_size=%d", __func__,
+		share_state->mem_size, share_state->num_pages,
+		share_state->buf_size);
+
+	share_state->sg = kcalloc(share_state->num_pages,
+				  sizeof(*share_state->sg), GFP_KERNEL);
+	if (!share_state->sg) {
+		result = ENOMEM;
+		goto err_rsrc_alloc_sg;
+	}
+
+	mem = vzalloc(share_state->buf_size);
+	if (!mem) {
+		result = -ENOMEM;
+		goto err_rsrc_alloc_mem;
+	}
+	share_state->sched_shared_vm = mem;
+	dev_dbg(share_state->dev, "%s: sched_shared_vm=%p  size=%d\n",
+		__func__, share_state->sched_shared_vm, share_state->buf_size);
+
+	sg_init_table(share_state->sg, share_state->num_pages);
+	for_each_sg(share_state->sg, sg, share_state->num_pages, i) {
+		struct page *pg = vmalloc_to_page(mem + (i * PAGE_SIZE));
+
+		if (!pg) {
+			result = -ENOMEM;
+			goto err_rsrc_sg_lookup;
+		}
+		sg_set_page(sg, pg, PAGE_SIZE, 0);
+	}
+
+	result = trusty_share_memory(share_state->dev, &mem_id, share_state->sg,
+				     share_state->num_pages, PAGE_KERNEL);
+	if (result != 0) {
+		dev_err(share_state->dev, "trusty_share_memory failed: %d\n",
+			result);
+		goto err_rsrc_share_mem;
+	}
+	dev_dbg(share_state->dev, "%s: sched_shared_mem_id=0x%llx", __func__,
+		mem_id);
+	share_state->sched_shared_mem_id = mem_id;
+
+	shared = (struct trusty_sched_shared *)share_state->sched_shared_vm;
+	shared->hdr_size = sizeof(struct trusty_sched_shared);
+	shared->percpu_data_size = sizeof(struct trusty_percpu_data);
+
+	return result;
+
+err_rsrc_share_mem:
+err_rsrc_sg_lookup:
+	vfree(share_state->sched_shared_vm);
+err_rsrc_alloc_mem:
+	kfree(share_state->sg);
+err_rsrc_alloc_sg:
+	return result;
+}
+
+struct trusty_sched_share_state *trusty_register_sched_share(struct device *device)
+{
+	int result = 0;
+	struct trusty_sched_share_state *sched_share_state = NULL;
+	struct trusty_sched_shared *shared;
+	uint sched_share_state_size;
+
+	sched_share_state_size = sizeof(*sched_share_state);
+
+	sched_share_state = kzalloc(sched_share_state_size, GFP_KERNEL);
+	if (!sched_share_state)
+		goto err_sched_state_alloc;
+	sched_share_state->dev = device;
+
+	result = trusty_sched_share_resources_allocate(sched_share_state);
+	if (result)
+		goto err_resources_alloc;
+
+	shared = (struct trusty_sched_shared *)sched_share_state->sched_shared_vm;
+	shared->cpu_count = nr_cpu_ids;
+
+	dev_dbg(device, "%s: calling api SMC_SC_SCHED_SHARE_REGISTER...\n",
+		__func__);
+
+	result = trusty_std_call32(
+		sched_share_state->dev, SMC_SC_SCHED_SHARE_REGISTER,
+		(u32)sched_share_state->sched_shared_mem_id,
+		(u32)(sched_share_state->sched_shared_mem_id >> 32),
+		sched_share_state->buf_size);
+	if (result == SM_ERR_UNDEFINED_SMC) {
+		dev_warn(
+			sched_share_state->dev,
+			"trusty-share not supported on secure side, error=%d\n",
+			result);
+		goto err_smc_std_call32;
+	} else if (result < 0) {
+		dev_err(device,
+			"trusty std call32 (SMC_SC_SCHED_SHARE_REGISTER) failed: %d\n",
+			result);
+		goto err_smc_std_call32;
+	}
+	dev_dbg(device, "%s: sched_share_state=%llx\n", __func__,
+		(u64)sched_share_state);
+
+	return sched_share_state;
+
+err_smc_std_call32:
+	result = trusty_reclaim_memory(sched_share_state->dev,
+				       sched_share_state->sched_shared_mem_id,
+				       sched_share_state->sg,
+				       sched_share_state->num_pages);
+	if (result != 0) {
+		dev_err(sched_share_state->dev,
+			"trusty_reclaim_memory() failed: ret=%d mem_id=0x%llx\n",
+			result, sched_share_state->sched_shared_mem_id);
+		/*
+		 * It is not safe to free this memory if trusty_reclaim_memory()
+		 * failed. Leak it in that case.
+		 */
+		dev_err(sched_share_state->dev,
+			"WARNING: leaking some allocated resources!!\n");
+	} else {
+		vfree(sched_share_state->sched_shared_vm);
+	}
+	kfree(sched_share_state->sg);
+err_resources_alloc:
+	kfree(sched_share_state);
+	dev_warn(sched_share_state->dev,
+		 "Trusty-Sched_Share API not available.\n");
+err_sched_state_alloc:
+	return NULL;
+}
+
+void trusty_unregister_sched_share(struct trusty_sched_share_state *sched_share_state)
+{
+	int result;
+
+	if (!sched_share_state)
+		return;
+
+	/* ask Trusty to release the Trusty-side resources */
+	result = trusty_std_call32(
+		sched_share_state->dev, SMC_SC_SCHED_SHARE_UNREGISTER,
+		(u32)sched_share_state->sched_shared_mem_id,
+		(u32)(sched_share_state->sched_shared_mem_id >> 32), 0);
+	if (result) {
+		dev_err(sched_share_state->dev,
+			"call SMC_SC_SCHED_SHARE_UNREGISTER failed, error=%d\n",
+			result);
+	}
+	result = trusty_reclaim_memory(sched_share_state->dev,
+				       sched_share_state->sched_shared_mem_id,
+				       sched_share_state->sg,
+				       sched_share_state->num_pages);
+	if (result) {
+		dev_err(sched_share_state->dev,
+			"trusty_reclaim_memory() failed: ret=%d mem_id=0x%llx\n",
+			result, sched_share_state->sched_shared_mem_id);
+		/*
+		 * It is not safe to free this memory if trusty_reclaim_memory()
+		 * failed. Leak it in that case.
+		 */
+		dev_err(sched_share_state->dev,
+			"WARNING: leaking some allocated resources!!\n");
+	} else {
+		vfree(sched_share_state->sched_shared_vm);
+	}
+
+	kfree(sched_share_state->sg);
+	kfree(sched_share_state);
+}
diff --git a/drivers/trusty/trusty-sched-share.h b/drivers/trusty/trusty-sched-share.h
new file mode 100644
index 0000000..7e59eec
--- /dev/null
+++ b/drivers/trusty/trusty-sched-share.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2022 Google, Inc.
+ *
+ * This header file defines the SMC API and the shared data info between
+ * Linux and Trusty.
+ *
+ * Important: Copy of this header file is used in Trusty.
+ * Trusty header file:
+ *   trusty/trusty/kernel/lib/trusty/include/lib/trusty/trusty_share.h
+ * Please keep the copies in sync.
+ */
+#ifndef _TRUSTY_SCHED_SHARE_H_
+#define _TRUSTY_SCHED_SHARE_H_
+
+#include <linux/trusty/smcall.h>
+
+/*
+ * trusty-shadow-priority valid values
+ */
+#define TRUSTY_SHADOW_PRIORITY_LOW 1
+#define TRUSTY_SHADOW_PRIORITY_NORMAL 2
+#define TRUSTY_SHADOW_PRIORITY_HIGH 3
+
+/**
+ * struct trusty_percpu_data - per-cpu trusty shared data
+ * @cur_shadow_priority: set by Trusty-Driver/Linux
+ * @ask_shadow_priority: set by Trusty Kernel
+ */
+struct trusty_percpu_data {
+	u32 cur_shadow_priority;
+	u32 ask_shadow_priority;
+};
+
+/**
+ * struct trusty_sched_shared - information in the shared memory.
+ * @hdr_size: size of the trusty_shared data-structure.
+ *            An instance of this data-structure is embedded at
+ *            the very beginning of the shared-memory block.
+ * @cpu_count: max number of available CPUs in the system.
+ * @percpu_data_size: size of the per-cpu data structure.
+ *                    The shared-memory block contains an array
+ *                    of per-cpu instances of a data-structure that
+ *                    can be indexed by cpu_id.
+ *
+ * NOTE: At the end of this data-structure, additional space is
+ * allocated to accommodate a variable length array as follows:
+ * 'struct trusty_percpu_data percpu_data_table[]',
+ * with 'cpu_count' as its number of elements.
+ */
+struct trusty_sched_shared {
+	u32 hdr_size;
+	u32 cpu_count;
+	u32 percpu_data_size;
+	/* Additional space is allocated here as noted above */
+};
+
+#endif /* _TRUSTY_SCHED_SHARE_H_ */
diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c
index ac08d51..fadead8 100644
--- a/drivers/trusty/trusty.c
+++ b/drivers/trusty/trusty.c
@@ -21,6 +21,7 @@
 
 #include "trusty-smc.h"
 #include "trusty-trace.h"
+#include "trusty-sched-share-api.h"
 
 struct trusty_state;
 static struct platform_driver trusty_driver;
@@ -46,6 +47,7 @@
 	struct list_head nop_queue;
 	spinlock_t nop_lock; /* protects nop_queue */
 	struct device_dma_parameters dma_parms;
+	struct trusty_sched_share_state *trusty_sched_share_state;
 	void *ffa_tx;
 	void *ffa_rx;
 	u16 ffa_local_id;
@@ -935,6 +937,8 @@
 		INIT_WORK(&tw->work, work_func);
 	}
 
+	s->trusty_sched_share_state = trusty_register_sched_share(&pdev->dev);
+
 	ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "Failed to add children: %d\n", ret);
@@ -971,6 +975,8 @@
 	unsigned int cpu;
 	struct trusty_state *s = platform_get_drvdata(pdev);
 
+	trusty_unregister_sched_share(s->trusty_sched_share_state);
+
 	device_for_each_child(&pdev->dev, NULL, trusty_remove_child);
 
 	for_each_possible_cpu(cpu) {
diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h
index aea3f60..9ce498d 100644
--- a/include/linux/trusty/smcall.h
+++ b/include/linux/trusty/smcall.h
@@ -68,6 +68,36 @@
  */
 #define SMC_SC_NOP		SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 3)
 
+/**
+ * SMC API for supporting shared-memory based trusty-linux info exchange
+ *
+ * SMC_SC_SCHED_SHARE_REGISTER - enter trusty to establish a shared-memory region
+ * Arguments:
+ * param[0]: shared-memory client-id
+ * param[1]: shared-memory buffer-id
+ * param[2]: shared-memory block size
+ *
+ * returns:
+ * SM_ERR_INTERNAL_FAILURE on failure.
+ * 0 on success.
+ */
+#define SMC_SC_SCHED_SHARE_REGISTER   SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 4)
+
+/**
+ * SMC API for supporting shared-memory based trusty-linux info exchange
+ *
+ * SMC_SC_SCHED_SHARE_UNREGISTER - enter trusty to release the shared-memory region
+ * Arguments:
+ * param[0]: shared-memory client-id
+ * param[1]: shared-memory buffer-id
+ *
+ * returns:
+ * SM_ERR_INTERNAL_FAILURE on failure.
+ * 0 on success.
+ */
+#define SMC_SC_SCHED_SHARE_UNREGISTER \
+	SMC_STDCALL_NR(SMC_ENTITY_SECURE_MONITOR, 5)
+
 /*
  * Return from secure os to non-secure os with return value in r1
  */