aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/nvmem/stm32-bsec-optee-ta.c
blob: f89ce791dd12a6b1de53b8070e6831f7155ee3d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * OP-TEE STM32MP BSEC PTA interface, used by STM32 ROMEM driver
 *
 * Copyright (C) 2022, STMicroelectronics - All Rights Reserved
 */

#include <linux/tee_drv.h>

#include "stm32-bsec-optee-ta.h"

/*
 * Read OTP memory
 *
 * [in]		value[0].a		OTP start offset in byte
 * [in]		value[0].b		Access type (0:shadow, 1:fuse, 2:lock)
 * [out]	memref[1].buffer	Output buffer to store read values
 * [out]	memref[1].size		Size of OTP to be read
 *
 * Return codes:
 * TEE_SUCCESS - Invoke command success
 * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
 * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller
 */
#define PTA_BSEC_READ_MEM		0x0

/*
 * Write OTP memory
 *
 * [in]		value[0].a		OTP start offset in byte
 * [in]		value[0].b		Access type (0:shadow, 1:fuse, 2:lock)
 * [in]		memref[1].buffer	Input buffer to read values
 * [in]		memref[1].size		Size of OTP to be written
 *
 * Return codes:
 * TEE_SUCCESS - Invoke command success
 * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
 * TEE_ERROR_ACCESS_DENIED - OTP not accessible by caller
 */
#define PTA_BSEC_WRITE_MEM		0x1

/* value of PTA_BSEC access type = value[in] b */
#define SHADOW_ACCESS	0
#define FUSE_ACCESS	1
#define LOCK_ACCESS	2

/* Bitfield definition for LOCK status */
#define LOCK_PERM			BIT(30)

/* OP-TEE STM32MP BSEC TA UUID */
static const uuid_t stm32mp_bsec_ta_uuid =
	UUID_INIT(0x94cf71ad, 0x80e6, 0x40b5,
		  0xa7, 0xc6, 0x3d, 0xc5, 0x01, 0xeb, 0x28, 0x03);

/*
 * Check whether this driver supports the BSEC TA in the TEE instance
 * represented by the params (ver/data) to this function.
 */
static int stm32_bsec_optee_ta_match(struct tee_ioctl_version_data *ver,
				     const void *data)
{
	/* Currently this driver only supports GP compliant, OP-TEE based TA */
	if ((ver->impl_id == TEE_IMPL_ID_OPTEE) &&
		(ver->gen_caps & TEE_GEN_CAP_GP))
		return 1;
	else
		return 0;
}

/* Open a session to OP-TEE for STM32MP BSEC TA */
static int stm32_bsec_ta_open_session(struct tee_context *ctx, u32 *id)
{
	struct tee_ioctl_open_session_arg sess_arg;
	int rc;

	memset(&sess_arg, 0, sizeof(sess_arg));
	export_uuid(sess_arg.uuid, &stm32mp_bsec_ta_uuid);
	sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
	sess_arg.num_params = 0;

	rc = tee_client_open_session(ctx, &sess_arg, NULL);
	if ((rc < 0) || (sess_arg.ret != 0)) {
		pr_err("%s: tee_client_open_session failed err:%#x, ret:%#x\n",
		       __func__, sess_arg.ret, rc);
		if (!rc)
			rc = -EINVAL;
	} else {
		*id = sess_arg.session;
	}

	return rc;
}

/* close a session to OP-TEE for STM32MP BSEC TA */
static void stm32_bsec_ta_close_session(void *ctx, u32 id)
{
	tee_client_close_session(ctx, id);
}

/* stm32_bsec_optee_ta_open() - initialize the STM32MP BSEC TA */
int stm32_bsec_optee_ta_open(struct tee_context **ctx)
{
	struct tee_context *tee_ctx;
	u32 session_id;
	int rc;

	/* Open context with TEE driver */
	tee_ctx = tee_client_open_context(NULL, stm32_bsec_optee_ta_match, NULL, NULL);
	if (IS_ERR(tee_ctx)) {
		rc = PTR_ERR(tee_ctx);
		if (rc == -ENOENT)
			return -EPROBE_DEFER;
		pr_err("%s: tee_client_open_context failed (%d)\n", __func__, rc);

		return rc;
	}

	/* Check STM32MP BSEC TA presence */
	rc = stm32_bsec_ta_open_session(tee_ctx, &session_id);
	if (rc) {
		tee_client_close_context(tee_ctx);
		return rc;
	}

	stm32_bsec_ta_close_session(tee_ctx, session_id);

	*ctx = tee_ctx;

	return 0;
}

/* stm32_bsec_optee_ta_open() - release the PTA STM32MP BSEC TA */
void stm32_bsec_optee_ta_close(void *ctx)
{
	tee_client_close_context(ctx);
}

/* stm32_bsec_optee_ta_read() - nvmem read access using PTA client driver */
int stm32_bsec_optee_ta_read(struct tee_context *ctx, unsigned int offset,
			     void *buf, size_t bytes)
{
	struct tee_shm *shm;
	struct tee_ioctl_invoke_arg arg;
	struct tee_param param[2];
	u8 *shm_buf;
	u32 start, num_bytes;
	int ret;
	u32 session_id;

	ret = stm32_bsec_ta_open_session(ctx, &session_id);
	if (ret)
		return ret;

	memset(&arg, 0, sizeof(arg));
	memset(&param, 0, sizeof(param));

	arg.func = PTA_BSEC_READ_MEM;
	arg.session = session_id;
	arg.num_params = 2;

	/* align access on 32bits */
	start = ALIGN_DOWN(offset, 4);
	num_bytes = round_up(offset + bytes - start, 4);
	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
	param[0].u.value.a = start;
	param[0].u.value.b = SHADOW_ACCESS;

	shm = tee_shm_alloc_kernel_buf(ctx, num_bytes);
	if (IS_ERR(shm)) {
		ret = PTR_ERR(shm);
		goto out_tee_session;
	}

	param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
	param[1].u.memref.shm = shm;
	param[1].u.memref.size = num_bytes;

	ret = tee_client_invoke_func(ctx, &arg, param);
	if (ret < 0 || arg.ret != 0) {
		pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n",
			arg.ret, ret);
		if (!ret)
			ret = -EIO;
	}
	if (!ret) {
		shm_buf = tee_shm_get_va(shm, 0);
		if (IS_ERR(shm_buf)) {
			ret = PTR_ERR(shm_buf);
			pr_err("tee_shm_get_va failed for transmit (%d)\n", ret);
		} else {
			/* read data from 32 bits aligned buffer */
			memcpy(buf, &shm_buf[offset % 4], bytes);
		}
	}

	tee_shm_free(shm);

out_tee_session:
	stm32_bsec_ta_close_session(ctx, session_id);

	return ret;
}

/* stm32_bsec_optee_ta_write() - nvmem write access using PTA client driver */
int stm32_bsec_optee_ta_write(struct tee_context *ctx, unsigned int lower,
			      unsigned int offset, void *buf, size_t bytes)
{	struct tee_shm *shm;
	struct tee_ioctl_invoke_arg arg;
	struct tee_param param[2];
	u8 *shm_buf;
	int ret;
	u32 session_id;

	ret = stm32_bsec_ta_open_session(ctx, &session_id);
	if (ret)
		return ret;

	/* Allow only writing complete 32-bits aligned words */
	if ((bytes % 4) || (offset % 4))
		return -EINVAL;

	memset(&arg, 0, sizeof(arg));
	memset(&param, 0, sizeof(param));

	arg.func = PTA_BSEC_WRITE_MEM;
	arg.session = session_id;
	arg.num_params = 2;

	param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
	param[0].u.value.a = offset;
	param[0].u.value.b = FUSE_ACCESS;

	shm = tee_shm_alloc_kernel_buf(ctx, bytes);
	if (IS_ERR(shm)) {
		ret = PTR_ERR(shm);
		goto out_tee_session;
	}

	param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
	param[1].u.memref.shm = shm;
	param[1].u.memref.size = bytes;

	shm_buf = tee_shm_get_va(shm, 0);
	if (IS_ERR(shm_buf)) {
		ret = PTR_ERR(shm_buf);
		pr_err("tee_shm_get_va failed for transmit (%d)\n", ret);
		tee_shm_free(shm);

		goto out_tee_session;
	}

	memcpy(shm_buf, buf, bytes);

	ret = tee_client_invoke_func(ctx, &arg, param);
	if (ret < 0 || arg.ret != 0) {
		pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n", arg.ret, ret);
		if (!ret)
			ret = -EIO;
	}
	pr_debug("Write OTPs %d to %zu, ret=%d\n", offset / 4, (offset + bytes) / 4, ret);

	/* Lock the upper OTPs with ECC protection, word programming only */
	if (!ret && ((offset + bytes) >= (lower * 4))) {
		u32 start, nb_lock;
		u32 *lock = (u32 *)shm_buf;
		int i;

		/*
		 * don't lock the lower OTPs, no ECC protection and incremental
		 * bit programming, a second write is allowed
		 */
		start = max_t(u32, offset, lower * 4);
		nb_lock = (offset + bytes - start) / 4;

		param[0].u.value.a = start;
		param[0].u.value.b = LOCK_ACCESS;
		param[1].u.memref.size = nb_lock * 4;

		for (i = 0; i < nb_lock; i++)
			lock[i] = LOCK_PERM;

		ret = tee_client_invoke_func(ctx, &arg, param);
		if (ret < 0 || arg.ret != 0) {
			pr_err("TA_BSEC invoke failed TEE err:%#x, ret:%#x\n", arg.ret, ret);
			if (!ret)
				ret = -EIO;
		}
		pr_debug("Lock upper OTPs %d to %d, ret=%d\n",
			 start / 4, start / 4 + nb_lock, ret);
	}

	tee_shm_free(shm);

out_tee_session:
	stm32_bsec_ta_close_session(ctx, session_id);

	return ret;
}