Unverified Commit 64429b9e authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'tee-kexec-fixes-for-v5.14' of...

Merge tag 'tee-kexec-fixes-for-v5.14' of git://git.linaro.org:/people/jens.wiklander/linux-tee into arm/fixes

tee: Improve support for kexec and kdump

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

* tag 'tee-kexec-fixes-for-v5.14' of git://git.linaro.org:/people/jens.wiklander/linux-tee:
  firmware: tee_bnxt: Release TEE shm, session, and context during kexec
  tpm_ftpm_tee: Free and unregister TEE shared memory during kexec
  tee: Correct inappropriate usage of TEE_SHM_DMA_BUF flag
  tee: add tee_shm_alloc_kernel_buf()
  optee: Clear stale cache entries during initialization
  optee: fix tee out of memory failure seen during kexec reboot
  optee: Refuse to load the driver under the kdump kernel
  optee: Fix memory leak when failing to register shm pages

Link: https://lore.kernel.org/r/20210726081039.GA2482361@jade


Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents bee75748 914ab19e
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -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;
	}
+11 −3
Original line number Diff line number Diff line
@@ -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,
	},
};

+34 −4
Original line number Diff line number Diff line
@@ -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;

@@ -416,11 +416,13 @@ void optee_enable_shm_cache(struct optee *optee)
}

/**
 * optee_disable_shm_cache() - Disables caching of some shared memory allocation
 *			      in OP-TEE
 * __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)
static 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);
@@ -449,6 +458,27 @@ void optee_disable_shm_cache(struct optee *optee)
	optee_cq_wait_final(&optee->call_queue, &w);
}

/**
 * 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);
}

#define PAGELIST_ENTRIES_PER_PAGE				\
	((OPTEE_MSG_NONCONTIG_PAGE_SIZE / sizeof(u64)) - 1)

+42 −1
Original line number Diff line number Diff line
@@ -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>
@@ -277,7 +278,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);
		/*
@@ -572,6 +574,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);
@@ -602,6 +611,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;
@@ -612,6 +633,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);
@@ -686,6 +717,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_unmapped_shm_cache(optee);

	optee_enable_shm_cache(optee);

	if (optee->sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM)
@@ -728,6 +768,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,
+1 −0
Original line number Diff line number Diff line
@@ -159,6 +159,7 @@ 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_unmapped_shm_cache(struct optee *optee);

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