aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>2014-12-12 14:46:39 -0500
committerPeter Huewe <peterhuewe@gmx.de>2015-01-17 08:00:12 -0500
commit30fc8d138e9123f374a3c3867e7c7c5cd4004941 (patch)
treeba7d83a8271e9cc9fe57864c97cfcb28d5484423
parent7a1d7e6dd76a2070e2d86826391468edc33bb6d6 (diff)
tpm: TPM 2.0 CRB Interface
tpm_crb is a driver for TPM 2.0 Command Response Buffer (CRB) Interface as defined in PC Client Platform TPM Profile (PTP) Specification. Only polling and single locality is supported as these are the limitations of the available hardware, Platform Trust Techonlogy (PTT) in Haswell CPUs. The driver always applies CRB with ACPI start because PTT reports using only ACPI start as start method but as a result of my testing it requires also CRB start. Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> Reviewed-by: Jasob Gunthorpe <jason.gunthorpe@obsidianresearch.com> Reviewed-by: Stefan Berger <stefanb@linux.vnet.ibm.com> Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
-rw-r--r--drivers/char/tpm/Kconfig9
-rw-r--r--drivers/char/tpm/Makefile1
-rw-r--r--drivers/char/tpm/tpm-interface.c3
-rw-r--r--drivers/char/tpm/tpm_crb.c354
4 files changed, 367 insertions, 0 deletions
diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index 3d0873b1ab5b..9d4e37549eb2 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -122,4 +122,13 @@ config TCG_XEN
122 To compile this driver as a module, choose M here; the module 122 To compile this driver as a module, choose M here; the module
123 will be called xen-tpmfront. 123 will be called xen-tpmfront.
124 124
125config TCG_CRB
126 tristate "TPM 2.0 CRB Interface"
127 depends on X86 && ACPI
128 ---help---
129 If you have a TPM security chip that is compliant with the
130 TCG CRB 2.0 TPM specification say Yes and it will be accessible
131 from within Linux. To compile this driver as a module, choose
132 M here; the module will be called tpm_crb.
133
125endif # TCG_TPM 134endif # TCG_TPM
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 88848edb081c..990cf183931d 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
22obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o 22obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
23obj-$(CONFIG_TCG_TIS_I2C_ST33) += tpm_i2c_stm_st33.o 23obj-$(CONFIG_TCG_TIS_I2C_ST33) += tpm_i2c_stm_st33.o
24obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o 24obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o
25obj-$(CONFIG_TCG_CRB) += tpm_crb.o
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index 20cf94d31386..bf53a3771da5 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -901,6 +901,9 @@ int tpm_pm_suspend(struct device *dev)
901 if (chip == NULL) 901 if (chip == NULL)
902 return -ENODEV; 902 return -ENODEV;
903 903
904 if (chip->flags & TPM_CHIP_FLAG_TPM2)
905 return tpm2_shutdown(chip, TPM2_SU_CLEAR);
906
904 /* for buggy tpm, flush pcrs with extend to selected dummy */ 907 /* for buggy tpm, flush pcrs with extend to selected dummy */
905 if (tpm_suspend_pcr) { 908 if (tpm_suspend_pcr) {
906 cmd.header.in = pcrextend_header; 909 cmd.header.in = pcrextend_header;
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
new file mode 100644
index 000000000000..c248a356d5c9
--- /dev/null
+++ b/drivers/char/tpm/tpm_crb.c
@@ -0,0 +1,354 @@
1/*
2 * Copyright (C) 2014 Intel Corporation
3 *
4 * Authors:
5 * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
6 *
7 * Maintained by: <tpmdd-devel@lists.sourceforge.net>
8 *
9 * This device driver implements the TPM interface as defined in
10 * the TCG CRB 2.0 TPM specification.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; version 2
15 * of the License.
16 */
17
18#include <linux/acpi.h>
19#include <linux/highmem.h>
20#include <linux/rculist.h>
21#include <linux/module.h>
22#include <linux/platform_device.h>
23#include "tpm.h"
24
25#define ACPI_SIG_TPM2 "TPM2"
26
27static const u8 CRB_ACPI_START_UUID[] = {
28 /* 0000 */ 0xAB, 0x6C, 0xBF, 0x6B, 0x63, 0x54, 0x14, 0x47,
29 /* 0008 */ 0xB7, 0xCD, 0xF0, 0x20, 0x3C, 0x03, 0x68, 0xD4
30};
31
32enum crb_defaults {
33 CRB_ACPI_START_REVISION_ID = 1,
34 CRB_ACPI_START_INDEX = 1,
35};
36
37enum crb_start_method {
38 CRB_SM_ACPI_START = 2,
39 CRB_SM_CRB = 7,
40 CRB_SM_CRB_WITH_ACPI_START = 8,
41};
42
43struct acpi_tpm2 {
44 struct acpi_table_header hdr;
45 u16 platform_class;
46 u16 reserved;
47 u64 control_area_pa;
48 u32 start_method;
49} __packed;
50
51enum crb_ca_request {
52 CRB_CA_REQ_GO_IDLE = BIT(0),
53 CRB_CA_REQ_CMD_READY = BIT(1),
54};
55
56enum crb_ca_status {
57 CRB_CA_STS_ERROR = BIT(0),
58 CRB_CA_STS_TPM_IDLE = BIT(1),
59};
60
61enum crb_start {
62 CRB_START_INVOKE = BIT(0),
63};
64
65enum crb_cancel {
66 CRB_CANCEL_INVOKE = BIT(0),
67};
68
69struct crb_control_area {
70 u32 req;
71 u32 sts;
72 u32 cancel;
73 u32 start;
74 u32 int_enable;
75 u32 int_sts;
76 u32 cmd_size;
77 u64 cmd_pa;
78 u32 rsp_size;
79 u64 rsp_pa;
80} __packed;
81
82enum crb_status {
83 CRB_STS_COMPLETE = BIT(0),
84};
85
86enum crb_flags {
87 CRB_FL_ACPI_START = BIT(0),
88 CRB_FL_CRB_START = BIT(1),
89};
90
91struct crb_priv {
92 unsigned int flags;
93 struct crb_control_area __iomem *cca;
94 u8 __iomem *cmd;
95 u8 __iomem *rsp;
96};
97
98#ifdef CONFIG_PM_SLEEP
99static int crb_resume(struct device *dev)
100{
101 int rc;
102 struct tpm_chip *chip = dev_get_drvdata(dev);
103
104 rc = tpm2_shutdown(chip, TPM2_SU_STATE);
105 if (!rc)
106 rc = tpm2_do_selftest(chip);
107
108 return rc;
109}
110
111static SIMPLE_DEV_PM_OPS(crb_pm, tpm_pm_suspend, crb_resume);
112#endif
113
114static u8 crb_status(struct tpm_chip *chip)
115{
116 struct crb_priv *priv = chip->vendor.priv;
117 u8 sts = 0;
118
119 if ((le32_to_cpu(ioread32(&priv->cca->start)) & CRB_START_INVOKE) !=
120 CRB_START_INVOKE)
121 sts |= CRB_STS_COMPLETE;
122
123 return sts;
124}
125
126static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count)
127{
128 struct crb_priv *priv = chip->vendor.priv;
129 unsigned int expected;
130
131 /* sanity check */
132 if (count < 6)
133 return -EIO;
134
135 if (le32_to_cpu(ioread32(&priv->cca->sts)) & CRB_CA_STS_ERROR)
136 return -EIO;
137
138 memcpy_fromio(buf, priv->rsp, 6);
139 expected = be32_to_cpup((__be32 *) &buf[2]);
140
141 if (expected > count)
142 return -EIO;
143
144 memcpy_fromio(&buf[6], &priv->rsp[6], expected - 6);
145
146 return expected;
147}
148
149static int crb_do_acpi_start(struct tpm_chip *chip)
150{
151 union acpi_object *obj;
152 int rc;
153
154 obj = acpi_evaluate_dsm(chip->acpi_dev_handle,
155 CRB_ACPI_START_UUID,
156 CRB_ACPI_START_REVISION_ID,
157 CRB_ACPI_START_INDEX,
158 NULL);
159 if (!obj)
160 return -ENXIO;
161 rc = obj->integer.value == 0 ? 0 : -ENXIO;
162 ACPI_FREE(obj);
163 return rc;
164}
165
166static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
167{
168 struct crb_priv *priv = chip->vendor.priv;
169 int rc = 0;
170
171 if (len > le32_to_cpu(ioread32(&priv->cca->cmd_size))) {
172 dev_err(&chip->dev,
173 "invalid command count value %x %zx\n",
174 (unsigned int) len,
175 (size_t) le32_to_cpu(ioread32(&priv->cca->cmd_size)));
176 return -E2BIG;
177 }
178
179 memcpy_toio(priv->cmd, buf, len);
180
181 /* Make sure that cmd is populated before issuing start. */
182 wmb();
183
184 if (priv->flags & CRB_FL_CRB_START)
185 iowrite32(cpu_to_le32(CRB_START_INVOKE), &priv->cca->start);
186
187 if (priv->flags & CRB_FL_ACPI_START)
188 rc = crb_do_acpi_start(chip);
189
190 return rc;
191}
192
193static void crb_cancel(struct tpm_chip *chip)
194{
195 struct crb_priv *priv = chip->vendor.priv;
196
197 iowrite32(cpu_to_le32(CRB_CANCEL_INVOKE), &priv->cca->cancel);
198
199 /* Make sure that cmd is populated before issuing cancel. */
200 wmb();
201
202 if ((priv->flags & CRB_FL_ACPI_START) && crb_do_acpi_start(chip))
203 dev_err(&chip->dev, "ACPI Start failed\n");
204
205 iowrite32(0, &priv->cca->cancel);
206}
207
208static bool crb_req_canceled(struct tpm_chip *chip, u8 status)
209{
210 struct crb_priv *priv = chip->vendor.priv;
211 u32 cancel = le32_to_cpu(ioread32(&priv->cca->cancel));
212
213 return (cancel & CRB_CANCEL_INVOKE) == CRB_CANCEL_INVOKE;
214}
215
216static const struct tpm_class_ops tpm_crb = {
217 .status = crb_status,
218 .recv = crb_recv,
219 .send = crb_send,
220 .cancel = crb_cancel,
221 .req_canceled = crb_req_canceled,
222 .req_complete_mask = CRB_STS_COMPLETE,
223 .req_complete_val = CRB_STS_COMPLETE,
224};
225
226static int crb_acpi_add(struct acpi_device *device)
227{
228 struct tpm_chip *chip;
229 struct acpi_tpm2 *buf;
230 struct crb_priv *priv;
231 struct device *dev = &device->dev;
232 acpi_status status;
233 u32 sm;
234 u64 pa;
235 int rc;
236
237 chip = tpmm_chip_alloc(dev, &tpm_crb);
238 if (IS_ERR(chip))
239 return PTR_ERR(chip);
240
241 chip->flags = TPM_CHIP_FLAG_TPM2;
242
243 status = acpi_get_table(ACPI_SIG_TPM2, 1,
244 (struct acpi_table_header **) &buf);
245 if (ACPI_FAILURE(status)) {
246 dev_err(dev, "failed to get TPM2 ACPI table\n");
247 return -ENODEV;
248 }
249
250 if (buf->hdr.length < sizeof(struct acpi_tpm2)) {
251 dev_err(dev, "TPM2 ACPI table has wrong size");
252 return -EINVAL;
253 }
254
255 priv = (struct crb_priv *) devm_kzalloc(dev, sizeof(struct crb_priv),
256 GFP_KERNEL);
257 if (!priv) {
258 dev_err(dev, "failed to devm_kzalloc for private data\n");
259 return -ENOMEM;
260 }
261
262 sm = le32_to_cpu(buf->start_method);
263
264 /* The reason for the extra quirk is that the PTT in 4th Gen Core CPUs
265 * report only ACPI start but in practice seems to require both
266 * ACPI start and CRB start.
267 */
268 if (sm == CRB_SM_CRB || sm == CRB_SM_CRB_WITH_ACPI_START ||
269 !strcmp(acpi_device_hid(device), "MSFT0101"))
270 priv->flags |= CRB_FL_CRB_START;
271
272 if (sm == CRB_SM_ACPI_START || sm == CRB_SM_CRB_WITH_ACPI_START)
273 priv->flags |= CRB_FL_ACPI_START;
274
275 priv->cca = (struct crb_control_area __iomem *)
276 devm_ioremap_nocache(dev, buf->control_area_pa, 0x1000);
277 if (!priv->cca) {
278 dev_err(dev, "ioremap of the control area failed\n");
279 return -ENOMEM;
280 }
281
282 memcpy_fromio(&pa, &priv->cca->cmd_pa, 8);
283 pa = le64_to_cpu(pa);
284 priv->cmd = devm_ioremap_nocache(dev, le64_to_cpu(pa),
285 ioread32(&priv->cca->cmd_size));
286 if (!priv->cmd) {
287 dev_err(dev, "ioremap of the command buffer failed\n");
288 return -ENOMEM;
289 }
290
291 memcpy_fromio(&pa, &priv->cca->rsp_pa, 8);
292 pa = le64_to_cpu(pa);
293 priv->rsp = devm_ioremap_nocache(dev, le64_to_cpu(pa),
294 ioread32(&priv->cca->rsp_size));
295 if (!priv->rsp) {
296 dev_err(dev, "ioremap of the response buffer failed\n");
297 return -ENOMEM;
298 }
299
300 chip->vendor.priv = priv;
301
302 /* Default timeouts and durations */
303 chip->vendor.timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
304 chip->vendor.timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B);
305 chip->vendor.timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C);
306 chip->vendor.timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D);
307 chip->vendor.duration[TPM_SHORT] =
308 msecs_to_jiffies(TPM2_DURATION_SHORT);
309 chip->vendor.duration[TPM_MEDIUM] =
310 msecs_to_jiffies(TPM2_DURATION_MEDIUM);
311 chip->vendor.duration[TPM_LONG] =
312 msecs_to_jiffies(TPM2_DURATION_LONG);
313
314 chip->acpi_dev_handle = device->handle;
315
316 rc = tpm2_do_selftest(chip);
317 if (rc)
318 return rc;
319
320 return tpm_chip_register(chip);
321}
322
323static int crb_acpi_remove(struct acpi_device *device)
324{
325 struct device *dev = &device->dev;
326 struct tpm_chip *chip = dev_get_drvdata(dev);
327
328 tpm_chip_unregister(chip);
329 return 0;
330}
331
332static struct acpi_device_id crb_device_ids[] = {
333 {"MSFT0101", 0},
334 {"", 0},
335};
336MODULE_DEVICE_TABLE(acpi, crb_device_ids);
337
338static struct acpi_driver crb_acpi_driver = {
339 .name = "tpm_crb",
340 .ids = crb_device_ids,
341 .ops = {
342 .add = crb_acpi_add,
343 .remove = crb_acpi_remove,
344 },
345 .drv = {
346 .pm = &crb_pm,
347 },
348};
349
350module_acpi_driver(crb_acpi_driver);
351MODULE_AUTHOR("Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>");
352MODULE_DESCRIPTION("TPM2 Driver");
353MODULE_VERSION("0.1");
354MODULE_LICENSE("GPL");