1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) Microsoft Corporation 4 * 5 * Implements a firmware TPM as described here: 6 * https://www.microsoft.com/en-us/research/publication/ftpm-software-implementation-tpm-chip/ 7 * 8 * A reference implementation is available here: 9 * https://github.com/microsoft/ms-tpm-20-ref/tree/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM 10 */ 11 12 #include <linux/acpi.h> 13 #include <linux/of.h> 14 #include <linux/platform_device.h> 15 #include <linux/tee_drv.h> 16 #include <linux/tpm.h> 17 #include <linux/uuid.h> 18 19 #include "tpm.h" 20 #include "tpm_ftpm_tee.h" 21 22 /* 23 * TA_FTPM_UUID: BC50D971-D4C9-42C4-82CB-343FB7F37896 24 * 25 * Randomly generated, and must correspond to the GUID on the TA side. 26 * Defined here in the reference implementation: 27 * https://github.com/microsoft/ms-tpm-20-ref/blob/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM/include/fTPM.h#L42 28 */ 29 static const uuid_t ftpm_ta_uuid = 30 UUID_INIT(0xBC50D971, 0xD4C9, 0x42C4, 31 0x82, 0xCB, 0x34, 0x3F, 0xB7, 0xF3, 0x78, 0x96); 32 33 /** 34 * ftpm_tee_tpm_op_send() - send TPM commands through the TEE shared memory 35 * and retrieve the response. 36 * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h 37 * @buf: the buffer to send and to store the response. 38 * @bufsiz: the size of the buffer. 39 * @cmd_len: the number of bytes to send. 40 * 41 * Return: 42 * In case of success, returns the number of bytes received. 43 * On failure, -errno 44 */ 45 static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, 46 size_t cmd_len) 47 { 48 struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent); 49 size_t resp_len; 50 int rc; 51 u8 *temp_buf; 52 struct tpm_header *resp_header; 53 struct tee_ioctl_invoke_arg transceive_args; 54 struct tee_param command_params[4]; 55 struct tee_shm *shm = pvt_data->shm; 56 57 if (cmd_len > MAX_COMMAND_SIZE) { 58 dev_err(&chip->dev, 59 "%s: len=%zd exceeds MAX_COMMAND_SIZE supported by fTPM TA\n", 60 __func__, cmd_len); 61 return -EIO; 62 } 63 64 memset(&transceive_args, 0, sizeof(transceive_args)); 65 memset(command_params, 0, sizeof(command_params)); 66 67 /* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of fTPM TA */ 68 transceive_args = (struct tee_ioctl_invoke_arg) { 69 .func = FTPM_OPTEE_TA_SUBMIT_COMMAND, 70 .session = pvt_data->session, 71 .num_params = 4, 72 }; 73 74 /* Fill FTPM_OPTEE_TA_SUBMIT_COMMAND parameters */ 75 command_params[0] = (struct tee_param) { 76 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, 77 .u.memref = { 78 .shm = shm, 79 .size = cmd_len, 80 .shm_offs = 0, 81 }, 82 }; 83 84 temp_buf = tee_shm_get_va(shm, 0); 85 if (IS_ERR(temp_buf)) { 86 dev_err(&chip->dev, "%s: tee_shm_get_va failed for transmit\n", 87 __func__); 88 return PTR_ERR(temp_buf); 89 } 90 memset(temp_buf, 0, (MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE)); 91 memcpy(temp_buf, buf, cmd_len); 92 93 command_params[1] = (struct tee_param) { 94 .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT, 95 .u.memref = { 96 .shm = shm, 97 .size = MAX_RESPONSE_SIZE, 98 .shm_offs = MAX_COMMAND_SIZE, 99 }, 100 }; 101 102 rc = tee_client_invoke_func(pvt_data->ctx, &transceive_args, 103 command_params); 104 if ((rc < 0) || (transceive_args.ret != 0)) { 105 dev_err(&chip->dev, "%s: SUBMIT_COMMAND invoke error: 0x%x\n", 106 __func__, transceive_args.ret); 107 return (rc < 0) ? rc : transceive_args.ret; 108 } 109 110 temp_buf = tee_shm_get_va(shm, command_params[1].u.memref.shm_offs); 111 if (IS_ERR(temp_buf)) { 112 dev_err(&chip->dev, "%s: tee_shm_get_va failed for receive\n", 113 __func__); 114 return PTR_ERR(temp_buf); 115 } 116 117 resp_header = (struct tpm_header *)temp_buf; 118 resp_len = be32_to_cpu(resp_header->length); 119 120 /* sanity check resp_len */ 121 if (resp_len < TPM_HEADER_SIZE) { 122 dev_err(&chip->dev, "%s: tpm response header too small\n", 123 __func__); 124 return -EIO; 125 } 126 if (resp_len > MAX_RESPONSE_SIZE) { 127 dev_err(&chip->dev, 128 "%s: resp_len=%zd exceeds MAX_RESPONSE_SIZE\n", 129 __func__, resp_len); 130 return -EIO; 131 } 132 if (resp_len > bufsiz) { 133 dev_err(&chip->dev, 134 "%s: resp_len=%zd exceeds bufsiz=%zd\n", 135 __func__, resp_len, bufsiz); 136 return -EIO; 137 } 138 139 memcpy(buf, temp_buf, resp_len); 140 141 return resp_len; 142 } 143 144 static const struct tpm_class_ops ftpm_tee_tpm_ops = { 145 .flags = TPM_OPS_AUTO_STARTUP, 146 .send = ftpm_tee_tpm_op_send, 147 }; 148 149 /* 150 * Check whether this driver supports the fTPM TA in the TEE instance 151 * represented by the params (ver/data) to this function. 152 */ 153 static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void *data) 154 { 155 /* 156 * Currently this driver only support GP Complaint OPTEE based fTPM TA 157 */ 158 if ((ver->impl_id == TEE_IMPL_ID_OPTEE) && 159 (ver->gen_caps & TEE_GEN_CAP_GP)) 160 return 1; 161 else 162 return 0; 163 } 164 165 /** 166 * ftpm_tee_probe_generic() - initialize the fTPM 167 * @dev: the device description. 168 * 169 * Return: 170 * On success, 0. On failure, -errno. 171 */ 172 static int ftpm_tee_probe_generic(struct device *dev) 173 { 174 int rc; 175 struct tpm_chip *chip; 176 struct ftpm_tee_private *pvt_data = NULL; 177 struct tee_ioctl_open_session_arg sess_arg; 178 179 pvt_data = devm_kzalloc(dev, sizeof(struct ftpm_tee_private), 180 GFP_KERNEL); 181 if (!pvt_data) 182 return -ENOMEM; 183 184 dev_set_drvdata(dev, pvt_data); 185 186 /* Open context with TEE driver */ 187 pvt_data->ctx = tee_client_open_context(NULL, ftpm_tee_match, NULL, 188 NULL); 189 if (IS_ERR(pvt_data->ctx)) { 190 if (PTR_ERR(pvt_data->ctx) == -ENOENT) 191 return -EPROBE_DEFER; 192 dev_err(dev, "%s: tee_client_open_context failed\n", __func__); 193 return PTR_ERR(pvt_data->ctx); 194 } 195 196 /* Open a session with fTPM TA */ 197 memset(&sess_arg, 0, sizeof(sess_arg)); 198 export_uuid(sess_arg.uuid, &ftpm_ta_uuid); 199 sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; 200 sess_arg.num_params = 0; 201 202 rc = tee_client_open_session(pvt_data->ctx, &sess_arg, NULL); 203 if ((rc < 0) || (sess_arg.ret != 0)) { 204 dev_err(dev, "%s: tee_client_open_session failed, err=%x\n", 205 __func__, sess_arg.ret); 206 rc = -EINVAL; 207 goto out_tee_session; 208 } 209 pvt_data->session = sess_arg.session; 210 211 /* Allocate dynamic shared memory with fTPM TA */ 212 pvt_data->shm = tee_shm_alloc_kernel_buf(pvt_data->ctx, 213 MAX_COMMAND_SIZE + 214 MAX_RESPONSE_SIZE); 215 if (IS_ERR(pvt_data->shm)) { 216 dev_err(dev, "%s: tee_shm_alloc_kernel_buf failed\n", __func__); 217 rc = -ENOMEM; 218 goto out_shm_alloc; 219 } 220 221 /* Allocate new struct tpm_chip instance */ 222 chip = tpm_chip_alloc(dev, &ftpm_tee_tpm_ops); 223 if (IS_ERR(chip)) { 224 dev_err(dev, "%s: tpm_chip_alloc failed\n", __func__); 225 rc = PTR_ERR(chip); 226 goto out_chip_alloc; 227 } 228 229 pvt_data->chip = chip; 230 pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_SYNC; 231 232 /* Create a character device for the fTPM */ 233 rc = tpm_chip_register(pvt_data->chip); 234 if (rc) { 235 dev_err(dev, "%s: tpm_chip_register failed with rc=%d\n", 236 __func__, rc); 237 goto out_chip; 238 } 239 240 return 0; 241 242 out_chip: 243 put_device(&pvt_data->chip->dev); 244 out_chip_alloc: 245 tee_shm_free(pvt_data->shm); 246 out_shm_alloc: 247 tee_client_close_session(pvt_data->ctx, pvt_data->session); 248 out_tee_session: 249 tee_client_close_context(pvt_data->ctx); 250 251 return rc; 252 } 253 254 static int ftpm_tee_probe(struct tee_client_device *tcdev) 255 { 256 struct device *dev = &tcdev->dev; 257 258 return ftpm_tee_probe_generic(dev); 259 } 260 261 static int ftpm_plat_tee_probe(struct platform_device *pdev) 262 { 263 struct device *dev = &pdev->dev; 264 265 return ftpm_tee_probe_generic(dev); 266 } 267 268 /** 269 * ftpm_tee_remove_generic() - remove the TPM device 270 * @dev: the device description. 271 * 272 * Return: 273 * 0 always. 274 */ 275 static void ftpm_tee_remove_generic(struct device *dev) 276 { 277 struct ftpm_tee_private *pvt_data = dev_get_drvdata(dev); 278 279 /* Release the chip */ 280 tpm_chip_unregister(pvt_data->chip); 281 282 /* frees chip */ 283 put_device(&pvt_data->chip->dev); 284 285 /* Free the shared memory pool */ 286 tee_shm_free(pvt_data->shm); 287 288 /* close the existing session with fTPM TA*/ 289 tee_client_close_session(pvt_data->ctx, pvt_data->session); 290 291 /* close the context with TEE driver */ 292 tee_client_close_context(pvt_data->ctx); 293 294 /* memory allocated with devm_kzalloc() is freed automatically */ 295 } 296 297 static void ftpm_tee_remove(struct tee_client_device *tcdev) 298 { 299 struct device *dev = &tcdev->dev; 300 301 ftpm_tee_remove_generic(dev); 302 } 303 304 static void ftpm_plat_tee_remove(struct platform_device *pdev) 305 { 306 struct device *dev = &pdev->dev; 307 308 ftpm_tee_remove_generic(dev); 309 } 310 311 /** 312 * ftpm_plat_tee_shutdown() - shutdown the TPM device 313 * @pdev: the platform_device description. 314 */ 315 static void ftpm_plat_tee_shutdown(struct platform_device *pdev) 316 { 317 struct ftpm_tee_private *pvt_data = dev_get_drvdata(&pdev->dev); 318 319 tee_shm_free(pvt_data->shm); 320 tee_client_close_session(pvt_data->ctx, pvt_data->session); 321 tee_client_close_context(pvt_data->ctx); 322 } 323 324 static const struct of_device_id of_ftpm_tee_ids[] = { 325 { .compatible = "microsoft,ftpm" }, 326 { } 327 }; 328 MODULE_DEVICE_TABLE(of, of_ftpm_tee_ids); 329 330 static struct platform_driver ftpm_tee_plat_driver = { 331 .driver = { 332 .name = "ftpm-tee", 333 .of_match_table = of_ftpm_tee_ids, 334 }, 335 .shutdown = ftpm_plat_tee_shutdown, 336 .probe = ftpm_plat_tee_probe, 337 .remove = ftpm_plat_tee_remove, 338 }; 339 340 /* UUID of the fTPM TA */ 341 static const struct tee_client_device_id optee_ftpm_id_table[] = { 342 {UUID_INIT(0xbc50d971, 0xd4c9, 0x42c4, 343 0x82, 0xcb, 0x34, 0x3f, 0xb7, 0xf3, 0x78, 0x96)}, 344 {} 345 }; 346 347 MODULE_DEVICE_TABLE(tee, optee_ftpm_id_table); 348 349 static struct tee_client_driver ftpm_tee_driver = { 350 .probe = ftpm_tee_probe, 351 .remove = ftpm_tee_remove, 352 .id_table = optee_ftpm_id_table, 353 .driver = { 354 .name = "optee-ftpm", 355 }, 356 }; 357 358 static int __init ftpm_mod_init(void) 359 { 360 int rc; 361 362 rc = platform_driver_register(&ftpm_tee_plat_driver); 363 if (rc) 364 return rc; 365 366 rc = tee_client_driver_register(&ftpm_tee_driver); 367 if (rc) { 368 platform_driver_unregister(&ftpm_tee_plat_driver); 369 return rc; 370 } 371 372 return 0; 373 } 374 375 static void __exit ftpm_mod_exit(void) 376 { 377 platform_driver_unregister(&ftpm_tee_plat_driver); 378 tee_client_driver_unregister(&ftpm_tee_driver); 379 } 380 381 module_init(ftpm_mod_init); 382 module_exit(ftpm_mod_exit); 383 384 MODULE_AUTHOR("Thirupathaiah Annapureddy <thiruan@microsoft.com>"); 385 MODULE_DESCRIPTION("TPM Driver for fTPM TA in TEE"); 386 MODULE_LICENSE("GPL v2"); 387