Commit 0f7e814c authored by Jason Gunthorpe's avatar Jason Gunthorpe Committed by Ben Hutchings

tpm: Provide a generic means to override the chip returned timeouts

commit 8e54caf4 upstream.

Some Atmel TPMs provide completely wrong timeouts from their
TPM_CAP_PROP_TIS_TIMEOUT query. This patch detects that and returns
new correct values via a DID/VID table in the TIS driver.

Tested on ARM using an AT97SC3204T FW version 37.16

[PHuewe: without this fix these 'broken' Atmel TPMs won't function on
older kernels]
Signed-off-by: default avatar"Berg, Christopher" <Christopher.Berg@atmel.com>
Signed-off-by: default avatarJason Gunthorpe <jgunthorpe@obsidianresearch.com>
Signed-off-by: default avatarPeter Huewe <peterhuewe@gmx.de>
[bwh: Backported to 3.2:
 - Adjust filename, context
 - s/chip->ops->/chip->vendor./]
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 3f3067bb
...@@ -533,11 +533,10 @@ EXPORT_SYMBOL_GPL(tpm_gen_interrupt); ...@@ -533,11 +533,10 @@ EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
void tpm_get_timeouts(struct tpm_chip *chip) void tpm_get_timeouts(struct tpm_chip *chip)
{ {
struct tpm_cmd_t tpm_cmd; struct tpm_cmd_t tpm_cmd;
struct timeout_t *timeout_cap; unsigned long new_timeout[4];
unsigned long old_timeout[4];
struct duration_t *duration_cap; struct duration_t *duration_cap;
ssize_t rc; ssize_t rc;
u32 timeout;
unsigned int scale = 1;
tpm_cmd.header.in = tpm_getcap_header; tpm_cmd.header.in = tpm_getcap_header;
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
...@@ -554,25 +553,46 @@ void tpm_get_timeouts(struct tpm_chip *chip) ...@@ -554,25 +553,46 @@ void tpm_get_timeouts(struct tpm_chip *chip)
!= sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32)) != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32))
return; return;
timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout; old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
/* Don't overwrite default if value is 0 */ old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
timeout = be32_to_cpu(timeout_cap->a); old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
if (timeout && timeout < 1000) { old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
/* timeouts in msec rather usec */ memcpy(new_timeout, old_timeout, sizeof(new_timeout));
scale = 1000;
chip->vendor.timeout_adjusted = true; /*
* Provide ability for vendor overrides of timeout values in case
* of misreporting.
*/
if (chip->vendor.update_timeouts != NULL)
chip->vendor.timeout_adjusted =
chip->vendor.update_timeouts(chip, new_timeout);
if (!chip->vendor.timeout_adjusted) {
/* Don't overwrite default if value is 0 */
if (new_timeout[0] != 0 && new_timeout[0] < 1000) {
int i;
/* timeouts in msec rather usec */
for (i = 0; i != ARRAY_SIZE(new_timeout); i++)
new_timeout[i] *= 1000;
chip->vendor.timeout_adjusted = true;
}
} }
if (timeout)
chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale); /* Report adjusted timeouts */
timeout = be32_to_cpu(timeout_cap->b); if (chip->vendor.timeout_adjusted) {
if (timeout) dev_info(chip->dev,
chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale); HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n",
timeout = be32_to_cpu(timeout_cap->c); old_timeout[0], new_timeout[0],
if (timeout) old_timeout[1], new_timeout[1],
chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale); old_timeout[2], new_timeout[2],
timeout = be32_to_cpu(timeout_cap->d); old_timeout[3], new_timeout[3]);
if (timeout) }
chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale);
chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]);
chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]);
chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]);
chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]);
duration: duration:
tpm_cmd.header.in = tpm_getcap_header; tpm_cmd.header.in = tpm_getcap_header;
......
...@@ -80,6 +80,9 @@ struct tpm_vendor_specific { ...@@ -80,6 +80,9 @@ struct tpm_vendor_specific {
int (*send) (struct tpm_chip *, u8 *, size_t); int (*send) (struct tpm_chip *, u8 *, size_t);
void (*cancel) (struct tpm_chip *); void (*cancel) (struct tpm_chip *);
u8 (*status) (struct tpm_chip *); u8 (*status) (struct tpm_chip *);
bool (*update_timeouts)(struct tpm_chip *chip,
unsigned long *timeout_cap);
void (*release) (struct device *); void (*release) (struct device *);
struct miscdevice miscdev; struct miscdevice miscdev;
struct attribute_group *attr_group; struct attribute_group *attr_group;
......
...@@ -396,6 +396,36 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) ...@@ -396,6 +396,36 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
return rc; return rc;
} }
struct tis_vendor_timeout_override {
u32 did_vid;
unsigned long timeout_us[4];
};
static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = {
/* Atmel 3204 */
{ 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000),
(TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } },
};
static bool tpm_tis_update_timeouts(struct tpm_chip *chip,
unsigned long *timeout_cap)
{
int i;
u32 did_vid;
did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) {
if (vendor_timeout_overrides[i].did_vid != did_vid)
continue;
memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us,
sizeof(vendor_timeout_overrides[i].timeout_us));
return true;
}
return false;
}
/* /*
* Early probing for iTPM with STS_DATA_EXPECT flaw. * Early probing for iTPM with STS_DATA_EXPECT flaw.
* Try sending command without itpm flag set and if that * Try sending command without itpm flag set and if that
...@@ -483,6 +513,7 @@ static struct tpm_vendor_specific tpm_tis = { ...@@ -483,6 +513,7 @@ static struct tpm_vendor_specific tpm_tis = {
.recv = tpm_tis_recv, .recv = tpm_tis_recv,
.send = tpm_tis_send, .send = tpm_tis_send,
.cancel = tpm_tis_ready, .cancel = tpm_tis_ready,
.update_timeouts = tpm_tis_update_timeouts,
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
.req_canceled = TPM_STS_COMMAND_READY, .req_canceled = TPM_STS_COMMAND_READY,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment