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() - 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(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_plat_tee_probe(struct platform_device *pdev) 255 { 256 struct device *dev = &pdev->dev; 257 258 return ftpm_tee_probe(dev); 259 } 260 261 /** 262 * ftpm_tee_remove() - remove the TPM device 263 * @dev: the device description. 264 * 265 * Return: 266 * 0 always. 267 */ 268 static int ftpm_tee_remove(struct device *dev) 269 { 270 struct ftpm_tee_private *pvt_data = dev_get_drvdata(dev); 271 272 /* Release the chip */ 273 tpm_chip_unregister(pvt_data->chip); 274 275 /* frees chip */ 276 put_device(&pvt_data->chip->dev); 277 278 /* Free the shared memory pool */ 279 tee_shm_free(pvt_data->shm); 280 281 /* close the existing session with fTPM TA*/ 282 tee_client_close_session(pvt_data->ctx, pvt_data->session); 283 284 /* close the context with TEE driver */ 285 tee_client_close_context(pvt_data->ctx); 286 287 /* memory allocated with devm_kzalloc() is freed automatically */ 288 289 return 0; 290 } 291 292 static void ftpm_plat_tee_remove(struct platform_device *pdev) 293 { 294 struct device *dev = &pdev->dev; 295 296 ftpm_tee_remove(dev); 297 } 298 299 /** 300 * ftpm_plat_tee_shutdown() - shutdown the TPM device 301 * @pdev: the platform_device description. 302 */ 303 static void ftpm_plat_tee_shutdown(struct platform_device *pdev) 304 { 305 struct ftpm_tee_private *pvt_data = dev_get_drvdata(&pdev->dev); 306 307 tee_shm_free(pvt_data->shm); 308 tee_client_close_session(pvt_data->ctx, pvt_data->session); 309 tee_client_close_context(pvt_data->ctx); 310 } 311 312 static const struct of_device_id of_ftpm_tee_ids[] = { 313 { .compatible = "microsoft,ftpm" }, 314 { } 315 }; 316 MODULE_DEVICE_TABLE(of, of_ftpm_tee_ids); 317 318 static struct platform_driver ftpm_tee_plat_driver = { 319 .driver = { 320 .name = "ftpm-tee", 321 .of_match_table = of_ftpm_tee_ids, 322 }, 323 .shutdown = ftpm_plat_tee_shutdown, 324 .probe = ftpm_plat_tee_probe, 325 .remove = ftpm_plat_tee_remove, 326 }; 327 328 /* UUID of the fTPM TA */ 329 static const struct tee_client_device_id optee_ftpm_id_table[] = { 330 {UUID_INIT(0xbc50d971, 0xd4c9, 0x42c4, 331 0x82, 0xcb, 0x34, 0x3f, 0xb7, 0xf3, 0x78, 0x96)}, 332 {} 333 }; 334 335 MODULE_DEVICE_TABLE(tee, optee_ftpm_id_table); 336 337 static struct tee_client_driver ftpm_tee_driver = { 338 .id_table = optee_ftpm_id_table, 339 .driver = { 340 .name = "optee-ftpm", 341 .bus = &tee_bus_type, 342 .probe = ftpm_tee_probe, 343 .remove = ftpm_tee_remove, 344 }, 345 }; 346 347 static int __init ftpm_mod_init(void) 348 { 349 int rc; 350 351 rc = platform_driver_register(&ftpm_tee_plat_driver); 352 if (rc) 353 return rc; 354 355 rc = driver_register(&ftpm_tee_driver.driver); 356 if (rc) { 357 platform_driver_unregister(&ftpm_tee_plat_driver); 358 return rc; 359 } 360 361 return 0; 362 } 363 364 static void __exit ftpm_mod_exit(void) 365 { 366 platform_driver_unregister(&ftpm_tee_plat_driver); 367 driver_unregister(&ftpm_tee_driver.driver); 368 } 369 370 module_init(ftpm_mod_init); 371 module_exit(ftpm_mod_exit); 372 373 MODULE_AUTHOR("Thirupathaiah Annapureddy <thiruan@microsoft.com>"); 374 MODULE_DESCRIPTION("TPM Driver for fTPM TA in TEE"); 375 MODULE_LICENSE("GPL v2"); 376