| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2016 Intel Corporation |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 8 | 9 | * |
|---|
| 9 | 10 | * This file contains TPM2 protocol implementations of the commands |
|---|
| 10 | 11 | * used by the kernel internally. |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is free software; you can redistribute it and/or |
|---|
| 13 | | - * modify it under the terms of the GNU General Public License |
|---|
| 14 | | - * as published by the Free Software Foundation; version 2 |
|---|
| 15 | | - * of the License. |
|---|
| 16 | 12 | */ |
|---|
| 17 | 13 | |
|---|
| 18 | 14 | #include <linux/gfp.h> |
|---|
| .. | .. |
|---|
| 38 | 34 | |
|---|
| 39 | 35 | for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) { |
|---|
| 40 | 36 | if (space->session_tbl[i]) |
|---|
| 41 | | - tpm2_flush_context_cmd(chip, space->session_tbl[i], |
|---|
| 42 | | - TPM_TRANSMIT_NESTED); |
|---|
| 37 | + tpm2_flush_context(chip, space->session_tbl[i]); |
|---|
| 43 | 38 | } |
|---|
| 44 | 39 | } |
|---|
| 45 | 40 | |
|---|
| .. | .. |
|---|
| 63 | 58 | |
|---|
| 64 | 59 | void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space) |
|---|
| 65 | 60 | { |
|---|
| 66 | | - mutex_lock(&chip->tpm_mutex); |
|---|
| 67 | | - tpm2_flush_sessions(chip, space); |
|---|
| 68 | | - mutex_unlock(&chip->tpm_mutex); |
|---|
| 61 | + |
|---|
| 62 | + if (tpm_try_get_ops(chip) == 0) { |
|---|
| 63 | + tpm2_flush_sessions(chip, space); |
|---|
| 64 | + tpm_put_ops(chip); |
|---|
| 65 | + } |
|---|
| 66 | + |
|---|
| 69 | 67 | kfree(space->context_buf); |
|---|
| 70 | 68 | kfree(space->session_buf); |
|---|
| 71 | 69 | } |
|---|
| .. | .. |
|---|
| 86 | 84 | body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size); |
|---|
| 87 | 85 | tpm_buf_append(&tbuf, &buf[*offset], body_size); |
|---|
| 88 | 86 | |
|---|
| 89 | | - rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 4, |
|---|
| 90 | | - TPM_TRANSMIT_NESTED, NULL); |
|---|
| 87 | + rc = tpm_transmit_cmd(chip, &tbuf, 4, NULL); |
|---|
| 91 | 88 | if (rc < 0) { |
|---|
| 92 | 89 | dev_warn(&chip->dev, "%s: failed with a system error %d\n", |
|---|
| 93 | 90 | __func__, rc); |
|---|
| .. | .. |
|---|
| 135 | 132 | |
|---|
| 136 | 133 | tpm_buf_append_u32(&tbuf, handle); |
|---|
| 137 | 134 | |
|---|
| 138 | | - rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 0, |
|---|
| 139 | | - TPM_TRANSMIT_NESTED, NULL); |
|---|
| 135 | + rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL); |
|---|
| 140 | 136 | if (rc < 0) { |
|---|
| 141 | 137 | dev_warn(&chip->dev, "%s: failed with a system error %d\n", |
|---|
| 142 | 138 | __func__, rc); |
|---|
| .. | .. |
|---|
| 165 | 161 | return 0; |
|---|
| 166 | 162 | } |
|---|
| 167 | 163 | |
|---|
| 168 | | -static void tpm2_flush_space(struct tpm_chip *chip) |
|---|
| 164 | +void tpm2_flush_space(struct tpm_chip *chip) |
|---|
| 169 | 165 | { |
|---|
| 170 | 166 | struct tpm_space *space = &chip->work_space; |
|---|
| 171 | 167 | int i; |
|---|
| 172 | 168 | |
|---|
| 173 | 169 | for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) |
|---|
| 174 | 170 | if (space->context_tbl[i] && ~space->context_tbl[i]) |
|---|
| 175 | | - tpm2_flush_context_cmd(chip, space->context_tbl[i], |
|---|
| 176 | | - TPM_TRANSMIT_NESTED); |
|---|
| 171 | + tpm2_flush_context(chip, space->context_tbl[i]); |
|---|
| 177 | 172 | |
|---|
| 178 | 173 | tpm2_flush_sessions(chip, space); |
|---|
| 179 | 174 | } |
|---|
| .. | .. |
|---|
| 267 | 262 | return 0; |
|---|
| 268 | 263 | } |
|---|
| 269 | 264 | |
|---|
| 270 | | -int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, |
|---|
| 271 | | - u8 *cmd) |
|---|
| 265 | +static int tpm_find_and_validate_cc(struct tpm_chip *chip, |
|---|
| 266 | + struct tpm_space *space, |
|---|
| 267 | + const void *cmd, size_t len) |
|---|
| 268 | +{ |
|---|
| 269 | + const struct tpm_header *header = (const void *)cmd; |
|---|
| 270 | + int i; |
|---|
| 271 | + u32 cc; |
|---|
| 272 | + u32 attrs; |
|---|
| 273 | + unsigned int nr_handles; |
|---|
| 274 | + |
|---|
| 275 | + if (len < TPM_HEADER_SIZE || !chip->nr_commands) |
|---|
| 276 | + return -EINVAL; |
|---|
| 277 | + |
|---|
| 278 | + cc = be32_to_cpu(header->ordinal); |
|---|
| 279 | + |
|---|
| 280 | + i = tpm2_find_cc(chip, cc); |
|---|
| 281 | + if (i < 0) { |
|---|
| 282 | + dev_dbg(&chip->dev, "0x%04X is an invalid command\n", |
|---|
| 283 | + cc); |
|---|
| 284 | + return -EOPNOTSUPP; |
|---|
| 285 | + } |
|---|
| 286 | + |
|---|
| 287 | + attrs = chip->cc_attrs_tbl[i]; |
|---|
| 288 | + nr_handles = |
|---|
| 289 | + 4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0)); |
|---|
| 290 | + if (len < TPM_HEADER_SIZE + 4 * nr_handles) |
|---|
| 291 | + goto err_len; |
|---|
| 292 | + |
|---|
| 293 | + return cc; |
|---|
| 294 | +err_len: |
|---|
| 295 | + dev_dbg(&chip->dev, "%s: insufficient command length %zu", __func__, |
|---|
| 296 | + len); |
|---|
| 297 | + return -EINVAL; |
|---|
| 298 | +} |
|---|
| 299 | + |
|---|
| 300 | +int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u8 *cmd, |
|---|
| 301 | + size_t cmdsiz) |
|---|
| 272 | 302 | { |
|---|
| 273 | 303 | int rc; |
|---|
| 304 | + int cc; |
|---|
| 274 | 305 | |
|---|
| 275 | 306 | if (!space) |
|---|
| 276 | 307 | return 0; |
|---|
| 308 | + |
|---|
| 309 | + cc = tpm_find_and_validate_cc(chip, space, cmd, cmdsiz); |
|---|
| 310 | + if (cc < 0) |
|---|
| 311 | + return cc; |
|---|
| 277 | 312 | |
|---|
| 278 | 313 | memcpy(&chip->work_space.context_tbl, &space->context_tbl, |
|---|
| 279 | 314 | sizeof(space->context_tbl)); |
|---|
| .. | .. |
|---|
| 296 | 331 | return rc; |
|---|
| 297 | 332 | } |
|---|
| 298 | 333 | |
|---|
| 334 | + chip->last_cc = cc; |
|---|
| 299 | 335 | return 0; |
|---|
| 300 | 336 | } |
|---|
| 301 | 337 | |
|---|
| .. | .. |
|---|
| 339 | 375 | size_t len) |
|---|
| 340 | 376 | { |
|---|
| 341 | 377 | struct tpm_space *space = &chip->work_space; |
|---|
| 342 | | - struct tpm_output_header *header = (void *)rsp; |
|---|
| 378 | + struct tpm_header *header = (struct tpm_header *)rsp; |
|---|
| 343 | 379 | u32 phandle; |
|---|
| 344 | 380 | u32 phandle_type; |
|---|
| 345 | 381 | u32 vhandle; |
|---|
| .. | .. |
|---|
| 378 | 414 | dev_err(&chip->dev, "%s: unknown handle 0x%08X\n", |
|---|
| 379 | 415 | __func__, phandle); |
|---|
| 380 | 416 | break; |
|---|
| 381 | | - }; |
|---|
| 417 | + } |
|---|
| 382 | 418 | |
|---|
| 383 | 419 | return 0; |
|---|
| 384 | 420 | out_no_slots: |
|---|
| 385 | | - tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_NESTED); |
|---|
| 421 | + tpm2_flush_context(chip, phandle); |
|---|
| 386 | 422 | dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__, |
|---|
| 387 | 423 | phandle); |
|---|
| 388 | 424 | return -ENOMEM; |
|---|
| .. | .. |
|---|
| 399 | 435 | size_t len) |
|---|
| 400 | 436 | { |
|---|
| 401 | 437 | struct tpm_space *space = &chip->work_space; |
|---|
| 402 | | - struct tpm_output_header *header = (void *)rsp; |
|---|
| 438 | + struct tpm_header *header = (struct tpm_header *)rsp; |
|---|
| 403 | 439 | struct tpm2_cap_handles *data; |
|---|
| 404 | 440 | u32 phandle; |
|---|
| 405 | 441 | u32 phandle_type; |
|---|
| .. | .. |
|---|
| 472 | 508 | } else if (rc) |
|---|
| 473 | 509 | return rc; |
|---|
| 474 | 510 | |
|---|
| 475 | | - tpm2_flush_context_cmd(chip, space->context_tbl[i], |
|---|
| 476 | | - TPM_TRANSMIT_NESTED); |
|---|
| 511 | + tpm2_flush_context(chip, space->context_tbl[i]); |
|---|
| 477 | 512 | space->context_tbl[i] = ~0; |
|---|
| 478 | 513 | } |
|---|
| 479 | 514 | |
|---|
| .. | .. |
|---|
| 497 | 532 | } |
|---|
| 498 | 533 | |
|---|
| 499 | 534 | int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, |
|---|
| 500 | | - u32 cc, u8 *buf, size_t *bufsiz) |
|---|
| 535 | + void *buf, size_t *bufsiz) |
|---|
| 501 | 536 | { |
|---|
| 502 | | - struct tpm_output_header *header = (void *)buf; |
|---|
| 537 | + struct tpm_header *header = buf; |
|---|
| 503 | 538 | int rc; |
|---|
| 504 | 539 | |
|---|
| 505 | 540 | if (!space) |
|---|
| 506 | 541 | return 0; |
|---|
| 507 | 542 | |
|---|
| 508 | | - rc = tpm2_map_response_header(chip, cc, buf, *bufsiz); |
|---|
| 543 | + rc = tpm2_map_response_header(chip, chip->last_cc, buf, *bufsiz); |
|---|
| 509 | 544 | if (rc) { |
|---|
| 510 | 545 | tpm2_flush_space(chip); |
|---|
| 511 | | - return rc; |
|---|
| 546 | + goto out; |
|---|
| 512 | 547 | } |
|---|
| 513 | 548 | |
|---|
| 514 | | - rc = tpm2_map_response_body(chip, cc, buf, *bufsiz); |
|---|
| 549 | + rc = tpm2_map_response_body(chip, chip->last_cc, buf, *bufsiz); |
|---|
| 515 | 550 | if (rc) { |
|---|
| 516 | 551 | tpm2_flush_space(chip); |
|---|
| 517 | | - return rc; |
|---|
| 552 | + goto out; |
|---|
| 518 | 553 | } |
|---|
| 519 | 554 | |
|---|
| 520 | 555 | rc = tpm2_save_space(chip); |
|---|
| 521 | 556 | if (rc) { |
|---|
| 522 | 557 | tpm2_flush_space(chip); |
|---|
| 523 | | - return rc; |
|---|
| 558 | + goto out; |
|---|
| 524 | 559 | } |
|---|
| 525 | 560 | |
|---|
| 526 | 561 | *bufsiz = be32_to_cpu(header->length); |
|---|
| .. | .. |
|---|
| 535 | 570 | space->buf_size); |
|---|
| 536 | 571 | |
|---|
| 537 | 572 | return 0; |
|---|
| 573 | +out: |
|---|
| 574 | + dev_err(&chip->dev, "%s: error %d\n", __func__, rc); |
|---|
| 575 | + return rc; |
|---|
| 576 | +} |
|---|
| 577 | + |
|---|
| 578 | +/* |
|---|
| 579 | + * Put the reference to the main device. |
|---|
| 580 | + */ |
|---|
| 581 | +static void tpm_devs_release(struct device *dev) |
|---|
| 582 | +{ |
|---|
| 583 | + struct tpm_chip *chip = container_of(dev, struct tpm_chip, devs); |
|---|
| 584 | + |
|---|
| 585 | + /* release the master device reference */ |
|---|
| 586 | + put_device(&chip->dev); |
|---|
| 587 | +} |
|---|
| 588 | + |
|---|
| 589 | +/* |
|---|
| 590 | + * Remove the device file for exposed TPM spaces and release the device |
|---|
| 591 | + * reference. This may also release the reference to the master device. |
|---|
| 592 | + */ |
|---|
| 593 | +void tpm_devs_remove(struct tpm_chip *chip) |
|---|
| 594 | +{ |
|---|
| 595 | + cdev_device_del(&chip->cdevs, &chip->devs); |
|---|
| 596 | + put_device(&chip->devs); |
|---|
| 597 | +} |
|---|
| 598 | + |
|---|
| 599 | +/* |
|---|
| 600 | + * Add a device file to expose TPM spaces. Also take a reference to the |
|---|
| 601 | + * main device. |
|---|
| 602 | + */ |
|---|
| 603 | +int tpm_devs_add(struct tpm_chip *chip) |
|---|
| 604 | +{ |
|---|
| 605 | + int rc; |
|---|
| 606 | + |
|---|
| 607 | + device_initialize(&chip->devs); |
|---|
| 608 | + chip->devs.parent = chip->dev.parent; |
|---|
| 609 | + chip->devs.class = tpmrm_class; |
|---|
| 610 | + |
|---|
| 611 | + /* |
|---|
| 612 | + * Get extra reference on main device to hold on behalf of devs. |
|---|
| 613 | + * This holds the chip structure while cdevs is in use. The |
|---|
| 614 | + * corresponding put is in the tpm_devs_release. |
|---|
| 615 | + */ |
|---|
| 616 | + get_device(&chip->dev); |
|---|
| 617 | + chip->devs.release = tpm_devs_release; |
|---|
| 618 | + chip->devs.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num + TPM_NUM_DEVICES); |
|---|
| 619 | + cdev_init(&chip->cdevs, &tpmrm_fops); |
|---|
| 620 | + chip->cdevs.owner = THIS_MODULE; |
|---|
| 621 | + |
|---|
| 622 | + rc = dev_set_name(&chip->devs, "tpmrm%d", chip->dev_num); |
|---|
| 623 | + if (rc) |
|---|
| 624 | + goto err_put_devs; |
|---|
| 625 | + |
|---|
| 626 | + rc = cdev_device_add(&chip->cdevs, &chip->devs); |
|---|
| 627 | + if (rc) { |
|---|
| 628 | + dev_err(&chip->devs, |
|---|
| 629 | + "unable to cdev_device_add() %s, major %d, minor %d, err=%d\n", |
|---|
| 630 | + dev_name(&chip->devs), MAJOR(chip->devs.devt), |
|---|
| 631 | + MINOR(chip->devs.devt), rc); |
|---|
| 632 | + goto err_put_devs; |
|---|
| 633 | + } |
|---|
| 634 | + |
|---|
| 635 | + return 0; |
|---|
| 636 | + |
|---|
| 637 | +err_put_devs: |
|---|
| 638 | + put_device(&chip->devs); |
|---|
| 639 | + |
|---|
| 640 | + return rc; |
|---|
| 538 | 641 | } |
|---|