| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2004 IBM Corporation |
|---|
| 3 | 4 | * Copyright (C) 2014 Intel Corporation |
|---|
| .. | .. |
|---|
| 12 | 13 | * Maintained by: <tpmdd-devel@lists.sourceforge.net> |
|---|
| 13 | 14 | * |
|---|
| 14 | 15 | * TPM chip management routines. |
|---|
| 15 | | - * |
|---|
| 16 | | - * This program is free software; you can redistribute it and/or |
|---|
| 17 | | - * modify it under the terms of the GNU General Public License as |
|---|
| 18 | | - * published by the Free Software Foundation, version 2 of the |
|---|
| 19 | | - * License. |
|---|
| 20 | | - * |
|---|
| 21 | 16 | */ |
|---|
| 22 | 17 | |
|---|
| 23 | 18 | #include <linux/poll.h> |
|---|
| .. | .. |
|---|
| 36 | 31 | struct class *tpm_class; |
|---|
| 37 | 32 | struct class *tpmrm_class; |
|---|
| 38 | 33 | dev_t tpm_devt; |
|---|
| 34 | + |
|---|
| 35 | +static int tpm_request_locality(struct tpm_chip *chip) |
|---|
| 36 | +{ |
|---|
| 37 | + int rc; |
|---|
| 38 | + |
|---|
| 39 | + if (!chip->ops->request_locality) |
|---|
| 40 | + return 0; |
|---|
| 41 | + |
|---|
| 42 | + rc = chip->ops->request_locality(chip, 0); |
|---|
| 43 | + if (rc < 0) |
|---|
| 44 | + return rc; |
|---|
| 45 | + |
|---|
| 46 | + chip->locality = rc; |
|---|
| 47 | + return 0; |
|---|
| 48 | +} |
|---|
| 49 | + |
|---|
| 50 | +static void tpm_relinquish_locality(struct tpm_chip *chip) |
|---|
| 51 | +{ |
|---|
| 52 | + int rc; |
|---|
| 53 | + |
|---|
| 54 | + if (!chip->ops->relinquish_locality) |
|---|
| 55 | + return; |
|---|
| 56 | + |
|---|
| 57 | + rc = chip->ops->relinquish_locality(chip, chip->locality); |
|---|
| 58 | + if (rc) |
|---|
| 59 | + dev_err(&chip->dev, "%s: : error %d\n", __func__, rc); |
|---|
| 60 | + |
|---|
| 61 | + chip->locality = -1; |
|---|
| 62 | +} |
|---|
| 63 | + |
|---|
| 64 | +static int tpm_cmd_ready(struct tpm_chip *chip) |
|---|
| 65 | +{ |
|---|
| 66 | + if (!chip->ops->cmd_ready) |
|---|
| 67 | + return 0; |
|---|
| 68 | + |
|---|
| 69 | + return chip->ops->cmd_ready(chip); |
|---|
| 70 | +} |
|---|
| 71 | + |
|---|
| 72 | +static int tpm_go_idle(struct tpm_chip *chip) |
|---|
| 73 | +{ |
|---|
| 74 | + if (!chip->ops->go_idle) |
|---|
| 75 | + return 0; |
|---|
| 76 | + |
|---|
| 77 | + return chip->ops->go_idle(chip); |
|---|
| 78 | +} |
|---|
| 79 | + |
|---|
| 80 | +static void tpm_clk_enable(struct tpm_chip *chip) |
|---|
| 81 | +{ |
|---|
| 82 | + if (chip->ops->clk_enable) |
|---|
| 83 | + chip->ops->clk_enable(chip, true); |
|---|
| 84 | +} |
|---|
| 85 | + |
|---|
| 86 | +static void tpm_clk_disable(struct tpm_chip *chip) |
|---|
| 87 | +{ |
|---|
| 88 | + if (chip->ops->clk_enable) |
|---|
| 89 | + chip->ops->clk_enable(chip, false); |
|---|
| 90 | +} |
|---|
| 91 | + |
|---|
| 92 | +/** |
|---|
| 93 | + * tpm_chip_start() - power on the TPM |
|---|
| 94 | + * @chip: a TPM chip to use |
|---|
| 95 | + * |
|---|
| 96 | + * Return: |
|---|
| 97 | + * * The response length - OK |
|---|
| 98 | + * * -errno - A system error |
|---|
| 99 | + */ |
|---|
| 100 | +int tpm_chip_start(struct tpm_chip *chip) |
|---|
| 101 | +{ |
|---|
| 102 | + int ret; |
|---|
| 103 | + |
|---|
| 104 | + tpm_clk_enable(chip); |
|---|
| 105 | + |
|---|
| 106 | + if (chip->locality == -1) { |
|---|
| 107 | + ret = tpm_request_locality(chip); |
|---|
| 108 | + if (ret) { |
|---|
| 109 | + tpm_clk_disable(chip); |
|---|
| 110 | + return ret; |
|---|
| 111 | + } |
|---|
| 112 | + } |
|---|
| 113 | + |
|---|
| 114 | + ret = tpm_cmd_ready(chip); |
|---|
| 115 | + if (ret) { |
|---|
| 116 | + tpm_relinquish_locality(chip); |
|---|
| 117 | + tpm_clk_disable(chip); |
|---|
| 118 | + return ret; |
|---|
| 119 | + } |
|---|
| 120 | + |
|---|
| 121 | + return 0; |
|---|
| 122 | +} |
|---|
| 123 | +EXPORT_SYMBOL_GPL(tpm_chip_start); |
|---|
| 124 | + |
|---|
| 125 | +/** |
|---|
| 126 | + * tpm_chip_stop() - power off the TPM |
|---|
| 127 | + * @chip: a TPM chip to use |
|---|
| 128 | + * |
|---|
| 129 | + * Return: |
|---|
| 130 | + * * The response length - OK |
|---|
| 131 | + * * -errno - A system error |
|---|
| 132 | + */ |
|---|
| 133 | +void tpm_chip_stop(struct tpm_chip *chip) |
|---|
| 134 | +{ |
|---|
| 135 | + tpm_go_idle(chip); |
|---|
| 136 | + tpm_relinquish_locality(chip); |
|---|
| 137 | + tpm_clk_disable(chip); |
|---|
| 138 | +} |
|---|
| 139 | +EXPORT_SYMBOL_GPL(tpm_chip_stop); |
|---|
| 39 | 140 | |
|---|
| 40 | 141 | /** |
|---|
| 41 | 142 | * tpm_try_get_ops() - Get a ref to the tpm_chip |
|---|
| .. | .. |
|---|
| 56 | 157 | |
|---|
| 57 | 158 | down_read(&chip->ops_sem); |
|---|
| 58 | 159 | if (!chip->ops) |
|---|
| 160 | + goto out_ops; |
|---|
| 161 | + |
|---|
| 162 | + mutex_lock(&chip->tpm_mutex); |
|---|
| 163 | + rc = tpm_chip_start(chip); |
|---|
| 164 | + if (rc) |
|---|
| 59 | 165 | goto out_lock; |
|---|
| 60 | 166 | |
|---|
| 61 | 167 | return 0; |
|---|
| 62 | 168 | out_lock: |
|---|
| 169 | + mutex_unlock(&chip->tpm_mutex); |
|---|
| 170 | +out_ops: |
|---|
| 63 | 171 | up_read(&chip->ops_sem); |
|---|
| 64 | 172 | put_device(&chip->dev); |
|---|
| 65 | 173 | return rc; |
|---|
| .. | .. |
|---|
| 75 | 183 | */ |
|---|
| 76 | 184 | void tpm_put_ops(struct tpm_chip *chip) |
|---|
| 77 | 185 | { |
|---|
| 186 | + tpm_chip_stop(chip); |
|---|
| 187 | + mutex_unlock(&chip->tpm_mutex); |
|---|
| 78 | 188 | up_read(&chip->ops_sem); |
|---|
| 79 | 189 | put_device(&chip->dev); |
|---|
| 80 | 190 | } |
|---|
| .. | .. |
|---|
| 160 | 270 | kfree(chip->log.bios_event_log); |
|---|
| 161 | 271 | kfree(chip->work_space.context_buf); |
|---|
| 162 | 272 | kfree(chip->work_space.session_buf); |
|---|
| 273 | + kfree(chip->allocated_banks); |
|---|
| 163 | 274 | kfree(chip); |
|---|
| 164 | | -} |
|---|
| 165 | | - |
|---|
| 166 | | -static void tpm_devs_release(struct device *dev) |
|---|
| 167 | | -{ |
|---|
| 168 | | - struct tpm_chip *chip = container_of(dev, struct tpm_chip, devs); |
|---|
| 169 | | - |
|---|
| 170 | | - /* release the master device reference */ |
|---|
| 171 | | - put_device(&chip->dev); |
|---|
| 172 | 275 | } |
|---|
| 173 | 276 | |
|---|
| 174 | 277 | /** |
|---|
| .. | .. |
|---|
| 176 | 279 | * @dev: device to which the chip is associated. |
|---|
| 177 | 280 | * |
|---|
| 178 | 281 | * Issues a TPM2_Shutdown command prior to loss of power, as required by the |
|---|
| 179 | | - * TPM 2.0 spec. |
|---|
| 180 | | - * Then, calls bus- and device- specific shutdown code. |
|---|
| 282 | + * TPM 2.0 spec. Then, calls bus- and device- specific shutdown code. |
|---|
| 181 | 283 | * |
|---|
| 182 | | - * XXX: This codepath relies on the fact that sysfs is not enabled for |
|---|
| 183 | | - * TPM2: sysfs uses an implicit lock on chip->ops, so this could race if TPM2 |
|---|
| 184 | | - * has sysfs support enabled before TPM sysfs's implicit locking is fixed. |
|---|
| 284 | + * Return: always 0 (i.e. success) |
|---|
| 185 | 285 | */ |
|---|
| 186 | 286 | static int tpm_class_shutdown(struct device *dev) |
|---|
| 187 | 287 | { |
|---|
| .. | .. |
|---|
| 189 | 289 | |
|---|
| 190 | 290 | down_write(&chip->ops_sem); |
|---|
| 191 | 291 | if (chip->flags & TPM_CHIP_FLAG_TPM2) { |
|---|
| 192 | | - tpm2_shutdown(chip, TPM2_SU_CLEAR); |
|---|
| 193 | | - chip->ops = NULL; |
|---|
| 292 | + if (!tpm_chip_start(chip)) { |
|---|
| 293 | + tpm2_shutdown(chip, TPM2_SU_CLEAR); |
|---|
| 294 | + tpm_chip_stop(chip); |
|---|
| 295 | + } |
|---|
| 194 | 296 | } |
|---|
| 195 | 297 | chip->ops = NULL; |
|---|
| 196 | 298 | up_write(&chip->ops_sem); |
|---|
| .. | .. |
|---|
| 234 | 336 | chip->dev_num = rc; |
|---|
| 235 | 337 | |
|---|
| 236 | 338 | device_initialize(&chip->dev); |
|---|
| 237 | | - device_initialize(&chip->devs); |
|---|
| 238 | 339 | |
|---|
| 239 | 340 | chip->dev.class = tpm_class; |
|---|
| 240 | 341 | chip->dev.class->shutdown_pre = tpm_class_shutdown; |
|---|
| .. | .. |
|---|
| 242 | 343 | chip->dev.parent = pdev; |
|---|
| 243 | 344 | chip->dev.groups = chip->groups; |
|---|
| 244 | 345 | |
|---|
| 245 | | - chip->devs.parent = pdev; |
|---|
| 246 | | - chip->devs.class = tpmrm_class; |
|---|
| 247 | | - chip->devs.release = tpm_devs_release; |
|---|
| 248 | | - /* get extra reference on main device to hold on |
|---|
| 249 | | - * behalf of devs. This holds the chip structure |
|---|
| 250 | | - * while cdevs is in use. The corresponding put |
|---|
| 251 | | - * is in the tpm_devs_release (TPM2 only) |
|---|
| 252 | | - */ |
|---|
| 253 | | - if (chip->flags & TPM_CHIP_FLAG_TPM2) |
|---|
| 254 | | - get_device(&chip->dev); |
|---|
| 255 | | - |
|---|
| 256 | 346 | if (chip->dev_num == 0) |
|---|
| 257 | 347 | chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR); |
|---|
| 258 | 348 | else |
|---|
| 259 | 349 | chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num); |
|---|
| 260 | 350 | |
|---|
| 261 | | - chip->devs.devt = |
|---|
| 262 | | - MKDEV(MAJOR(tpm_devt), chip->dev_num + TPM_NUM_DEVICES); |
|---|
| 263 | | - |
|---|
| 264 | 351 | rc = dev_set_name(&chip->dev, "tpm%d", chip->dev_num); |
|---|
| 265 | | - if (rc) |
|---|
| 266 | | - goto out; |
|---|
| 267 | | - rc = dev_set_name(&chip->devs, "tpmrm%d", chip->dev_num); |
|---|
| 268 | 352 | if (rc) |
|---|
| 269 | 353 | goto out; |
|---|
| 270 | 354 | |
|---|
| .. | .. |
|---|
| 272 | 356 | chip->flags |= TPM_CHIP_FLAG_VIRTUAL; |
|---|
| 273 | 357 | |
|---|
| 274 | 358 | cdev_init(&chip->cdev, &tpm_fops); |
|---|
| 275 | | - cdev_init(&chip->cdevs, &tpmrm_fops); |
|---|
| 276 | 359 | chip->cdev.owner = THIS_MODULE; |
|---|
| 277 | | - chip->cdevs.owner = THIS_MODULE; |
|---|
| 278 | 360 | |
|---|
| 279 | 361 | rc = tpm2_init_space(&chip->work_space, TPM2_SPACE_BUFFER_SIZE); |
|---|
| 280 | 362 | if (rc) { |
|---|
| .. | .. |
|---|
| 286 | 368 | return chip; |
|---|
| 287 | 369 | |
|---|
| 288 | 370 | out: |
|---|
| 289 | | - put_device(&chip->devs); |
|---|
| 290 | 371 | put_device(&chip->dev); |
|---|
| 291 | 372 | return ERR_PTR(rc); |
|---|
| 292 | 373 | } |
|---|
| .. | .. |
|---|
| 335 | 416 | } |
|---|
| 336 | 417 | |
|---|
| 337 | 418 | if (chip->flags & TPM_CHIP_FLAG_TPM2) { |
|---|
| 338 | | - rc = cdev_device_add(&chip->cdevs, &chip->devs); |
|---|
| 339 | | - if (rc) { |
|---|
| 340 | | - dev_err(&chip->devs, |
|---|
| 341 | | - "unable to cdev_device_add() %s, major %d, minor %d, err=%d\n", |
|---|
| 342 | | - dev_name(&chip->devs), MAJOR(chip->devs.devt), |
|---|
| 343 | | - MINOR(chip->devs.devt), rc); |
|---|
| 344 | | - return rc; |
|---|
| 345 | | - } |
|---|
| 419 | + rc = tpm_devs_add(chip); |
|---|
| 420 | + if (rc) |
|---|
| 421 | + goto err_del_cdev; |
|---|
| 346 | 422 | } |
|---|
| 347 | 423 | |
|---|
| 348 | 424 | /* Make the chip available. */ |
|---|
| .. | .. |
|---|
| 350 | 426 | idr_replace(&dev_nums_idr, chip, chip->dev_num); |
|---|
| 351 | 427 | mutex_unlock(&idr_lock); |
|---|
| 352 | 428 | |
|---|
| 429 | + return 0; |
|---|
| 430 | + |
|---|
| 431 | +err_del_cdev: |
|---|
| 432 | + cdev_device_del(&chip->cdev, &chip->dev); |
|---|
| 353 | 433 | return rc; |
|---|
| 354 | 434 | } |
|---|
| 355 | 435 | |
|---|
| .. | .. |
|---|
| 364 | 444 | |
|---|
| 365 | 445 | /* Make the driver uncallable. */ |
|---|
| 366 | 446 | down_write(&chip->ops_sem); |
|---|
| 367 | | - if (chip->flags & TPM_CHIP_FLAG_TPM2) |
|---|
| 368 | | - tpm2_shutdown(chip, TPM2_SU_CLEAR); |
|---|
| 447 | + if (chip->flags & TPM_CHIP_FLAG_TPM2) { |
|---|
| 448 | + if (!tpm_chip_start(chip)) { |
|---|
| 449 | + tpm2_shutdown(chip, TPM2_SU_CLEAR); |
|---|
| 450 | + tpm_chip_stop(chip); |
|---|
| 451 | + } |
|---|
| 452 | + } |
|---|
| 369 | 453 | chip->ops = NULL; |
|---|
| 370 | 454 | up_write(&chip->ops_sem); |
|---|
| 371 | 455 | } |
|---|
| .. | .. |
|---|
| 395 | 479 | if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL)) |
|---|
| 396 | 480 | return 0; |
|---|
| 397 | 481 | |
|---|
| 398 | | - rc = __compat_only_sysfs_link_entry_to_kobj( |
|---|
| 399 | | - &chip->dev.parent->kobj, &chip->dev.kobj, "ppi"); |
|---|
| 482 | + rc = compat_only_sysfs_link_entry_to_kobj( |
|---|
| 483 | + &chip->dev.parent->kobj, &chip->dev.kobj, "ppi", NULL); |
|---|
| 400 | 484 | if (rc && rc != -ENOENT) |
|---|
| 401 | 485 | return rc; |
|---|
| 402 | 486 | |
|---|
| 403 | 487 | /* All the names from tpm-sysfs */ |
|---|
| 404 | 488 | for (i = chip->groups[0]->attrs; *i != NULL; ++i) { |
|---|
| 405 | | - rc = __compat_only_sysfs_link_entry_to_kobj( |
|---|
| 406 | | - &chip->dev.parent->kobj, &chip->dev.kobj, (*i)->name); |
|---|
| 489 | + rc = compat_only_sysfs_link_entry_to_kobj( |
|---|
| 490 | + &chip->dev.parent->kobj, &chip->dev.kobj, (*i)->name, NULL); |
|---|
| 407 | 491 | if (rc) { |
|---|
| 408 | 492 | tpm_del_legacy_sysfs(chip); |
|---|
| 409 | 493 | return rc; |
|---|
| .. | .. |
|---|
| 432 | 516 | return hwrng_register(&chip->hwrng); |
|---|
| 433 | 517 | } |
|---|
| 434 | 518 | |
|---|
| 519 | +static int tpm_get_pcr_allocation(struct tpm_chip *chip) |
|---|
| 520 | +{ |
|---|
| 521 | + int rc; |
|---|
| 522 | + |
|---|
| 523 | + rc = (chip->flags & TPM_CHIP_FLAG_TPM2) ? |
|---|
| 524 | + tpm2_get_pcr_allocation(chip) : |
|---|
| 525 | + tpm1_get_pcr_allocation(chip); |
|---|
| 526 | + |
|---|
| 527 | + if (rc > 0) |
|---|
| 528 | + return -ENODEV; |
|---|
| 529 | + |
|---|
| 530 | + return rc; |
|---|
| 531 | +} |
|---|
| 532 | + |
|---|
| 435 | 533 | /* |
|---|
| 436 | 534 | * tpm_chip_register() - create a character device for the TPM chip |
|---|
| 437 | 535 | * @chip: TPM chip to use. |
|---|
| .. | .. |
|---|
| 447 | 545 | { |
|---|
| 448 | 546 | int rc; |
|---|
| 449 | 547 | |
|---|
| 450 | | - if (chip->ops->flags & TPM_OPS_AUTO_STARTUP) { |
|---|
| 451 | | - if (chip->flags & TPM_CHIP_FLAG_TPM2) |
|---|
| 452 | | - rc = tpm2_auto_startup(chip); |
|---|
| 453 | | - else |
|---|
| 454 | | - rc = tpm1_auto_startup(chip); |
|---|
| 455 | | - if (rc) |
|---|
| 456 | | - return rc; |
|---|
| 548 | + rc = tpm_chip_start(chip); |
|---|
| 549 | + if (rc) |
|---|
| 550 | + return rc; |
|---|
| 551 | + rc = tpm_auto_startup(chip); |
|---|
| 552 | + if (rc) { |
|---|
| 553 | + tpm_chip_stop(chip); |
|---|
| 554 | + return rc; |
|---|
| 457 | 555 | } |
|---|
| 556 | + |
|---|
| 557 | + rc = tpm_get_pcr_allocation(chip); |
|---|
| 558 | + tpm_chip_stop(chip); |
|---|
| 559 | + if (rc) |
|---|
| 560 | + return rc; |
|---|
| 458 | 561 | |
|---|
| 459 | 562 | tpm_sysfs_add_device(chip); |
|---|
| 460 | 563 | |
|---|
| .. | .. |
|---|
| 508 | 611 | hwrng_unregister(&chip->hwrng); |
|---|
| 509 | 612 | tpm_bios_log_teardown(chip); |
|---|
| 510 | 613 | if (chip->flags & TPM_CHIP_FLAG_TPM2) |
|---|
| 511 | | - cdev_device_del(&chip->cdevs, &chip->devs); |
|---|
| 614 | + tpm_devs_remove(chip); |
|---|
| 512 | 615 | tpm_del_char_device(chip); |
|---|
| 513 | 616 | } |
|---|
| 514 | 617 | EXPORT_SYMBOL_GPL(tpm_chip_unregister); |
|---|