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> |
afb5abc2 JS |
29 | #include "tpm.h" |
30 | #include "tpm_eventlog.h" | |
31 | ||
15516788 SB |
32 | DEFINE_IDR(dev_nums_idr); |
33 | static DEFINE_MUTEX(idr_lock); | |
afb5abc2 | 34 | |
313d21ee | 35 | struct class *tpm_class; |
fdc915f7 | 36 | struct class *tpmrm_class; |
313d21ee JS |
37 | dev_t tpm_devt; |
38 | ||
4e26195f JG |
39 | /** |
40 | * tpm_try_get_ops() - Get a ref to the tpm_chip | |
41 | * @chip: Chip to ref | |
42 | * | |
43 | * The caller must already have some kind of locking to ensure that chip is | |
44 | * valid. This function will lock the chip so that the ops member can be | |
45 | * accessed safely. The locking prevents tpm_chip_unregister from | |
46 | * completing, so it should not be held for long periods. | |
47 | * | |
48 | * Returns -ERRNO if the chip could not be got. | |
afb5abc2 | 49 | */ |
4e26195f JG |
50 | int tpm_try_get_ops(struct tpm_chip *chip) |
51 | { | |
52 | int rc = -EIO; | |
53 | ||
54 | get_device(&chip->dev); | |
55 | ||
56 | down_read(&chip->ops_sem); | |
57 | if (!chip->ops) | |
58 | goto out_lock; | |
59 | ||
4e26195f JG |
60 | return 0; |
61 | out_lock: | |
62 | up_read(&chip->ops_sem); | |
63 | put_device(&chip->dev); | |
64 | return rc; | |
65 | } | |
66 | EXPORT_SYMBOL_GPL(tpm_try_get_ops); | |
67 | ||
68 | /** | |
69 | * tpm_put_ops() - Release a ref to the tpm_chip | |
70 | * @chip: Chip to put | |
71 | * | |
72 | * This is the opposite pair to tpm_try_get_ops(). After this returns chip may | |
73 | * be kfree'd. | |
74 | */ | |
75 | void tpm_put_ops(struct tpm_chip *chip) | |
76 | { | |
4e26195f JG |
77 | up_read(&chip->ops_sem); |
78 | put_device(&chip->dev); | |
79 | } | |
80 | EXPORT_SYMBOL_GPL(tpm_put_ops); | |
81 | ||
82 | /** | |
83 | * tpm_chip_find_get() - return tpm_chip for a given chip number | |
84 | * @chip_num: id to find | |
85 | * | |
86 | * The return'd chip has been tpm_try_get_ops'd and must be released via | |
87 | * tpm_put_ops | |
37f4915f | 88 | */ |
afb5abc2 JS |
89 | struct tpm_chip *tpm_chip_find_get(int chip_num) |
90 | { | |
15516788 SB |
91 | struct tpm_chip *chip, *res = NULL; |
92 | int chip_prev; | |
93 | ||
94 | mutex_lock(&idr_lock); | |
95 | ||
96 | if (chip_num == TPM_ANY_NUM) { | |
97 | chip_num = 0; | |
98 | do { | |
99 | chip_prev = chip_num; | |
100 | chip = idr_get_next(&dev_nums_idr, &chip_num); | |
101 | if (chip && !tpm_try_get_ops(chip)) { | |
102 | res = chip; | |
103 | break; | |
104 | } | |
105 | } while (chip_prev != chip_num); | |
106 | } else { | |
37f4915f | 107 | chip = idr_find(&dev_nums_idr, chip_num); |
15516788 SB |
108 | if (chip && !tpm_try_get_ops(chip)) |
109 | res = chip; | |
110 | } | |
afb5abc2 | 111 | |
15516788 | 112 | mutex_unlock(&idr_lock); |
afb5abc2 | 113 | |
15516788 | 114 | return res; |
afb5abc2 JS |
115 | } |
116 | ||
117 | /** | |
313d21ee JS |
118 | * tpm_dev_release() - free chip memory and the device number |
119 | * @dev: the character device for the TPM chip | |
afb5abc2 | 120 | * |
313d21ee | 121 | * This is used as the release function for the character device. |
afb5abc2 | 122 | */ |
313d21ee | 123 | static void tpm_dev_release(struct device *dev) |
afb5abc2 | 124 | { |
313d21ee | 125 | struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev); |
afb5abc2 | 126 | |
15516788 SB |
127 | mutex_lock(&idr_lock); |
128 | idr_remove(&dev_nums_idr, chip->dev_num); | |
129 | mutex_unlock(&idr_lock); | |
130 | ||
748935ee | 131 | kfree(chip->log.bios_event_log); |
745b361e | 132 | kfree(chip->work_space.context_buf); |
4d57856a | 133 | kfree(chip->work_space.session_buf); |
afb5abc2 JS |
134 | kfree(chip); |
135 | } | |
136 | ||
fdc915f7 JB |
137 | static void tpm_devs_release(struct device *dev) |
138 | { | |
139 | struct tpm_chip *chip = container_of(dev, struct tpm_chip, devs); | |
140 | ||
141 | /* release the master device reference */ | |
142 | put_device(&chip->dev); | |
143 | } | |
144 | ||
afb5abc2 | 145 | /** |
3897cd9c JG |
146 | * tpm_chip_alloc() - allocate a new struct tpm_chip instance |
147 | * @pdev: device to which the chip is associated | |
148 | * At this point pdev mst be initialized, but does not have to | |
149 | * be registered | |
afb5abc2 JS |
150 | * @ops: struct tpm_class_ops instance |
151 | * | |
152 | * Allocates a new struct tpm_chip instance and assigns a free | |
3897cd9c | 153 | * device number for it. Must be paired with put_device(&chip->dev). |
afb5abc2 | 154 | */ |
2998b02b | 155 | struct tpm_chip *tpm_chip_alloc(struct device *pdev, |
3897cd9c | 156 | const struct tpm_class_ops *ops) |
afb5abc2 JS |
157 | { |
158 | struct tpm_chip *chip; | |
4f3b193d | 159 | int rc; |
afb5abc2 JS |
160 | |
161 | chip = kzalloc(sizeof(*chip), GFP_KERNEL); | |
162 | if (chip == NULL) | |
163 | return ERR_PTR(-ENOMEM); | |
164 | ||
165 | mutex_init(&chip->tpm_mutex); | |
4e26195f | 166 | init_rwsem(&chip->ops_sem); |
afb5abc2 JS |
167 | |
168 | chip->ops = ops; | |
169 | ||
15516788 SB |
170 | mutex_lock(&idr_lock); |
171 | rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL); | |
172 | mutex_unlock(&idr_lock); | |
173 | if (rc < 0) { | |
2998b02b | 174 | dev_err(pdev, "No available tpm device numbers\n"); |
afb5abc2 | 175 | kfree(chip); |
15516788 | 176 | return ERR_PTR(rc); |
afb5abc2 | 177 | } |
15516788 | 178 | chip->dev_num = rc; |
afb5abc2 | 179 | |
3635e2ec | 180 | device_initialize(&chip->dev); |
fdc915f7 | 181 | device_initialize(&chip->devs); |
afb5abc2 | 182 | |
313d21ee JS |
183 | chip->dev.class = tpm_class; |
184 | chip->dev.release = tpm_dev_release; | |
2998b02b | 185 | chip->dev.parent = pdev; |
9b774d5c | 186 | chip->dev.groups = chip->groups; |
313d21ee | 187 | |
fdc915f7 JB |
188 | chip->devs.parent = pdev; |
189 | chip->devs.class = tpmrm_class; | |
190 | chip->devs.release = tpm_devs_release; | |
191 | /* get extra reference on main device to hold on | |
192 | * behalf of devs. This holds the chip structure | |
193 | * while cdevs is in use. The corresponding put | |
194 | * is in the tpm_devs_release | |
195 | */ | |
196 | get_device(&chip->dev); | |
197 | ||
313d21ee JS |
198 | if (chip->dev_num == 0) |
199 | chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR); | |
200 | else | |
201 | chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num); | |
202 | ||
fdc915f7 JB |
203 | chip->devs.devt = |
204 | MKDEV(MAJOR(tpm_devt), chip->dev_num + TPM_NUM_DEVICES); | |
205 | ||
3635e2ec | 206 | rc = dev_set_name(&chip->dev, "tpm%d", chip->dev_num); |
fdc915f7 JB |
207 | if (rc) |
208 | goto out; | |
209 | rc = dev_set_name(&chip->devs, "tpmrm%d", chip->dev_num); | |
3635e2ec JG |
210 | if (rc) |
211 | goto out; | |
313d21ee | 212 | |
2998b02b | 213 | if (!pdev) |
2f9f5377 SB |
214 | chip->flags |= TPM_CHIP_FLAG_VIRTUAL; |
215 | ||
313d21ee | 216 | cdev_init(&chip->cdev, &tpm_fops); |
fdc915f7 | 217 | cdev_init(&chip->cdevs, &tpmrm_fops); |
2072df40 | 218 | chip->cdev.owner = THIS_MODULE; |
fdc915f7 | 219 | chip->cdevs.owner = THIS_MODULE; |
ba0ef854 | 220 | chip->cdev.kobj.parent = &chip->dev.kobj; |
fdc915f7 | 221 | chip->cdevs.kobj.parent = &chip->devs.kobj; |
313d21ee | 222 | |
745b361e JS |
223 | chip->work_space.context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); |
224 | if (!chip->work_space.context_buf) { | |
225 | rc = -ENOMEM; | |
226 | goto out; | |
227 | } | |
4d57856a JB |
228 | chip->work_space.session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); |
229 | if (!chip->work_space.session_buf) { | |
230 | rc = -ENOMEM; | |
231 | goto out; | |
232 | } | |
745b361e | 233 | |
3897cd9c JG |
234 | return chip; |
235 | ||
236 | out: | |
fdc915f7 | 237 | put_device(&chip->devs); |
3897cd9c JG |
238 | put_device(&chip->dev); |
239 | return ERR_PTR(rc); | |
240 | } | |
241 | EXPORT_SYMBOL_GPL(tpm_chip_alloc); | |
242 | ||
243 | /** | |
244 | * tpmm_chip_alloc() - allocate a new struct tpm_chip instance | |
245 | * @pdev: parent device to which the chip is associated | |
246 | * @ops: struct tpm_class_ops instance | |
247 | * | |
248 | * Same as tpm_chip_alloc except devm is used to do the put_device | |
249 | */ | |
250 | struct tpm_chip *tpmm_chip_alloc(struct device *pdev, | |
251 | const struct tpm_class_ops *ops) | |
252 | { | |
253 | struct tpm_chip *chip; | |
254 | int rc; | |
255 | ||
256 | chip = tpm_chip_alloc(pdev, ops); | |
257 | if (IS_ERR(chip)) | |
258 | return chip; | |
259 | ||
2b88cd96 SM |
260 | rc = devm_add_action_or_reset(pdev, |
261 | (void (*)(void *)) put_device, | |
262 | &chip->dev); | |
263 | if (rc) | |
4f3b193d | 264 | return ERR_PTR(rc); |
8e0ee3c9 | 265 | |
3897cd9c | 266 | dev_set_drvdata(pdev, chip); |
3635e2ec | 267 | |
3897cd9c | 268 | return chip; |
afb5abc2 JS |
269 | } |
270 | EXPORT_SYMBOL_GPL(tpmm_chip_alloc); | |
271 | ||
72c91ce8 | 272 | static int tpm_add_char_device(struct tpm_chip *chip) |
313d21ee JS |
273 | { |
274 | int rc; | |
275 | ||
d972b052 | 276 | rc = cdev_add(&chip->cdev, chip->dev.devt, 1); |
313d21ee JS |
277 | if (rc) { |
278 | dev_err(&chip->dev, | |
d972b052 | 279 | "unable to cdev_add() %s, major %d, minor %d, err=%d\n", |
3635e2ec | 280 | dev_name(&chip->dev), MAJOR(chip->dev.devt), |
313d21ee | 281 | MINOR(chip->dev.devt), rc); |
313d21ee JS |
282 | return rc; |
283 | } | |
284 | ||
d972b052 | 285 | rc = device_add(&chip->dev); |
313d21ee JS |
286 | if (rc) { |
287 | dev_err(&chip->dev, | |
d972b052 | 288 | "unable to device_register() %s, major %d, minor %d, err=%d\n", |
3635e2ec | 289 | dev_name(&chip->dev), MAJOR(chip->dev.devt), |
313d21ee JS |
290 | MINOR(chip->dev.devt), rc); |
291 | ||
72c91ce8 | 292 | cdev_del(&chip->cdev); |
313d21ee JS |
293 | return rc; |
294 | } | |
295 | ||
fdc915f7 JB |
296 | if (chip->flags & TPM_CHIP_FLAG_TPM2) |
297 | rc = cdev_add(&chip->cdevs, chip->devs.devt, 1); | |
298 | if (rc) { | |
299 | dev_err(&chip->dev, | |
300 | "unable to cdev_add() %s, major %d, minor %d, err=%d\n", | |
301 | dev_name(&chip->devs), MAJOR(chip->devs.devt), | |
302 | MINOR(chip->devs.devt), rc); | |
fdc915f7 JB |
303 | return rc; |
304 | } | |
305 | ||
306 | if (chip->flags & TPM_CHIP_FLAG_TPM2) | |
307 | rc = device_add(&chip->devs); | |
308 | if (rc) { | |
309 | dev_err(&chip->dev, | |
310 | "unable to device_register() %s, major %d, minor %d, err=%d\n", | |
311 | dev_name(&chip->devs), MAJOR(chip->devs.devt), | |
312 | MINOR(chip->devs.devt), rc); | |
313 | cdev_del(&chip->cdevs); | |
fdc915f7 JB |
314 | return rc; |
315 | } | |
316 | ||
15516788 SB |
317 | /* Make the chip available. */ |
318 | mutex_lock(&idr_lock); | |
319 | idr_replace(&dev_nums_idr, chip, chip->dev_num); | |
320 | mutex_unlock(&idr_lock); | |
321 | ||
313d21ee JS |
322 | return rc; |
323 | } | |
324 | ||
72c91ce8 | 325 | static void tpm_del_char_device(struct tpm_chip *chip) |
313d21ee JS |
326 | { |
327 | cdev_del(&chip->cdev); | |
15516788 SB |
328 | device_del(&chip->dev); |
329 | ||
330 | /* Make the chip unavailable. */ | |
331 | mutex_lock(&idr_lock); | |
332 | idr_replace(&dev_nums_idr, NULL, chip->dev_num); | |
333 | mutex_unlock(&idr_lock); | |
4e26195f JG |
334 | |
335 | /* Make the driver uncallable. */ | |
336 | down_write(&chip->ops_sem); | |
c0dff1f4 JS |
337 | if (chip->flags & TPM_CHIP_FLAG_TPM2) |
338 | tpm2_shutdown(chip, TPM2_SU_CLEAR); | |
4e26195f JG |
339 | chip->ops = NULL; |
340 | up_write(&chip->ops_sem); | |
313d21ee JS |
341 | } |
342 | ||
062807f2 JG |
343 | static void tpm_del_legacy_sysfs(struct tpm_chip *chip) |
344 | { | |
345 | struct attribute **i; | |
346 | ||
2f9f5377 | 347 | if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL)) |
062807f2 | 348 | return; |
34d47b63 | 349 | |
062807f2 JG |
350 | sysfs_remove_link(&chip->dev.parent->kobj, "ppi"); |
351 | ||
352 | for (i = chip->groups[0]->attrs; *i != NULL; ++i) | |
353 | sysfs_remove_link(&chip->dev.parent->kobj, (*i)->name); | |
34d47b63 JS |
354 | } |
355 | ||
062807f2 JG |
356 | /* For compatibility with legacy sysfs paths we provide symlinks from the |
357 | * parent dev directory to selected names within the tpm chip directory. Old | |
358 | * kernel versions created these files directly under the parent. | |
359 | */ | |
360 | static int tpm_add_legacy_sysfs(struct tpm_chip *chip) | |
361 | { | |
362 | struct attribute **i; | |
363 | int rc; | |
364 | ||
2f9f5377 | 365 | if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL)) |
062807f2 JG |
366 | return 0; |
367 | ||
368 | rc = __compat_only_sysfs_link_entry_to_kobj( | |
369 | &chip->dev.parent->kobj, &chip->dev.kobj, "ppi"); | |
370 | if (rc && rc != -ENOENT) | |
371 | return rc; | |
372 | ||
373 | /* All the names from tpm-sysfs */ | |
374 | for (i = chip->groups[0]->attrs; *i != NULL; ++i) { | |
375 | rc = __compat_only_sysfs_link_entry_to_kobj( | |
376 | &chip->dev.parent->kobj, &chip->dev.kobj, (*i)->name); | |
377 | if (rc) { | |
378 | tpm_del_legacy_sysfs(chip); | |
379 | return rc; | |
380 | } | |
381 | } | |
382 | ||
383 | return 0; | |
384 | } | |
afb5abc2 | 385 | /* |
313d21ee | 386 | * tpm_chip_register() - create a character device for the TPM chip |
afb5abc2 JS |
387 | * @chip: TPM chip to use. |
388 | * | |
d972b052 JS |
389 | * Creates a character device for the TPM chip and adds sysfs attributes for |
390 | * the device. As the last step this function adds the chip to the list of TPM | |
391 | * chips available for in-kernel use. | |
afb5abc2 | 392 | * |
d972b052 JS |
393 | * This function should be only called after the chip initialization is |
394 | * complete. | |
afb5abc2 JS |
395 | */ |
396 | int tpm_chip_register(struct tpm_chip *chip) | |
397 | { | |
398 | int rc; | |
399 | ||
cae8b441 JG |
400 | if (chip->ops->flags & TPM_OPS_AUTO_STARTUP) { |
401 | if (chip->flags & TPM_CHIP_FLAG_TPM2) | |
402 | rc = tpm2_auto_startup(chip); | |
403 | else | |
404 | rc = tpm1_auto_startup(chip); | |
405 | if (rc) | |
406 | return rc; | |
407 | } | |
408 | ||
7518a21a JS |
409 | tpm_sysfs_add_device(chip); |
410 | ||
411 | rc = tpm_bios_log_setup(chip); | |
0cf577a0 | 412 | if (rc != 0 && rc != -ENODEV) |
34d47b63 | 413 | return rc; |
afb5abc2 | 414 | |
9b774d5c JS |
415 | tpm_add_ppi(chip); |
416 | ||
72c91ce8 | 417 | rc = tpm_add_char_device(chip); |
062807f2 | 418 | if (rc) { |
7518a21a | 419 | tpm_bios_log_teardown(chip); |
062807f2 JG |
420 | return rc; |
421 | } | |
d972b052 | 422 | |
062807f2 JG |
423 | rc = tpm_add_legacy_sysfs(chip); |
424 | if (rc) { | |
425 | tpm_chip_unregister(chip); | |
426 | return rc; | |
d56e4f75 JS |
427 | } |
428 | ||
afb5abc2 | 429 | return 0; |
afb5abc2 JS |
430 | } |
431 | EXPORT_SYMBOL_GPL(tpm_chip_register); | |
432 | ||
433 | /* | |
434 | * tpm_chip_unregister() - release the TPM driver | |
435 | * @chip: TPM chip to use. | |
436 | * | |
437 | * Takes the chip first away from the list of available TPM chips and then | |
438 | * cleans up all the resources reserved by tpm_chip_register(). | |
439 | * | |
4e26195f JG |
440 | * Once this function returns the driver call backs in 'op's will not be |
441 | * running and will no longer start. | |
442 | * | |
afb5abc2 JS |
443 | * NOTE: This function should be only called before deinitializing chip |
444 | * resources. | |
445 | */ | |
446 | void tpm_chip_unregister(struct tpm_chip *chip) | |
447 | { | |
062807f2 | 448 | tpm_del_legacy_sysfs(chip); |
7518a21a | 449 | tpm_bios_log_teardown(chip); |
fdc915f7 JB |
450 | if (chip->flags & TPM_CHIP_FLAG_TPM2) { |
451 | cdev_del(&chip->cdevs); | |
452 | device_del(&chip->devs); | |
453 | } | |
72c91ce8 | 454 | tpm_del_char_device(chip); |
afb5abc2 JS |
455 | } |
456 | EXPORT_SYMBOL_GPL(tpm_chip_unregister); |