2021-06-10 21:11:46

by Tyler Hicks

[permalink] [raw]
Subject: [PATCH v4 0/8] tee: Improve support for kexec and kdump

v4:
- Incorporated 'tee: add tee_shm_alloc_kernel_buf()' from Jens to remove
the need to expose TEE_SHM_REGISTER to callers of tee_shm_alloc()
- Updated 'tee: Support kernel shm registration without dma-buf backing'
to drop the TEE_SHM_DMA_BUF flag when tee_shm_alloc_kernel_buf() calls
tee_shm_alloc()
- Updated the final two patches, against ftpm and tee_bnxt_fw, to use
tee_shm_alloc_kernel_buf() instead of tee_shm_alloc()
- Minor cleanups to the commit messages of the updates patches
v3: https://lore.kernel.org/lkml/[email protected]/
v2: https://lore.kernel.org/lkml/[email protected]/
v1: https://lore.kernel.org/lkml/[email protected]/

This series fixes several bugs uncovered while exercising the OP-TEE
(Open Portable Trusted Execution Environment), ftpm (firmware TPM), and
tee_bnxt_fw (Broadcom BNXT firmware manager) drivers with kexec and
kdump (emergency kexec) based workflows.

The majority of the problems are caused by missing .shutdown hooks in
the drivers. The .shutdown hooks are used by the normal kexec code path
to let the drivers clean up prior to executing the target kernel. The
.remove hooks, which are already implemented in these drivers, are not
called as part of the kexec code path. This resulted in shared memory
regions, that were cached and/or registered with OP-TEE, not being
cleared/unregistered prior to kexec. The new kernel would then run into
problems when handling the previously cached virtual addresses or trying
to register newly allocated shared memory objects that overlapped with
the previously registered virtual addresses. The TEE didn't receive
notification that the old virtual addresses were no longer meaningful
and that a new kernel, with a new address space, would soon be running.

However, implementing .shutdown hooks was not enough for supporting
kexec. There was an additional problem caused by the TEE driver's
reliance on the dma-buf subsystem for multi-page shared memory objects
that were registered with the TEE. Shared memory objects backed by a
dma-buf use a different mechanism for reference counting. When the final
reference is released, work is scheduled to be executed to unregister
the shared memory with the TEE but that work is only completed prior to
the current task returning the userspace. In the case of a kexec
operation, the current task that's calling the driver .shutdown hooks
never returns to userspace prior to the kexec operation so the shared
memory was never unregistered. This eventually caused problems from
overlapping shared memory regions that were registered with the TEE
after several kexec operations. The large 4M contiguous region
allocated by the tee_bnxt_fw driver reliably ran into this issue on the
fourth kexec on a system with 8G of RAM.

The use of dma-buf makes sense for shared memory that's in use by
userspace but dma-buf's aren't needed for shared memory that will only
used by the driver. This series separates dma-buf backed shared memory
allocated by the kernel from multi-page shared memory that the kernel
simply needs registered with the TEE for private use.

One other noteworthy change in this series is to completely refuse to
load the OP-TEE driver in the kdump kernel. This is needed because the
secure world may have had all of its threads in suspended state when the
regular kernel crashed. The kdump kernel would then hang during boot
because the OP-TEE driver's .probe function would attempt to use a
secure world thread when they're all in suspended state. Another problem
is that shared memory allocations could fail under the kdump kernel
because the previously registered were not unregistered (the .shutdown
hook is not called when kexec'ing into the kdump kernel).

The first patch in the series fixes potential memory leaks that are not
directly related to kexec or kdump but were noticed during the
development of this series.

Tyler

Allen Pais (2):
optee: fix tee out of memory failure seen during kexec reboot
firmware: tee_bnxt: Release TEE shm, session, and context during kexec

Jens Wiklander (1):
tee: add tee_shm_alloc_kernel_buf()

Tyler Hicks (5):
optee: Fix memory leak when failing to register shm pages
optee: Refuse to load the driver under the kdump kernel
optee: Clear stale cache entries during initialization
tee: Support kernel shm registration without dma-buf backing
tpm_ftpm_tee: Free and unregister TEE shared memory during kexec

drivers/char/tpm/tpm_ftpm_tee.c | 8 ++---
drivers/firmware/broadcom/tee_bnxt_fw.c | 14 +++++++--
drivers/tee/optee/call.c | 11 ++++++-
drivers/tee/optee/core.c | 42 ++++++++++++++++++++++++-
drivers/tee/optee/optee_private.h | 2 +-
drivers/tee/optee/shm_pool.c | 17 +++++++---
drivers/tee/tee_shm.c | 29 ++++++++++++++++-
include/linux/tee_drv.h | 1 +
8 files changed, 108 insertions(+), 16 deletions(-)

--
2.25.1


2021-06-10 21:11:57

by Tyler Hicks

[permalink] [raw]
Subject: [PATCH v4 5/8] tee: add tee_shm_alloc_kernel_buf()

From: Jens Wiklander <[email protected]>

Adds a new function tee_shm_alloc_kernel_buf() to allocate shared memory
from a kernel driver. This function can later be made more lightweight
by unnecessary dma-buf export.

Signed-off-by: Jens Wiklander <[email protected]>
Reviewed-by: Tyler Hicks <[email protected]>
---
drivers/tee/tee_shm.c | 18 ++++++++++++++++++
include/linux/tee_drv.h | 1 +
2 files changed, 19 insertions(+)

diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index 00472f5ce22e..c65e44707cd6 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -193,6 +193,24 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
}
EXPORT_SYMBOL_GPL(tee_shm_alloc);

+/**
+ * tee_shm_alloc_kernel_buf() - Allocate shared memory for kernel buffer
+ * @ctx: Context that allocates the shared memory
+ * @size: Requested size of shared memory
+ *
+ * The returned memory registered in secure world and is suitable to be
+ * passed as a memory buffer in parameter argument to
+ * tee_client_invoke_func(). The memory allocated is later freed with a
+ * call to tee_shm_free().
+ *
+ * @returns a pointer to 'struct tee_shm'
+ */
+struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size)
+{
+ return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
+}
+EXPORT_SYMBOL_GPL(tee_shm_alloc_kernel_buf);
+
struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr,
size_t length, u32 flags)
{
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index 54269e47ac9a..8990f7628387 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -332,6 +332,7 @@ void *tee_get_drvdata(struct tee_device *teedev);
* @returns a pointer to 'struct tee_shm'
*/
struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags);
+struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size);

/**
* tee_shm_register() - Register shared memory buffer
--
2.25.1

2021-06-10 21:12:12

by Tyler Hicks

[permalink] [raw]
Subject: [PATCH v4 1/8] optee: Fix memory leak when failing to register shm pages

Free the previously allocated pages when we encounter an error condition
while attempting to register the pages with the secure world.

Fixes: a249dd200d03 ("tee: optee: Fix dynamic shm pool allocations")
Fixes: 5a769f6ff439 ("optee: Fix multi page dynamic shm pool alloc")
Signed-off-by: Tyler Hicks <[email protected]>
---
drivers/tee/optee/shm_pool.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c
index d767eebf30bd..da06ce9b9313 100644
--- a/drivers/tee/optee/shm_pool.c
+++ b/drivers/tee/optee/shm_pool.c
@@ -32,8 +32,10 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
struct page **pages;

pages = kcalloc(nr_pages, sizeof(pages), GFP_KERNEL);
- if (!pages)
- return -ENOMEM;
+ if (!pages) {
+ rc = -ENOMEM;
+ goto err;
+ }

for (i = 0; i < nr_pages; i++) {
pages[i] = page;
@@ -44,8 +46,14 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
rc = optee_shm_register(shm->ctx, shm, pages, nr_pages,
(unsigned long)shm->kaddr);
kfree(pages);
+ if (rc)
+ goto err;
}

+ return 0;
+
+err:
+ __free_pages(page, order);
return rc;
}

--
2.25.1

2021-06-10 21:12:12

by Tyler Hicks

[permalink] [raw]
Subject: [PATCH v4 3/8] optee: fix tee out of memory failure seen during kexec reboot

From: Allen Pais <[email protected]>

The following out of memory errors are seen on kexec reboot
from the optee core.

[ 0.368428] tee_bnxt_fw optee-clnt0: tee_shm_alloc failed
[ 0.368461] tee_bnxt_fw: probe of optee-clnt0 failed with error -22

tee_shm_release() is not invoked on dma shm buffer.

Implement .shutdown() method to handle the release of the buffers
correctly.

More info:
https://github.com/OP-TEE/optee_os/issues/3637

Signed-off-by: Allen Pais <[email protected]>
Reviewed-by: Tyler Hicks <[email protected]>
---
drivers/tee/optee/core.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 5288cd767d82..0987074d7ed0 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -573,6 +573,13 @@ static optee_invoke_fn *get_invoke_func(struct device *dev)
return ERR_PTR(-EINVAL);
}

+/* optee_remove - Device Removal Routine
+ * @pdev: platform device information struct
+ *
+ * optee_remove is called by platform subsystem to alert the driver
+ * that it should release the device
+ */
+
static int optee_remove(struct platform_device *pdev)
{
struct optee *optee = platform_get_drvdata(pdev);
@@ -603,6 +610,18 @@ static int optee_remove(struct platform_device *pdev)
return 0;
}

+/* optee_shutdown - Device Removal Routine
+ * @pdev: platform device information struct
+ *
+ * platform_shutdown is called by the platform subsystem to alert
+ * the driver that a shutdown, reboot, or kexec is happening and
+ * device must be disabled.
+ */
+static void optee_shutdown(struct platform_device *pdev)
+{
+ optee_disable_shm_cache(platform_get_drvdata(pdev));
+}
+
static int optee_probe(struct platform_device *pdev)
{
optee_invoke_fn *invoke_fn;
@@ -739,6 +758,7 @@ MODULE_DEVICE_TABLE(of, optee_dt_match);
static struct platform_driver optee_driver = {
.probe = optee_probe,
.remove = optee_remove,
+ .shutdown = optee_shutdown,
.driver = {
.name = "optee",
.of_match_table = optee_dt_match,
--
2.25.1

2021-06-10 21:12:15

by Tyler Hicks

[permalink] [raw]
Subject: [PATCH v4 8/8] firmware: tee_bnxt: Release TEE shm, session, and context during kexec

From: Allen Pais <[email protected]>

Implement a .shutdown hook that will be called during a kexec operation
so that the TEE shared memory, session, and context that were set up
during .probe can be properly freed/closed.

Additionally, don't use dma-buf backed shared memory for the
fw_shm_pool. dma-buf backed shared memory cannot be reliably freed and
unregistered during a kexec operation even when tee_shm_free() is called
on the shm from a .shutdown hook. The problem occurs because
dma_buf_put() calls fput() which then uses task_work_add(), with the
TWA_RESUME parameter, to queue tee_shm_release() to be called before the
current task returns to user mode. However, the current task never
returns to user mode before the kexec completes so the memory is never
freed nor unregistered.

Use tee_shm_alloc_kernel_buf() to avoid dma-buf backed shared memory
allocation so that tee_shm_free() can directly call tee_shm_release().
This will ensure that the shm can be freed and unregistered during a
kexec operation.

Fixes: 246880958ac9 ("firmware: broadcom: add OP-TEE based BNXT f/w manager")
Signed-off-by: Allen Pais <[email protected]>
Co-developed-by: Tyler Hicks <[email protected]>
Signed-off-by: Tyler Hicks <[email protected]>
---
drivers/firmware/broadcom/tee_bnxt_fw.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/drivers/firmware/broadcom/tee_bnxt_fw.c b/drivers/firmware/broadcom/tee_bnxt_fw.c
index ed10da5313e8..a5bf4c3f6dc7 100644
--- a/drivers/firmware/broadcom/tee_bnxt_fw.c
+++ b/drivers/firmware/broadcom/tee_bnxt_fw.c
@@ -212,10 +212,9 @@ static int tee_bnxt_fw_probe(struct device *dev)

pvt_data.dev = dev;

- fw_shm_pool = tee_shm_alloc(pvt_data.ctx, MAX_SHM_MEM_SZ,
- TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
+ fw_shm_pool = tee_shm_alloc_kernel_buf(pvt_data.ctx, MAX_SHM_MEM_SZ);
if (IS_ERR(fw_shm_pool)) {
- dev_err(pvt_data.dev, "tee_shm_alloc failed\n");
+ dev_err(pvt_data.dev, "tee_shm_alloc_kernel_buf failed\n");
err = PTR_ERR(fw_shm_pool);
goto out_sess;
}
@@ -242,6 +241,14 @@ static int tee_bnxt_fw_remove(struct device *dev)
return 0;
}

+static void tee_bnxt_fw_shutdown(struct device *dev)
+{
+ tee_shm_free(pvt_data.fw_shm_pool);
+ tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
+ tee_client_close_context(pvt_data.ctx);
+ pvt_data.ctx = NULL;
+}
+
static const struct tee_client_device_id tee_bnxt_fw_id_table[] = {
{UUID_INIT(0x6272636D, 0x2019, 0x0716,
0x42, 0x43, 0x4D, 0x5F, 0x53, 0x43, 0x48, 0x49)},
@@ -257,6 +264,7 @@ static struct tee_client_driver tee_bnxt_fw_driver = {
.bus = &tee_bus_type,
.probe = tee_bnxt_fw_probe,
.remove = tee_bnxt_fw_remove,
+ .shutdown = tee_bnxt_fw_shutdown,
},
};

--
2.25.1

2021-06-10 21:12:17

by Tyler Hicks

[permalink] [raw]
Subject: [PATCH v4 2/8] optee: Refuse to load the driver under the kdump kernel

Fix a hung task issue, seen when booting the kdump kernel, that is
caused by all of the secure world threads being in a permanent suspended
state:

INFO: task swapper/0:1 blocked for more than 120 seconds.
Not tainted 5.4.83 #1
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
swapper/0 D 0 1 0 0x00000028
Call trace:
__switch_to+0xc8/0x118
__schedule+0x2e0/0x700
schedule+0x38/0xb8
schedule_timeout+0x258/0x388
wait_for_completion+0x16c/0x4b8
optee_cq_wait_for_completion+0x28/0xa8
optee_disable_shm_cache+0xb8/0xf8
optee_probe+0x560/0x61c
platform_drv_probe+0x58/0xa8
really_probe+0xe0/0x338
driver_probe_device+0x5c/0xf0
device_driver_attach+0x74/0x80
__driver_attach+0x64/0xe0
bus_for_each_dev+0x84/0xd8
driver_attach+0x30/0x40
bus_add_driver+0x188/0x1e8
driver_register+0x64/0x110
__platform_driver_register+0x54/0x60
optee_driver_init+0x20/0x28
do_one_initcall+0x54/0x24c
kernel_init_freeable+0x1e8/0x2c0
kernel_init+0x18/0x118
ret_from_fork+0x10/0x18

The invoke_fn hook returned OPTEE_SMC_RETURN_ETHREAD_LIMIT, indicating
that the secure world threads were all in a suspended state at the time
of the kernel crash. This intermittently prevented the kdump kernel from
booting, resulting in a failure to collect the kernel dump.

Make kernel dump collection more reliable on systems utilizing OP-TEE by
refusing to load the driver under the kdump kernel.

Signed-off-by: Tyler Hicks <[email protected]>
---
drivers/tee/optee/core.c | 11 +++++++++++
1 file changed, 11 insertions(+)

diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index ddb8f9ecf307..5288cd767d82 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -6,6 +6,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/arm-smccc.h>
+#include <linux/crash_dump.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -612,6 +613,16 @@ static int optee_probe(struct platform_device *pdev)
u32 sec_caps;
int rc;

+ /*
+ * The kernel may have crashed at the same time that all available
+ * secure world threads were suspended and we cannot reschedule the
+ * suspended threads without access to the crashed kernel's wait_queue.
+ * Therefore, we cannot reliably initialize the OP-TEE driver in the
+ * kdump kernel.
+ */
+ if (is_kdump_kernel())
+ return -ENODEV;
+
invoke_fn = get_invoke_func(&pdev->dev);
if (IS_ERR(invoke_fn))
return PTR_ERR(invoke_fn);
--
2.25.1

2021-06-10 21:12:21

by Tyler Hicks

[permalink] [raw]
Subject: [PATCH v4 4/8] optee: Clear stale cache entries during initialization

The shm cache could contain invalid addresses if
optee_disable_shm_cache() was not called from the .shutdown hook of the
previous kernel before a kexec. These addresses could be unmapped or
they could point to mapped but unintended locations in memory.

Clear the shared memory cache, while being careful to not translate the
addresses returned from OPTEE_SMC_DISABLE_SHM_CACHE, during driver
initialization. Once all pre-cache shm objects are removed, proceed with
enabling the cache so that we know that we can handle cached shm objects
with confidence later in the .shutdown hook.

Signed-off-by: Tyler Hicks <[email protected]>
---
drivers/tee/optee/call.c | 11 ++++++++++-
drivers/tee/optee/core.c | 13 +++++++++++--
drivers/tee/optee/optee_private.h | 2 +-
3 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
index 6e6eb836e9b6..5dcba6105ed7 100644
--- a/drivers/tee/optee/call.c
+++ b/drivers/tee/optee/call.c
@@ -419,8 +419,10 @@ void optee_enable_shm_cache(struct optee *optee)
* optee_disable_shm_cache() - Disables caching of some shared memory allocation
* in OP-TEE
* @optee: main service struct
+ * @is_mapped: true if the cached shared memory addresses were mapped by this
+ * kernel, are safe to dereference, and should be freed
*/
-void optee_disable_shm_cache(struct optee *optee)
+void optee_disable_shm_cache(struct optee *optee, bool is_mapped)
{
struct optee_call_waiter w;

@@ -439,6 +441,13 @@ void optee_disable_shm_cache(struct optee *optee)
if (res.result.status == OPTEE_SMC_RETURN_OK) {
struct tee_shm *shm;

+ /*
+ * Shared memory references that were not mapped by
+ * this kernel must be ignored to prevent a crash.
+ */
+ if (!is_mapped)
+ continue;
+
shm = reg_pair_to_ptr(res.result.shm_upper32,
res.result.shm_lower32);
tee_shm_free(shm);
diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index 0987074d7ed0..6974e1104bd4 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -589,7 +589,7 @@ static int optee_remove(struct platform_device *pdev)
* reference counters and also avoid wild pointers in secure world
* into the old shared memory range.
*/
- optee_disable_shm_cache(optee);
+ optee_disable_shm_cache(optee, true);

/*
* The two devices have to be unregistered before we can free the
@@ -619,7 +619,7 @@ static int optee_remove(struct platform_device *pdev)
*/
static void optee_shutdown(struct platform_device *pdev)
{
- optee_disable_shm_cache(platform_get_drvdata(pdev));
+ optee_disable_shm_cache(platform_get_drvdata(pdev), true);
}

static int optee_probe(struct platform_device *pdev)
@@ -716,6 +716,15 @@ static int optee_probe(struct platform_device *pdev)
optee->memremaped_shm = memremaped_shm;
optee->pool = pool;

+ /*
+ * Ensure that there are no pre-existing shm objects before enabling
+ * the shm cache so that there's no chance of receiving an invalid
+ * address during shutdown. This could occur, for example, if we're
+ * kexec booting from an older kernel that did not properly cleanup the
+ * shm cache.
+ */
+ optee_disable_shm_cache(optee, false);
+
optee_enable_shm_cache(optee);

if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
index e25b216a14ef..16d8c82213e7 100644
--- a/drivers/tee/optee/optee_private.h
+++ b/drivers/tee/optee/optee_private.h
@@ -158,7 +158,7 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session);

void optee_enable_shm_cache(struct optee *optee);
-void optee_disable_shm_cache(struct optee *optee);
+void optee_disable_shm_cache(struct optee *optee, bool is_mapped);

int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
struct page **pages, size_t num_pages,
--
2.25.1

2021-06-10 21:12:43

by Tyler Hicks

[permalink] [raw]
Subject: [PATCH v4 6/8] tee: Support kernel shm registration without dma-buf backing

Uncouple the registration of kernel shared memory buffers from the
TEE_SHM_DMA_BUF flag. Drivers may wish to allocate multi-page contiguous
shared memory regions but do not need them to be backed by a dma-buf
when the memory region is only used by the driver.

If the TEE implementation does not require shared memory to be
registered, clear the flag prior to calling the corresponding pool alloc
function. Update the OP-TEE driver to respect TEE_SHM_REGISTER, rather
than TEE_SHM_DMA_BUF, when deciding whether to (un)register on
alloc/free operations. The AMD-TEE driver continues to ignore the
TEE_SHM_REGISTER flag.

Allow callers of tee_shm_alloc_kernel_buf() to allocate and register a
shared memory region without the backing of dma-buf.

Signed-off-by: Tyler Hicks <[email protected]>
---
drivers/tee/optee/shm_pool.c | 5 ++---
drivers/tee/tee_shm.c | 13 +++++++++++--
2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c
index da06ce9b9313..6054343a29fb 100644
--- a/drivers/tee/optee/shm_pool.c
+++ b/drivers/tee/optee/shm_pool.c
@@ -27,7 +27,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
shm->paddr = page_to_phys(page);
shm->size = PAGE_SIZE << order;

- if (shm->flags & TEE_SHM_DMA_BUF) {
+ if (shm->flags & TEE_SHM_REGISTER) {
unsigned int nr_pages = 1 << order, i;
struct page **pages;

@@ -42,7 +42,6 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
page++;
}

- shm->flags |= TEE_SHM_REGISTER;
rc = optee_shm_register(shm->ctx, shm, pages, nr_pages,
(unsigned long)shm->kaddr);
kfree(pages);
@@ -60,7 +59,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
static void pool_op_free(struct tee_shm_pool_mgr *poolm,
struct tee_shm *shm)
{
- if (shm->flags & TEE_SHM_DMA_BUF)
+ if (shm->flags & TEE_SHM_REGISTER)
optee_shm_unregister(shm->ctx, shm);

free_pages((unsigned long)shm->kaddr, get_order(shm->size));
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index c65e44707cd6..26a76f817c57 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -117,7 +117,7 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
return ERR_PTR(-EINVAL);
}

- if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF))) {
+ if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF | TEE_SHM_REGISTER))) {
dev_err(teedev->dev.parent, "invalid shm flags 0x%x", flags);
return ERR_PTR(-EINVAL);
}
@@ -137,6 +137,15 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
goto err_dev_put;
}

+ if (!teedev->desc->ops->shm_register ||
+ !teedev->desc->ops->shm_unregister) {
+ /* registration is not required by the TEE implementation */
+ flags &= ~TEE_SHM_REGISTER;
+ } else if (flags & TEE_SHM_DMA_BUF) {
+ /* all dma-buf backed shm allocations are registered */
+ flags |= TEE_SHM_REGISTER;
+ }
+
shm->flags = flags | TEE_SHM_POOL;
shm->ctx = ctx;
if (flags & TEE_SHM_DMA_BUF)
@@ -207,7 +216,7 @@ EXPORT_SYMBOL_GPL(tee_shm_alloc);
*/
struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size)
{
- return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
+ return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED | TEE_SHM_REGISTER);
}
EXPORT_SYMBOL_GPL(tee_shm_alloc_kernel_buf);

--
2.25.1

2021-06-10 21:14:25

by Tyler Hicks

[permalink] [raw]
Subject: [PATCH v4 7/8] tpm_ftpm_tee: Free and unregister TEE shared memory during kexec

dma-buf backed shared memory cannot be reliably freed and unregistered
during a kexec operation even when tee_shm_free() is called on the shm
from a .shutdown hook. The problem occurs because dma_buf_put() calls
fput() which then uses task_work_add(), with the TWA_RESUME parameter,
to queue tee_shm_release() to be called before the current task returns
to user mode. However, the current task never returns to user mode
before the kexec completes so the memory is never freed nor
unregistered.

Use tee_shm_alloc_kernel_buf() to avoid dma-buf backed shared memory
allocation so that tee_shm_free() can directly call tee_shm_release().
This will ensure that the shm can be freed and unregistered during a
kexec operation.

Fixes: 09e574831b27 ("tpm/tpm_ftpm_tee: A driver for firmware TPM running inside TEE")
Fixes: 1760eb689ed6 ("tpm/tpm_ftpm_tee: add shutdown call back")
Signed-off-by: Tyler Hicks <[email protected]>
---
drivers/char/tpm/tpm_ftpm_tee.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c
index 2ccdf8ac6994..6e3235565a4d 100644
--- a/drivers/char/tpm/tpm_ftpm_tee.c
+++ b/drivers/char/tpm/tpm_ftpm_tee.c
@@ -254,11 +254,11 @@ static int ftpm_tee_probe(struct device *dev)
pvt_data->session = sess_arg.session;

/* Allocate dynamic shared memory with fTPM TA */
- pvt_data->shm = tee_shm_alloc(pvt_data->ctx,
- MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE,
- TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
+ pvt_data->shm = tee_shm_alloc_kernel_buf(pvt_data->ctx,
+ MAX_COMMAND_SIZE +
+ MAX_RESPONSE_SIZE);
if (IS_ERR(pvt_data->shm)) {
- dev_err(dev, "%s: tee_shm_alloc failed\n", __func__);
+ dev_err(dev, "%s: tee_shm_alloc_kernel_buf failed\n", __func__);
rc = -ENOMEM;
goto out_shm_alloc;
}
--
2.25.1

2021-06-11 05:19:09

by Sumit Garg

[permalink] [raw]
Subject: Re: [PATCH v4 6/8] tee: Support kernel shm registration without dma-buf backing

On Fri, 11 Jun 2021 at 02:39, Tyler Hicks <[email protected]> wrote:
>
> Uncouple the registration of kernel shared memory buffers from the
> TEE_SHM_DMA_BUF flag. Drivers may wish to allocate multi-page contiguous
> shared memory regions but do not need them to be backed by a dma-buf
> when the memory region is only used by the driver.
>
> If the TEE implementation does not require shared memory to be
> registered, clear the flag prior to calling the corresponding pool alloc
> function. Update the OP-TEE driver to respect TEE_SHM_REGISTER, rather
> than TEE_SHM_DMA_BUF, when deciding whether to (un)register on
> alloc/free operations.

> The AMD-TEE driver continues to ignore the
> TEE_SHM_REGISTER flag.
>

That's the main point that no other TEE implementation would honour
TEE_SHM_REGISTER and I think it's just the incorrect usage of
TEE_SHM_REGISTER flag to suffice OP-TEE underlying implementation.

> Allow callers of tee_shm_alloc_kernel_buf() to allocate and register a
> shared memory region without the backing of dma-buf.
>
> Signed-off-by: Tyler Hicks <[email protected]>
> ---
> drivers/tee/optee/shm_pool.c | 5 ++---
> drivers/tee/tee_shm.c | 13 +++++++++++--
> 2 files changed, 13 insertions(+), 5 deletions(-)
>

This patch is just mixing two separate approaches to TEE shared
memory. Have a look at alternative suggestions below.

> diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c
> index da06ce9b9313..6054343a29fb 100644
> --- a/drivers/tee/optee/shm_pool.c
> +++ b/drivers/tee/optee/shm_pool.c
> @@ -27,7 +27,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> shm->paddr = page_to_phys(page);
> shm->size = PAGE_SIZE << order;
>
> - if (shm->flags & TEE_SHM_DMA_BUF) {
> + if (shm->flags & TEE_SHM_REGISTER) {

Here you can just do following check instead:

if (!(shm->flags & TEE_SHM_PRIV)) {

And this flag needs to be passed from the call sites here [1] [2].

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/tee/optee/core.c#n280
[2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/tee/optee/call.c#n186

> unsigned int nr_pages = 1 << order, i;
> struct page **pages;
>
> @@ -42,7 +42,6 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> page++;
> }
>
> - shm->flags |= TEE_SHM_REGISTER;

This should remain as it is.

> rc = optee_shm_register(shm->ctx, shm, pages, nr_pages,
> (unsigned long)shm->kaddr);
> kfree(pages);
> @@ -60,7 +59,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> static void pool_op_free(struct tee_shm_pool_mgr *poolm,
> struct tee_shm *shm)
> {
> - if (shm->flags & TEE_SHM_DMA_BUF)
> + if (shm->flags & TEE_SHM_REGISTER)

Same as above.

> optee_shm_unregister(shm->ctx, shm);
>
> free_pages((unsigned long)shm->kaddr, get_order(shm->size));
> diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
> index c65e44707cd6..26a76f817c57 100644
> --- a/drivers/tee/tee_shm.c
> +++ b/drivers/tee/tee_shm.c
> @@ -117,7 +117,7 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
> return ERR_PTR(-EINVAL);
> }
>
> - if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF))) {
> + if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF | TEE_SHM_REGISTER))) {

No need for this change.

> dev_err(teedev->dev.parent, "invalid shm flags 0x%x", flags);
> return ERR_PTR(-EINVAL);
> }
> @@ -137,6 +137,15 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
> goto err_dev_put;
> }
>
> + if (!teedev->desc->ops->shm_register ||
> + !teedev->desc->ops->shm_unregister) {
> + /* registration is not required by the TEE implementation */
> + flags &= ~TEE_SHM_REGISTER;
> + } else if (flags & TEE_SHM_DMA_BUF) {
> + /* all dma-buf backed shm allocations are registered */
> + flags |= TEE_SHM_REGISTER;
> + }
> +

This change isn't required as well as underlying TEE implementation:
OP-TEE in this case knows how to implement shared memory allocation
whether to use reserved shared memory pool or dynamic shared memory
pool. For more details see shared memory pool creation in
optee_probe().

> shm->flags = flags | TEE_SHM_POOL;
> shm->ctx = ctx;
> if (flags & TEE_SHM_DMA_BUF)
> @@ -207,7 +216,7 @@ EXPORT_SYMBOL_GPL(tee_shm_alloc);
> */
> struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size)
> {
> - return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
> + return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED | TEE_SHM_REGISTER);

Here it could just be:

return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED);

-Sumit

> }
> EXPORT_SYMBOL_GPL(tee_shm_alloc_kernel_buf);
>
> --
> 2.25.1
>

2021-06-11 09:07:21

by Jens Wiklander

[permalink] [raw]
Subject: Re: [PATCH v4 1/8] optee: Fix memory leak when failing to register shm pages

On Thu, Jun 10, 2021 at 11:09 PM Tyler Hicks
<[email protected]> wrote:
>
> Free the previously allocated pages when we encounter an error condition
> while attempting to register the pages with the secure world.
>
> Fixes: a249dd200d03 ("tee: optee: Fix dynamic shm pool allocations")
> Fixes: 5a769f6ff439 ("optee: Fix multi page dynamic shm pool alloc")
> Signed-off-by: Tyler Hicks <[email protected]>
> ---
> drivers/tee/optee/shm_pool.c | 12 ++++++++++--
> 1 file changed, 10 insertions(+), 2 deletions(-)

Looks good.

Reviewed-by: Jens Wiklander <[email protected]>

2021-06-11 09:11:21

by Jens Wiklander

[permalink] [raw]
Subject: Re: [PATCH v4 2/8] optee: Refuse to load the driver under the kdump kernel

On Thu, Jun 10, 2021 at 11:09 PM Tyler Hicks
<[email protected]> wrote:
>
> Fix a hung task issue, seen when booting the kdump kernel, that is
> caused by all of the secure world threads being in a permanent suspended
> state:
>
> INFO: task swapper/0:1 blocked for more than 120 seconds.
> Not tainted 5.4.83 #1
> "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> swapper/0 D 0 1 0 0x00000028
> Call trace:
> __switch_to+0xc8/0x118
> __schedule+0x2e0/0x700
> schedule+0x38/0xb8
> schedule_timeout+0x258/0x388
> wait_for_completion+0x16c/0x4b8
> optee_cq_wait_for_completion+0x28/0xa8
> optee_disable_shm_cache+0xb8/0xf8
> optee_probe+0x560/0x61c
> platform_drv_probe+0x58/0xa8
> really_probe+0xe0/0x338
> driver_probe_device+0x5c/0xf0
> device_driver_attach+0x74/0x80
> __driver_attach+0x64/0xe0
> bus_for_each_dev+0x84/0xd8
> driver_attach+0x30/0x40
> bus_add_driver+0x188/0x1e8
> driver_register+0x64/0x110
> __platform_driver_register+0x54/0x60
> optee_driver_init+0x20/0x28
> do_one_initcall+0x54/0x24c
> kernel_init_freeable+0x1e8/0x2c0
> kernel_init+0x18/0x118
> ret_from_fork+0x10/0x18
>
> The invoke_fn hook returned OPTEE_SMC_RETURN_ETHREAD_LIMIT, indicating
> that the secure world threads were all in a suspended state at the time
> of the kernel crash. This intermittently prevented the kdump kernel from
> booting, resulting in a failure to collect the kernel dump.
>
> Make kernel dump collection more reliable on systems utilizing OP-TEE by
> refusing to load the driver under the kdump kernel.
>
> Signed-off-by: Tyler Hicks <[email protected]>
> ---
> drivers/tee/optee/core.c | 11 +++++++++++
> 1 file changed, 11 insertions(+)

Looks good
Reviewed-by: Jens Wiklander <[email protected]>

2021-06-11 09:15:25

by Jens Wiklander

[permalink] [raw]
Subject: Re: [PATCH v4 3/8] optee: fix tee out of memory failure seen during kexec reboot

On Thu, Jun 10, 2021 at 11:09 PM Tyler Hicks
<[email protected]> wrote:
>
> From: Allen Pais <[email protected]>
>
> The following out of memory errors are seen on kexec reboot
> from the optee core.
>
> [ 0.368428] tee_bnxt_fw optee-clnt0: tee_shm_alloc failed
> [ 0.368461] tee_bnxt_fw: probe of optee-clnt0 failed with error -22
>
> tee_shm_release() is not invoked on dma shm buffer.
>
> Implement .shutdown() method to handle the release of the buffers
> correctly.
>
> More info:
> https://github.com/OP-TEE/optee_os/issues/3637
>
> Signed-off-by: Allen Pais <[email protected]>
> Reviewed-by: Tyler Hicks <[email protected]>

Do we really need this considering the patch "optee: Refuse to load
the driver under the kdump kernel"?

Jens

2021-06-11 12:54:53

by Tyler Hicks

[permalink] [raw]
Subject: Re: [PATCH v4 3/8] optee: fix tee out of memory failure seen during kexec reboot

On 2021-06-11 11:11:33, Jens Wiklander wrote:
> On Thu, Jun 10, 2021 at 11:09 PM Tyler Hicks
> <[email protected]> wrote:
> >
> > From: Allen Pais <[email protected]>
> >
> > The following out of memory errors are seen on kexec reboot
> > from the optee core.
> >
> > [ 0.368428] tee_bnxt_fw optee-clnt0: tee_shm_alloc failed
> > [ 0.368461] tee_bnxt_fw: probe of optee-clnt0 failed with error -22
> >
> > tee_shm_release() is not invoked on dma shm buffer.
> >
> > Implement .shutdown() method to handle the release of the buffers
> > correctly.
> >
> > More info:
> > https://github.com/OP-TEE/optee_os/issues/3637
> >
> > Signed-off-by: Allen Pais <[email protected]>
> > Reviewed-by: Tyler Hicks <[email protected]>
>
> Do we really need this considering the patch "optee: Refuse to load
> the driver under the kdump kernel"?

Yes. That patch fixes boot hangs when all of the OP-TEE threads were in
the suspended state at the time of a kernel panic. The kexec into the
kdump kernel after a panic is an "emergency" kexec that doesn't even
call .shutdown hooks. There's no way for the OP-TEE driver to clean up
after itself.

This patch disables the shm cache (and unregisters the shm buffers)
during a normal kexec from one perfectly working kernel into a new
kernel. This is required because the new kernel will not be able to
handle the virtual addresses that were cached under the old kernel. The
new kernel has an entirely different memory layout and the old addresses
point to unmapped memory or memory that's mapped but probably not a TEE
shm.

Tyler

>
> Jens
>

2021-06-11 13:13:25

by Tyler Hicks

[permalink] [raw]
Subject: Re: [PATCH v4 6/8] tee: Support kernel shm registration without dma-buf backing

On 2021-06-11 10:46:20, Sumit Garg wrote:
> On Fri, 11 Jun 2021 at 02:39, Tyler Hicks <[email protected]> wrote:
> >
> > Uncouple the registration of kernel shared memory buffers from the
> > TEE_SHM_DMA_BUF flag. Drivers may wish to allocate multi-page contiguous
> > shared memory regions but do not need them to be backed by a dma-buf
> > when the memory region is only used by the driver.
> >
> > If the TEE implementation does not require shared memory to be
> > registered, clear the flag prior to calling the corresponding pool alloc
> > function. Update the OP-TEE driver to respect TEE_SHM_REGISTER, rather
> > than TEE_SHM_DMA_BUF, when deciding whether to (un)register on
> > alloc/free operations.
>
> > The AMD-TEE driver continues to ignore the
> > TEE_SHM_REGISTER flag.
> >
>
> That's the main point that no other TEE implementation would honour
> TEE_SHM_REGISTER and I think it's just the incorrect usage of
> TEE_SHM_REGISTER flag to suffice OP-TEE underlying implementation.
>
> > Allow callers of tee_shm_alloc_kernel_buf() to allocate and register a
> > shared memory region without the backing of dma-buf.
> >
> > Signed-off-by: Tyler Hicks <[email protected]>
> > ---
> > drivers/tee/optee/shm_pool.c | 5 ++---
> > drivers/tee/tee_shm.c | 13 +++++++++++--
> > 2 files changed, 13 insertions(+), 5 deletions(-)
> >
>
> This patch is just mixing two separate approaches to TEE shared
> memory. Have a look at alternative suggestions below.
>
> > diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c
> > index da06ce9b9313..6054343a29fb 100644
> > --- a/drivers/tee/optee/shm_pool.c
> > +++ b/drivers/tee/optee/shm_pool.c
> > @@ -27,7 +27,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> > shm->paddr = page_to_phys(page);
> > shm->size = PAGE_SIZE << order;
> >
> > - if (shm->flags & TEE_SHM_DMA_BUF) {
> > + if (shm->flags & TEE_SHM_REGISTER) {
>
> Here you can just do following check instead:
>
> if (!(shm->flags & TEE_SHM_PRIV)) {

This is a bug fix series that's intended to fix the current and older
kernels. tee_shm_alloc_anon_kernel_buf()/TEE_SHM_PRIV is not present in
older kernels and isn't required to fix these kexec/kdump bugs. Your
suggestion feels like something that should be done in the allocator
rewrite that Jens is working on to clean all of this up going forward.

Tyler

>
> And this flag needs to be passed from the call sites here [1] [2].
>
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/tee/optee/core.c#n280
> [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/tee/optee/call.c#n186
>
> > unsigned int nr_pages = 1 << order, i;
> > struct page **pages;
> >
> > @@ -42,7 +42,6 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> > page++;
> > }
> >
> > - shm->flags |= TEE_SHM_REGISTER;
>
> This should remain as it is.
>
> > rc = optee_shm_register(shm->ctx, shm, pages, nr_pages,
> > (unsigned long)shm->kaddr);
> > kfree(pages);
> > @@ -60,7 +59,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> > static void pool_op_free(struct tee_shm_pool_mgr *poolm,
> > struct tee_shm *shm)
> > {
> > - if (shm->flags & TEE_SHM_DMA_BUF)
> > + if (shm->flags & TEE_SHM_REGISTER)
>
> Same as above.
>
> > optee_shm_unregister(shm->ctx, shm);
> >
> > free_pages((unsigned long)shm->kaddr, get_order(shm->size));
> > diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
> > index c65e44707cd6..26a76f817c57 100644
> > --- a/drivers/tee/tee_shm.c
> > +++ b/drivers/tee/tee_shm.c
> > @@ -117,7 +117,7 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
> > return ERR_PTR(-EINVAL);
> > }
> >
> > - if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF))) {
> > + if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF | TEE_SHM_REGISTER))) {
>
> No need for this change.
>
> > dev_err(teedev->dev.parent, "invalid shm flags 0x%x", flags);
> > return ERR_PTR(-EINVAL);
> > }
> > @@ -137,6 +137,15 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
> > goto err_dev_put;
> > }
> >
> > + if (!teedev->desc->ops->shm_register ||
> > + !teedev->desc->ops->shm_unregister) {
> > + /* registration is not required by the TEE implementation */
> > + flags &= ~TEE_SHM_REGISTER;
> > + } else if (flags & TEE_SHM_DMA_BUF) {
> > + /* all dma-buf backed shm allocations are registered */
> > + flags |= TEE_SHM_REGISTER;
> > + }
> > +
>
> This change isn't required as well as underlying TEE implementation:
> OP-TEE in this case knows how to implement shared memory allocation
> whether to use reserved shared memory pool or dynamic shared memory
> pool. For more details see shared memory pool creation in
> optee_probe().
>
> > shm->flags = flags | TEE_SHM_POOL;
> > shm->ctx = ctx;
> > if (flags & TEE_SHM_DMA_BUF)
> > @@ -207,7 +216,7 @@ EXPORT_SYMBOL_GPL(tee_shm_alloc);
> > */
> > struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size)
> > {
> > - return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
> > + return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED | TEE_SHM_REGISTER);
>
> Here it could just be:
>
> return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED);
>
> -Sumit
>
> > }
> > EXPORT_SYMBOL_GPL(tee_shm_alloc_kernel_buf);
> >
> > --
> > 2.25.1
> >
>

2021-06-11 13:19:44

by Tyler Hicks

[permalink] [raw]
Subject: Re: [PATCH v4 6/8] tee: Support kernel shm registration without dma-buf backing

On 2021-06-11 08:10:01, Tyler Hicks wrote:
> On 2021-06-11 10:46:20, Sumit Garg wrote:
> > On Fri, 11 Jun 2021 at 02:39, Tyler Hicks <[email protected]> wrote:
> > >
> > > Uncouple the registration of kernel shared memory buffers from the
> > > TEE_SHM_DMA_BUF flag. Drivers may wish to allocate multi-page contiguous
> > > shared memory regions but do not need them to be backed by a dma-buf
> > > when the memory region is only used by the driver.
> > >
> > > If the TEE implementation does not require shared memory to be
> > > registered, clear the flag prior to calling the corresponding pool alloc
> > > function. Update the OP-TEE driver to respect TEE_SHM_REGISTER, rather
> > > than TEE_SHM_DMA_BUF, when deciding whether to (un)register on
> > > alloc/free operations.
> >
> > > The AMD-TEE driver continues to ignore the
> > > TEE_SHM_REGISTER flag.
> > >
> >
> > That's the main point that no other TEE implementation would honour
> > TEE_SHM_REGISTER and I think it's just the incorrect usage of
> > TEE_SHM_REGISTER flag to suffice OP-TEE underlying implementation.
> >
> > > Allow callers of tee_shm_alloc_kernel_buf() to allocate and register a
> > > shared memory region without the backing of dma-buf.
> > >
> > > Signed-off-by: Tyler Hicks <[email protected]>
> > > ---
> > > drivers/tee/optee/shm_pool.c | 5 ++---
> > > drivers/tee/tee_shm.c | 13 +++++++++++--
> > > 2 files changed, 13 insertions(+), 5 deletions(-)
> > >
> >
> > This patch is just mixing two separate approaches to TEE shared
> > memory. Have a look at alternative suggestions below.
> >
> > > diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c
> > > index da06ce9b9313..6054343a29fb 100644
> > > --- a/drivers/tee/optee/shm_pool.c
> > > +++ b/drivers/tee/optee/shm_pool.c
> > > @@ -27,7 +27,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> > > shm->paddr = page_to_phys(page);
> > > shm->size = PAGE_SIZE << order;
> > >
> > > - if (shm->flags & TEE_SHM_DMA_BUF) {
> > > + if (shm->flags & TEE_SHM_REGISTER) {
> >
> > Here you can just do following check instead:
> >
> > if (!(shm->flags & TEE_SHM_PRIV)) {
>
> This is a bug fix series that's intended to fix the current and older
> kernels. tee_shm_alloc_anon_kernel_buf()/TEE_SHM_PRIV is not present in
> older kernels and isn't required to fix these kexec/kdump bugs. Your
> suggestion feels like something that should be done in the allocator
> rewrite that Jens is working on to clean all of this up going forward.

I want to add that I do fully agree with you that TEE_SHM_REGISTER is an
OP-TEE thing and not a TEE thing. Ideally, it wouldn't be defined in
tee_drv.h and would be completely private to the OP-TEE driver.
Likewise, I don't think that tee_shm_register() should exist (certainly
not at the TEE level) because it only works with OP-TEE.

That said, I think the first step is to fix the kexec/kdump bugs and the
second step is to clean up the code to remove the layering violation of
exposing shm registration from the TEE interfaces.

Tyler

>
> Tyler
>
> >
> > And this flag needs to be passed from the call sites here [1] [2].
> >
> > [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/tee/optee/core.c#n280
> > [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/tee/optee/call.c#n186
> >
> > > unsigned int nr_pages = 1 << order, i;
> > > struct page **pages;
> > >
> > > @@ -42,7 +42,6 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> > > page++;
> > > }
> > >
> > > - shm->flags |= TEE_SHM_REGISTER;
> >
> > This should remain as it is.
> >
> > > rc = optee_shm_register(shm->ctx, shm, pages, nr_pages,
> > > (unsigned long)shm->kaddr);
> > > kfree(pages);
> > > @@ -60,7 +59,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> > > static void pool_op_free(struct tee_shm_pool_mgr *poolm,
> > > struct tee_shm *shm)
> > > {
> > > - if (shm->flags & TEE_SHM_DMA_BUF)
> > > + if (shm->flags & TEE_SHM_REGISTER)
> >
> > Same as above.
> >
> > > optee_shm_unregister(shm->ctx, shm);
> > >
> > > free_pages((unsigned long)shm->kaddr, get_order(shm->size));
> > > diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
> > > index c65e44707cd6..26a76f817c57 100644
> > > --- a/drivers/tee/tee_shm.c
> > > +++ b/drivers/tee/tee_shm.c
> > > @@ -117,7 +117,7 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
> > > return ERR_PTR(-EINVAL);
> > > }
> > >
> > > - if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF))) {
> > > + if ((flags & ~(TEE_SHM_MAPPED | TEE_SHM_DMA_BUF | TEE_SHM_REGISTER))) {
> >
> > No need for this change.
> >
> > > dev_err(teedev->dev.parent, "invalid shm flags 0x%x", flags);
> > > return ERR_PTR(-EINVAL);
> > > }
> > > @@ -137,6 +137,15 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags)
> > > goto err_dev_put;
> > > }
> > >
> > > + if (!teedev->desc->ops->shm_register ||
> > > + !teedev->desc->ops->shm_unregister) {
> > > + /* registration is not required by the TEE implementation */
> > > + flags &= ~TEE_SHM_REGISTER;
> > > + } else if (flags & TEE_SHM_DMA_BUF) {
> > > + /* all dma-buf backed shm allocations are registered */
> > > + flags |= TEE_SHM_REGISTER;
> > > + }
> > > +
> >
> > This change isn't required as well as underlying TEE implementation:
> > OP-TEE in this case knows how to implement shared memory allocation
> > whether to use reserved shared memory pool or dynamic shared memory
> > pool. For more details see shared memory pool creation in
> > optee_probe().
> >
> > > shm->flags = flags | TEE_SHM_POOL;
> > > shm->ctx = ctx;
> > > if (flags & TEE_SHM_DMA_BUF)
> > > @@ -207,7 +216,7 @@ EXPORT_SYMBOL_GPL(tee_shm_alloc);
> > > */
> > > struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size)
> > > {
> > > - return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
> > > + return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED | TEE_SHM_REGISTER);
> >
> > Here it could just be:
> >
> > return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED);
> >
> > -Sumit
> >
> > > }
> > > EXPORT_SYMBOL_GPL(tee_shm_alloc_kernel_buf);
> > >
> > > --
> > > 2.25.1
> > >
> >

2021-06-12 08:24:49

by Sumit Garg

[permalink] [raw]
Subject: Re: [PATCH v4 6/8] tee: Support kernel shm registration without dma-buf backing

On Fri, 11 Jun 2021 at 18:46, Tyler Hicks <[email protected]> wrote:
>
> On 2021-06-11 08:10:01, Tyler Hicks wrote:
> > On 2021-06-11 10:46:20, Sumit Garg wrote:
> > > On Fri, 11 Jun 2021 at 02:39, Tyler Hicks <[email protected]> wrote:
> > > >
> > > > Uncouple the registration of kernel shared memory buffers from the
> > > > TEE_SHM_DMA_BUF flag. Drivers may wish to allocate multi-page contiguous
> > > > shared memory regions but do not need them to be backed by a dma-buf
> > > > when the memory region is only used by the driver.
> > > >
> > > > If the TEE implementation does not require shared memory to be
> > > > registered, clear the flag prior to calling the corresponding pool alloc
> > > > function. Update the OP-TEE driver to respect TEE_SHM_REGISTER, rather
> > > > than TEE_SHM_DMA_BUF, when deciding whether to (un)register on
> > > > alloc/free operations.
> > >
> > > > The AMD-TEE driver continues to ignore the
> > > > TEE_SHM_REGISTER flag.
> > > >
> > >
> > > That's the main point that no other TEE implementation would honour
> > > TEE_SHM_REGISTER and I think it's just the incorrect usage of
> > > TEE_SHM_REGISTER flag to suffice OP-TEE underlying implementation.
> > >
> > > > Allow callers of tee_shm_alloc_kernel_buf() to allocate and register a
> > > > shared memory region without the backing of dma-buf.
> > > >
> > > > Signed-off-by: Tyler Hicks <[email protected]>
> > > > ---
> > > > drivers/tee/optee/shm_pool.c | 5 ++---
> > > > drivers/tee/tee_shm.c | 13 +++++++++++--
> > > > 2 files changed, 13 insertions(+), 5 deletions(-)
> > > >
> > >
> > > This patch is just mixing two separate approaches to TEE shared
> > > memory. Have a look at alternative suggestions below.
> > >
> > > > diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c
> > > > index da06ce9b9313..6054343a29fb 100644
> > > > --- a/drivers/tee/optee/shm_pool.c
> > > > +++ b/drivers/tee/optee/shm_pool.c
> > > > @@ -27,7 +27,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> > > > shm->paddr = page_to_phys(page);
> > > > shm->size = PAGE_SIZE << order;
> > > >
> > > > - if (shm->flags & TEE_SHM_DMA_BUF) {
> > > > + if (shm->flags & TEE_SHM_REGISTER) {
> > >
> > > Here you can just do following check instead:
> > >
> > > if (!(shm->flags & TEE_SHM_PRIV)) {
> >
> > This is a bug fix series that's intended to fix the current and older
> > kernels. tee_shm_alloc_anon_kernel_buf()/TEE_SHM_PRIV is not present in
> > older kernels and isn't required to fix these kexec/kdump bugs. Your
> > suggestion feels like something that should be done in the allocator
> > rewrite that Jens is working on to clean all of this up going forward.
>
> I want to add that I do fully agree with you that TEE_SHM_REGISTER is an
> OP-TEE thing and not a TEE thing. Ideally, it wouldn't be defined in
> tee_drv.h and would be completely private to the OP-TEE driver.
> Likewise, I don't think that tee_shm_register() should exist (certainly
> not at the TEE level) because it only works with OP-TEE.

I think there is some confusion going on. tee_shm_register() is a
standard interface that is listed in TEE client API specification as
an alternative approach to tee_shm_alloc(). As I earlier mentioned,
please read through "3.2.4. Shared Memory" in TEE Client API
Specification.

In the initial times, OP-TEE only supported tee_shm_alloc() approach
but with the addition of dynamic shared memory feature it became
possible to support tee_shm_register() as well but we had to add new
capability as TEE_GEN_CAP_REG_MEM in order to maintain OP-TEE
backwards compatibility. It can very well be the same case for AMD-TEE
which currently only supports tee_shm_alloc() approach.

The reason for confusion here seems to be that OP-TEE driver is
providing a way to leverage dynamic shared memory approach in order to
implement tee_shm_alloc() but that doesn't mean at TEE level we should
intermix both approaches via using TEE_SHM_REGISTER to implement
tee_shm_alloc().

>
> That said, I think the first step is to fix the kexec/kdump bugs and the
> second step is to clean up the code to remove the layering violation of
> exposing shm registration from the TEE interfaces.
>

Doesn't the following patch sound suitable to be backported to a
stable kernel? It has even less changes compared to your patch as well
:).

-Sumit

-------------------------------------------
Subject: [PATCH] tee: Correct inappropriate usage of TEE_SHM_DMA_BUF flag

Currently TEE_SHM_DMA_BUF flag has been inappropriately used to not
register shared memory allocated for private usage by underlying TEE
driver: OP-TEE in this case. So rather add a new flag as TEE_SHM_PRIV
that can be utilized by underlying TEE drivers for private allocation
and usage of shared memory.

With this corrected, allow tee_shm_alloc_kernel_buf() to allocate a
shared memory region without the backing of dma-buf.

Signed-off-by: Sumit Garg <[email protected]>
---
drivers/tee/optee/call.c | 2 +-
drivers/tee/optee/core.c | 3 ++-
drivers/tee/optee/shm_pool.c | 8 ++++++--
drivers/tee/tee_shm.c | 2 +-
include/linux/tee_drv.h | 1 +
5 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
index 6132cc8d014c..faaa13c9172b 100644
--- a/drivers/tee/optee/call.c
+++ b/drivers/tee/optee/call.c
@@ -184,7 +184,7 @@ static struct tee_shm *get_msg_arg(struct
tee_context *ctx, size_t num_params,
struct optee_msg_arg *ma;

shm = tee_shm_alloc(ctx, OPTEE_MSG_GET_ARG_SIZE(num_params),
- TEE_SHM_MAPPED);
+ TEE_SHM_MAPPED | TEE_SHM_PRIV);
if (IS_ERR(shm))
return shm;

diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
index ddb8f9ecf307..eac0e91ec559 100644
--- a/drivers/tee/optee/core.c
+++ b/drivers/tee/optee/core.c
@@ -277,7 +277,8 @@ static void optee_release(struct tee_context *ctx)
if (!ctxdata)
return;

- shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), TEE_SHM_MAPPED);
+ shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg),
+ TEE_SHM_MAPPED | TEE_SHM_PRIV);
if (!IS_ERR(shm)) {
arg = tee_shm_get_va(shm, 0);
/*
diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c
index d767eebf30bd..3b4a3853a10f 100644
--- a/drivers/tee/optee/shm_pool.c
+++ b/drivers/tee/optee/shm_pool.c
@@ -27,7 +27,11 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
shm->paddr = page_to_phys(page);
shm->size = PAGE_SIZE << order;

- if (shm->flags & TEE_SHM_DMA_BUF) {
+ /*
+ * Shared memory private to the OP-TEE driver doesn't need
+ * to be registered with OP-TEE.
+ */
+ if (!(shm->flags & TEE_SHM_PRIV)) {
unsigned int nr_pages = 1 << order, i;
struct page **pages;

@@ -52,7 +56,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
static void pool_op_free(struct tee_shm_pool_mgr *poolm,
struct tee_shm *shm)
{
- if (shm->flags & TEE_SHM_DMA_BUF)
+ if (!(shm->flags & TEE_SHM_PRIV))
optee_shm_unregister(shm->ctx, shm);

free_pages((unsigned long)shm->kaddr, get_order(shm->size));
diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
index c425ad80d6a6..f8b73e734094 100644
--- a/drivers/tee/tee_shm.c
+++ b/drivers/tee/tee_shm.c
@@ -207,7 +207,7 @@ EXPORT_SYMBOL_GPL(tee_shm_alloc);
*/
struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size)
{
- return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
+ return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED);
}
EXPORT_SYMBOL_GPL(tee_shm_alloc_kernel_buf);
diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
index 8990f7628387..3ebfea0781f1 100644
--- a/include/linux/tee_drv.h
+++ b/include/linux/tee_drv.h
@@ -27,6 +27,7 @@
#define TEE_SHM_USER_MAPPED BIT(4) /* Memory mapped in user space */
#define TEE_SHM_POOL BIT(5) /* Memory allocated from pool */
#define TEE_SHM_KERNEL_MAPPED BIT(6) /* Memory mapped in kernel space */
+#define TEE_SHM_PRIV BIT(7) /* Memory private to TEE driver */

struct device;
struct tee_device;

2021-06-13 08:19:42

by Tyler Hicks

[permalink] [raw]
Subject: Re: [PATCH v4 6/8] tee: Support kernel shm registration without dma-buf backing

On 2021-06-12 13:49:38, Sumit Garg wrote:
> On Fri, 11 Jun 2021 at 18:46, Tyler Hicks <[email protected]> wrote:
> >
> > On 2021-06-11 08:10:01, Tyler Hicks wrote:
> > > On 2021-06-11 10:46:20, Sumit Garg wrote:
> > > > On Fri, 11 Jun 2021 at 02:39, Tyler Hicks <[email protected]> wrote:
> > > > >
> > > > > Uncouple the registration of kernel shared memory buffers from the
> > > > > TEE_SHM_DMA_BUF flag. Drivers may wish to allocate multi-page contiguous
> > > > > shared memory regions but do not need them to be backed by a dma-buf
> > > > > when the memory region is only used by the driver.
> > > > >
> > > > > If the TEE implementation does not require shared memory to be
> > > > > registered, clear the flag prior to calling the corresponding pool alloc
> > > > > function. Update the OP-TEE driver to respect TEE_SHM_REGISTER, rather
> > > > > than TEE_SHM_DMA_BUF, when deciding whether to (un)register on
> > > > > alloc/free operations.
> > > >
> > > > > The AMD-TEE driver continues to ignore the
> > > > > TEE_SHM_REGISTER flag.
> > > > >
> > > >
> > > > That's the main point that no other TEE implementation would honour
> > > > TEE_SHM_REGISTER and I think it's just the incorrect usage of
> > > > TEE_SHM_REGISTER flag to suffice OP-TEE underlying implementation.
> > > >
> > > > > Allow callers of tee_shm_alloc_kernel_buf() to allocate and register a
> > > > > shared memory region without the backing of dma-buf.
> > > > >
> > > > > Signed-off-by: Tyler Hicks <[email protected]>
> > > > > ---
> > > > > drivers/tee/optee/shm_pool.c | 5 ++---
> > > > > drivers/tee/tee_shm.c | 13 +++++++++++--
> > > > > 2 files changed, 13 insertions(+), 5 deletions(-)
> > > > >
> > > >
> > > > This patch is just mixing two separate approaches to TEE shared
> > > > memory. Have a look at alternative suggestions below.
> > > >
> > > > > diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c
> > > > > index da06ce9b9313..6054343a29fb 100644
> > > > > --- a/drivers/tee/optee/shm_pool.c
> > > > > +++ b/drivers/tee/optee/shm_pool.c
> > > > > @@ -27,7 +27,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> > > > > shm->paddr = page_to_phys(page);
> > > > > shm->size = PAGE_SIZE << order;
> > > > >
> > > > > - if (shm->flags & TEE_SHM_DMA_BUF) {
> > > > > + if (shm->flags & TEE_SHM_REGISTER) {
> > > >
> > > > Here you can just do following check instead:
> > > >
> > > > if (!(shm->flags & TEE_SHM_PRIV)) {
> > >
> > > This is a bug fix series that's intended to fix the current and older
> > > kernels. tee_shm_alloc_anon_kernel_buf()/TEE_SHM_PRIV is not present in
> > > older kernels and isn't required to fix these kexec/kdump bugs. Your
> > > suggestion feels like something that should be done in the allocator
> > > rewrite that Jens is working on to clean all of this up going forward.
> >
> > I want to add that I do fully agree with you that TEE_SHM_REGISTER is an
> > OP-TEE thing and not a TEE thing. Ideally, it wouldn't be defined in
> > tee_drv.h and would be completely private to the OP-TEE driver.
> > Likewise, I don't think that tee_shm_register() should exist (certainly
> > not at the TEE level) because it only works with OP-TEE.
>
> I think there is some confusion going on. tee_shm_register() is a
> standard interface that is listed in TEE client API specification as
> an alternative approach to tee_shm_alloc(). As I earlier mentioned,
> please read through "3.2.4. Shared Memory" in TEE Client API
> Specification.

Thanks for the reminder to go read this spec. I had forgotten to read it
after you previously mentioned it.

Yes, there was confusion on my part due to reading the code but not
discovering/reading the spec prior to now.

> In the initial times, OP-TEE only supported tee_shm_alloc() approach
> but with the addition of dynamic shared memory feature it became
> possible to support tee_shm_register() as well but we had to add new
> capability as TEE_GEN_CAP_REG_MEM in order to maintain OP-TEE
> backwards compatibility. It can very well be the same case for AMD-TEE
> which currently only supports tee_shm_alloc() approach.
>
> The reason for confusion here seems to be that OP-TEE driver is
> providing a way to leverage dynamic shared memory approach in order to
> implement tee_shm_alloc() but that doesn't mean at TEE level we should
> intermix both approaches via using TEE_SHM_REGISTER to implement
> tee_shm_alloc().

I think that was the reason for my confusion. I didn't understand why
AMD-TEE didn't have the same need to register memory.

>
> >
> > That said, I think the first step is to fix the kexec/kdump bugs and the
> > second step is to clean up the code to remove the layering violation of
> > exposing shm registration from the TEE interfaces.
> >
>
> Doesn't the following patch sound suitable to be backported to a
> stable kernel? It has even less changes compared to your patch as well
> :).
>
> -Sumit
>
> -------------------------------------------
> Subject: [PATCH] tee: Correct inappropriate usage of TEE_SHM_DMA_BUF flag
>
> Currently TEE_SHM_DMA_BUF flag has been inappropriately used to not

I think the "not" at the end of this line should be removed, right?

> register shared memory allocated for private usage by underlying TEE
> driver: OP-TEE in this case. So rather add a new flag as TEE_SHM_PRIV
> that can be utilized by underlying TEE drivers for private allocation
> and usage of shared memory.
>
> With this corrected, allow tee_shm_alloc_kernel_buf() to allocate a
> shared memory region without the backing of dma-buf.

I noticed a couple things wrong with this patch during testing.

The first is obviously an oversight. tee_shm_alloc() needs to be updated
to accept the TEE_SHM_PRIV or it'll throw an "invalid shm flags ..."
error.

The second issue is a little more unclear. I am testing these patches
along with a debugging patch that prints a bit of info when
optee_shm_register() or optee_shm_unregister() is called so that I can
track the (un)registrations. I built a kernel with my v4 series but with
this patch in place of my 'tee: Support kernel shm registration without
dma-buf backing' patch. I performed 10x kexec operations and then let
the system set idle for a while. After a while of sitting idle, I
noticed a couple calls to optee_shm_register() that I hadn't seen before
(and haven't seen again). It made me worried that your patch could
result in us registering shared memory that we previously weren't
registering so I decided to try to perform another kexec operation to
ensure that the two shms were properly unregistered. At this point, the
system became unresponsive and I wasn't able to get a stack trace or any
more useful information about what happened.

Unfortunately, my debugging patch was only printing the shm's virtual
address and the shm's size. The shm's size was 4096 bytes.

My current thought is that the two new/unexpected calls to
optee_shm_register() were triggered by one of the tee_shm_alloc()'s in
drivers/tee/optee/rpc.c. Neither of those shm allocations would have
been registered before your change but they both would be after your
change. I think the easy fix is to use the TEE_SHM_PRIV flag for both of
the allocations in rpc.c. Do you agree? If so, I'll make these changes
and fold this patch into my series and send out a v5.

I still can't explain why the system became unresponsive after the two
registrations...

>
> Signed-off-by: Sumit Garg <[email protected]>
> ---
> drivers/tee/optee/call.c | 2 +-
> drivers/tee/optee/core.c | 3 ++-
> drivers/tee/optee/shm_pool.c | 8 ++++++--
> drivers/tee/tee_shm.c | 2 +-
> include/linux/tee_drv.h | 1 +
> 5 files changed, 11 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
> index 6132cc8d014c..faaa13c9172b 100644
> --- a/drivers/tee/optee/call.c
> +++ b/drivers/tee/optee/call.c
> @@ -184,7 +184,7 @@ static struct tee_shm *get_msg_arg(struct
> tee_context *ctx, size_t num_params,
> struct optee_msg_arg *ma;
>
> shm = tee_shm_alloc(ctx, OPTEE_MSG_GET_ARG_SIZE(num_params),
> - TEE_SHM_MAPPED);
> + TEE_SHM_MAPPED | TEE_SHM_PRIV);
> if (IS_ERR(shm))
> return shm;
>
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index ddb8f9ecf307..eac0e91ec559 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -277,7 +277,8 @@ static void optee_release(struct tee_context *ctx)
> if (!ctxdata)
> return;
>
> - shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), TEE_SHM_MAPPED);
> + shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg),
> + TEE_SHM_MAPPED | TEE_SHM_PRIV);
> if (!IS_ERR(shm)) {
> arg = tee_shm_get_va(shm, 0);
> /*
> diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c
> index d767eebf30bd..3b4a3853a10f 100644
> --- a/drivers/tee/optee/shm_pool.c
> +++ b/drivers/tee/optee/shm_pool.c
> @@ -27,7 +27,11 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> shm->paddr = page_to_phys(page);
> shm->size = PAGE_SIZE << order;
>
> - if (shm->flags & TEE_SHM_DMA_BUF) {
> + /*
> + * Shared memory private to the OP-TEE driver doesn't need
> + * to be registered with OP-TEE.
> + */
> + if (!(shm->flags & TEE_SHM_PRIV)) {
> unsigned int nr_pages = 1 << order, i;
> struct page **pages;
>
> @@ -52,7 +56,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> static void pool_op_free(struct tee_shm_pool_mgr *poolm,
> struct tee_shm *shm)
> {
> - if (shm->flags & TEE_SHM_DMA_BUF)
> + if (!(shm->flags & TEE_SHM_PRIV))
> optee_shm_unregister(shm->ctx, shm);
>
> free_pages((unsigned long)shm->kaddr, get_order(shm->size));
> diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
> index c425ad80d6a6..f8b73e734094 100644
> --- a/drivers/tee/tee_shm.c
> +++ b/drivers/tee/tee_shm.c
> @@ -207,7 +207,7 @@ EXPORT_SYMBOL_GPL(tee_shm_alloc);
> */
> struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size)
> {
> - return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
> + return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED);
> }
> EXPORT_SYMBOL_GPL(tee_shm_alloc_kernel_buf);

This was a malformed patch due to this hunk. I think an empty line was
left out of the context.

Tyler

> diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
> index 8990f7628387..3ebfea0781f1 100644
> --- a/include/linux/tee_drv.h
> +++ b/include/linux/tee_drv.h
> @@ -27,6 +27,7 @@
> #define TEE_SHM_USER_MAPPED BIT(4) /* Memory mapped in user space */
> #define TEE_SHM_POOL BIT(5) /* Memory allocated from pool */
> #define TEE_SHM_KERNEL_MAPPED BIT(6) /* Memory mapped in kernel space */
> +#define TEE_SHM_PRIV BIT(7) /* Memory private to TEE driver */
>
> struct device;
> struct tee_device;
>

2021-06-14 05:02:48

by Sumit Garg

[permalink] [raw]
Subject: Re: [PATCH v4 6/8] tee: Support kernel shm registration without dma-buf backing

On Sun, 13 Jun 2021 at 13:46, Tyler Hicks <[email protected]> wrote:
>
> On 2021-06-12 13:49:38, Sumit Garg wrote:
> > On Fri, 11 Jun 2021 at 18:46, Tyler Hicks <[email protected]> wrote:
> > >
> > > On 2021-06-11 08:10:01, Tyler Hicks wrote:
> > > > On 2021-06-11 10:46:20, Sumit Garg wrote:
> > > > > On Fri, 11 Jun 2021 at 02:39, Tyler Hicks <[email protected]> wrote:
> > > > > >
> > > > > > Uncouple the registration of kernel shared memory buffers from the
> > > > > > TEE_SHM_DMA_BUF flag. Drivers may wish to allocate multi-page contiguous
> > > > > > shared memory regions but do not need them to be backed by a dma-buf
> > > > > > when the memory region is only used by the driver.
> > > > > >
> > > > > > If the TEE implementation does not require shared memory to be
> > > > > > registered, clear the flag prior to calling the corresponding pool alloc
> > > > > > function. Update the OP-TEE driver to respect TEE_SHM_REGISTER, rather
> > > > > > than TEE_SHM_DMA_BUF, when deciding whether to (un)register on
> > > > > > alloc/free operations.
> > > > >
> > > > > > The AMD-TEE driver continues to ignore the
> > > > > > TEE_SHM_REGISTER flag.
> > > > > >
> > > > >
> > > > > That's the main point that no other TEE implementation would honour
> > > > > TEE_SHM_REGISTER and I think it's just the incorrect usage of
> > > > > TEE_SHM_REGISTER flag to suffice OP-TEE underlying implementation.
> > > > >
> > > > > > Allow callers of tee_shm_alloc_kernel_buf() to allocate and register a
> > > > > > shared memory region without the backing of dma-buf.
> > > > > >
> > > > > > Signed-off-by: Tyler Hicks <[email protected]>
> > > > > > ---
> > > > > > drivers/tee/optee/shm_pool.c | 5 ++---
> > > > > > drivers/tee/tee_shm.c | 13 +++++++++++--
> > > > > > 2 files changed, 13 insertions(+), 5 deletions(-)
> > > > > >
> > > > >
> > > > > This patch is just mixing two separate approaches to TEE shared
> > > > > memory. Have a look at alternative suggestions below.
> > > > >
> > > > > > diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c
> > > > > > index da06ce9b9313..6054343a29fb 100644
> > > > > > --- a/drivers/tee/optee/shm_pool.c
> > > > > > +++ b/drivers/tee/optee/shm_pool.c
> > > > > > @@ -27,7 +27,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> > > > > > shm->paddr = page_to_phys(page);
> > > > > > shm->size = PAGE_SIZE << order;
> > > > > >
> > > > > > - if (shm->flags & TEE_SHM_DMA_BUF) {
> > > > > > + if (shm->flags & TEE_SHM_REGISTER) {
> > > > >
> > > > > Here you can just do following check instead:
> > > > >
> > > > > if (!(shm->flags & TEE_SHM_PRIV)) {
> > > >
> > > > This is a bug fix series that's intended to fix the current and older
> > > > kernels. tee_shm_alloc_anon_kernel_buf()/TEE_SHM_PRIV is not present in
> > > > older kernels and isn't required to fix these kexec/kdump bugs. Your
> > > > suggestion feels like something that should be done in the allocator
> > > > rewrite that Jens is working on to clean all of this up going forward.
> > >
> > > I want to add that I do fully agree with you that TEE_SHM_REGISTER is an
> > > OP-TEE thing and not a TEE thing. Ideally, it wouldn't be defined in
> > > tee_drv.h and would be completely private to the OP-TEE driver.
> > > Likewise, I don't think that tee_shm_register() should exist (certainly
> > > not at the TEE level) because it only works with OP-TEE.
> >
> > I think there is some confusion going on. tee_shm_register() is a
> > standard interface that is listed in TEE client API specification as
> > an alternative approach to tee_shm_alloc(). As I earlier mentioned,
> > please read through "3.2.4. Shared Memory" in TEE Client API
> > Specification.
>
> Thanks for the reminder to go read this spec. I had forgotten to read it
> after you previously mentioned it.
>
> Yes, there was confusion on my part due to reading the code but not
> discovering/reading the spec prior to now.
>
> > In the initial times, OP-TEE only supported tee_shm_alloc() approach
> > but with the addition of dynamic shared memory feature it became
> > possible to support tee_shm_register() as well but we had to add new
> > capability as TEE_GEN_CAP_REG_MEM in order to maintain OP-TEE
> > backwards compatibility. It can very well be the same case for AMD-TEE
> > which currently only supports tee_shm_alloc() approach.
> >
> > The reason for confusion here seems to be that OP-TEE driver is
> > providing a way to leverage dynamic shared memory approach in order to
> > implement tee_shm_alloc() but that doesn't mean at TEE level we should
> > intermix both approaches via using TEE_SHM_REGISTER to implement
> > tee_shm_alloc().
>
> I think that was the reason for my confusion. I didn't understand why
> AMD-TEE didn't have the same need to register memory.
>
> >
> > >
> > > That said, I think the first step is to fix the kexec/kdump bugs and the
> > > second step is to clean up the code to remove the layering violation of
> > > exposing shm registration from the TEE interfaces.
> > >
> >
> > Doesn't the following patch sound suitable to be backported to a
> > stable kernel? It has even less changes compared to your patch as well
> > :).
> >
> > -Sumit
> >
> > -------------------------------------------
> > Subject: [PATCH] tee: Correct inappropriate usage of TEE_SHM_DMA_BUF flag
> >
> > Currently TEE_SHM_DMA_BUF flag has been inappropriately used to not
>
> I think the "not" at the end of this line should be removed, right?
>

No, actually shared memory allocated for OP-TEE driver internal usage
didn't enable this flag while in all other invocations TEE_SHM_DMA_BUF
is enabled as the driver's private shared memory isn't required to be
registered with OP-TEE.

> > register shared memory allocated for private usage by underlying TEE
> > driver: OP-TEE in this case. So rather add a new flag as TEE_SHM_PRIV
> > that can be utilized by underlying TEE drivers for private allocation
> > and usage of shared memory.
> >
> > With this corrected, allow tee_shm_alloc_kernel_buf() to allocate a
> > shared memory region without the backing of dma-buf.
>
> I noticed a couple things wrong with this patch during testing.
>

Yeah you are right. Apologies for my oversight as this patch was
compile tested only.

> The first is obviously an oversight. tee_shm_alloc() needs to be updated
> to accept the TEE_SHM_PRIV or it'll throw an "invalid shm flags ..."
> error.
>

Agree.

> The second issue is a little more unclear. I am testing these patches
> along with a debugging patch that prints a bit of info when
> optee_shm_register() or optee_shm_unregister() is called so that I can
> track the (un)registrations. I built a kernel with my v4 series but with
> this patch in place of my 'tee: Support kernel shm registration without
> dma-buf backing' patch. I performed 10x kexec operations and then let
> the system set idle for a while. After a while of sitting idle, I
> noticed a couple calls to optee_shm_register() that I hadn't seen before
> (and haven't seen again). It made me worried that your patch could
> result in us registering shared memory that we previously weren't
> registering so I decided to try to perform another kexec operation to
> ensure that the two shms were properly unregistered. At this point, the
> system became unresponsive and I wasn't able to get a stack trace or any
> more useful information about what happened.
>
> Unfortunately, my debugging patch was only printing the shm's virtual
> address and the shm's size. The shm's size was 4096 bytes.
>
> My current thought is that the two new/unexpected calls to
> optee_shm_register() were triggered by one of the tee_shm_alloc()'s in
> drivers/tee/optee/rpc.c. Neither of those shm allocations would have
> been registered before your change but they both would be after your
> change. I think the easy fix is to use the TEE_SHM_PRIV flag for both of
> the allocations in rpc.c. Do you agree? If so, I'll make these changes
> and fold this patch into my series and send out a v5.

Yeah I agree, please use TEE_SHM_PRIV for private shm allocations in
drivers/tee/optee/rpc.c as well.

>
> I still can't explain why the system became unresponsive after the two
> registrations...

Probably the two registrations created a cyclic loop and cores got
hung in there.

-Sumit

>
> >
> > Signed-off-by: Sumit Garg <[email protected]>
> > ---
> > drivers/tee/optee/call.c | 2 +-
> > drivers/tee/optee/core.c | 3 ++-
> > drivers/tee/optee/shm_pool.c | 8 ++++++--
> > drivers/tee/tee_shm.c | 2 +-
> > include/linux/tee_drv.h | 1 +
> > 5 files changed, 11 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
> > index 6132cc8d014c..faaa13c9172b 100644
> > --- a/drivers/tee/optee/call.c
> > +++ b/drivers/tee/optee/call.c
> > @@ -184,7 +184,7 @@ static struct tee_shm *get_msg_arg(struct
> > tee_context *ctx, size_t num_params,
> > struct optee_msg_arg *ma;
> >
> > shm = tee_shm_alloc(ctx, OPTEE_MSG_GET_ARG_SIZE(num_params),
> > - TEE_SHM_MAPPED);
> > + TEE_SHM_MAPPED | TEE_SHM_PRIV);
> > if (IS_ERR(shm))
> > return shm;
> >
> > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> > index ddb8f9ecf307..eac0e91ec559 100644
> > --- a/drivers/tee/optee/core.c
> > +++ b/drivers/tee/optee/core.c
> > @@ -277,7 +277,8 @@ static void optee_release(struct tee_context *ctx)
> > if (!ctxdata)
> > return;
> >
> > - shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), TEE_SHM_MAPPED);
> > + shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg),
> > + TEE_SHM_MAPPED | TEE_SHM_PRIV);
> > if (!IS_ERR(shm)) {
> > arg = tee_shm_get_va(shm, 0);
> > /*
> > diff --git a/drivers/tee/optee/shm_pool.c b/drivers/tee/optee/shm_pool.c
> > index d767eebf30bd..3b4a3853a10f 100644
> > --- a/drivers/tee/optee/shm_pool.c
> > +++ b/drivers/tee/optee/shm_pool.c
> > @@ -27,7 +27,11 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> > shm->paddr = page_to_phys(page);
> > shm->size = PAGE_SIZE << order;
> >
> > - if (shm->flags & TEE_SHM_DMA_BUF) {
> > + /*
> > + * Shared memory private to the OP-TEE driver doesn't need
> > + * to be registered with OP-TEE.
> > + */
> > + if (!(shm->flags & TEE_SHM_PRIV)) {
> > unsigned int nr_pages = 1 << order, i;
> > struct page **pages;
> >
> > @@ -52,7 +56,7 @@ static int pool_op_alloc(struct tee_shm_pool_mgr *poolm,
> > static void pool_op_free(struct tee_shm_pool_mgr *poolm,
> > struct tee_shm *shm)
> > {
> > - if (shm->flags & TEE_SHM_DMA_BUF)
> > + if (!(shm->flags & TEE_SHM_PRIV))
> > optee_shm_unregister(shm->ctx, shm);
> >
> > free_pages((unsigned long)shm->kaddr, get_order(shm->size));
> > diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c
> > index c425ad80d6a6..f8b73e734094 100644
> > --- a/drivers/tee/tee_shm.c
> > +++ b/drivers/tee/tee_shm.c
> > @@ -207,7 +207,7 @@ EXPORT_SYMBOL_GPL(tee_shm_alloc);
> > */
> > struct tee_shm *tee_shm_alloc_kernel_buf(struct tee_context *ctx, size_t size)
> > {
> > - return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
> > + return tee_shm_alloc(ctx, size, TEE_SHM_MAPPED);
> > }
> > EXPORT_SYMBOL_GPL(tee_shm_alloc_kernel_buf);
>
> This was a malformed patch due to this hunk. I think an empty line was
> left out of the context.
>
> Tyler
>
> > diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h
> > index 8990f7628387..3ebfea0781f1 100644
> > --- a/include/linux/tee_drv.h
> > +++ b/include/linux/tee_drv.h
> > @@ -27,6 +27,7 @@
> > #define TEE_SHM_USER_MAPPED BIT(4) /* Memory mapped in user space */
> > #define TEE_SHM_POOL BIT(5) /* Memory allocated from pool */
> > #define TEE_SHM_KERNEL_MAPPED BIT(6) /* Memory mapped in kernel space */
> > +#define TEE_SHM_PRIV BIT(7) /* Memory private to TEE driver */
> >
> > struct device;
> > struct tee_device;
> >

2021-06-14 07:25:26

by Jens Wiklander

[permalink] [raw]
Subject: Re: [PATCH v4 3/8] optee: fix tee out of memory failure seen during kexec reboot

On Thu, Jun 10, 2021 at 04:09:08PM -0500, Tyler Hicks wrote:
> From: Allen Pais <[email protected]>
>
> The following out of memory errors are seen on kexec reboot
> from the optee core.
>
> [ 0.368428] tee_bnxt_fw optee-clnt0: tee_shm_alloc failed
> [ 0.368461] tee_bnxt_fw: probe of optee-clnt0 failed with error -22
>
> tee_shm_release() is not invoked on dma shm buffer.
>
> Implement .shutdown() method to handle the release of the buffers
> correctly.
>
> More info:
> https://github.com/OP-TEE/optee_os/issues/3637
>
> Signed-off-by: Allen Pais <[email protected]>
> Reviewed-by: Tyler Hicks <[email protected]>
> ---
> drivers/tee/optee/core.c | 20 ++++++++++++++++++++
> 1 file changed, 20 insertions(+)

Reviewed-by: Jens Wiklander <[email protected]>

2021-06-14 07:26:00

by Jens Wiklander

[permalink] [raw]
Subject: Re: [PATCH v4 3/8] optee: fix tee out of memory failure seen during kexec reboot

On Fri, Jun 11, 2021 at 07:53:26AM -0500, Tyler Hicks wrote:
> On 2021-06-11 11:11:33, Jens Wiklander wrote:
> > On Thu, Jun 10, 2021 at 11:09 PM Tyler Hicks
> > <[email protected]> wrote:
> > >
> > > From: Allen Pais <[email protected]>
> > >
> > > The following out of memory errors are seen on kexec reboot
> > > from the optee core.
> > >
> > > [ 0.368428] tee_bnxt_fw optee-clnt0: tee_shm_alloc failed
> > > [ 0.368461] tee_bnxt_fw: probe of optee-clnt0 failed with error -22
> > >
> > > tee_shm_release() is not invoked on dma shm buffer.
> > >
> > > Implement .shutdown() method to handle the release of the buffers
> > > correctly.
> > >
> > > More info:
> > > https://github.com/OP-TEE/optee_os/issues/3637
> > >
> > > Signed-off-by: Allen Pais <[email protected]>
> > > Reviewed-by: Tyler Hicks <[email protected]>
> >
> > Do we really need this considering the patch "optee: Refuse to load
> > the driver under the kdump kernel"?
>
> Yes. That patch fixes boot hangs when all of the OP-TEE threads were in
> the suspended state at the time of a kernel panic. The kexec into the
> kdump kernel after a panic is an "emergency" kexec that doesn't even
> call .shutdown hooks. There's no way for the OP-TEE driver to clean up
> after itself.
>
> This patch disables the shm cache (and unregisters the shm buffers)
> during a normal kexec from one perfectly working kernel into a new
> kernel. This is required because the new kernel will not be able to
> handle the virtual addresses that were cached under the old kernel. The
> new kernel has an entirely different memory layout and the old addresses
> point to unmapped memory or memory that's mapped but probably not a TEE
> shm.

Got it, thanks.

Jens

2021-06-14 08:29:44

by Jens Wiklander

[permalink] [raw]
Subject: Re: [PATCH v4 4/8] optee: Clear stale cache entries during initialization

On Thu, Jun 10, 2021 at 04:09:09PM -0500, Tyler Hicks wrote:
> The shm cache could contain invalid addresses if
> optee_disable_shm_cache() was not called from the .shutdown hook of the
> previous kernel before a kexec. These addresses could be unmapped or
> they could point to mapped but unintended locations in memory.
>
> Clear the shared memory cache, while being careful to not translate the
> addresses returned from OPTEE_SMC_DISABLE_SHM_CACHE, during driver
> initialization. Once all pre-cache shm objects are removed, proceed with
> enabling the cache so that we know that we can handle cached shm objects
> with confidence later in the .shutdown hook.
>
> Signed-off-by: Tyler Hicks <[email protected]>
> ---
> drivers/tee/optee/call.c | 11 ++++++++++-
> drivers/tee/optee/core.c | 13 +++++++++++--
> drivers/tee/optee/optee_private.h | 2 +-
> 3 files changed, 22 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
> index 6e6eb836e9b6..5dcba6105ed7 100644
> --- a/drivers/tee/optee/call.c
> +++ b/drivers/tee/optee/call.c
> @@ -419,8 +419,10 @@ void optee_enable_shm_cache(struct optee *optee)
> * optee_disable_shm_cache() - Disables caching of some shared memory allocation
> * in OP-TEE
> * @optee: main service struct
> + * @is_mapped: true if the cached shared memory addresses were mapped by this
> + * kernel, are safe to dereference, and should be freed
> */
> -void optee_disable_shm_cache(struct optee *optee)
> +void optee_disable_shm_cache(struct optee *optee, bool is_mapped)
> {
> struct optee_call_waiter w;
>
> @@ -439,6 +441,13 @@ void optee_disable_shm_cache(struct optee *optee)
> if (res.result.status == OPTEE_SMC_RETURN_OK) {
> struct tee_shm *shm;
>
> + /*
> + * Shared memory references that were not mapped by
> + * this kernel must be ignored to prevent a crash.
> + */
> + if (!is_mapped)
> + continue;
> +
> shm = reg_pair_to_ptr(res.result.shm_upper32,
> res.result.shm_lower32);
> tee_shm_free(shm);
> diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> index 0987074d7ed0..6974e1104bd4 100644
> --- a/drivers/tee/optee/core.c
> +++ b/drivers/tee/optee/core.c
> @@ -589,7 +589,7 @@ static int optee_remove(struct platform_device *pdev)
> * reference counters and also avoid wild pointers in secure world
> * into the old shared memory range.
> */
> - optee_disable_shm_cache(optee);
> + optee_disable_shm_cache(optee, true);

Naked "true" or "false" parameters are normally not very descriptive.
Would it make sense to write this as:
optee_disable_shm_cache(optee, true /*is_mapped*/);
instead (same for the other call sites in this patch)? That way it would
be easier to see what it is that is true or false.

/Jens

>
> /*
> * The two devices have to be unregistered before we can free the
> @@ -619,7 +619,7 @@ static int optee_remove(struct platform_device *pdev)
> */
> static void optee_shutdown(struct platform_device *pdev)
> {
> - optee_disable_shm_cache(platform_get_drvdata(pdev));
> + optee_disable_shm_cache(platform_get_drvdata(pdev), true);
> }
>
> static int optee_probe(struct platform_device *pdev)
> @@ -716,6 +716,15 @@ static int optee_probe(struct platform_device *pdev)
> optee->memremaped_shm = memremaped_shm;
> optee->pool = pool;
>
> + /*
> + * Ensure that there are no pre-existing shm objects before enabling
> + * the shm cache so that there's no chance of receiving an invalid
> + * address during shutdown. This could occur, for example, if we're
> + * kexec booting from an older kernel that did not properly cleanup the
> + * shm cache.
> + */
> + optee_disable_shm_cache(optee, false);
> +
> optee_enable_shm_cache(optee);
>
> if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
> diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> index e25b216a14ef..16d8c82213e7 100644
> --- a/drivers/tee/optee/optee_private.h
> +++ b/drivers/tee/optee/optee_private.h
> @@ -158,7 +158,7 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
> int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session);
>
> void optee_enable_shm_cache(struct optee *optee);
> -void optee_disable_shm_cache(struct optee *optee);
> +void optee_disable_shm_cache(struct optee *optee, bool is_mapped);
>
> int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
> struct page **pages, size_t num_pages,
> --
> 2.25.1
>

2021-06-14 19:11:00

by Tyler Hicks

[permalink] [raw]
Subject: Re: [PATCH v4 4/8] optee: Clear stale cache entries during initialization

On 2021-06-14 10:27:15, Jens Wiklander wrote:
> On Thu, Jun 10, 2021 at 04:09:09PM -0500, Tyler Hicks wrote:
> > The shm cache could contain invalid addresses if
> > optee_disable_shm_cache() was not called from the .shutdown hook of the
> > previous kernel before a kexec. These addresses could be unmapped or
> > they could point to mapped but unintended locations in memory.
> >
> > Clear the shared memory cache, while being careful to not translate the
> > addresses returned from OPTEE_SMC_DISABLE_SHM_CACHE, during driver
> > initialization. Once all pre-cache shm objects are removed, proceed with
> > enabling the cache so that we know that we can handle cached shm objects
> > with confidence later in the .shutdown hook.
> >
> > Signed-off-by: Tyler Hicks <[email protected]>
> > ---
> > drivers/tee/optee/call.c | 11 ++++++++++-
> > drivers/tee/optee/core.c | 13 +++++++++++--
> > drivers/tee/optee/optee_private.h | 2 +-
> > 3 files changed, 22 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
> > index 6e6eb836e9b6..5dcba6105ed7 100644
> > --- a/drivers/tee/optee/call.c
> > +++ b/drivers/tee/optee/call.c
> > @@ -419,8 +419,10 @@ void optee_enable_shm_cache(struct optee *optee)
> > * optee_disable_shm_cache() - Disables caching of some shared memory allocation
> > * in OP-TEE
> > * @optee: main service struct
> > + * @is_mapped: true if the cached shared memory addresses were mapped by this
> > + * kernel, are safe to dereference, and should be freed
> > */
> > -void optee_disable_shm_cache(struct optee *optee)
> > +void optee_disable_shm_cache(struct optee *optee, bool is_mapped)
> > {
> > struct optee_call_waiter w;
> >
> > @@ -439,6 +441,13 @@ void optee_disable_shm_cache(struct optee *optee)
> > if (res.result.status == OPTEE_SMC_RETURN_OK) {
> > struct tee_shm *shm;
> >
> > + /*
> > + * Shared memory references that were not mapped by
> > + * this kernel must be ignored to prevent a crash.
> > + */
> > + if (!is_mapped)
> > + continue;
> > +
> > shm = reg_pair_to_ptr(res.result.shm_upper32,
> > res.result.shm_lower32);
> > tee_shm_free(shm);
> > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> > index 0987074d7ed0..6974e1104bd4 100644
> > --- a/drivers/tee/optee/core.c
> > +++ b/drivers/tee/optee/core.c
> > @@ -589,7 +589,7 @@ static int optee_remove(struct platform_device *pdev)
> > * reference counters and also avoid wild pointers in secure world
> > * into the old shared memory range.
> > */
> > - optee_disable_shm_cache(optee);
> > + optee_disable_shm_cache(optee, true);
>
> Naked "true" or "false" parameters are normally not very descriptive.
> Would it make sense to write this as:
> optee_disable_shm_cache(optee, true /*is_mapped*/);
> instead (same for the other call sites in this patch)? That way it would
> be easier to see what it is that is true or false.

Yeah, I understand the issue with the naked bools. What about turning
'optee_disable_shm_cache(struct optee *optee, bool is_mapped)' into
'__optee_disable_shm_cache(struct optee *optee, bool is_mapped)' and
introducing these two wrappers:

/**
* optee_disable_shm_cache() - Disables caching of mapped shared memory
* allocations in OP-TEE
* @optee: main service struct
*/
void optee_disable_shm_cache(struct optee *optee)
{
return __optee_disable_shm_cache(optee, true);
}

/**
* optee_disable_unmapped_shm_cache() - Disables caching of shared memory
* allocations in OP-TEE which are not
* currently mapped
* @optee: main service struct
*/
void optee_disable_unmapped_shm_cache(struct optee *optee)
{
return __optee_disable_shm_cache(optee, false);
}

Existing callers of optee_disable_shm_cache() remain unchanged and we just add
one caller of optee_disable_unmapped_shm_cache() with this patch.

Tyler

>
> /Jens
>
> >
> > /*
> > * The two devices have to be unregistered before we can free the
> > @@ -619,7 +619,7 @@ static int optee_remove(struct platform_device *pdev)
> > */
> > static void optee_shutdown(struct platform_device *pdev)
> > {
> > - optee_disable_shm_cache(platform_get_drvdata(pdev));
> > + optee_disable_shm_cache(platform_get_drvdata(pdev), true);
> > }
> >
> > static int optee_probe(struct platform_device *pdev)
> > @@ -716,6 +716,15 @@ static int optee_probe(struct platform_device *pdev)
> > optee->memremaped_shm = memremaped_shm;
> > optee->pool = pool;
> >
> > + /*
> > + * Ensure that there are no pre-existing shm objects before enabling
> > + * the shm cache so that there's no chance of receiving an invalid
> > + * address during shutdown. This could occur, for example, if we're
> > + * kexec booting from an older kernel that did not properly cleanup the
> > + * shm cache.
> > + */
> > + optee_disable_shm_cache(optee, false);
> > +
> > optee_enable_shm_cache(optee);
> >
> > if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
> > diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h
> > index e25b216a14ef..16d8c82213e7 100644
> > --- a/drivers/tee/optee/optee_private.h
> > +++ b/drivers/tee/optee/optee_private.h
> > @@ -158,7 +158,7 @@ int optee_invoke_func(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg,
> > int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session);
> >
> > void optee_enable_shm_cache(struct optee *optee);
> > -void optee_disable_shm_cache(struct optee *optee);
> > +void optee_disable_shm_cache(struct optee *optee, bool is_mapped);
> >
> > int optee_shm_register(struct tee_context *ctx, struct tee_shm *shm,
> > struct page **pages, size_t num_pages,
> > --
> > 2.25.1
> >
>

2021-06-14 19:18:35

by Jens Wiklander

[permalink] [raw]
Subject: Re: [PATCH v4 4/8] optee: Clear stale cache entries during initialization

On Mon, Jun 14, 2021 at 02:06:46PM -0500, Tyler Hicks wrote:
> On 2021-06-14 10:27:15, Jens Wiklander wrote:
> > On Thu, Jun 10, 2021 at 04:09:09PM -0500, Tyler Hicks wrote:
> > > The shm cache could contain invalid addresses if
> > > optee_disable_shm_cache() was not called from the .shutdown hook of the
> > > previous kernel before a kexec. These addresses could be unmapped or
> > > they could point to mapped but unintended locations in memory.
> > >
> > > Clear the shared memory cache, while being careful to not translate the
> > > addresses returned from OPTEE_SMC_DISABLE_SHM_CACHE, during driver
> > > initialization. Once all pre-cache shm objects are removed, proceed with
> > > enabling the cache so that we know that we can handle cached shm objects
> > > with confidence later in the .shutdown hook.
> > >
> > > Signed-off-by: Tyler Hicks <[email protected]>
> > > ---
> > > drivers/tee/optee/call.c | 11 ++++++++++-
> > > drivers/tee/optee/core.c | 13 +++++++++++--
> > > drivers/tee/optee/optee_private.h | 2 +-
> > > 3 files changed, 22 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c
> > > index 6e6eb836e9b6..5dcba6105ed7 100644
> > > --- a/drivers/tee/optee/call.c
> > > +++ b/drivers/tee/optee/call.c
> > > @@ -419,8 +419,10 @@ void optee_enable_shm_cache(struct optee *optee)
> > > * optee_disable_shm_cache() - Disables caching of some shared memory allocation
> > > * in OP-TEE
> > > * @optee: main service struct
> > > + * @is_mapped: true if the cached shared memory addresses were mapped by this
> > > + * kernel, are safe to dereference, and should be freed
> > > */
> > > -void optee_disable_shm_cache(struct optee *optee)
> > > +void optee_disable_shm_cache(struct optee *optee, bool is_mapped)
> > > {
> > > struct optee_call_waiter w;
> > >
> > > @@ -439,6 +441,13 @@ void optee_disable_shm_cache(struct optee *optee)
> > > if (res.result.status == OPTEE_SMC_RETURN_OK) {
> > > struct tee_shm *shm;
> > >
> > > + /*
> > > + * Shared memory references that were not mapped by
> > > + * this kernel must be ignored to prevent a crash.
> > > + */
> > > + if (!is_mapped)
> > > + continue;
> > > +
> > > shm = reg_pair_to_ptr(res.result.shm_upper32,
> > > res.result.shm_lower32);
> > > tee_shm_free(shm);
> > > diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c
> > > index 0987074d7ed0..6974e1104bd4 100644
> > > --- a/drivers/tee/optee/core.c
> > > +++ b/drivers/tee/optee/core.c
> > > @@ -589,7 +589,7 @@ static int optee_remove(struct platform_device *pdev)
> > > * reference counters and also avoid wild pointers in secure world
> > > * into the old shared memory range.
> > > */
> > > - optee_disable_shm_cache(optee);
> > > + optee_disable_shm_cache(optee, true);
> >
> > Naked "true" or "false" parameters are normally not very descriptive.
> > Would it make sense to write this as:
> > optee_disable_shm_cache(optee, true /*is_mapped*/);
> > instead (same for the other call sites in this patch)? That way it would
> > be easier to see what it is that is true or false.
>
> Yeah, I understand the issue with the naked bools. What about turning
> 'optee_disable_shm_cache(struct optee *optee, bool is_mapped)' into
> '__optee_disable_shm_cache(struct optee *optee, bool is_mapped)' and
> introducing these two wrappers:
>
> /**
> * optee_disable_shm_cache() - Disables caching of mapped shared memory
> * allocations in OP-TEE
> * @optee: main service struct
> */
> void optee_disable_shm_cache(struct optee *optee)
> {
> return __optee_disable_shm_cache(optee, true);
> }
>
> /**
> * optee_disable_unmapped_shm_cache() - Disables caching of shared memory
> * allocations in OP-TEE which are not
> * currently mapped
> * @optee: main service struct
> */
> void optee_disable_unmapped_shm_cache(struct optee *optee)
> {
> return __optee_disable_shm_cache(optee, false);
> }
>
> Existing callers of optee_disable_shm_cache() remain unchanged and we just add
> one caller of optee_disable_unmapped_shm_cache() with this patch.
>

Sounds good.

Jens

2021-06-15 13:06:37

by Jarkko Sakkinen

[permalink] [raw]
Subject: Re: [PATCH v4 7/8] tpm_ftpm_tee: Free and unregister TEE shared memory during kexec

On Thu, Jun 10, 2021 at 04:09:12PM -0500, Tyler Hicks wrote:
> dma-buf backed shared memory cannot be reliably freed and unregistered
> during a kexec operation even when tee_shm_free() is called on the shm
> from a .shutdown hook. The problem occurs because dma_buf_put() calls
> fput() which then uses task_work_add(), with the TWA_RESUME parameter,
> to queue tee_shm_release() to be called before the current task returns
> to user mode. However, the current task never returns to user mode
> before the kexec completes so the memory is never freed nor
> unregistered.
>
> Use tee_shm_alloc_kernel_buf() to avoid dma-buf backed shared memory
> allocation so that tee_shm_free() can directly call tee_shm_release().
> This will ensure that the shm can be freed and unregistered during a
> kexec operation.
>
> Fixes: 09e574831b27 ("tpm/tpm_ftpm_tee: A driver for firmware TPM running inside TEE")
> Fixes: 1760eb689ed6 ("tpm/tpm_ftpm_tee: add shutdown call back")
> Signed-off-by: Tyler Hicks <[email protected]>


Acked-by: Jarkko Sakkinen <[email protected]>

> ---
> drivers/char/tpm/tpm_ftpm_tee.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c
> index 2ccdf8ac6994..6e3235565a4d 100644
> --- a/drivers/char/tpm/tpm_ftpm_tee.c
> +++ b/drivers/char/tpm/tpm_ftpm_tee.c
> @@ -254,11 +254,11 @@ static int ftpm_tee_probe(struct device *dev)
> pvt_data->session = sess_arg.session;
>
> /* Allocate dynamic shared memory with fTPM TA */
> - pvt_data->shm = tee_shm_alloc(pvt_data->ctx,
> - MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE,
> - TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
> + pvt_data->shm = tee_shm_alloc_kernel_buf(pvt_data->ctx,
> + MAX_COMMAND_SIZE +
> + MAX_RESPONSE_SIZE);
> if (IS_ERR(pvt_data->shm)) {
> - dev_err(dev, "%s: tee_shm_alloc failed\n", __func__);
> + dev_err(dev, "%s: tee_shm_alloc_kernel_buf failed\n", __func__);
> rc = -ENOMEM;
> goto out_shm_alloc;
> }
> --
> 2.25.1
>
>

/Jarkko