162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * OP-TEE STM32MP BSEC PTA interface, used by STM32 ROMEM driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2022, STMicroelectronics - All Rights Reserved
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/tee_drv.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "stm32-bsec-optee-ta.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/*
1362306a36Sopenharmony_ci * Read OTP memory
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * [in]		value[0].a		OTP start offset in byte
1662306a36Sopenharmony_ci * [in]		value[0].b		Access type (0:shadow, 1:fuse, 2:lock)
1762306a36Sopenharmony_ci * [out]	memref[1].buffer	Output buffer to store read values
1862306a36Sopenharmony_ci * [out]	memref[1].size		Size of OTP to be read
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * Return codes:
2162306a36Sopenharmony_ci * TEE_SUCCESS - Invoke command success
2262306a36Sopenharmony_ci * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
2362306a36Sopenharmony_ci * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci#define PTA_BSEC_READ_MEM		0x0
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/*
2862306a36Sopenharmony_ci * Write OTP memory
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * [in]		value[0].a		OTP start offset in byte
3162306a36Sopenharmony_ci * [in]		value[0].b		Access type (0:shadow, 1:fuse, 2:lock)
3262306a36Sopenharmony_ci * [in]		memref[1].buffer	Input buffer to read values
3362306a36Sopenharmony_ci * [in]		memref[1].size		Size of OTP to be written
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * Return codes:
3662306a36Sopenharmony_ci * TEE_SUCCESS - Invoke command success
3762306a36Sopenharmony_ci * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
3862306a36Sopenharmony_ci * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci#define PTA_BSEC_WRITE_MEM		0x1
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* value of PTA_BSEC access type = value[in] b */
4362306a36Sopenharmony_ci#define SHADOW_ACCESS	0
4462306a36Sopenharmony_ci#define FUSE_ACCESS	1
4562306a36Sopenharmony_ci#define LOCK_ACCESS	2
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Bitfield definition for LOCK status */
4862306a36Sopenharmony_ci#define LOCK_PERM			BIT(30)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* OP-TEE STM32MP BSEC TA UUID */
5162306a36Sopenharmony_cistatic const uuid_t stm32mp_bsec_ta_uuid =
5262306a36Sopenharmony_ci	UUID_INIT(0x94cf71ad, 0x80e6, 0x40b5,
5362306a36Sopenharmony_ci		  0xa7, 0xc6, 0x3d, 0xc5, 0x01, 0xeb, 0x28, 0x03);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci/*
5662306a36Sopenharmony_ci * Check whether this driver supports the BSEC TA in the TEE instance
5762306a36Sopenharmony_ci * represented by the params (ver/data) to this function.
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_cistatic int stm32_bsec_optee_ta_match(struct tee_ioctl_version_data *ver,
6062306a36Sopenharmony_ci				     const void *data)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	/* Currently this driver only supports GP compliant, OP-TEE based TA */
6362306a36Sopenharmony_ci	if ((ver->impl_id == TEE_IMPL_ID_OPTEE) &&
6462306a36Sopenharmony_ci		(ver->gen_caps & TEE_GEN_CAP_GP))
6562306a36Sopenharmony_ci		return 1;
6662306a36Sopenharmony_ci	else
6762306a36Sopenharmony_ci		return 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* Open a session to OP-TEE for STM32MP BSEC TA */
7162306a36Sopenharmony_cistatic int stm32_bsec_ta_open_session(struct tee_context *ctx, u32 *id)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct tee_ioctl_open_session_arg sess_arg;
7462306a36Sopenharmony_ci	int rc;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	memset(&sess_arg, 0, sizeof(sess_arg));
7762306a36Sopenharmony_ci	export_uuid(sess_arg.uuid, &stm32mp_bsec_ta_uuid);
7862306a36Sopenharmony_ci	sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
7962306a36Sopenharmony_ci	sess_arg.num_params = 0;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	rc = tee_client_open_session(ctx, &sess_arg, NULL);
8262306a36Sopenharmony_ci	if ((rc < 0) || (sess_arg.ret != 0)) {
8362306a36Sopenharmony_ci		pr_err("%s: tee_client_open_session failed err:%#x, ret:%#x\n",
8462306a36Sopenharmony_ci		       __func__, sess_arg.ret, rc);
8562306a36Sopenharmony_ci		if (!rc)
8662306a36Sopenharmony_ci			rc = -EINVAL;
8762306a36Sopenharmony_ci	} else {
8862306a36Sopenharmony_ci		*id = sess_arg.session;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return rc;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* close a session to OP-TEE for STM32MP BSEC TA */
9562306a36Sopenharmony_cistatic void stm32_bsec_ta_close_session(void *ctx, u32 id)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	tee_client_close_session(ctx, id);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/* stm32_bsec_optee_ta_open() - initialize the STM32MP BSEC TA */
10162306a36Sopenharmony_ciint stm32_bsec_optee_ta_open(struct tee_context **ctx)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct tee_context *tee_ctx;
10462306a36Sopenharmony_ci	u32 session_id;
10562306a36Sopenharmony_ci	int rc;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* Open context with TEE driver */
10862306a36Sopenharmony_ci	tee_ctx = tee_client_open_context(NULL, stm32_bsec_optee_ta_match, NULL, NULL);
10962306a36Sopenharmony_ci	if (IS_ERR(tee_ctx)) {
11062306a36Sopenharmony_ci		rc = PTR_ERR(tee_ctx);
11162306a36Sopenharmony_ci		if (rc == -ENOENT)
11262306a36Sopenharmony_ci			return -EPROBE_DEFER;
11362306a36Sopenharmony_ci		pr_err("%s: tee_client_open_context failed (%d)\n", __func__, rc);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		return rc;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* Check STM32MP BSEC TA presence */
11962306a36Sopenharmony_ci	rc = stm32_bsec_ta_open_session(tee_ctx, &session_id);
12062306a36Sopenharmony_ci	if (rc) {
12162306a36Sopenharmony_ci		tee_client_close_context(tee_ctx);
12262306a36Sopenharmony_ci		return rc;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	stm32_bsec_ta_close_session(tee_ctx, session_id);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	*ctx = tee_ctx;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return 0;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/* stm32_bsec_optee_ta_open() - release the PTA STM32MP BSEC TA */
13362306a36Sopenharmony_civoid stm32_bsec_optee_ta_close(void *ctx)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	tee_client_close_context(ctx);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/* stm32_bsec_optee_ta_read() - nvmem read access using PTA client driver */
13962306a36Sopenharmony_ciint stm32_bsec_optee_ta_read(struct tee_context *ctx, unsigned int offset,
14062306a36Sopenharmony_ci			     void *buf, size_t bytes)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct tee_shm *shm;
14362306a36Sopenharmony_ci	struct tee_ioctl_invoke_arg arg;
14462306a36Sopenharmony_ci	struct tee_param param[2];
14562306a36Sopenharmony_ci	u8 *shm_buf;
14662306a36Sopenharmony_ci	u32 start, num_bytes;
14762306a36Sopenharmony_ci	int ret;
14862306a36Sopenharmony_ci	u32 session_id;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	ret = stm32_bsec_ta_open_session(ctx, &session_id);
15162306a36Sopenharmony_ci	if (ret)
15262306a36Sopenharmony_ci		return ret;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	memset(&arg, 0, sizeof(arg));
15562306a36Sopenharmony_ci	memset(&param, 0, sizeof(param));
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	arg.func = PTA_BSEC_READ_MEM;
15862306a36Sopenharmony_ci	arg.session = session_id;
15962306a36Sopenharmony_ci	arg.num_params = 2;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* align access on 32bits */
16262306a36Sopenharmony_ci	start = ALIGN_DOWN(offset, 4);
16362306a36Sopenharmony_ci	num_bytes = round_up(offset + bytes - start, 4);
16462306a36Sopenharmony_ci	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
16562306a36Sopenharmony_ci	param[0].u.value.a = start;
16662306a36Sopenharmony_ci	param[0].u.value.b = SHADOW_ACCESS;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	shm = tee_shm_alloc_kernel_buf(ctx, num_bytes);
16962306a36Sopenharmony_ci	if (IS_ERR(shm)) {
17062306a36Sopenharmony_ci		ret = PTR_ERR(shm);
17162306a36Sopenharmony_ci		goto out_tee_session;
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
17562306a36Sopenharmony_ci	param[1].u.memref.shm = shm;
17662306a36Sopenharmony_ci	param[1].u.memref.size = num_bytes;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	ret = tee_client_invoke_func(ctx, &arg, param);
17962306a36Sopenharmony_ci	if (ret < 0 || arg.ret != 0) {
18062306a36Sopenharmony_ci		pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n",
18162306a36Sopenharmony_ci			arg.ret, ret);
18262306a36Sopenharmony_ci		if (!ret)
18362306a36Sopenharmony_ci			ret = -EIO;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci	if (!ret) {
18662306a36Sopenharmony_ci		shm_buf = tee_shm_get_va(shm, 0);
18762306a36Sopenharmony_ci		if (IS_ERR(shm_buf)) {
18862306a36Sopenharmony_ci			ret = PTR_ERR(shm_buf);
18962306a36Sopenharmony_ci			pr_err("tee_shm_get_va failed for transmit (%d)\n", ret);
19062306a36Sopenharmony_ci		} else {
19162306a36Sopenharmony_ci			/* read data from 32 bits aligned buffer */
19262306a36Sopenharmony_ci			memcpy(buf, &shm_buf[offset % 4], bytes);
19362306a36Sopenharmony_ci		}
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	tee_shm_free(shm);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ciout_tee_session:
19962306a36Sopenharmony_ci	stm32_bsec_ta_close_session(ctx, session_id);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return ret;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci/* stm32_bsec_optee_ta_write() - nvmem write access using PTA client driver */
20562306a36Sopenharmony_ciint stm32_bsec_optee_ta_write(struct tee_context *ctx, unsigned int lower,
20662306a36Sopenharmony_ci			      unsigned int offset, void *buf, size_t bytes)
20762306a36Sopenharmony_ci{	struct tee_shm *shm;
20862306a36Sopenharmony_ci	struct tee_ioctl_invoke_arg arg;
20962306a36Sopenharmony_ci	struct tee_param param[2];
21062306a36Sopenharmony_ci	u8 *shm_buf;
21162306a36Sopenharmony_ci	int ret;
21262306a36Sopenharmony_ci	u32 session_id;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	ret = stm32_bsec_ta_open_session(ctx, &session_id);
21562306a36Sopenharmony_ci	if (ret)
21662306a36Sopenharmony_ci		return ret;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* Allow only writing complete 32-bits aligned words */
21962306a36Sopenharmony_ci	if ((bytes % 4) || (offset % 4))
22062306a36Sopenharmony_ci		return -EINVAL;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	memset(&arg, 0, sizeof(arg));
22362306a36Sopenharmony_ci	memset(&param, 0, sizeof(param));
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	arg.func = PTA_BSEC_WRITE_MEM;
22662306a36Sopenharmony_ci	arg.session = session_id;
22762306a36Sopenharmony_ci	arg.num_params = 2;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
23062306a36Sopenharmony_ci	param[0].u.value.a = offset;
23162306a36Sopenharmony_ci	param[0].u.value.b = FUSE_ACCESS;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	shm = tee_shm_alloc_kernel_buf(ctx, bytes);
23462306a36Sopenharmony_ci	if (IS_ERR(shm)) {
23562306a36Sopenharmony_ci		ret = PTR_ERR(shm);
23662306a36Sopenharmony_ci		goto out_tee_session;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
24062306a36Sopenharmony_ci	param[1].u.memref.shm = shm;
24162306a36Sopenharmony_ci	param[1].u.memref.size = bytes;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	shm_buf = tee_shm_get_va(shm, 0);
24462306a36Sopenharmony_ci	if (IS_ERR(shm_buf)) {
24562306a36Sopenharmony_ci		ret = PTR_ERR(shm_buf);
24662306a36Sopenharmony_ci		pr_err("tee_shm_get_va failed for transmit (%d)\n", ret);
24762306a36Sopenharmony_ci		tee_shm_free(shm);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		goto out_tee_session;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	memcpy(shm_buf, buf, bytes);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	ret = tee_client_invoke_func(ctx, &arg, param);
25562306a36Sopenharmony_ci	if (ret < 0 || arg.ret != 0) {
25662306a36Sopenharmony_ci		pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n", arg.ret, ret);
25762306a36Sopenharmony_ci		if (!ret)
25862306a36Sopenharmony_ci			ret = -EIO;
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci	pr_debug("Write OTPs %d to %zu, ret=%d\n", offset / 4, (offset + bytes) / 4, ret);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/* Lock the upper OTPs with ECC protection, word programming only */
26362306a36Sopenharmony_ci	if (!ret && ((offset + bytes) >= (lower * 4))) {
26462306a36Sopenharmony_ci		u32 start, nb_lock;
26562306a36Sopenharmony_ci		u32 *lock = (u32 *)shm_buf;
26662306a36Sopenharmony_ci		int i;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		/*
26962306a36Sopenharmony_ci		 * don't lock the lower OTPs, no ECC protection and incremental
27062306a36Sopenharmony_ci		 * bit programming, a second write is allowed
27162306a36Sopenharmony_ci		 */
27262306a36Sopenharmony_ci		start = max_t(u32, offset, lower * 4);
27362306a36Sopenharmony_ci		nb_lock = (offset + bytes - start) / 4;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		param[0].u.value.a = start;
27662306a36Sopenharmony_ci		param[0].u.value.b = LOCK_ACCESS;
27762306a36Sopenharmony_ci		param[1].u.memref.size = nb_lock * 4;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		for (i = 0; i < nb_lock; i++)
28062306a36Sopenharmony_ci			lock[i] = LOCK_PERM;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		ret = tee_client_invoke_func(ctx, &arg, param);
28362306a36Sopenharmony_ci		if (ret < 0 || arg.ret != 0) {
28462306a36Sopenharmony_ci			pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n", arg.ret, ret);
28562306a36Sopenharmony_ci			if (!ret)
28662306a36Sopenharmony_ci				ret = -EIO;
28762306a36Sopenharmony_ci		}
28862306a36Sopenharmony_ci		pr_debug("Lock upper OTPs %d to %d, ret=%d\n",
28962306a36Sopenharmony_ci			 start / 4, start / 4 + nb_lock, ret);
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	tee_shm_free(shm);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ciout_tee_session:
29562306a36Sopenharmony_ci	stm32_bsec_ta_close_session(ctx, session_id);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	return ret;
29862306a36Sopenharmony_ci}
299