Commit | Line | Data |
---|---|---|
afb5abc2 JS |
1 | /* |
2 | * Copyright (C) 2004 IBM Corporation | |
3 | * Copyright (C) 2014 Intel Corporation | |
4 | * | |
5 | * Authors: | |
6 | * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> | |
7 | * Leendert van Doorn <leendert@watson.ibm.com> | |
8 | * Dave Safford <safford@watson.ibm.com> | |
9 | * Reiner Sailer <sailer@watson.ibm.com> | |
10 | * Kylene Hall <kjhall@us.ibm.com> | |
11 | * | |
12 | * Maintained by: <tpmdd-devel@lists.sourceforge.net> | |
13 | * | |
14 | * 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 | */ | |
22 | ||
23 | #include <linux/poll.h> | |
24 | #include <linux/slab.h> | |
25 | #include <linux/mutex.h> | |
26 | #include <linux/spinlock.h> | |
27 | #include <linux/freezer.h> | |
313d21ee | 28 | #include <linux/major.h> |
fd3ec366 | 29 | #include <linux/tpm_eventlog.h> |
6e592a06 | 30 | #include <linux/hw_random.h> |
afb5abc2 | 31 | #include "tpm.h" |
afb5abc2 | 32 | |
15516788 SB |
33 | DEFINE_IDR(dev_nums_idr); |
34 | static DEFINE_MUTEX(idr_lock); | |
afb5abc2 | 35 | |
313d21ee | 36 | struct class *tpm_class; |
fdc915f7 | 37 | struct class *tpmrm_class; |
313d21ee JS |
38 | dev_t tpm_devt; |
39 | ||
4e26195f JG |
40 | /** |
41 | * tpm_try_get_ops() - Get a ref to the tpm_chip | |
42 | * @chip: Chip to ref | |
43 | * | |
44 | * The caller must already have some kind of locking to ensure that chip is | |
45 | * valid. This function will lock the chip so that the ops member can be | |
46 | * accessed safely. The locking prevents tpm_chip_unregister from | |
47 | * completing, so it should not be held for long periods. | |
48 | * | |
49 | * Returns -ERRNO if the chip could not be got. | |
afb5abc2 | 50 | */ |
4e26195f JG |
51 | int tpm_try_get_ops(struct tpm_chip *chip) |
52 | { | |
53 | int rc = -EIO; | |
54 | ||
55 | get_device(&chip->dev); | |
56 | ||
57 | down_read(&chip->ops_sem); | |
58 | if (!chip->ops) | |
59 | goto out_lock; | |
60 | ||
4e26195f JG |
61 | return 0; |
62 | out_lock: | |
63 | up_read(&chip->ops_sem); | |
64 | put_device(&chip->dev); | |
65 | return rc; | |
66 | } | |
67 | EXPORT_SYMBOL_GPL(tpm_try_get_ops); | |
68 | ||
69 | /** | |
70 | * tpm_put_ops() - Release a ref to the tpm_chip | |
71 | * @chip: Chip to put | |
72 | * | |
73 | * This is the opposite pair to tpm_try_get_ops(). After this returns chip may | |
74 | * be kfree'd. | |
75 | */ | |
76 | void tpm_put_ops(struct tpm_chip *chip) | |
77 | { | |
4e26195f JG |
78 | up_read(&chip->ops_sem); |
79 | put_device(&chip->dev); | |
80 | } | |
81 | EXPORT_SYMBOL_GPL(tpm_put_ops); | |
82 | ||
aaae8153 SB |
83 | /** |
84 | * tpm_default_chip() - find a TPM chip and get a reference to it | |
85 | */ | |
86 | struct tpm_chip *tpm_default_chip(void) | |
87 | { | |
88 | struct tpm_chip *chip, *res = NULL; | |
89 | int chip_num = 0; | |
90 | int chip_prev; | |
91 | ||
92 | mutex_lock(&idr_lock); | |
93 | ||
94 | do { | |
95 | chip_prev = chip_num; | |
96 | chip = idr_get_next(&dev_nums_idr, &chip_num); | |
97 | if (chip) { | |
98 | get_device(&chip->dev); | |
99 | res = chip; | |
100 | break; | |
101 | } | |
102 | } while (chip_prev != chip_num); | |
103 | ||
104 | mutex_unlock(&idr_lock); | |
105 | ||
106 | return res; | |
107 | } | |
108 | EXPORT_SYMBOL_GPL(tpm_default_chip); | |
109 | ||
4e26195f | 110 | /** |
fc1d52b7 | 111 | * tpm_find_get_ops() - find and reserve a TPM chip |
aad887f6 | 112 | * @chip: a &struct tpm_chip instance, %NULL for the default chip |
4e26195f | 113 | * |
aad887f6 | 114 | * Finds a TPM chip and reserves its class device and operations. The chip must |
fc1d52b7 SB |
115 | * be released with tpm_put_ops() after use. |
116 | * This function is for internal use only. It supports existing TPM callers | |
117 | * by accepting NULL, but those callers should be converted to pass in a chip | |
118 | * directly. | |
aad887f6 JS |
119 | * |
120 | * Return: | |
121 | * A reserved &struct tpm_chip instance. | |
122 | * %NULL if a chip is not found. | |
123 | * %NULL if the chip is not available. | |
37f4915f | 124 | */ |
fc1d52b7 | 125 | struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip) |
afb5abc2 | 126 | { |
eccc9bb8 | 127 | int rc; |
15516788 | 128 | |
eccc9bb8 | 129 | if (chip) { |
aad887f6 | 130 | if (!tpm_try_get_ops(chip)) |
eccc9bb8 SB |
131 | return chip; |
132 | return NULL; | |
15516788 | 133 | } |
afb5abc2 | 134 | |
eccc9bb8 SB |
135 | chip = tpm_default_chip(); |
136 | if (!chip) | |
137 | return NULL; | |
138 | rc = tpm_try_get_ops(chip); | |
139 | /* release additional reference we got from tpm_default_chip() */ | |
140 | put_device(&chip->dev); | |
141 | if (rc) | |
142 | return NULL; | |
143 | return chip; | |
afb5abc2 JS |
144 | } |
145 | ||
146 | /** | |
313d21ee JS |
147 | * tpm_dev_release() - free chip memory and the device number |
148 | * @dev: the character device for the TPM chip | |
afb5abc2 | 149 | * |
313d21ee | 150 | * This is used as the release function for the character device. |
afb5abc2 | 151 | */ |
313d21ee | 152 | static void tpm_dev_release(struct device *dev) |
afb5abc2 | 153 | { |
313d21ee | 154 | struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev); |
afb5abc2 | 155 | |
15516788 SB |
156 | mutex_lock(&idr_lock); |
157 | idr_remove(&dev_nums_idr, chip->dev_num); | |
158 | mutex_unlock(&idr_lock); | |
159 | ||
748935ee | 160 | kfree(chip->log.bios_event_log); |
745b361e | 161 | kfree(chip->work_space.context_buf); |
4d57856a | 162 | kfree(chip->work_space.session_buf); |
afb5abc2 JS |
163 | kfree(chip); |
164 | } | |
165 | ||
fdc915f7 JB |
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 | } | |
173 | ||
d1bd4a79 JZ |
174 | /** |
175 | * tpm_class_shutdown() - prepare the TPM device for loss of power. | |
176 | * @dev: device to which the chip is associated. | |
177 | * | |
178 | * 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. | |
181 | * | |
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. | |
185 | */ | |
186 | static int tpm_class_shutdown(struct device *dev) | |
187 | { | |
188 | struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev); | |
189 | ||
190 | if (chip->flags & TPM_CHIP_FLAG_TPM2) { | |
191 | down_write(&chip->ops_sem); | |
192 | tpm2_shutdown(chip, TPM2_SU_CLEAR); | |
193 | chip->ops = NULL; | |
194 | up_write(&chip->ops_sem); | |
195 | } | |
7521621e | 196 | |
d1bd4a79 JZ |
197 | return 0; |
198 | } | |
199 | ||
afb5abc2 | 200 | /** |
3897cd9c JG |
201 | * tpm_chip_alloc() - allocate a new struct tpm_chip instance |
202 | * @pdev: device to which the chip is associated | |
203 | * At this point pdev mst be initialized, but does not have to | |
204 | * be registered | |
afb5abc2 JS |
205 | * @ops: struct tpm_class_ops instance |
206 | * | |
207 | * Allocates a new struct tpm_chip instance and assigns a free | |
3897cd9c | 208 | * device number for it. Must be paired with put_device(&chip->dev). |
afb5abc2 | 209 | */ |
2998b02b | 210 | struct tpm_chip *tpm_chip_alloc(struct device *pdev, |
3897cd9c | 211 | const struct tpm_class_ops *ops) |
afb5abc2 JS |
212 | { |
213 | struct tpm_chip *chip; | |
4f3b193d | 214 | int rc; |
afb5abc2 JS |
215 | |
216 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); | |
217 | if (chip == NULL) | |
218 | return ERR_PTR(-ENOMEM); | |
219 | ||
220 | mutex_init(&chip->tpm_mutex); | |
4e26195f | 221 | init_rwsem(&chip->ops_sem); |
afb5abc2 JS |
222 | |
223 | chip->ops = ops; | |
224 | ||
15516788 SB |
225 | mutex_lock(&idr_lock); |
226 | rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL); | |
227 | mutex_unlock(&idr_lock); | |
228 | if (rc < 0) { | |
2998b02b | 229 | dev_err(pdev, "No available tpm device numbers\n"); |
afb5abc2 | 230 | kfree(chip); |
15516788 | 231 | return ERR_PTR(rc); |
afb5abc2 | 232 | } |
15516788 | 233 | chip->dev_num = rc; |
afb5abc2 | 234 | |
3635e2ec | 235 | device_initialize(&chip->dev); |
fdc915f7 | 236 | device_initialize(&chip->devs); |
afb5abc2 | 237 | |
313d21ee | 238 | chip->dev.class = tpm_class; |
7521621e | 239 | chip->dev.class->shutdown_pre = tpm_class_shutdown; |
313d21ee | 240 | chip->dev.release = tpm_dev_release; |
2998b02b | 241 | chip->dev.parent = pdev; |
9b774d5c | 242 | chip->dev.groups = chip->groups; |
313d21ee | 243 | |
fdc915f7 JB |
244 | chip->devs.parent = pdev; |
245 | chip->devs.class = tpmrm_class; | |
246 | chip->devs.release = tpm_devs_release; | |
247 | /* get extra reference on main device to hold on | |
248 | * behalf of devs. This holds the chip structure | |
249 | * while cdevs is in use. The corresponding put | |
8979b02a | 250 | * is in the tpm_devs_release (TPM2 only) |
fdc915f7 | 251 | */ |
8979b02a SB |
252 | if (chip->flags & TPM_CHIP_FLAG_TPM2) |
253 | get_device(&chip->dev); | |
fdc915f7 | 254 | |
313d21ee JS |
255 | if (chip->dev_num == 0) |
256 | chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR); | |
257 | else | |
258 | chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num); | |
259 | ||
fdc915f7 JB |
260 | chip->devs.devt = |
261 | MKDEV(MAJOR(tpm_devt), chip->dev_num + TPM_NUM_DEVICES); | |
262 | ||
3635e2ec | 263 | rc = dev_set_name(&chip->dev, "tpm%d", chip->dev_num); |
fdc915f7 JB |
264 | if (rc) |
265 | goto out; | |
266 | rc = dev_set_name(&chip->devs, "tpmrm%d", chip->dev_num); | |
3635e2ec JG |
267 | if (rc) |
268 | goto out; | |
313d21ee | 269 | |
2998b02b | 270 | if (!pdev) |
2f9f5377 SB |
271 | chip->flags |= TPM_CHIP_FLAG_VIRTUAL; |
272 | ||
313d21ee | 273 | cdev_init(&chip->cdev, &tpm_fops); |
fdc915f7 | 274 | cdev_init(&chip->cdevs, &tpmrm_fops); |
2072df40 | 275 | chip->cdev.owner = THIS_MODULE; |
fdc915f7 | 276 | chip->cdevs.owner = THIS_MODULE; |
313d21ee | 277 | |
745b361e JS |
278 | chip->work_space.context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); |
279 | if (!chip->work_space.context_buf) { | |
280 | rc = -ENOMEM; | |
281 | goto out; | |
282 | } | |
4d57856a JB |
283 | chip->work_space.session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); |
284 | if (!chip->work_space.session_buf) { | |
285 | rc = -ENOMEM; | |
286 | goto out; | |
287 | } | |
745b361e | 288 | |
877c57d0 | 289 | chip->locality = -1; |
3897cd9c JG |
290 | return chip; |
291 | ||
292 | out: | |
fdc915f7 | 293 | put_device(&chip->devs); |
3897cd9c JG |
294 | put_device(&chip->dev); |
295 | return ERR_PTR(rc); | |
296 | } | |
297 | EXPORT_SYMBOL_GPL(tpm_chip_alloc); | |
298 | ||
299 | /** | |
300 | * tpmm_chip_alloc() - allocate a new struct tpm_chip instance | |
301 | * @pdev: parent device to which the chip is associated | |
302 | * @ops: struct tpm_class_ops instance | |
303 | * | |
304 | * Same as tpm_chip_alloc except devm is used to do the put_device | |
305 | */ | |
306 | struct tpm_chip *tpmm_chip_alloc(struct device *pdev, | |
307 | const struct tpm_class_ops *ops) | |
308 | { | |
309 | struct tpm_chip *chip; | |
310 | int rc; | |
311 | ||
312 | chip = tpm_chip_alloc(pdev, ops); | |
313 | if (IS_ERR(chip)) | |
314 | return chip; | |
315 | ||
2b88cd96 SM |
316 | rc = devm_add_action_or_reset(pdev, |
317 | (void (*)(void *)) put_device, | |
318 | &chip->dev); | |
319 | if (rc) | |
4f3b193d | 320 | return ERR_PTR(rc); |
8e0ee3c9 | 321 | |
3897cd9c | 322 | dev_set_drvdata(pdev, chip); |
3635e2ec | 323 | |
3897cd9c | 324 | return chip; |
afb5abc2 JS |
325 | } |
326 | EXPORT_SYMBOL_GPL(tpmm_chip_alloc); | |
327 | ||
72c91ce8 | 328 | static int tpm_add_char_device(struct tpm_chip *chip) |
313d21ee JS |
329 | { |
330 | int rc; | |
331 | ||
8dbbf582 | 332 | rc = cdev_device_add(&chip->cdev, &chip->dev); |
313d21ee JS |
333 | if (rc) { |
334 | dev_err(&chip->dev, | |
8dbbf582 | 335 | "unable to cdev_device_add() %s, major %d, minor %d, err=%d\n", |
3635e2ec | 336 | dev_name(&chip->dev), MAJOR(chip->dev.devt), |
313d21ee | 337 | MINOR(chip->dev.devt), rc); |
313d21ee JS |
338 | return rc; |
339 | } | |
340 | ||
af82455f LT |
341 | if (chip->flags & TPM_CHIP_FLAG_TPM2) { |
342 | rc = cdev_device_add(&chip->cdevs, &chip->devs); | |
343 | if (rc) { | |
344 | dev_err(&chip->devs, | |
345 | "unable to cdev_device_add() %s, major %d, minor %d, err=%d\n", | |
346 | dev_name(&chip->devs), MAJOR(chip->devs.devt), | |
347 | MINOR(chip->devs.devt), rc); | |
348 | return rc; | |
349 | } | |
fdc915f7 JB |
350 | } |
351 | ||
15516788 SB |
352 | /* Make the chip available. */ |
353 | mutex_lock(&idr_lock); | |
354 | idr_replace(&dev_nums_idr, chip, chip->dev_num); | |
355 | mutex_unlock(&idr_lock); | |
356 | ||
313d21ee JS |
357 | return rc; |
358 | } | |
359 | ||
72c91ce8 | 360 | static void tpm_del_char_device(struct tpm_chip *chip) |
313d21ee | 361 | { |
8dbbf582 | 362 | cdev_device_del(&chip->cdev, &chip->dev); |
15516788 SB |
363 | |
364 | /* Make the chip unavailable. */ | |
365 | mutex_lock(&idr_lock); | |
366 | idr_replace(&dev_nums_idr, NULL, chip->dev_num); | |
367 | mutex_unlock(&idr_lock); | |
4e26195f JG |
368 | |
369 | /* Make the driver uncallable. */ | |
370 | down_write(&chip->ops_sem); | |
c0dff1f4 JS |
371 | if (chip->flags & TPM_CHIP_FLAG_TPM2) |
372 | tpm2_shutdown(chip, TPM2_SU_CLEAR); | |
4e26195f JG |
373 | chip->ops = NULL; |
374 | up_write(&chip->ops_sem); | |
313d21ee JS |
375 | } |
376 | ||
062807f2 JG |
377 | static void tpm_del_legacy_sysfs(struct tpm_chip *chip) |
378 | { | |
379 | struct attribute **i; | |
380 | ||
2f9f5377 | 381 | if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL)) |
062807f2 | 382 | return; |
34d47b63 | 383 | |
062807f2 JG |
384 | sysfs_remove_link(&chip->dev.parent->kobj, "ppi"); |
385 | ||
386 | for (i = chip->groups[0]->attrs; *i != NULL; ++i) | |
387 | sysfs_remove_link(&chip->dev.parent->kobj, (*i)->name); | |
34d47b63 JS |
388 | } |
389 | ||
062807f2 JG |
390 | /* For compatibility with legacy sysfs paths we provide symlinks from the |
391 | * parent dev directory to selected names within the tpm chip directory. Old | |
392 | * kernel versions created these files directly under the parent. | |
393 | */ | |
394 | static int tpm_add_legacy_sysfs(struct tpm_chip *chip) | |
395 | { | |
396 | struct attribute **i; | |
397 | int rc; | |
398 | ||
2f9f5377 | 399 | if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL)) |
062807f2 JG |
400 | return 0; |
401 | ||
402 | rc = __compat_only_sysfs_link_entry_to_kobj( | |
403 | &chip->dev.parent->kobj, &chip->dev.kobj, "ppi"); | |
404 | if (rc && rc != -ENOENT) | |
405 | return rc; | |
406 | ||
407 | /* All the names from tpm-sysfs */ | |
408 | for (i = chip->groups[0]->attrs; *i != NULL; ++i) { | |
409 | rc = __compat_only_sysfs_link_entry_to_kobj( | |
410 | &chip->dev.parent->kobj, &chip->dev.kobj, (*i)->name); | |
411 | if (rc) { | |
412 | tpm_del_legacy_sysfs(chip); | |
413 | return rc; | |
414 | } | |
415 | } | |
416 | ||
417 | return 0; | |
418 | } | |
6e592a06 JG |
419 | |
420 | static int tpm_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait) | |
421 | { | |
422 | struct tpm_chip *chip = container_of(rng, struct tpm_chip, hwrng); | |
423 | ||
424 | return tpm_get_random(chip, data, max); | |
425 | } | |
426 | ||
427 | static int tpm_add_hwrng(struct tpm_chip *chip) | |
428 | { | |
429 | if (!IS_ENABLED(CONFIG_HW_RANDOM_TPM)) | |
430 | return 0; | |
431 | ||
432 | snprintf(chip->hwrng_name, sizeof(chip->hwrng_name), | |
433 | "tpm-rng-%d", chip->dev_num); | |
434 | chip->hwrng.name = chip->hwrng_name; | |
435 | chip->hwrng.read = tpm_hwrng_read; | |
436 | return hwrng_register(&chip->hwrng); | |
437 | } | |
438 | ||
afb5abc2 | 439 | /* |
313d21ee | 440 | * tpm_chip_register() - create a character device for the TPM chip |
afb5abc2 JS |
441 | * @chip: TPM chip to use. |
442 | * | |
d972b052 JS |
443 | * Creates a character device for the TPM chip and adds sysfs attributes for |
444 | * the device. As the last step this function adds the chip to the list of TPM | |
445 | * chips available for in-kernel use. | |
afb5abc2 | 446 | * |
d972b052 JS |
447 | * This function should be only called after the chip initialization is |
448 | * complete. | |
afb5abc2 JS |
449 | */ |
450 | int tpm_chip_register(struct tpm_chip *chip) | |
451 | { | |
452 | int rc; | |
453 | ||
cae8b441 JG |
454 | if (chip->ops->flags & TPM_OPS_AUTO_STARTUP) { |
455 | if (chip->flags & TPM_CHIP_FLAG_TPM2) | |
456 | rc = tpm2_auto_startup(chip); | |
457 | else | |
458 | rc = tpm1_auto_startup(chip); | |
459 | if (rc) | |
460 | return rc; | |
461 | } | |
462 | ||
7518a21a JS |
463 | tpm_sysfs_add_device(chip); |
464 | ||
465 | rc = tpm_bios_log_setup(chip); | |
0cf577a0 | 466 | if (rc != 0 && rc != -ENODEV) |
34d47b63 | 467 | return rc; |
afb5abc2 | 468 | |
9b774d5c JS |
469 | tpm_add_ppi(chip); |
470 | ||
6e592a06 JG |
471 | rc = tpm_add_hwrng(chip); |
472 | if (rc) | |
473 | goto out_ppi; | |
474 | ||
72c91ce8 | 475 | rc = tpm_add_char_device(chip); |
6e592a06 JG |
476 | if (rc) |
477 | goto out_hwrng; | |
d972b052 | 478 | |
062807f2 JG |
479 | rc = tpm_add_legacy_sysfs(chip); |
480 | if (rc) { | |
481 | tpm_chip_unregister(chip); | |
482 | return rc; | |
d56e4f75 JS |
483 | } |
484 | ||
afb5abc2 | 485 | return 0; |
6e592a06 JG |
486 | |
487 | out_hwrng: | |
488 | if (IS_ENABLED(CONFIG_HW_RANDOM_TPM)) | |
489 | hwrng_unregister(&chip->hwrng); | |
490 | out_ppi: | |
491 | tpm_bios_log_teardown(chip); | |
492 | ||
493 | return rc; | |
afb5abc2 JS |
494 | } |
495 | EXPORT_SYMBOL_GPL(tpm_chip_register); | |
496 | ||
497 | /* | |
498 | * tpm_chip_unregister() - release the TPM driver | |
499 | * @chip: TPM chip to use. | |
500 | * | |
501 | * Takes the chip first away from the list of available TPM chips and then | |
502 | * cleans up all the resources reserved by tpm_chip_register(). | |
503 | * | |
4e26195f JG |
504 | * Once this function returns the driver call backs in 'op's will not be |
505 | * running and will no longer start. | |
506 | * | |
afb5abc2 JS |
507 | * NOTE: This function should be only called before deinitializing chip |
508 | * resources. | |
509 | */ | |
510 | void tpm_chip_unregister(struct tpm_chip *chip) | |
511 | { | |
062807f2 | 512 | tpm_del_legacy_sysfs(chip); |
6e592a06 JG |
513 | if (IS_ENABLED(CONFIG_HW_RANDOM_TPM)) |
514 | hwrng_unregister(&chip->hwrng); | |
7518a21a | 515 | tpm_bios_log_teardown(chip); |
af82455f LT |
516 | if (chip->flags & TPM_CHIP_FLAG_TPM2) |
517 | cdev_device_del(&chip->cdevs, &chip->devs); | |
72c91ce8 | 518 | tpm_del_char_device(chip); |
afb5abc2 JS |
519 | } |
520 | EXPORT_SYMBOL_GPL(tpm_chip_unregister); |