Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) Microsoft Corporation
0004  *
0005  * Implements a firmware TPM as described here:
0006  * https://www.microsoft.com/en-us/research/publication/ftpm-software-implementation-tpm-chip/
0007  *
0008  * A reference implementation is available here:
0009  * https://github.com/microsoft/ms-tpm-20-ref/tree/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM
0010  */
0011 
0012 #include <linux/acpi.h>
0013 #include <linux/of.h>
0014 #include <linux/of_platform.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/tee_drv.h>
0017 #include <linux/tpm.h>
0018 #include <linux/uuid.h>
0019 
0020 #include "tpm.h"
0021 #include "tpm_ftpm_tee.h"
0022 
0023 /*
0024  * TA_FTPM_UUID: BC50D971-D4C9-42C4-82CB-343FB7F37896
0025  *
0026  * Randomly generated, and must correspond to the GUID on the TA side.
0027  * Defined here in the reference implementation:
0028  * https://github.com/microsoft/ms-tpm-20-ref/blob/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM/include/fTPM.h#L42
0029  */
0030 static const uuid_t ftpm_ta_uuid =
0031     UUID_INIT(0xBC50D971, 0xD4C9, 0x42C4,
0032           0x82, 0xCB, 0x34, 0x3F, 0xB7, 0xF3, 0x78, 0x96);
0033 
0034 /**
0035  * ftpm_tee_tpm_op_recv() - retrieve fTPM response.
0036  * @chip:   the tpm_chip description as specified in driver/char/tpm/tpm.h.
0037  * @buf:    the buffer to store data.
0038  * @count:  the number of bytes to read.
0039  *
0040  * Return:
0041  *  In case of success the number of bytes received.
0042  *  On failure, -errno.
0043  */
0044 static int ftpm_tee_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count)
0045 {
0046     struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent);
0047     size_t len;
0048 
0049     len = pvt_data->resp_len;
0050     if (count < len) {
0051         dev_err(&chip->dev,
0052             "%s: Invalid size in recv: count=%zd, resp_len=%zd\n",
0053             __func__, count, len);
0054         return -EIO;
0055     }
0056 
0057     memcpy(buf, pvt_data->resp_buf, len);
0058     pvt_data->resp_len = 0;
0059 
0060     return len;
0061 }
0062 
0063 /**
0064  * ftpm_tee_tpm_op_send() - send TPM commands through the TEE shared memory.
0065  * @chip:   the tpm_chip description as specified in driver/char/tpm/tpm.h
0066  * @buf:    the buffer to send.
0067  * @len:    the number of bytes to send.
0068  *
0069  * Return:
0070  *  In case of success, returns 0.
0071  *  On failure, -errno
0072  */
0073 static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len)
0074 {
0075     struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent);
0076     size_t resp_len;
0077     int rc;
0078     u8 *temp_buf;
0079     struct tpm_header *resp_header;
0080     struct tee_ioctl_invoke_arg transceive_args;
0081     struct tee_param command_params[4];
0082     struct tee_shm *shm = pvt_data->shm;
0083 
0084     if (len > MAX_COMMAND_SIZE) {
0085         dev_err(&chip->dev,
0086             "%s: len=%zd exceeds MAX_COMMAND_SIZE supported by fTPM TA\n",
0087             __func__, len);
0088         return -EIO;
0089     }
0090 
0091     memset(&transceive_args, 0, sizeof(transceive_args));
0092     memset(command_params, 0, sizeof(command_params));
0093     pvt_data->resp_len = 0;
0094 
0095     /* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of fTPM TA */
0096     transceive_args = (struct tee_ioctl_invoke_arg) {
0097         .func = FTPM_OPTEE_TA_SUBMIT_COMMAND,
0098         .session = pvt_data->session,
0099         .num_params = 4,
0100     };
0101 
0102     /* Fill FTPM_OPTEE_TA_SUBMIT_COMMAND parameters */
0103     command_params[0] = (struct tee_param) {
0104         .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
0105         .u.memref = {
0106             .shm = shm,
0107             .size = len,
0108             .shm_offs = 0,
0109         },
0110     };
0111 
0112     temp_buf = tee_shm_get_va(shm, 0);
0113     if (IS_ERR(temp_buf)) {
0114         dev_err(&chip->dev, "%s: tee_shm_get_va failed for transmit\n",
0115             __func__);
0116         return PTR_ERR(temp_buf);
0117     }
0118     memset(temp_buf, 0, (MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE));
0119     memcpy(temp_buf, buf, len);
0120 
0121     command_params[1] = (struct tee_param) {
0122         .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT,
0123         .u.memref = {
0124             .shm = shm,
0125             .size = MAX_RESPONSE_SIZE,
0126             .shm_offs = MAX_COMMAND_SIZE,
0127         },
0128     };
0129 
0130     rc = tee_client_invoke_func(pvt_data->ctx, &transceive_args,
0131                     command_params);
0132     if ((rc < 0) || (transceive_args.ret != 0)) {
0133         dev_err(&chip->dev, "%s: SUBMIT_COMMAND invoke error: 0x%x\n",
0134             __func__, transceive_args.ret);
0135         return (rc < 0) ? rc : transceive_args.ret;
0136     }
0137 
0138     temp_buf = tee_shm_get_va(shm, command_params[1].u.memref.shm_offs);
0139     if (IS_ERR(temp_buf)) {
0140         dev_err(&chip->dev, "%s: tee_shm_get_va failed for receive\n",
0141             __func__);
0142         return PTR_ERR(temp_buf);
0143     }
0144 
0145     resp_header = (struct tpm_header *)temp_buf;
0146     resp_len = be32_to_cpu(resp_header->length);
0147 
0148     /* sanity check resp_len */
0149     if (resp_len < TPM_HEADER_SIZE) {
0150         dev_err(&chip->dev, "%s: tpm response header too small\n",
0151             __func__);
0152         return -EIO;
0153     }
0154     if (resp_len > MAX_RESPONSE_SIZE) {
0155         dev_err(&chip->dev,
0156             "%s: resp_len=%zd exceeds MAX_RESPONSE_SIZE\n",
0157             __func__, resp_len);
0158         return -EIO;
0159     }
0160 
0161     /* sanity checks look good, cache the response */
0162     memcpy(pvt_data->resp_buf, temp_buf, resp_len);
0163     pvt_data->resp_len = resp_len;
0164 
0165     return 0;
0166 }
0167 
0168 static void ftpm_tee_tpm_op_cancel(struct tpm_chip *chip)
0169 {
0170     /* not supported */
0171 }
0172 
0173 static u8 ftpm_tee_tpm_op_status(struct tpm_chip *chip)
0174 {
0175     return 0;
0176 }
0177 
0178 static bool ftpm_tee_tpm_req_canceled(struct tpm_chip *chip, u8 status)
0179 {
0180     return false;
0181 }
0182 
0183 static const struct tpm_class_ops ftpm_tee_tpm_ops = {
0184     .flags = TPM_OPS_AUTO_STARTUP,
0185     .recv = ftpm_tee_tpm_op_recv,
0186     .send = ftpm_tee_tpm_op_send,
0187     .cancel = ftpm_tee_tpm_op_cancel,
0188     .status = ftpm_tee_tpm_op_status,
0189     .req_complete_mask = 0,
0190     .req_complete_val = 0,
0191     .req_canceled = ftpm_tee_tpm_req_canceled,
0192 };
0193 
0194 /*
0195  * Check whether this driver supports the fTPM TA in the TEE instance
0196  * represented by the params (ver/data) to this function.
0197  */
0198 static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void *data)
0199 {
0200     /*
0201      * Currently this driver only support GP Complaint OPTEE based fTPM TA
0202      */
0203     if ((ver->impl_id == TEE_IMPL_ID_OPTEE) &&
0204         (ver->gen_caps & TEE_GEN_CAP_GP))
0205         return 1;
0206     else
0207         return 0;
0208 }
0209 
0210 /**
0211  * ftpm_tee_probe() - initialize the fTPM
0212  * @pdev: the platform_device description.
0213  *
0214  * Return:
0215  *  On success, 0. On failure, -errno.
0216  */
0217 static int ftpm_tee_probe(struct device *dev)
0218 {
0219     int rc;
0220     struct tpm_chip *chip;
0221     struct ftpm_tee_private *pvt_data = NULL;
0222     struct tee_ioctl_open_session_arg sess_arg;
0223 
0224     pvt_data = devm_kzalloc(dev, sizeof(struct ftpm_tee_private),
0225                 GFP_KERNEL);
0226     if (!pvt_data)
0227         return -ENOMEM;
0228 
0229     dev_set_drvdata(dev, pvt_data);
0230 
0231     /* Open context with TEE driver */
0232     pvt_data->ctx = tee_client_open_context(NULL, ftpm_tee_match, NULL,
0233                         NULL);
0234     if (IS_ERR(pvt_data->ctx)) {
0235         if (PTR_ERR(pvt_data->ctx) == -ENOENT)
0236             return -EPROBE_DEFER;
0237         dev_err(dev, "%s: tee_client_open_context failed\n", __func__);
0238         return PTR_ERR(pvt_data->ctx);
0239     }
0240 
0241     /* Open a session with fTPM TA */
0242     memset(&sess_arg, 0, sizeof(sess_arg));
0243     export_uuid(sess_arg.uuid, &ftpm_ta_uuid);
0244     sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
0245     sess_arg.num_params = 0;
0246 
0247     rc = tee_client_open_session(pvt_data->ctx, &sess_arg, NULL);
0248     if ((rc < 0) || (sess_arg.ret != 0)) {
0249         dev_err(dev, "%s: tee_client_open_session failed, err=%x\n",
0250             __func__, sess_arg.ret);
0251         rc = -EINVAL;
0252         goto out_tee_session;
0253     }
0254     pvt_data->session = sess_arg.session;
0255 
0256     /* Allocate dynamic shared memory with fTPM TA */
0257     pvt_data->shm = tee_shm_alloc_kernel_buf(pvt_data->ctx,
0258                          MAX_COMMAND_SIZE +
0259                          MAX_RESPONSE_SIZE);
0260     if (IS_ERR(pvt_data->shm)) {
0261         dev_err(dev, "%s: tee_shm_alloc_kernel_buf failed\n", __func__);
0262         rc = -ENOMEM;
0263         goto out_shm_alloc;
0264     }
0265 
0266     /* Allocate new struct tpm_chip instance */
0267     chip = tpm_chip_alloc(dev, &ftpm_tee_tpm_ops);
0268     if (IS_ERR(chip)) {
0269         dev_err(dev, "%s: tpm_chip_alloc failed\n", __func__);
0270         rc = PTR_ERR(chip);
0271         goto out_chip_alloc;
0272     }
0273 
0274     pvt_data->chip = chip;
0275     pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2;
0276 
0277     /* Create a character device for the fTPM */
0278     rc = tpm_chip_register(pvt_data->chip);
0279     if (rc) {
0280         dev_err(dev, "%s: tpm_chip_register failed with rc=%d\n",
0281             __func__, rc);
0282         goto out_chip;
0283     }
0284 
0285     return 0;
0286 
0287 out_chip:
0288     put_device(&pvt_data->chip->dev);
0289 out_chip_alloc:
0290     tee_shm_free(pvt_data->shm);
0291 out_shm_alloc:
0292     tee_client_close_session(pvt_data->ctx, pvt_data->session);
0293 out_tee_session:
0294     tee_client_close_context(pvt_data->ctx);
0295 
0296     return rc;
0297 }
0298 
0299 static int ftpm_plat_tee_probe(struct platform_device *pdev)
0300 {
0301     struct device *dev = &pdev->dev;
0302 
0303     return ftpm_tee_probe(dev);
0304 }
0305 
0306 /**
0307  * ftpm_tee_remove() - remove the TPM device
0308  * @pdev: the platform_device description.
0309  *
0310  * Return:
0311  *  0 always.
0312  */
0313 static int ftpm_tee_remove(struct device *dev)
0314 {
0315     struct ftpm_tee_private *pvt_data = dev_get_drvdata(dev);
0316 
0317     /* Release the chip */
0318     tpm_chip_unregister(pvt_data->chip);
0319 
0320     /* frees chip */
0321     put_device(&pvt_data->chip->dev);
0322 
0323     /* Free the shared memory pool */
0324     tee_shm_free(pvt_data->shm);
0325 
0326     /* close the existing session with fTPM TA*/
0327     tee_client_close_session(pvt_data->ctx, pvt_data->session);
0328 
0329     /* close the context with TEE driver */
0330     tee_client_close_context(pvt_data->ctx);
0331 
0332     /* memory allocated with devm_kzalloc() is freed automatically */
0333 
0334     return 0;
0335 }
0336 
0337 static int ftpm_plat_tee_remove(struct platform_device *pdev)
0338 {
0339     struct device *dev = &pdev->dev;
0340 
0341     return ftpm_tee_remove(dev);
0342 }
0343 
0344 /**
0345  * ftpm_tee_shutdown() - shutdown the TPM device
0346  * @pdev: the platform_device description.
0347  */
0348 static void ftpm_plat_tee_shutdown(struct platform_device *pdev)
0349 {
0350     struct ftpm_tee_private *pvt_data = dev_get_drvdata(&pdev->dev);
0351 
0352     tee_shm_free(pvt_data->shm);
0353     tee_client_close_session(pvt_data->ctx, pvt_data->session);
0354     tee_client_close_context(pvt_data->ctx);
0355 }
0356 
0357 static const struct of_device_id of_ftpm_tee_ids[] = {
0358     { .compatible = "microsoft,ftpm" },
0359     { }
0360 };
0361 MODULE_DEVICE_TABLE(of, of_ftpm_tee_ids);
0362 
0363 static struct platform_driver ftpm_tee_plat_driver = {
0364     .driver = {
0365         .name = "ftpm-tee",
0366         .of_match_table = of_match_ptr(of_ftpm_tee_ids),
0367     },
0368     .shutdown = ftpm_plat_tee_shutdown,
0369     .probe = ftpm_plat_tee_probe,
0370     .remove = ftpm_plat_tee_remove,
0371 };
0372 
0373 /* UUID of the fTPM TA */
0374 static const struct tee_client_device_id optee_ftpm_id_table[] = {
0375     {UUID_INIT(0xbc50d971, 0xd4c9, 0x42c4,
0376            0x82, 0xcb, 0x34, 0x3f, 0xb7, 0xf3, 0x78, 0x96)},
0377     {}
0378 };
0379 
0380 MODULE_DEVICE_TABLE(tee, optee_ftpm_id_table);
0381 
0382 static struct tee_client_driver ftpm_tee_driver = {
0383     .id_table   = optee_ftpm_id_table,
0384     .driver     = {
0385         .name       = "optee-ftpm",
0386         .bus        = &tee_bus_type,
0387         .probe      = ftpm_tee_probe,
0388         .remove     = ftpm_tee_remove,
0389     },
0390 };
0391 
0392 static int __init ftpm_mod_init(void)
0393 {
0394     int rc;
0395 
0396     rc = platform_driver_register(&ftpm_tee_plat_driver);
0397     if (rc)
0398         return rc;
0399 
0400     return driver_register(&ftpm_tee_driver.driver);
0401 }
0402 
0403 static void __exit ftpm_mod_exit(void)
0404 {
0405     platform_driver_unregister(&ftpm_tee_plat_driver);
0406     driver_unregister(&ftpm_tee_driver.driver);
0407 }
0408 
0409 module_init(ftpm_mod_init);
0410 module_exit(ftpm_mod_exit);
0411 
0412 MODULE_AUTHOR("Thirupathaiah Annapureddy <thiruan@microsoft.com>");
0413 MODULE_DESCRIPTION("TPM Driver for fTPM TA in TEE");
0414 MODULE_LICENSE("GPL v2");