]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/3.16.3/tpm-provide-a-generic-means-to-override-the-chip-returned-timeouts.patch
4.9-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 3.16.3 / tpm-provide-a-generic-means-to-override-the-chip-returned-timeouts.patch
1 From 8e54caf407b98efa05409e1fee0e5381abd2b088 Mon Sep 17 00:00:00 2001
2 From: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
3 Date: Wed, 21 May 2014 18:26:44 -0600
4 Subject: tpm: Provide a generic means to override the chip returned timeouts
5
6 From: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
7
8 commit 8e54caf407b98efa05409e1fee0e5381abd2b088 upstream.
9
10 Some Atmel TPMs provide completely wrong timeouts from their
11 TPM_CAP_PROP_TIS_TIMEOUT query. This patch detects that and returns
12 new correct values via a DID/VID table in the TIS driver.
13
14 Tested on ARM using an AT97SC3204T FW version 37.16
15
16 [PHuewe: without this fix these 'broken' Atmel TPMs won't function on
17 older kernels]
18 Signed-off-by: "Berg, Christopher" <Christopher.Berg@atmel.com>
19 Signed-off-by: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
20 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
21
22 Signed-off-by: Peter Huewe <peterhuewe@gmx.de>
23
24 ---
25 drivers/char/tpm/tpm-interface.c | 62 +++++++++++++++++++++++++--------------
26 drivers/char/tpm/tpm_tis.c | 31 +++++++++++++++++++
27 include/linux/tpm.h | 3 +
28 3 files changed, 75 insertions(+), 21 deletions(-)
29
30 --- a/drivers/char/tpm/tpm-interface.c
31 +++ b/drivers/char/tpm/tpm-interface.c
32 @@ -491,11 +491,10 @@ static int tpm_startup(struct tpm_chip *
33 int tpm_get_timeouts(struct tpm_chip *chip)
34 {
35 struct tpm_cmd_t tpm_cmd;
36 - struct timeout_t *timeout_cap;
37 + unsigned long new_timeout[4];
38 + unsigned long old_timeout[4];
39 struct duration_t *duration_cap;
40 ssize_t rc;
41 - u32 timeout;
42 - unsigned int scale = 1;
43
44 tpm_cmd.header.in = tpm_getcap_header;
45 tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
46 @@ -529,25 +528,46 @@ int tpm_get_timeouts(struct tpm_chip *ch
47 != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
48 return -EINVAL;
49
50 - timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
51 - /* Don't overwrite default if value is 0 */
52 - timeout = be32_to_cpu(timeout_cap->a);
53 - if (timeout && timeout < 1000) {
54 - /* timeouts in msec rather usec */
55 - scale = 1000;
56 - chip->vendor.timeout_adjusted = true;
57 + old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
58 + old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
59 + old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
60 + old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
61 + memcpy(new_timeout, old_timeout, sizeof(new_timeout));
62 +
63 + /*
64 + * Provide ability for vendor overrides of timeout values in case
65 + * of misreporting.
66 + */
67 + if (chip->ops->update_timeouts != NULL)
68 + chip->vendor.timeout_adjusted =
69 + chip->ops->update_timeouts(chip, new_timeout);
70 +
71 + if (!chip->vendor.timeout_adjusted) {
72 + /* Don't overwrite default if value is 0 */
73 + if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
74 + int i;
75 +
76 + /* timeouts in msec rather usec */
77 + for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
78 + new_timeout[i] *= 1000;
79 + chip->vendor.timeout_adjusted = true;
80 + }
81 }
82 - if (timeout)
83 - chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale);
84 - timeout = be32_to_cpu(timeout_cap->b);
85 - if (timeout)
86 - chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale);
87 - timeout = be32_to_cpu(timeout_cap->c);
88 - if (timeout)
89 - chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale);
90 - timeout = be32_to_cpu(timeout_cap->d);
91 - if (timeout)
92 - chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale);
93 +
94 + /* Report adjusted timeouts */
95 + if (chip->vendor.timeout_adjusted) {
96 + dev_info(chip->dev,
97 + HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
98 + old_timeout[0], new_timeout[0],
99 + old_timeout[1], new_timeout[1],
100 + old_timeout[2], new_timeout[2],
101 + old_timeout[3], new_timeout[3]);
102 + }
103 +
104 + chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
105 + chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
106 + chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
107 + chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
108
109 duration:
110 tpm_cmd.header.in = tpm_getcap_header;
111 --- a/drivers/char/tpm/tpm_tis.c
112 +++ b/drivers/char/tpm/tpm_tis.c
113 @@ -373,6 +373,36 @@ out_err:
114 return rc;
115 }
116
117 +struct tis_vendor_timeout_override {
118 + u32 did_vid;
119 + unsigned long timeout_us[4];
120 +};
121 +
122 +static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
123 + /* Atmel 3204 */
124 + { 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
125 + (TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
126 +};
127 +
128 +static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
129 + unsigned long *timeout_cap)
130 +{
131 + int i;
132 + u32 did_vid;
133 +
134 + did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
135 +
136 + for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
137 + if (vendor_timeout_overrides[i].did_vid != did_vid)
138 + continue;
139 + memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
140 + sizeof(vendor_timeout_overrides[i].timeout_us));
141 + return true;
142 + }
143 +
144 + return false;
145 +}
146 +
147 /*
148 * Early probing for iTPM with STS_DATA_EXPECT flaw.
149 * Try sending command without itpm flag set and if that
150 @@ -437,6 +467,7 @@ static const struct tpm_class_ops tpm_ti
151 .recv = tpm_tis_recv,
152 .send = tpm_tis_send,
153 .cancel = tpm_tis_ready,
154 + .update_timeouts = tpm_tis_update_timeouts,
155 .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
156 .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
157 .req_canceled = tpm_tis_req_canceled,
158 --- a/include/linux/tpm.h
159 +++ b/include/linux/tpm.h
160 @@ -39,6 +39,9 @@ struct tpm_class_ops {
161 int (*send) (struct tpm_chip *chip, u8 *buf, size_t len);
162 void (*cancel) (struct tpm_chip *chip);
163 u8 (*status) (struct tpm_chip *chip);
164 + bool (*update_timeouts)(struct tpm_chip *chip,
165 + unsigned long *timeout_cap);
166 +
167 };
168
169 #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)