root/drivers/char/tpm/tpm_ftpm_tee.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ftpm_tee_tpm_op_recv
  2. ftpm_tee_tpm_op_send
  3. ftpm_tee_tpm_op_cancel
  4. ftpm_tee_tpm_op_status
  5. ftpm_tee_tpm_req_canceled
  6. ftpm_tee_match
  7. ftpm_tee_probe
  8. ftpm_tee_remove
  9. ftpm_tee_shutdown

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

/* [<][>][^][v][top][bottom][index][help] */