.. | .. |
---|
| 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); |
---|