.. | .. |
---|
| 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 | } |
---|