1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) Microsoft Corporation
4 *
5 * Authors:
6 * Thirupathaiah Annapureddy <thiruan@microsoft.com>
7 *
8 * Description:
9 * Device Driver for a firmware TPM as described here:
10 * https://www.microsoft.com/en-us/research/publication/ftpm-software-implementation-tpm-chip/
11 *
12 * A reference implementation is available here:
13 * https://github.com/microsoft/ms-tpm-20-ref/tree/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM
14 */
15
16#include <common.h>
17#include <dm.h>
18#include <log.h>
19#include <tpm-v2.h>
20#include <tee.h>
21#include <tee/optee_service.h>
22
23#include "tpm_tis.h"
24#include "tpm2_ftpm_tee.h"
25
26OPTEE_SERVICE_DRIVER(optee_ftpm, TA_FTPM_UUID, "ftpm_tee");
27
28/**
29 * ftpm_tee_transceive() - send fTPM commands and retrieve fTPM response.
30 * @sendbuf - address of the data to send, byte by byte
31 * @send_size - length of the data to send
32 * @recvbuf - address where to read the response, byte by byte.
33 * @recv_len - pointer to the size of buffer
34 *
35 * Return:
36 *	In case of success, returns 0.
37 *	On failure, -errno
38 */
39static int ftpm_tee_transceive(struct udevice *dev, const u8 *sendbuf,
40				size_t send_size, u8 *recvbuf,
41				size_t *recv_len)
42{
43	struct ftpm_tee_private *context = dev_get_priv(dev);
44	int rc = 0;
45	size_t resp_len;
46	u8 *resp_buf;
47	struct tpm_output_header *resp_header;
48	struct tee_invoke_arg transceive_args;
49	struct tee_param command_params[4];
50	struct tee_shm *shm;
51
52	if (send_size > MAX_COMMAND_SIZE) {
53		debug("%s:send_size=%zd exceeds MAX_COMMAND_SIZE\n",
54			__func__, send_size);
55		return -EIO;
56	}
57
58	shm = context->shm;
59	memset(&transceive_args, 0, sizeof(transceive_args));
60	memset(command_params, 0, sizeof(command_params));
61
62	/* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of fTPM TA */
63	transceive_args = (struct tee_invoke_arg) {
64		.func = FTPM_OPTEE_TA_SUBMIT_COMMAND,
65		.session = context->session,
66	};
67
68	/* Fill FTPM_OPTEE_TA_SUBMIT_COMMAND parameters */
69	/* request */
70	command_params[0] = (struct tee_param) {
71		.attr = TEE_PARAM_ATTR_TYPE_MEMREF_INPUT,
72		.u.memref = {
73			.shm = shm,
74			.size = send_size,
75			.shm_offs = 0,
76		},
77	};
78	memset(command_params[0].u.memref.shm->addr, 0,
79		(MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE));
80	memcpy(command_params[0].u.memref.shm->addr, sendbuf, send_size);
81
82	/* response */
83	command_params[1] = (struct tee_param) {
84		.attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT,
85		.u.memref = {
86			.shm = shm,
87			.size = MAX_RESPONSE_SIZE,
88			.shm_offs = MAX_COMMAND_SIZE,
89		},
90	};
91
92	rc = tee_invoke_func(context->tee_dev, &transceive_args, 4,
93				command_params);
94	if ((rc < 0) || (transceive_args.ret != 0)) {
95		debug("%s:SUBMIT_COMMAND invoke error: 0x%x\n",
96			__func__, transceive_args.ret);
97		return (rc < 0) ? rc : transceive_args.ret;
98	}
99
100	resp_buf = command_params[1].u.memref.shm->addr +
101		command_params[1].u.memref.shm_offs;
102	resp_header = (struct tpm_output_header *)resp_buf;
103	resp_len = be32_to_cpu(resp_header->length);
104
105	/* sanity check resp_len*/
106	if (resp_len < TPM_HEADER_SIZE) {
107		debug("%s:tpm response header too small\n", __func__);
108		return -EIO;
109	}
110	if (resp_len > MAX_RESPONSE_SIZE) {
111		debug("%s:resp_len=%zd exceeds MAX_RESPONSE_SIZE\n",
112			__func__, resp_len);
113		return -EIO;
114	}
115	if (resp_len > *recv_len) {
116		debug("%s:response length is bigger than receive buffer\n",
117			__func__);
118		return -EIO;
119	}
120
121	/* sanity checks look good, copy the response */
122	memcpy(recvbuf,  resp_buf,  resp_len);
123	*recv_len  = resp_len;
124
125	return 0;
126}
127
128static int ftpm_tee_open(struct udevice *dev)
129{
130	struct ftpm_tee_private *context = dev_get_priv(dev);
131
132	if (context->is_open)
133		return -EBUSY;
134
135	context->is_open = 1;
136
137	return 0;
138}
139
140static int ftpm_tee_close(struct udevice *dev)
141{
142	struct ftpm_tee_private *context = dev_get_priv(dev);
143
144	if (context->is_open)
145		context->is_open = 0;
146
147	return 0;
148}
149
150static int ftpm_tee_desc(struct udevice *dev, char *buf, int size)
151{
152	if (size < 32)
153		return -ENOSPC;
154
155	return snprintf(buf, size, "Microsoft OP-TEE fTPM");
156}
157
158static int ftpm_tee_match(struct tee_version_data *vers, const void *data)
159{
160	debug("%s:vers->gen_caps =0x%x\n", __func__, vers->gen_caps);
161
162	/*
163	 * Currently this driver only support GP Complaint OPTEE based fTPM TA
164	 */
165	return vers->gen_caps & TEE_GEN_CAP_GP;
166}
167
168static int ftpm_tee_probe(struct udevice *dev)
169{
170	int rc;
171	struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
172	struct ftpm_tee_private *context = dev_get_priv(dev);
173	struct tee_open_session_arg sess_arg;
174	const struct tee_optee_ta_uuid uuid = TA_FTPM_UUID;
175
176	memset(context, 0, sizeof(*context));
177
178	/* Use the TPM v2 stack */
179	priv->version = TPM_V2;
180	priv->pcr_count = 24;
181	priv->pcr_select_min = 3;
182
183	/* Find TEE device */
184	context->tee_dev = tee_find_device(NULL, ftpm_tee_match, NULL, NULL);
185	if (!context->tee_dev) {
186		debug("%s:tee_find_device failed\n", __func__);
187		return -ENODEV;
188	}
189
190	/* Open a session with the fTPM TA */
191	memset(&sess_arg, 0, sizeof(sess_arg));
192	sess_arg.clnt_login = TEE_LOGIN_REE_KERNEL;
193	tee_optee_ta_uuid_to_octets(sess_arg.uuid, &uuid);
194
195	rc = tee_open_session(context->tee_dev, &sess_arg, 0, NULL);
196	if ((rc < 0) || (sess_arg.ret != 0)) {
197		debug("%s:tee_open_session failed, err=%x\n",
198			__func__, sess_arg.ret);
199		return -EIO;
200	}
201	context->session = sess_arg.session;
202
203	/* Allocate dynamic shared memory with fTPM TA */
204	rc = tee_shm_alloc(context->tee_dev,
205			MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE,
206			0, &context->shm);
207	if (rc) {
208		debug("%s:tee_shm_alloc failed with rc = %d\n", __func__, rc);
209		goto out_shm_alloc;
210	}
211
212	return 0;
213
214out_shm_alloc:
215	tee_close_session(context->tee_dev, context->session);
216
217	return rc;
218}
219
220static int ftpm_tee_remove(struct udevice *dev)
221{
222	struct ftpm_tee_private *context = dev_get_priv(dev);
223	int rc;
224
225	/* tee_pre_remove frees any leftover TEE shared memory */
226
227	/* close the existing session with fTPM TA*/
228	rc = tee_close_session(context->tee_dev, context->session);
229	debug("%s: tee_close_session - rc =%d\n", __func__, rc);
230
231	return 0;
232}
233
234static const struct tpm_ops ftpm_tee_ops = {
235	.open		= ftpm_tee_open,
236	.close		= ftpm_tee_close,
237	.get_desc	= ftpm_tee_desc,
238	.xfer		= ftpm_tee_transceive,
239};
240
241static const struct udevice_id ftpm_tee_ids[] = {
242	{ .compatible = "microsoft,ftpm" },
243	{ }
244};
245
246U_BOOT_DRIVER(ftpm_tee) = {
247	.name   = "ftpm_tee",
248	.id     = UCLASS_TPM,
249	.of_match = ftpm_tee_ids,
250	.ops    = &ftpm_tee_ops,
251	.probe	= ftpm_tee_probe,
252	.remove	= ftpm_tee_remove,
253	.flags	= DM_FLAG_OS_PREPARE,
254	.priv_auto	= sizeof(struct ftpm_tee_private),
255};
256