linux/drivers/char/tpm/tpm_ftpm_tee.c
<<
>>
Prefs
   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 */
  30static 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 */
  44static 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 */
  73static 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
 168static void ftpm_tee_tpm_op_cancel(struct tpm_chip *chip)
 169{
 170        /* not supported */
 171}
 172
 173static u8 ftpm_tee_tpm_op_status(struct tpm_chip *chip)
 174{
 175        return 0;
 176}
 177
 178static bool ftpm_tee_tpm_req_canceled(struct tpm_chip *chip, u8 status)
 179{
 180        return 0;
 181}
 182
 183static 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 */
 198static 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 */
 217static int ftpm_tee_probe(struct device *dev)
 218{
 219        int rc;
 220        struct tpm_chip *chip;
 221        struct ftpm_tee_private *pvt_data = NULL;
 222        struct tee_ioctl_open_session_arg sess_arg;
 223
 224        pvt_data = devm_kzalloc(dev, sizeof(struct ftpm_tee_private),
 225                                GFP_KERNEL);
 226        if (!pvt_data)
 227                return -ENOMEM;
 228
 229        dev_set_drvdata(dev, pvt_data);
 230
 231        /* Open context with TEE driver */
 232        pvt_data->ctx = tee_client_open_context(NULL, ftpm_tee_match, NULL,
 233                                                NULL);
 234        if (IS_ERR(pvt_data->ctx)) {
 235                if (PTR_ERR(pvt_data->ctx) == -ENOENT)
 236                        return -EPROBE_DEFER;
 237                dev_err(dev, "%s: tee_client_open_context failed\n", __func__);
 238                return PTR_ERR(pvt_data->ctx);
 239        }
 240
 241        /* Open a session with fTPM TA */
 242        memset(&sess_arg, 0, sizeof(sess_arg));
 243        export_uuid(sess_arg.uuid, &ftpm_ta_uuid);
 244        sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
 245        sess_arg.num_params = 0;
 246
 247        rc = tee_client_open_session(pvt_data->ctx, &sess_arg, NULL);
 248        if ((rc < 0) || (sess_arg.ret != 0)) {
 249                dev_err(dev, "%s: tee_client_open_session failed, err=%x\n",
 250                        __func__, sess_arg.ret);
 251                rc = -EINVAL;
 252                goto out_tee_session;
 253        }
 254        pvt_data->session = sess_arg.session;
 255
 256        /* Allocate dynamic shared memory with fTPM TA */
 257        pvt_data->shm = tee_shm_alloc_kernel_buf(pvt_data->ctx,
 258                                                 MAX_COMMAND_SIZE +
 259                                                 MAX_RESPONSE_SIZE);
 260        if (IS_ERR(pvt_data->shm)) {
 261                dev_err(dev, "%s: tee_shm_alloc_kernel_buf failed\n", __func__);
 262                rc = -ENOMEM;
 263                goto out_shm_alloc;
 264        }
 265
 266        /* Allocate new struct tpm_chip instance */
 267        chip = tpm_chip_alloc(dev, &ftpm_tee_tpm_ops);
 268        if (IS_ERR(chip)) {
 269                dev_err(dev, "%s: tpm_chip_alloc failed\n", __func__);
 270                rc = PTR_ERR(chip);
 271                goto out_chip_alloc;
 272        }
 273
 274        pvt_data->chip = chip;
 275        pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2;
 276
 277        /* Create a character device for the fTPM */
 278        rc = tpm_chip_register(pvt_data->chip);
 279        if (rc) {
 280                dev_err(dev, "%s: tpm_chip_register failed with rc=%d\n",
 281                        __func__, rc);
 282                goto out_chip;
 283        }
 284
 285        return 0;
 286
 287out_chip:
 288        put_device(&pvt_data->chip->dev);
 289out_chip_alloc:
 290        tee_shm_free(pvt_data->shm);
 291out_shm_alloc:
 292        tee_client_close_session(pvt_data->ctx, pvt_data->session);
 293out_tee_session:
 294        tee_client_close_context(pvt_data->ctx);
 295
 296        return rc;
 297}
 298
 299static int ftpm_plat_tee_probe(struct platform_device *pdev)
 300{
 301        struct device *dev = &pdev->dev;
 302
 303        return ftpm_tee_probe(dev);
 304}
 305
 306/**
 307 * ftpm_tee_remove() - remove the TPM device
 308 * @pdev: the platform_device description.
 309 *
 310 * Return:
 311 *      0 always.
 312 */
 313static int ftpm_tee_remove(struct device *dev)
 314{
 315        struct ftpm_tee_private *pvt_data = dev_get_drvdata(dev);
 316
 317        /* Release the chip */
 318        tpm_chip_unregister(pvt_data->chip);
 319
 320        /* frees chip */
 321        put_device(&pvt_data->chip->dev);
 322
 323        /* Free the shared memory pool */
 324        tee_shm_free(pvt_data->shm);
 325
 326        /* close the existing session with fTPM TA*/
 327        tee_client_close_session(pvt_data->ctx, pvt_data->session);
 328
 329        /* close the context with TEE driver */
 330        tee_client_close_context(pvt_data->ctx);
 331
 332        /* memory allocated with devm_kzalloc() is freed automatically */
 333
 334        return 0;
 335}
 336
 337static int ftpm_plat_tee_remove(struct platform_device *pdev)
 338{
 339        struct device *dev = &pdev->dev;
 340
 341        return ftpm_tee_remove(dev);
 342}
 343
 344/**
 345 * ftpm_tee_shutdown() - shutdown the TPM device
 346 * @pdev: the platform_device description.
 347 */
 348static void ftpm_plat_tee_shutdown(struct platform_device *pdev)
 349{
 350        struct ftpm_tee_private *pvt_data = dev_get_drvdata(&pdev->dev);
 351
 352        tee_shm_free(pvt_data->shm);
 353        tee_client_close_session(pvt_data->ctx, pvt_data->session);
 354        tee_client_close_context(pvt_data->ctx);
 355}
 356
 357static const struct of_device_id of_ftpm_tee_ids[] = {
 358        { .compatible = "microsoft,ftpm" },
 359        { }
 360};
 361MODULE_DEVICE_TABLE(of, of_ftpm_tee_ids);
 362
 363static struct platform_driver ftpm_tee_plat_driver = {
 364        .driver = {
 365                .name = "ftpm-tee",
 366                .of_match_table = of_match_ptr(of_ftpm_tee_ids),
 367        },
 368        .shutdown = ftpm_plat_tee_shutdown,
 369        .probe = ftpm_plat_tee_probe,
 370        .remove = ftpm_plat_tee_remove,
 371};
 372
 373/* UUID of the fTPM TA */
 374static const struct tee_client_device_id optee_ftpm_id_table[] = {
 375        {UUID_INIT(0xbc50d971, 0xd4c9, 0x42c4,
 376                   0x82, 0xcb, 0x34, 0x3f, 0xb7, 0xf3, 0x78, 0x96)},
 377        {}
 378};
 379
 380MODULE_DEVICE_TABLE(tee, optee_ftpm_id_table);
 381
 382static struct tee_client_driver ftpm_tee_driver = {
 383        .id_table       = optee_ftpm_id_table,
 384        .driver         = {
 385                .name           = "optee-ftpm",
 386                .bus            = &tee_bus_type,
 387                .probe          = ftpm_tee_probe,
 388                .remove         = ftpm_tee_remove,
 389        },
 390};
 391
 392static int __init ftpm_mod_init(void)
 393{
 394        int rc;
 395
 396        rc = platform_driver_register(&ftpm_tee_plat_driver);
 397        if (rc)
 398                return rc;
 399
 400        return driver_register(&ftpm_tee_driver.driver);
 401}
 402
 403static void __exit ftpm_mod_exit(void)
 404{
 405        platform_driver_unregister(&ftpm_tee_plat_driver);
 406        driver_unregister(&ftpm_tee_driver.driver);
 407}
 408
 409module_init(ftpm_mod_init);
 410module_exit(ftpm_mod_exit);
 411
 412MODULE_AUTHOR("Thirupathaiah Annapureddy <thiruan@microsoft.com>");
 413MODULE_DESCRIPTION("TPM Driver for fTPM TA in TEE");
 414MODULE_LICENSE("GPL v2");
 415