Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
1
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
bcfff83
Documentation
LICENSES
arch
block
certs
crypto
drivers
accessibility
acpi
amba
android
ata
atm
auxdisplay
base
bcma
block
bluetooth
bus
cdrom
char
agp
hw_random
ipmi
mwave
pcmcia
tpm
eventlog
st33zp24
Kconfig
Makefile
tpm-chip.c
tpm-dev-common.c
tpm-dev.c
tpm-dev.h
tpm-interface.c
tpm-sysfs.c
tpm.h
tpm1-cmd.c
tpm2-cmd.c
tpm2-space.c
tpm_atmel.c
tpm_atmel.h
tpm_crb.c
tpm_i2c_atmel.c
tpm_i2c_infineon.c
tpm_i2c_nuvoton.c
tpm_ibmvtpm.c
tpm_ibmvtpm.h
tpm_infineon.c
tpm_nsc.c
tpm_ppi.c
tpm_tis.c
tpm_tis_core.c
tpm_tis_core.h
tpm_tis_spi.c
tpm_vtpm_proxy.c
tpmrm-dev.c
xen-tpmfront.c
xilinx_hwicap
xillybus
Kconfig
Makefile
adi.c
apm-emulation.c
applicom.c
applicom.h
bsr.c
ds1620.c
dsp56k.c
dtlk.c
efirtc.c
generic_nvram.c
hangcheck-timer.c
hpet.c
lp.c
mbcs.c
mbcs.h
mem.c
misc.c
mspec.c
nsc_gpio.c
nvram.c
nwbutton.c
nwbutton.h
nwflash.c
pc8736x_gpio.c
powernv-op-panel.c
ppdev.c
ps3flash.c
random.c
raw.c
rtc.c
scx200_gpio.c
snsc.c
snsc.h
snsc_event.c
sonypi.c
tb0219.c
tlclk.c
toshiba.c
ttyprintk.c
uv_mmtimer.c
virtio_console.c
clk
clocksource
connector
cpufreq
cpuidle
crypto
dax
dca
devfreq
dio
dma-buf
dma
edac
eisa
extcon
firewire
firmware
fmc
fpga
fsi
gnss
gpio
gpu
hid
hsi
hv
hwmon
hwspinlock
hwtracing
i2c
i3c
ide
idle
iio
infiniband
input
iommu
ipack
irqchip
isdn
leds
lightnvm
macintosh
mailbox
mcb
md
media
memory
memstick
message
mfd
misc
mmc
mtd
mux
net
nfc
ntb
nubus
nvdimm
nvme
nvmem
of
opp
oprofile
parisc
parport
pci
pcmcia
perf
phy
pinctrl
platform
pnp
power
powercap
pps
ps3
ptp
pwm
rapidio
ras
regulator
remoteproc
reset
rpmsg
rtc
s390
sbus
scsi
sfi
sh
siox
slimbus
sn
soc
soundwire
spi
spmi
ssb
staging
target
tc
tee
thermal
thunderbolt
tty
uio
usb
uwb
vfio
vhost
video
virt
virtio
visorbus
vlynq
vme
w1
watchdog
xen
zorro
Kconfig
Makefile
firmware
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
drivers
/
char
/
tpm
/
tpm-interface.c
Copy path
Blame
Blame
Latest commit
History
History
584 lines (500 loc) · 14 KB
Breadcrumbs
linux
/
drivers
/
char
/
tpm
/
tpm-interface.c
Top
File metadata and controls
Code
Blame
584 lines (500 loc) · 14 KB
Raw
/* * Copyright (C) 2004 IBM Corporation * Copyright (C) 2014 Intel Corporation * * Authors: * Leendert van Doorn <leendert@watson.ibm.com> * Dave Safford <safford@watson.ibm.com> * Reiner Sailer <sailer@watson.ibm.com> * Kylene Hall <kjhall@us.ibm.com> * * Maintained by: <tpmdd-devel@lists.sourceforge.net> * * Device driver for TCG/TCPA TPM (trusted platform module). * Specifications at www.trustedcomputinggroup.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 2 of the * License. * * Note, the TPM chip is not interrupt driven (only polling) * and can have very long timeouts (minutes!). Hence the unusual * calls to msleep. * */ #include <linux/poll.h> #include <linux/slab.h> #include <linux/mutex.h> #include <linux/spinlock.h> #include <linux/freezer.h> #include <linux/tpm_eventlog.h> #include "tpm.h" /* * Bug workaround - some TPM's don't flush the most * recently changed pcr on suspend, so force the flush * with an extend to the selected _unused_ non-volatile pcr. */ static u32 tpm_suspend_pcr; module_param_named(suspend_pcr, tpm_suspend_pcr, uint, 0644); MODULE_PARM_DESC(suspend_pcr, "PCR to use for dummy writes to facilitate flush on suspend."); /** * tpm_calc_ordinal_duration() - calculate the maximum command duration * @chip: TPM chip to use. * @ordinal: TPM command ordinal. * * The function returns the maximum amount of time the chip could take * to return the result for a particular ordinal in jiffies. * * Return: A maximal duration time for an ordinal in jiffies. */ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) { if (chip->flags & TPM_CHIP_FLAG_TPM2) return tpm2_calc_ordinal_duration(chip, ordinal); else return tpm1_calc_ordinal_duration(chip, ordinal); } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) { struct tpm_header *header = buf; int rc; ssize_t len = 0; u32 count, ordinal; unsigned long stop; if (bufsiz < TPM_HEADER_SIZE) return -EINVAL; if (bufsiz > TPM_BUFSIZE) bufsiz = TPM_BUFSIZE; count = be32_to_cpu(header->length); ordinal = be32_to_cpu(header->ordinal); if (count == 0) return -ENODATA; if (count > bufsiz) { dev_err(&chip->dev, "invalid count value %x %zx\n", count, bufsiz); return -E2BIG; } rc = chip->ops->send(chip, buf, count); if (rc < 0) { if (rc != -EPIPE) dev_err(&chip->dev, "%s: send(): error %d\n", __func__, rc); return rc; } /* A sanity check. send() should just return zero on success e.g. * not the command length. */ if (rc > 0) { dev_warn(&chip->dev, "%s: send(): invalid value %d\n", __func__, rc); rc = 0; } if (chip->flags & TPM_CHIP_FLAG_IRQ) goto out_recv; stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); do { u8 status = chip->ops->status(chip); if ((status & chip->ops->req_complete_mask) == chip->ops->req_complete_val) goto out_recv; if (chip->ops->req_canceled(chip, status)) { dev_err(&chip->dev, "Operation Canceled\n"); return -ECANCELED; } tpm_msleep(TPM_TIMEOUT_POLL); rmb(); } while (time_before(jiffies, stop)); chip->ops->cancel(chip); dev_err(&chip->dev, "Operation Timed out\n"); return -ETIME; out_recv: len = chip->ops->recv(chip, buf, bufsiz); if (len < 0) { rc = len; dev_err(&chip->dev, "tpm_transmit: tpm_recv: error %d\n", rc); } else if (len < TPM_HEADER_SIZE || len != be32_to_cpu(header->length)) rc = -EFAULT; return rc ? rc : len; } /** * tpm_transmit - Internal kernel interface to transmit TPM commands. * @chip: a TPM chip to use * @buf: a TPM command buffer * @bufsiz: length of the TPM command buffer * * A wrapper around tpm_try_transmit() that handles TPM2_RC_RETRY returns from * the TPM and retransmits the command after a delay up to a maximum wait of * TPM2_DURATION_LONG. * * Note that TPM 1.x never returns TPM2_RC_RETRY so the retry logic is TPM 2.0 * only. * * Return: * * The response length - OK * * -errno - A system error */ ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz) { struct tpm_header *header = (struct tpm_header *)buf; /* space for header and handles */ u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)]; unsigned int delay_msec = TPM2_DURATION_SHORT; u32 rc = 0; ssize_t ret; const size_t save_size = min(sizeof(save), bufsiz); /* the command code is where the return code will be */ u32 cc = be32_to_cpu(header->return_code); /* * Subtlety here: if we have a space, the handles will be * transformed, so when we restore the header we also have to * restore the handles. */ memcpy(save, buf, save_size); for (;;) { ret = tpm_try_transmit(chip, buf, bufsiz); if (ret < 0) break; rc = be32_to_cpu(header->return_code); if (rc != TPM2_RC_RETRY && rc != TPM2_RC_TESTING) break; /* * return immediately if self test returns test * still running to shorten boot time. */ if (rc == TPM2_RC_TESTING && cc == TPM2_CC_SELF_TEST) break; if (delay_msec > TPM2_DURATION_LONG) { if (rc == TPM2_RC_RETRY) dev_err(&chip->dev, "in retry loop\n"); else dev_err(&chip->dev, "self test is still running\n"); break; } tpm_msleep(delay_msec); delay_msec *= 2; memcpy(buf, save, save_size); } return ret; } /** * tpm_transmit_cmd - send a tpm command to the device * @chip: a TPM chip to use * @buf: a TPM command buffer * @min_rsp_body_length: minimum expected length of response body * @desc: command description used in the error message * * Return: * * 0 - OK * * -errno - A system error * * TPM_RC - A TPM error */ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf, size_t min_rsp_body_length, const char *desc) { const struct tpm_header *header = (struct tpm_header *)buf->data; int err; ssize_t len; len = tpm_transmit(chip, buf->data, PAGE_SIZE); if (len < 0) return len; err = be32_to_cpu(header->return_code); if (err != 0 && err != TPM_ERR_DISABLED && err != TPM_ERR_DEACTIVATED && err != TPM2_RC_TESTING && desc) dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err, desc); if (err) return err; if (len < min_rsp_body_length + TPM_HEADER_SIZE) return -EFAULT; return 0; } EXPORT_SYMBOL_GPL(tpm_transmit_cmd); int tpm_get_timeouts(struct tpm_chip *chip) { if (chip->flags & TPM_CHIP_FLAG_HAVE_TIMEOUTS) return 0; if (chip->flags & TPM_CHIP_FLAG_TPM2) return tpm2_get_timeouts(chip); else return tpm1_get_timeouts(chip); } EXPORT_SYMBOL_GPL(tpm_get_timeouts); /** * tpm_is_tpm2 - do we a have a TPM2 chip? * @chip: a &struct tpm_chip instance, %NULL for the default chip * * Return: * 1 if we have a TPM2 chip. * 0 if we don't have a TPM2 chip. * A negative number for system errors (errno). */ int tpm_is_tpm2(struct tpm_chip *chip) { int rc; chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0; tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_is_tpm2); /** * tpm_pcr_read - read a PCR value from SHA1 bank * @chip: a &struct tpm_chip instance, %NULL for the default chip * @pcr_idx: the PCR to be retrieved * @res_buf: the value of the PCR * * Return: same as with tpm_transmit_cmd() */ int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf) { int rc; chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; if (chip->flags & TPM_CHIP_FLAG_TPM2) rc = tpm2_pcr_read(chip, pcr_idx, res_buf); else rc = tpm1_pcr_read(chip, pcr_idx, res_buf); tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_pcr_read); /** * tpm_pcr_extend - extend a PCR value in SHA1 bank. * @chip: a &struct tpm_chip instance, %NULL for the default chip * @pcr_idx: the PCR to be retrieved * @hash: the hash value used to extend the PCR value * * Note: with TPM 2.0 extends also those banks with a known digest size to the * cryto subsystem in order to prevent malicious use of those PCR banks. In the * future we should dynamically determine digest sizes. * * Return: same as with tpm_transmit_cmd() */ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash) { int rc; struct tpm2_digest *digest_list; int i; chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; if (chip->flags & TPM_CHIP_FLAG_TPM2) { digest_list = kcalloc(chip->nr_allocated_banks, sizeof(*digest_list), GFP_KERNEL); if (!digest_list) return -ENOMEM; for (i = 0; i < chip->nr_allocated_banks; i++) { digest_list[i].alg_id = chip->allocated_banks[i]; memcpy(digest_list[i].digest, hash, TPM_DIGEST_SIZE); } rc = tpm2_pcr_extend(chip, pcr_idx, chip->nr_allocated_banks, digest_list); kfree(digest_list); tpm_put_ops(chip); return rc; } rc = tpm1_pcr_extend(chip, pcr_idx, hash, "attempting extend a PCR value"); tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_pcr_extend); /** * tpm_send - send a TPM command * @chip: a &struct tpm_chip instance, %NULL for the default chip * @cmd: a TPM command buffer * @buflen: the length of the TPM command buffer * * Return: same as with tpm_transmit_cmd() */ int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen) { struct tpm_buf buf; int rc; chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; rc = tpm_buf_init(&buf, 0, 0); if (rc) goto out; memcpy(buf.data, cmd, buflen); rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to a send a command"); tpm_buf_destroy(&buf); out: tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_send); int tpm_auto_startup(struct tpm_chip *chip) { int rc; if (!(chip->ops->flags & TPM_OPS_AUTO_STARTUP)) return 0; if (chip->flags & TPM_CHIP_FLAG_TPM2) rc = tpm2_auto_startup(chip); else rc = tpm1_auto_startup(chip); return rc; } /* * We are about to suspend. Save the TPM state * so that it can be restored. */ int tpm_pm_suspend(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); int rc = 0; if (!chip) return -ENODEV; if (chip->flags & TPM_CHIP_FLAG_ALWAYS_POWERED) return 0; if (chip->flags & TPM_CHIP_FLAG_TPM2) { mutex_lock(&chip->tpm_mutex); if (!tpm_chip_start(chip)) { tpm2_shutdown(chip, TPM2_SU_STATE); tpm_chip_stop(chip); } mutex_unlock(&chip->tpm_mutex); } else { rc = tpm1_pm_suspend(chip, tpm_suspend_pcr); } return rc; } EXPORT_SYMBOL_GPL(tpm_pm_suspend); /* * Resume from a power safe. The BIOS already restored * the TPM state. */ int tpm_pm_resume(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); if (chip == NULL) return -ENODEV; return 0; } EXPORT_SYMBOL_GPL(tpm_pm_resume); /** * tpm_get_random() - get random bytes from the TPM's RNG * @chip: a &struct tpm_chip instance, %NULL for the default chip * @out: destination buffer for the random bytes * @max: the max number of bytes to write to @out * * Return: number of random bytes read or a negative error value. */ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max) { int rc; if (!out || max > TPM_MAX_RNG_DATA) return -EINVAL; chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; if (chip->flags & TPM_CHIP_FLAG_TPM2) rc = tpm2_get_random(chip, out, max); else rc = tpm1_get_random(chip, out, max); tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_get_random); /** * tpm_seal_trusted() - seal a trusted key payload * @chip: a &struct tpm_chip instance, %NULL for the default chip * @options: authentication values and other options * @payload: the key data in clear and encrypted form * * Note: only TPM 2.0 chip are supported. TPM 1.x implementation is located in * the keyring subsystem. * * Return: same as with tpm_transmit_cmd() */ int tpm_seal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, struct trusted_key_options *options) { int rc; chip = tpm_find_get_ops(chip); if (!chip || !(chip->flags & TPM_CHIP_FLAG_TPM2)) return -ENODEV; rc = tpm2_seal_trusted(chip, payload, options); tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_seal_trusted); /** * tpm_unseal_trusted() - unseal a trusted key * @chip: a &struct tpm_chip instance, %NULL for the default chip * @options: authentication values and other options * @payload: the key data in clear and encrypted form * * Note: only TPM 2.0 chip are supported. TPM 1.x implementation is located in * the keyring subsystem. * * Return: same as with tpm_transmit_cmd() */ int tpm_unseal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, struct trusted_key_options *options) { int rc; chip = tpm_find_get_ops(chip); if (!chip || !(chip->flags & TPM_CHIP_FLAG_TPM2)) return -ENODEV; rc = tpm2_unseal_trusted(chip, payload, options); tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_unseal_trusted); static int __init tpm_init(void) { int rc; tpm_class = class_create(THIS_MODULE, "tpm"); if (IS_ERR(tpm_class)) { pr_err("couldn't create tpm class\n"); return PTR_ERR(tpm_class); } tpmrm_class = class_create(THIS_MODULE, "tpmrm"); if (IS_ERR(tpmrm_class)) { pr_err("couldn't create tpmrm class\n"); rc = PTR_ERR(tpmrm_class); goto out_destroy_tpm_class; } rc = alloc_chrdev_region(&tpm_devt, 0, 2*TPM_NUM_DEVICES, "tpm"); if (rc < 0) { pr_err("tpm: failed to allocate char dev region\n"); goto out_destroy_tpmrm_class; } rc = tpm_dev_common_init(); if (rc) { pr_err("tpm: failed to allocate char dev region\n"); goto out_unreg_chrdev; } return 0; out_unreg_chrdev: unregister_chrdev_region(tpm_devt, 2 * TPM_NUM_DEVICES); out_destroy_tpmrm_class: class_destroy(tpmrm_class); out_destroy_tpm_class: class_destroy(tpm_class); return rc; } static void __exit tpm_exit(void) { idr_destroy(&dev_nums_idr); class_destroy(tpm_class); class_destroy(tpmrm_class); unregister_chrdev_region(tpm_devt, 2*TPM_NUM_DEVICES); tpm_dev_common_exit(); } subsys_initcall(tpm_init); module_exit(tpm_exit); MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)"); MODULE_DESCRIPTION("TPM Driver"); MODULE_VERSION("2.0"); MODULE_LICENSE("GPL");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
You can’t perform that action at this time.