Commit aec04cbd authored by Jarkko Sakkinen's avatar Jarkko Sakkinen Committed by Peter Huewe

tpm: TPM 2.0 FIFO Interface

Detect TPM 2.0 by sending idempotent TPM 2.x command. Ordinals for
TPM 2.0 are higher than TPM 1.x commands so this should be fail-safe.
Using STS3 is unreliable because some chips just report 0xff and not
what the spec says.

Before TPM family is detected, timeouts are set to the maximum values
for both TPM 1.x and TPM 2.x. In addition to this, suspend/resume
functionality is implemented for TPM 2.x.
Signed-off-by: default avatarJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: default avatarWill Arthur <will.c.arthur@intel.com>
Reviewed-by: default avatarJasob Gunthorpe <jason.gunthorpe@obsidianresearch.com>
Reviewed-by: default avatarStefan Berger <stefanb@linux.vnet.ibm.com>
Reviewed-by: default avatarPeter Huewe <peterhuewe@gmx.de>
Tested-by: default avatarPeter Huewe <peterhuewe@gmx.de>
Signed-off-by: default avatarPeter Huewe <peterhuewe@gmx.de>
parent 30fc8d13
/* /*
* Copyright (C) 2005, 2006 IBM Corporation * Copyright (C) 2005, 2006 IBM Corporation
* Copyright (C) 2014 Intel Corporation
* *
* Authors: * Authors:
* Leendert van Doorn <leendert@watson.ibm.com> * Leendert van Doorn <leendert@watson.ibm.com>
...@@ -64,12 +65,22 @@ enum tis_defaults { ...@@ -64,12 +65,22 @@ enum tis_defaults {
TIS_LONG_TIMEOUT = 2000, /* 2 sec */ TIS_LONG_TIMEOUT = 2000, /* 2 sec */
}; };
/* Some timeout values are needed before it is known whether the chip is
* TPM 1.0 or TPM 2.0.
*/
#define TIS_TIMEOUT_A_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A)
#define TIS_TIMEOUT_B_MAX max(TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B)
#define TIS_TIMEOUT_C_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C)
#define TIS_TIMEOUT_D_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D)
#define TPM_ACCESS(l) (0x0000 | ((l) << 12)) #define TPM_ACCESS(l) (0x0000 | ((l) << 12))
#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12)) #define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12))
#define TPM_INT_VECTOR(l) (0x000C | ((l) << 12)) #define TPM_INT_VECTOR(l) (0x000C | ((l) << 12))
#define TPM_INT_STATUS(l) (0x0010 | ((l) << 12)) #define TPM_INT_STATUS(l) (0x0010 | ((l) << 12))
#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12)) #define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12))
#define TPM_STS(l) (0x0018 | ((l) << 12)) #define TPM_STS(l) (0x0018 | ((l) << 12))
#define TPM_STS3(l) (0x001b | ((l) << 12))
#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12)) #define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12))
#define TPM_DID_VID(l) (0x0F00 | ((l) << 12)) #define TPM_DID_VID(l) (0x0F00 | ((l) << 12))
...@@ -363,6 +374,7 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) ...@@ -363,6 +374,7 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len)
{ {
int rc; int rc;
u32 ordinal; u32 ordinal;
unsigned long dur;
rc = tpm_tis_send_data(chip, buf, len); rc = tpm_tis_send_data(chip, buf, len);
if (rc < 0) if (rc < 0)
...@@ -374,9 +386,14 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) ...@@ -374,9 +386,14 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len)
if (chip->vendor.irq) { if (chip->vendor.irq) {
ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); ordinal = be32_to_cpu(*((__be32 *) (buf + 6)));
if (chip->flags & TPM_CHIP_FLAG_TPM2)
dur = tpm2_calc_ordinal_duration(chip, ordinal);
else
dur = tpm_calc_ordinal_duration(chip, ordinal);
if (wait_for_tpm_stat if (wait_for_tpm_stat
(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, dur,
tpm_calc_ordinal_duration(chip, ordinal),
&chip->vendor.read_queue, false) < 0) { &chip->vendor.read_queue, false) < 0) {
rc = -ETIME; rc = -ETIME;
goto out_err; goto out_err;
...@@ -598,17 +615,19 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, ...@@ -598,17 +615,19 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle,
return PTR_ERR(chip); return PTR_ERR(chip);
chip->vendor.priv = priv; chip->vendor.priv = priv;
#ifdef CONFIG_ACPI
chip->acpi_dev_handle = acpi_dev_handle; chip->acpi_dev_handle = acpi_dev_handle;
#endif
chip->vendor.iobase = devm_ioremap(dev, start, len); chip->vendor.iobase = devm_ioremap(dev, start, len);
if (!chip->vendor.iobase) if (!chip->vendor.iobase)
return -EIO; return -EIO;
/* Default timeouts */ /* Maximum timeouts */
chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.timeout_a = TIS_TIMEOUT_A_MAX;
chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); chip->vendor.timeout_b = TIS_TIMEOUT_B_MAX;
chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.timeout_c = TIS_TIMEOUT_C_MAX;
chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.timeout_d = TIS_TIMEOUT_D_MAX;
if (wait_startup(chip, 0) != 0) { if (wait_startup(chip, 0) != 0) {
rc = -ENODEV; rc = -ENODEV;
...@@ -620,11 +639,18 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, ...@@ -620,11 +639,18 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle,
goto out_err; goto out_err;
} }
/* Every TPM 2.x command has a higher ordinal than TPM 1.x commands.
* Therefore, we can use an idempotent TPM 2.x command to probe TPM 2.x.
*/
rc = tpm2_gen_interrupt(chip, true);
if (rc == 0 || rc == TPM2_RC_INITIALIZE)
chip->flags |= TPM_CHIP_FLAG_TPM2;
vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
chip->vendor.manufacturer_id = vendor; chip->vendor.manufacturer_id = vendor;
dev_info(dev, dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n",
"1.2 TPM (device-id 0x%X, rev-id %d)\n", (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2",
vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
if (!itpm) { if (!itpm) {
...@@ -720,6 +746,9 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, ...@@ -720,6 +746,9 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle,
chip->vendor.probed_irq = 0; chip->vendor.probed_irq = 0;
/* Generate Interrupts */ /* Generate Interrupts */
if (chip->flags & TPM_CHIP_FLAG_TPM2)
tpm2_gen_interrupt(chip, false);
else
tpm_gen_interrupt(chip); tpm_gen_interrupt(chip);
chip->vendor.irq = chip->vendor.probed_irq; chip->vendor.irq = chip->vendor.probed_irq;
...@@ -765,6 +794,33 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, ...@@ -765,6 +794,33 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle,
} }
} }
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
chip->vendor.timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
chip->vendor.timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B);
chip->vendor.timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C);
chip->vendor.timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D);
chip->vendor.duration[TPM_SHORT] =
msecs_to_jiffies(TPM2_DURATION_SHORT);
chip->vendor.duration[TPM_MEDIUM] =
msecs_to_jiffies(TPM2_DURATION_MEDIUM);
chip->vendor.duration[TPM_LONG] =
msecs_to_jiffies(TPM2_DURATION_LONG);
rc = tpm2_do_selftest(chip);
if (rc == TPM2_RC_INITIALIZE) {
dev_warn(dev, "Firmware has not started TPM\n");
rc = tpm2_startup(chip, TPM2_SU_CLEAR);
if (!rc)
rc = tpm2_do_selftest(chip);
}
if (rc) {
dev_err(dev, "TPM self test failed\n");
if (rc > 0)
rc = -ENODEV;
goto out_err;
}
} else {
if (tpm_get_timeouts(chip)) { if (tpm_get_timeouts(chip)) {
dev_err(dev, "Could not get TPM timeouts and durations\n"); dev_err(dev, "Could not get TPM timeouts and durations\n");
rc = -ENODEV; rc = -ENODEV;
...@@ -776,6 +832,7 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, ...@@ -776,6 +832,7 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle,
rc = -ENODEV; rc = -ENODEV;
goto out_err; goto out_err;
} }
}
return tpm_chip_register(chip); return tpm_chip_register(chip);
out_err: out_err:
...@@ -808,14 +865,23 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) ...@@ -808,14 +865,23 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
static int tpm_tis_resume(struct device *dev) static int tpm_tis_resume(struct device *dev)
{ {
struct tpm_chip *chip = dev_get_drvdata(dev); struct tpm_chip *chip = dev_get_drvdata(dev);
int ret; int ret = 0;
if (chip->vendor.irq) if (chip->vendor.irq)
tpm_tis_reenable_interrupts(chip); tpm_tis_reenable_interrupts(chip);
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
/* NOP if firmware properly does this. */
tpm2_startup(chip, TPM2_SU_STATE);
ret = tpm2_shutdown(chip, TPM2_SU_STATE);
if (!ret)
ret = tpm2_do_selftest(chip);
} else {
ret = tpm_pm_resume(dev); ret = tpm_pm_resume(dev);
if (!ret) if (!ret)
tpm_do_selftest(chip); tpm_do_selftest(chip);
}
return ret; return ret;
} }
......
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