648 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2016 Intel Corporation4 *5 * Authors:6 * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>7 *8 * Maintained by: <tpmdd-devel@lists.sourceforge.net>9 *10 * This file contains TPM2 protocol implementations of the commands11 * used by the kernel internally.12 */13 14#include <linux/gfp.h>15#include <linux/unaligned.h>16#include "tpm.h"17 18enum tpm2_handle_types {19 TPM2_HT_HMAC_SESSION = 0x02000000,20 TPM2_HT_POLICY_SESSION = 0x03000000,21 TPM2_HT_TRANSIENT = 0x80000000,22};23 24struct tpm2_context {25 __be64 sequence;26 __be32 saved_handle;27 __be32 hierarchy;28 __be16 blob_size;29} __packed;30 31static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space)32{33 int i;34 35 for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) {36 if (space->session_tbl[i])37 tpm2_flush_context(chip, space->session_tbl[i]);38 }39}40 41int tpm2_init_space(struct tpm_space *space, unsigned int buf_size)42{43 space->context_buf = kzalloc(buf_size, GFP_KERNEL);44 if (!space->context_buf)45 return -ENOMEM;46 47 space->session_buf = kzalloc(buf_size, GFP_KERNEL);48 if (space->session_buf == NULL) {49 kfree(space->context_buf);50 /* Prevent caller getting a dangling pointer. */51 space->context_buf = NULL;52 return -ENOMEM;53 }54 55 space->buf_size = buf_size;56 return 0;57}58 59void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)60{61 62 if (tpm_try_get_ops(chip) == 0) {63 tpm2_flush_sessions(chip, space);64 tpm_put_ops(chip);65 }66 67 kfree(space->context_buf);68 kfree(space->session_buf);69}70 71int tpm2_load_context(struct tpm_chip *chip, u8 *buf,72 unsigned int *offset, u32 *handle)73{74 struct tpm_buf tbuf;75 struct tpm2_context *ctx;76 unsigned int body_size;77 int rc;78 79 rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);80 if (rc)81 return rc;82 83 ctx = (struct tpm2_context *)&buf[*offset];84 body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);85 tpm_buf_append(&tbuf, &buf[*offset], body_size);86 87 rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL);88 if (rc < 0) {89 dev_warn(&chip->dev, "%s: failed with a system error %d\n",90 __func__, rc);91 tpm_buf_destroy(&tbuf);92 return -EFAULT;93 } else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||94 rc == TPM2_RC_REFERENCE_H0) {95 /*96 * TPM_RC_HANDLE means that the session context can't97 * be loaded because of an internal counter mismatch98 * that makes the TPM think there might have been a99 * replay. This might happen if the context was saved100 * and loaded outside the space.101 *102 * TPM_RC_REFERENCE_H0 means the session has been103 * flushed outside the space104 */105 *handle = 0;106 tpm_buf_destroy(&tbuf);107 return -ENOENT;108 } else if (tpm2_rc_value(rc) == TPM2_RC_INTEGRITY) {109 tpm_buf_destroy(&tbuf);110 return -EINVAL;111 } else if (rc > 0) {112 dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",113 __func__, rc);114 tpm_buf_destroy(&tbuf);115 return -EFAULT;116 }117 118 *handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);119 *offset += body_size;120 121 tpm_buf_destroy(&tbuf);122 return 0;123}124 125int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,126 unsigned int buf_size, unsigned int *offset)127{128 struct tpm_buf tbuf;129 unsigned int body_size;130 int rc;131 132 rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);133 if (rc)134 return rc;135 136 tpm_buf_append_u32(&tbuf, handle);137 138 rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL);139 if (rc < 0) {140 dev_warn(&chip->dev, "%s: failed with a system error %d\n",141 __func__, rc);142 tpm_buf_destroy(&tbuf);143 return -EFAULT;144 } else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {145 tpm_buf_destroy(&tbuf);146 return -ENOENT;147 } else if (rc) {148 dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",149 __func__, rc);150 tpm_buf_destroy(&tbuf);151 return -EFAULT;152 }153 154 body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;155 if ((*offset + body_size) > buf_size) {156 dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);157 tpm_buf_destroy(&tbuf);158 return -ENOMEM;159 }160 161 memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);162 *offset += body_size;163 tpm_buf_destroy(&tbuf);164 return 0;165}166 167void tpm2_flush_space(struct tpm_chip *chip)168{169 struct tpm_space *space = &chip->work_space;170 int i;171 172 if (!space)173 return;174 175 for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++)176 if (space->context_tbl[i] && ~space->context_tbl[i])177 tpm2_flush_context(chip, space->context_tbl[i]);178 179 tpm2_flush_sessions(chip, space);180}181 182static int tpm2_load_space(struct tpm_chip *chip)183{184 struct tpm_space *space = &chip->work_space;185 unsigned int offset;186 int i;187 int rc;188 189 for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {190 if (!space->context_tbl[i])191 continue;192 193 /* sanity check, should never happen */194 if (~space->context_tbl[i]) {195 dev_err(&chip->dev, "context table is inconsistent");196 return -EFAULT;197 }198 199 rc = tpm2_load_context(chip, space->context_buf, &offset,200 &space->context_tbl[i]);201 if (rc)202 return rc;203 }204 205 for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {206 u32 handle;207 208 if (!space->session_tbl[i])209 continue;210 211 rc = tpm2_load_context(chip, space->session_buf,212 &offset, &handle);213 if (rc == -ENOENT) {214 /* load failed, just forget session */215 space->session_tbl[i] = 0;216 } else if (rc) {217 tpm2_flush_space(chip);218 return rc;219 }220 if (handle != space->session_tbl[i]) {221 dev_warn(&chip->dev, "session restored to wrong handle\n");222 tpm2_flush_space(chip);223 return -EFAULT;224 }225 }226 227 return 0;228}229 230static bool tpm2_map_to_phandle(struct tpm_space *space, void *handle)231{232 u32 vhandle = be32_to_cpup((__be32 *)handle);233 u32 phandle;234 int i;235 236 i = 0xFFFFFF - (vhandle & 0xFFFFFF);237 if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i])238 return false;239 240 phandle = space->context_tbl[i];241 *((__be32 *)handle) = cpu_to_be32(phandle);242 return true;243}244 245static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd)246{247 struct tpm_space *space = &chip->work_space;248 unsigned int nr_handles;249 u32 attrs;250 __be32 *handle;251 int i;252 253 i = tpm2_find_cc(chip, cc);254 if (i < 0)255 return -EINVAL;256 257 attrs = chip->cc_attrs_tbl[i];258 nr_handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0);259 260 handle = (__be32 *)&cmd[TPM_HEADER_SIZE];261 for (i = 0; i < nr_handles; i++, handle++) {262 if ((be32_to_cpu(*handle) & 0xFF000000) == TPM2_HT_TRANSIENT) {263 if (!tpm2_map_to_phandle(space, handle))264 return -EINVAL;265 }266 }267 268 return 0;269}270 271static int tpm_find_and_validate_cc(struct tpm_chip *chip,272 struct tpm_space *space,273 const void *cmd, size_t len)274{275 const struct tpm_header *header = (const void *)cmd;276 int i;277 u32 cc;278 u32 attrs;279 unsigned int nr_handles;280 281 if (len < TPM_HEADER_SIZE || !chip->nr_commands)282 return -EINVAL;283 284 cc = be32_to_cpu(header->ordinal);285 286 i = tpm2_find_cc(chip, cc);287 if (i < 0) {288 dev_dbg(&chip->dev, "0x%04X is an invalid command\n",289 cc);290 return -EOPNOTSUPP;291 }292 293 attrs = chip->cc_attrs_tbl[i];294 nr_handles =295 4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0));296 if (len < TPM_HEADER_SIZE + 4 * nr_handles)297 goto err_len;298 299 return cc;300err_len:301 dev_dbg(&chip->dev, "%s: insufficient command length %zu", __func__,302 len);303 return -EINVAL;304}305 306int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd,307 size_t cmdsiz)308{309 int rc;310 int cc;311 312 if (!space)313 return 0;314 315 cc = tpm_find_and_validate_cc(chip, space, cmd, cmdsiz);316 if (cc < 0)317 return cc;318 319 memcpy(&chip->work_space.context_tbl, &space->context_tbl,320 sizeof(space->context_tbl));321 memcpy(&chip->work_space.session_tbl, &space->session_tbl,322 sizeof(space->session_tbl));323 memcpy(chip->work_space.context_buf, space->context_buf,324 space->buf_size);325 memcpy(chip->work_space.session_buf, space->session_buf,326 space->buf_size);327 328 rc = tpm2_load_space(chip);329 if (rc) {330 tpm2_flush_space(chip);331 return rc;332 }333 334 rc = tpm2_map_command(chip, cc, cmd);335 if (rc) {336 tpm2_flush_space(chip);337 return rc;338 }339 340 chip->last_cc = cc;341 return 0;342}343 344static bool tpm2_add_session(struct tpm_chip *chip, u32 handle)345{346 struct tpm_space *space = &chip->work_space;347 int i;348 349 for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++)350 if (space->session_tbl[i] == 0)351 break;352 353 if (i == ARRAY_SIZE(space->session_tbl))354 return false;355 356 space->session_tbl[i] = handle;357 return true;358}359 360static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc)361{362 int i;363 364 for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) {365 if (alloc) {366 if (!space->context_tbl[i]) {367 space->context_tbl[i] = phandle;368 break;369 }370 } else if (space->context_tbl[i] == phandle)371 break;372 }373 374 if (i == ARRAY_SIZE(space->context_tbl))375 return 0;376 377 return TPM2_HT_TRANSIENT | (0xFFFFFF - i);378}379 380static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp,381 size_t len)382{383 struct tpm_space *space = &chip->work_space;384 struct tpm_header *header = (struct tpm_header *)rsp;385 u32 phandle;386 u32 phandle_type;387 u32 vhandle;388 u32 attrs;389 int i;390 391 if (be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS)392 return 0;393 394 i = tpm2_find_cc(chip, cc);395 /* sanity check, should never happen */396 if (i < 0)397 return -EFAULT;398 399 attrs = chip->cc_attrs_tbl[i];400 if (!((attrs >> TPM2_CC_ATTR_RHANDLE) & 1))401 return 0;402 403 phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]);404 phandle_type = phandle & 0xFF000000;405 406 switch (phandle_type) {407 case TPM2_HT_TRANSIENT:408 vhandle = tpm2_map_to_vhandle(space, phandle, true);409 if (!vhandle)410 goto out_no_slots;411 412 *(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle);413 break;414 case TPM2_HT_HMAC_SESSION:415 case TPM2_HT_POLICY_SESSION:416 if (!tpm2_add_session(chip, phandle))417 goto out_no_slots;418 break;419 default:420 dev_err(&chip->dev, "%s: unknown handle 0x%08X\n",421 __func__, phandle);422 break;423 }424 425 return 0;426out_no_slots:427 tpm2_flush_context(chip, phandle);428 dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__,429 phandle);430 return -ENOMEM;431}432 433struct tpm2_cap_handles {434 u8 more_data;435 __be32 capability;436 __be32 count;437 __be32 handles[];438} __packed;439 440static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp,441 size_t len)442{443 struct tpm_space *space = &chip->work_space;444 struct tpm_header *header = (struct tpm_header *)rsp;445 struct tpm2_cap_handles *data;446 u32 phandle;447 u32 phandle_type;448 u32 vhandle;449 int i;450 int j;451 452 if (cc != TPM2_CC_GET_CAPABILITY ||453 be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) {454 return 0;455 }456 457 if (len < TPM_HEADER_SIZE + 9)458 return -EFAULT;459 460 data = (void *)&rsp[TPM_HEADER_SIZE];461 if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES)462 return 0;463 464 if (be32_to_cpu(data->count) > (UINT_MAX - TPM_HEADER_SIZE - 9) / 4)465 return -EFAULT;466 467 if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count))468 return -EFAULT;469 470 for (i = 0, j = 0; i < be32_to_cpu(data->count); i++) {471 phandle = be32_to_cpup((__be32 *)&data->handles[i]);472 phandle_type = phandle & 0xFF000000;473 474 switch (phandle_type) {475 case TPM2_HT_TRANSIENT:476 vhandle = tpm2_map_to_vhandle(space, phandle, false);477 if (!vhandle)478 break;479 480 data->handles[j] = cpu_to_be32(vhandle);481 j++;482 break;483 484 default:485 data->handles[j] = cpu_to_be32(phandle);486 j++;487 break;488 }489 490 }491 492 header->length = cpu_to_be32(TPM_HEADER_SIZE + 9 + 4 * j);493 data->count = cpu_to_be32(j);494 return 0;495}496 497static int tpm2_save_space(struct tpm_chip *chip)498{499 struct tpm_space *space = &chip->work_space;500 unsigned int offset;501 int i;502 int rc;503 504 for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) {505 if (!(space->context_tbl[i] && ~space->context_tbl[i]))506 continue;507 508 rc = tpm2_save_context(chip, space->context_tbl[i],509 space->context_buf, space->buf_size,510 &offset);511 if (rc == -ENOENT) {512 space->context_tbl[i] = 0;513 continue;514 } else if (rc)515 return rc;516 517 tpm2_flush_context(chip, space->context_tbl[i]);518 space->context_tbl[i] = ~0;519 }520 521 for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) {522 if (!space->session_tbl[i])523 continue;524 525 rc = tpm2_save_context(chip, space->session_tbl[i],526 space->session_buf, space->buf_size,527 &offset);528 if (rc == -ENOENT) {529 /* handle error saving session, just forget it */530 space->session_tbl[i] = 0;531 } else if (rc < 0) {532 tpm2_flush_space(chip);533 return rc;534 }535 }536 537 return 0;538}539 540int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space,541 void *buf, size_t *bufsiz)542{543 struct tpm_header *header = buf;544 int rc;545 546 if (!space)547 return 0;548 549 rc = tpm2_map_response_header(chip, chip->last_cc, buf, *bufsiz);550 if (rc) {551 tpm2_flush_space(chip);552 goto out;553 }554 555 rc = tpm2_map_response_body(chip, chip->last_cc, buf, *bufsiz);556 if (rc) {557 tpm2_flush_space(chip);558 goto out;559 }560 561 rc = tpm2_save_space(chip);562 if (rc) {563 tpm2_flush_space(chip);564 goto out;565 }566 567 *bufsiz = be32_to_cpu(header->length);568 569 memcpy(&space->context_tbl, &chip->work_space.context_tbl,570 sizeof(space->context_tbl));571 memcpy(&space->session_tbl, &chip->work_space.session_tbl,572 sizeof(space->session_tbl));573 memcpy(space->context_buf, chip->work_space.context_buf,574 space->buf_size);575 memcpy(space->session_buf, chip->work_space.session_buf,576 space->buf_size);577 578 return 0;579out:580 dev_err(&chip->dev, "%s: error %d\n", __func__, rc);581 return rc;582}583 584/*585 * Put the reference to the main device.586 */587static void tpm_devs_release(struct device *dev)588{589 struct tpm_chip *chip = container_of(dev, struct tpm_chip, devs);590 591 /* release the master device reference */592 put_device(&chip->dev);593}594 595/*596 * Remove the device file for exposed TPM spaces and release the device597 * reference. This may also release the reference to the master device.598 */599void tpm_devs_remove(struct tpm_chip *chip)600{601 cdev_device_del(&chip->cdevs, &chip->devs);602 put_device(&chip->devs);603}604 605/*606 * Add a device file to expose TPM spaces. Also take a reference to the607 * main device.608 */609int tpm_devs_add(struct tpm_chip *chip)610{611 int rc;612 613 device_initialize(&chip->devs);614 chip->devs.parent = chip->dev.parent;615 chip->devs.class = &tpmrm_class;616 617 /*618 * Get extra reference on main device to hold on behalf of devs.619 * This holds the chip structure while cdevs is in use. The620 * corresponding put is in the tpm_devs_release.621 */622 get_device(&chip->dev);623 chip->devs.release = tpm_devs_release;624 chip->devs.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num + TPM_NUM_DEVICES);625 cdev_init(&chip->cdevs, &tpmrm_fops);626 chip->cdevs.owner = THIS_MODULE;627 628 rc = dev_set_name(&chip->devs, "tpmrm%d", chip->dev_num);629 if (rc)630 goto err_put_devs;631 632 rc = cdev_device_add(&chip->cdevs, &chip->devs);633 if (rc) {634 dev_err(&chip->devs,635 "unable to cdev_device_add() %s, major %d, minor %d, err=%d\n",636 dev_name(&chip->devs), MAJOR(chip->devs.devt),637 MINOR(chip->devs.devt), rc);638 goto err_put_devs;639 }640 641 return 0;642 643err_put_devs:644 put_device(&chip->devs);645 646 return rc;647}648