xref: /linux/drivers/char/tpm/tpm_crb_ffa.c (revision 7a9b709e7cc5ce1ffb84ce07bf6d157e1de758df)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2024 Arm Ltd.
4  *
5  * This device driver implements the TPM CRB start method
6  * as defined in the TPM Service Command Response Buffer
7  * Interface Over FF-A (DEN0138).
8  */
9 
10 #define pr_fmt(fmt) "CRB_FFA: " fmt
11 
12 #include <linux/arm_ffa.h>
13 #include "tpm_crb_ffa.h"
14 
15 /* TPM service function status codes */
16 #define CRB_FFA_OK			0x05000001
17 #define CRB_FFA_OK_RESULTS_RETURNED	0x05000002
18 #define CRB_FFA_NOFUNC			0x8e000001
19 #define CRB_FFA_NOTSUP			0x8e000002
20 #define CRB_FFA_INVARG			0x8e000005
21 #define CRB_FFA_INV_CRB_CTRL_DATA	0x8e000006
22 #define CRB_FFA_ALREADY			0x8e000009
23 #define CRB_FFA_DENIED			0x8e00000a
24 #define CRB_FFA_NOMEM			0x8e00000b
25 
26 #define CRB_FFA_VERSION_MAJOR	1
27 #define CRB_FFA_VERSION_MINOR	0
28 
29 /* version encoding */
30 #define CRB_FFA_MAJOR_VERSION_MASK  GENMASK(30, 16)
31 #define CRB_FFA_MINOR_VERSION_MASK  GENMASK(15, 0)
32 #define CRB_FFA_MAJOR_VERSION(x)    ((u16)(FIELD_GET(CRB_FFA_MAJOR_VERSION_MASK, (x))))
33 #define CRB_FFA_MINOR_VERSION(x)    ((u16)(FIELD_GET(CRB_FFA_MINOR_VERSION_MASK, (x))))
34 
35 /*
36  * Normal world sends requests with FFA_MSG_SEND_DIRECT_REQ and
37  * responses are returned with FFA_MSG_SEND_DIRECT_RESP for normal
38  * messages.
39  *
40  * All requests with FFA_MSG_SEND_DIRECT_REQ and FFA_MSG_SEND_DIRECT_RESP
41  * are using the AArch32 SMC calling convention with register usage as
42  * defined in FF-A specification:
43  * w0:    Function ID (0x8400006F or 0x84000070)
44  * w1:    Source/Destination IDs
45  * w2:    Reserved (MBZ)
46  * w3-w7: Implementation defined, free to be used below
47  */
48 
49 /*
50  * Returns the version of the interface that is available
51  * Call register usage:
52  * w3:    Not used (MBZ)
53  * w4:    TPM service function ID, CRB_FFA_GET_INTERFACE_VERSION
54  * w5-w7: Reserved (MBZ)
55  *
56  * Return register usage:
57  * w3:    Not used (MBZ)
58  * w4:    TPM service function status
59  * w5:    TPM service interface version
60  *        Bits[31:16]: major version
61  *        Bits[15:0]: minor version
62  * w6-w7: Reserved (MBZ)
63  *
64  * Possible function status codes in register w4:
65  *     CRB_FFA_OK_RESULTS_RETURNED: The version of the interface has been
66  *                                  returned.
67  */
68 #define CRB_FFA_GET_INTERFACE_VERSION 0x0f000001
69 
70 /*
71  * Return information on a given feature of the TPM service
72  * Call register usage:
73  * w3:    Not used (MBZ)
74  * w4:    TPM service function ID, CRB_FFA_START
75  * w5:    Start function qualifier
76  *            Bits[31:8] (MBZ)
77  *            Bits[7:0]
78  *              0: Notifies TPM that a command is ready to be processed
79  *              1: Notifies TPM that a locality request is ready to be processed
80  * w6:    TPM locality, one of 0..4
81  *            -If the start function qualifier is 0, identifies the locality
82  *             from where the command originated.
83  *            -If the start function qualifier is 1, identifies the locality
84  *             of the locality request
85  * w6-w7: Reserved (MBZ)
86  *
87  * Return register usage:
88  * w3:    Not used (MBZ)
89  * w4:    TPM service function status
90  * w5-w7: Reserved (MBZ)
91  *
92  * Possible function status codes in register w4:
93  *     CRB_FFA_OK: the TPM service has been notified successfully
94  *     CRB_FFA_INVARG: one or more arguments are not valid
95  *     CRB_FFA_INV_CRB_CTRL_DATA: CRB control data or locality control
96  *         data at the given TPM locality is not valid
97  *     CRB_FFA_DENIED: the TPM has previously disabled locality requests and
98  *         command processing at the given locality
99  */
100 #define CRB_FFA_START 0x0f000201
101 
102 struct tpm_crb_ffa {
103 	struct ffa_device *ffa_dev;
104 	u16 major_version;
105 	u16 minor_version;
106 	/* lock to protect sending of FF-A messages: */
107 	struct mutex msg_data_lock;
108 	struct ffa_send_direct_data direct_msg_data;
109 };
110 
111 static struct tpm_crb_ffa *tpm_crb_ffa;
112 
113 static int tpm_crb_ffa_to_linux_errno(int errno)
114 {
115 	int rc;
116 
117 	switch (errno) {
118 	case CRB_FFA_OK:
119 		rc = 0;
120 		break;
121 	case CRB_FFA_OK_RESULTS_RETURNED:
122 		rc = 0;
123 		break;
124 	case CRB_FFA_NOFUNC:
125 		rc = -ENOENT;
126 		break;
127 	case CRB_FFA_NOTSUP:
128 		rc = -EPERM;
129 		break;
130 	case CRB_FFA_INVARG:
131 		rc = -EINVAL;
132 		break;
133 	case CRB_FFA_INV_CRB_CTRL_DATA:
134 		rc = -ENOEXEC;
135 		break;
136 	case CRB_FFA_ALREADY:
137 		rc = -EEXIST;
138 		break;
139 	case CRB_FFA_DENIED:
140 		rc = -EACCES;
141 		break;
142 	case CRB_FFA_NOMEM:
143 		rc = -ENOMEM;
144 		break;
145 	default:
146 		rc = -EINVAL;
147 	}
148 
149 	return rc;
150 }
151 
152 /**
153  * tpm_crb_ffa_init - called by the CRB driver to do any needed initialization
154  *
155  * This function is called by the tpm_crb driver during the tpm_crb
156  * driver's initialization. If the tpm_crb_ffa has not been probed
157  * yet, returns -ENOENT in order to force a retry.  If th ffa_crb
158  * driver had been probed  but failed with an error, returns -ENODEV
159  * in order to prevent further retries.
160  *
161  * Return: 0 on success, negative error code on failure.
162  */
163 int tpm_crb_ffa_init(void)
164 {
165 	if (!tpm_crb_ffa)
166 		return -ENOENT;
167 
168 	if (IS_ERR_VALUE(tpm_crb_ffa))
169 		return -ENODEV;
170 
171 	return 0;
172 }
173 EXPORT_SYMBOL_GPL(tpm_crb_ffa_init);
174 
175 static int __tpm_crb_ffa_send_recieve(unsigned long func_id,
176 				      unsigned long a0,
177 				      unsigned long a1,
178 				      unsigned long a2)
179 {
180 	const struct ffa_msg_ops *msg_ops;
181 	int ret;
182 
183 	if (!tpm_crb_ffa)
184 		return -ENOENT;
185 
186 	msg_ops = tpm_crb_ffa->ffa_dev->ops->msg_ops;
187 
188 	memset(&tpm_crb_ffa->direct_msg_data, 0x00,
189 	       sizeof(struct ffa_send_direct_data));
190 
191 	tpm_crb_ffa->direct_msg_data.data1 = func_id;
192 	tpm_crb_ffa->direct_msg_data.data2 = a0;
193 	tpm_crb_ffa->direct_msg_data.data3 = a1;
194 	tpm_crb_ffa->direct_msg_data.data4 = a2;
195 
196 	ret = msg_ops->sync_send_receive(tpm_crb_ffa->ffa_dev,
197 			&tpm_crb_ffa->direct_msg_data);
198 	if (!ret)
199 		ret = tpm_crb_ffa_to_linux_errno(tpm_crb_ffa->direct_msg_data.data1);
200 
201 	return ret;
202 }
203 
204 /**
205  * tpm_crb_ffa_get_interface_version() - gets the ABI version of the TPM service
206  * @major: Pointer to caller-allocated buffer to hold the major version
207  *         number the ABI
208  * @minor: Pointer to caller-allocated buffer to hold the minor version
209  *         number the ABI
210  *
211  * Returns the major and minor version of the ABI of the FF-A based TPM.
212  * Allows the caller to evaluate its compatibility with the version of
213  * the ABI.
214  *
215  * Return: 0 on success, negative error code on failure.
216  */
217 int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor)
218 {
219 	int rc;
220 
221 	if (!tpm_crb_ffa)
222 		return -ENOENT;
223 
224 	if (IS_ERR_VALUE(tpm_crb_ffa))
225 		return -ENODEV;
226 
227 	if (!major || !minor)
228 		return -EINVAL;
229 
230 	guard(mutex)(&tpm_crb_ffa->msg_data_lock);
231 
232 	rc = __tpm_crb_ffa_send_recieve(CRB_FFA_GET_INTERFACE_VERSION, 0x00, 0x00, 0x00);
233 	if (!rc) {
234 		*major = CRB_FFA_MAJOR_VERSION(tpm_crb_ffa->direct_msg_data.data2);
235 		*minor = CRB_FFA_MINOR_VERSION(tpm_crb_ffa->direct_msg_data.data2);
236 	}
237 
238 	return rc;
239 }
240 EXPORT_SYMBOL_GPL(tpm_crb_ffa_get_interface_version);
241 
242 /**
243  * tpm_crb_ffa_start() - signals the TPM that a field has changed in the CRB
244  * @request_type: Identifies whether the change to the CRB is in the command
245  *                fields or locality fields.
246  * @locality: Specifies the locality number.
247  *
248  * Used by the CRB driver
249  * that might be useful to those using or modifying it.  Begins with
250  * empty comment line, and may include additional embedded empty
251  * comment lines.
252  *
253  * Return: 0 on success, negative error code on failure.
254  */
255 int tpm_crb_ffa_start(int request_type, int locality)
256 {
257 	if (!tpm_crb_ffa)
258 		return -ENOENT;
259 
260 	if (IS_ERR_VALUE(tpm_crb_ffa))
261 		return -ENODEV;
262 
263 	guard(mutex)(&tpm_crb_ffa->msg_data_lock);
264 
265 	return __tpm_crb_ffa_send_recieve(CRB_FFA_START, request_type, locality, 0x00);
266 }
267 EXPORT_SYMBOL_GPL(tpm_crb_ffa_start);
268 
269 static int tpm_crb_ffa_probe(struct ffa_device *ffa_dev)
270 {
271 	struct tpm_crb_ffa *p;
272 	int rc;
273 
274 	/* only one instance of a TPM partition is supported */
275 	if (tpm_crb_ffa && !IS_ERR_VALUE(tpm_crb_ffa))
276 		return -EEXIST;
277 
278 	tpm_crb_ffa = ERR_PTR(-ENODEV); // set tpm_crb_ffa so we can detect probe failure
279 
280 	if (!ffa_partition_supports_direct_recv(ffa_dev)) {
281 		pr_err("TPM partition doesn't support direct message receive.\n");
282 		return -EINVAL;
283 	}
284 
285 	p = kzalloc(sizeof(*tpm_crb_ffa), GFP_KERNEL);
286 	if (!p)
287 		return -ENOMEM;
288 	tpm_crb_ffa = p;
289 
290 	mutex_init(&tpm_crb_ffa->msg_data_lock);
291 	tpm_crb_ffa->ffa_dev = ffa_dev;
292 	ffa_dev_set_drvdata(ffa_dev, tpm_crb_ffa);
293 
294 	/* if TPM is aarch32 use 32-bit SMCs */
295 	if (!ffa_partition_check_property(ffa_dev, FFA_PARTITION_AARCH64_EXEC))
296 		ffa_dev->ops->msg_ops->mode_32bit_set(ffa_dev);
297 
298 	/* verify compatibility of TPM service version number */
299 	rc = tpm_crb_ffa_get_interface_version(&tpm_crb_ffa->major_version,
300 					       &tpm_crb_ffa->minor_version);
301 	if (rc) {
302 		pr_err("failed to get crb interface version. rc:%d", rc);
303 		goto out;
304 	}
305 
306 	pr_info("ABI version %u.%u", tpm_crb_ffa->major_version,
307 		tpm_crb_ffa->minor_version);
308 
309 	if (tpm_crb_ffa->major_version != CRB_FFA_VERSION_MAJOR ||
310 	    (tpm_crb_ffa->minor_version > 0 &&
311 	    tpm_crb_ffa->minor_version < CRB_FFA_VERSION_MINOR)) {
312 		pr_err("Incompatible ABI version");
313 		goto out;
314 	}
315 
316 	return 0;
317 
318 out:
319 	kfree(tpm_crb_ffa);
320 	tpm_crb_ffa = ERR_PTR(-ENODEV);
321 	return -EINVAL;
322 }
323 
324 static void tpm_crb_ffa_remove(struct ffa_device *ffa_dev)
325 {
326 	kfree(tpm_crb_ffa);
327 	tpm_crb_ffa = NULL;
328 }
329 
330 static const struct ffa_device_id tpm_crb_ffa_device_id[] = {
331 	/* 17b862a4-1806-4faf-86b3-089a58353861 */
332 	{ UUID_INIT(0x17b862a4, 0x1806, 0x4faf,
333 		    0x86, 0xb3, 0x08, 0x9a, 0x58, 0x35, 0x38, 0x61) },
334 	{}
335 };
336 
337 static struct ffa_driver tpm_crb_ffa_driver = {
338 	.name = "ffa-crb",
339 	.probe = tpm_crb_ffa_probe,
340 	.remove = tpm_crb_ffa_remove,
341 	.id_table = tpm_crb_ffa_device_id,
342 };
343 
344 module_ffa_driver(tpm_crb_ffa_driver);
345 
346 MODULE_AUTHOR("Arm");
347 MODULE_DESCRIPTION("TPM CRB FFA driver");
348 MODULE_LICENSE("GPL");
349