|
@@ -430,17 +430,27 @@ out:
|
|
|
#define TPM_GET_CAP_RET_UINT32_2_IDX 18
|
|
|
#define TPM_GET_CAP_RET_UINT32_3_IDX 22
|
|
|
#define TPM_GET_CAP_RET_UINT32_4_IDX 26
|
|
|
+#define TPM_GET_CAP_PERM_DISABLE_IDX 16
|
|
|
+#define TPM_GET_CAP_PERM_INACTIVE_IDX 18
|
|
|
+#define TPM_GET_CAP_RET_BOOL_1_IDX 14
|
|
|
+#define TPM_GET_CAP_TEMP_INACTIVE_IDX 16
|
|
|
|
|
|
#define TPM_CAP_IDX 13
|
|
|
#define TPM_CAP_SUBCAP_IDX 21
|
|
|
|
|
|
enum tpm_capabilities {
|
|
|
+ TPM_CAP_FLAG = 4,
|
|
|
TPM_CAP_PROP = 5,
|
|
|
};
|
|
|
|
|
|
enum tpm_sub_capabilities {
|
|
|
TPM_CAP_PROP_PCR = 0x1,
|
|
|
TPM_CAP_PROP_MANUFACTURER = 0x3,
|
|
|
+ TPM_CAP_FLAG_PERM = 0x8,
|
|
|
+ TPM_CAP_FLAG_VOL = 0x9,
|
|
|
+ TPM_CAP_PROP_OWNER = 0x11,
|
|
|
+ TPM_CAP_PROP_TIS_TIMEOUT = 0x15,
|
|
|
+ TPM_CAP_PROP_TIS_DURATION = 0x20,
|
|
|
};
|
|
|
|
|
|
/*
|
|
@@ -474,6 +484,180 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, u8 *data, int len,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+void tpm_gen_interrupt(struct tpm_chip *chip)
|
|
|
+{
|
|
|
+ u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];
|
|
|
+ ssize_t rc;
|
|
|
+
|
|
|
+ memcpy(data, tpm_cap, sizeof(tpm_cap));
|
|
|
+ data[TPM_CAP_IDX] = TPM_CAP_PROP;
|
|
|
+ data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;
|
|
|
+
|
|
|
+ rc = transmit_cmd(chip, data, sizeof(data),
|
|
|
+ "attempting to determine the timeouts");
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
|
|
|
+
|
|
|
+void tpm_get_timeouts(struct tpm_chip *chip)
|
|
|
+{
|
|
|
+ u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];
|
|
|
+ ssize_t rc;
|
|
|
+ u32 timeout;
|
|
|
+
|
|
|
+ memcpy(data, tpm_cap, sizeof(tpm_cap));
|
|
|
+ data[TPM_CAP_IDX] = TPM_CAP_PROP;
|
|
|
+ data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;
|
|
|
+
|
|
|
+ rc = transmit_cmd(chip, data, sizeof(data),
|
|
|
+ "attempting to determine the timeouts");
|
|
|
+ if (rc)
|
|
|
+ goto duration;
|
|
|
+
|
|
|
+ if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))
|
|
|
+ != 4 * sizeof(u32))
|
|
|
+ goto duration;
|
|
|
+
|
|
|
+ /* Don't overwrite default if value is 0 */
|
|
|
+ timeout =
|
|
|
+ be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)));
|
|
|
+ if (timeout)
|
|
|
+ chip->vendor.timeout_a = timeout;
|
|
|
+ timeout =
|
|
|
+ be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX)));
|
|
|
+ if (timeout)
|
|
|
+ chip->vendor.timeout_b = timeout;
|
|
|
+ timeout =
|
|
|
+ be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX)));
|
|
|
+ if (timeout)
|
|
|
+ chip->vendor.timeout_c = timeout;
|
|
|
+ timeout =
|
|
|
+ be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_4_IDX)));
|
|
|
+ if (timeout)
|
|
|
+ chip->vendor.timeout_d = timeout;
|
|
|
+
|
|
|
+duration:
|
|
|
+ memcpy(data, tpm_cap, sizeof(tpm_cap));
|
|
|
+ data[TPM_CAP_IDX] = TPM_CAP_PROP;
|
|
|
+ data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_DURATION;
|
|
|
+
|
|
|
+ rc = transmit_cmd(chip, data, sizeof(data),
|
|
|
+ "attempting to determine the durations");
|
|
|
+ if (rc)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))
|
|
|
+ != 3 * sizeof(u32))
|
|
|
+ return;
|
|
|
+
|
|
|
+ chip->vendor.duration[TPM_SHORT] =
|
|
|
+ be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)));
|
|
|
+ chip->vendor.duration[TPM_MEDIUM] =
|
|
|
+ be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX)));
|
|
|
+ chip->vendor.duration[TPM_LONG] =
|
|
|
+ be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX)));
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(tpm_get_timeouts);
|
|
|
+
|
|
|
+void tpm_continue_selftest(struct tpm_chip *chip)
|
|
|
+{
|
|
|
+ u8 data[] = {
|
|
|
+ 0, 193, /* TPM_TAG_RQU_COMMAND */
|
|
|
+ 0, 0, 0, 10, /* length */
|
|
|
+ 0, 0, 0, 83, /* TPM_ORD_GetCapability */
|
|
|
+ };
|
|
|
+
|
|
|
+ tpm_transmit(chip, data, sizeof(data));
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(tpm_continue_selftest);
|
|
|
+
|
|
|
+ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
|
|
|
+ char *buf)
|
|
|
+{
|
|
|
+ u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 35)];
|
|
|
+ ssize_t rc;
|
|
|
+
|
|
|
+ struct tpm_chip *chip = dev_get_drvdata(dev);
|
|
|
+ if (chip == NULL)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ memcpy(data, tpm_cap, sizeof(tpm_cap));
|
|
|
+ data[TPM_CAP_IDX] = TPM_CAP_FLAG;
|
|
|
+ data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM;
|
|
|
+
|
|
|
+ rc = transmit_cmd(chip, data, sizeof(data),
|
|
|
+ "attemtping to determine the permanent state");
|
|
|
+ if (rc)
|
|
|
+ return 0;
|
|
|
+ return sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_DISABLE_IDX]);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(tpm_show_enabled);
|
|
|
+
|
|
|
+ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr,
|
|
|
+ char *buf)
|
|
|
+{
|
|
|
+ u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 35)];
|
|
|
+ ssize_t rc;
|
|
|
+
|
|
|
+ struct tpm_chip *chip = dev_get_drvdata(dev);
|
|
|
+ if (chip == NULL)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ memcpy(data, tpm_cap, sizeof(tpm_cap));
|
|
|
+ data[TPM_CAP_IDX] = TPM_CAP_FLAG;
|
|
|
+ data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM;
|
|
|
+
|
|
|
+ rc = transmit_cmd(chip, data, sizeof(data),
|
|
|
+ "attemtping to determine the permanent state");
|
|
|
+ if (rc)
|
|
|
+ return 0;
|
|
|
+ return sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_INACTIVE_IDX]);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(tpm_show_active);
|
|
|
+
|
|
|
+ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr,
|
|
|
+ char *buf)
|
|
|
+{
|
|
|
+ u8 data[sizeof(tpm_cap)];
|
|
|
+ ssize_t rc;
|
|
|
+
|
|
|
+ struct tpm_chip *chip = dev_get_drvdata(dev);
|
|
|
+ if (chip == NULL)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ memcpy(data, tpm_cap, sizeof(tpm_cap));
|
|
|
+ data[TPM_CAP_IDX] = TPM_CAP_PROP;
|
|
|
+ data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_OWNER;
|
|
|
+
|
|
|
+ rc = transmit_cmd(chip, data, sizeof(data),
|
|
|
+ "attempting to determine the owner state");
|
|
|
+ if (rc)
|
|
|
+ return 0;
|
|
|
+ return sprintf(buf, "%d\n", data[TPM_GET_CAP_RET_BOOL_1_IDX]);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(tpm_show_owned);
|
|
|
+
|
|
|
+ssize_t tpm_show_temp_deactivated(struct device * dev,
|
|
|
+ struct device_attribute * attr, char *buf)
|
|
|
+{
|
|
|
+ u8 data[sizeof(tpm_cap)];
|
|
|
+ ssize_t rc;
|
|
|
+
|
|
|
+ struct tpm_chip *chip = dev_get_drvdata(dev);
|
|
|
+ if (chip == NULL)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ memcpy(data, tpm_cap, sizeof(tpm_cap));
|
|
|
+ data[TPM_CAP_IDX] = TPM_CAP_FLAG;
|
|
|
+ data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_VOL;
|
|
|
+
|
|
|
+ rc = transmit_cmd(chip, data, sizeof(data),
|
|
|
+ "attempting to determine the temporary state");
|
|
|
+ if (rc)
|
|
|
+ return 0;
|
|
|
+ return sprintf(buf, "%d\n", data[TPM_GET_CAP_TEMP_INACTIVE_IDX]);
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
|
|
|
+
|
|
|
static const u8 pcrread[] = {
|
|
|
0, 193, /* TPM_TAG_RQU_COMMAND */
|
|
|
0, 0, 0, 14, /* length */
|
|
@@ -484,7 +668,7 @@ static const u8 pcrread[] = {
|
|
|
ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr,
|
|
|
char *buf)
|
|
|
{
|
|
|
- u8 data[30];
|
|
|
+ u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(pcrread)), 30)];
|
|
|
ssize_t rc;
|
|
|
int i, j, num_pcrs;
|
|
|
__be32 index;
|
|
@@ -588,6 +772,7 @@ out:
|
|
|
EXPORT_SYMBOL_GPL(tpm_show_pubek);
|
|
|
|
|
|
#define CAP_VERSION_1_1 6
|
|
|
+#define CAP_VERSION_1_2 0x1A
|
|
|
#define CAP_VERSION_IDX 13
|
|
|
static const u8 cap_version[] = {
|
|
|
0, 193, /* TPM_TAG_RQU_COMMAND */
|
|
@@ -600,7 +785,7 @@ static const u8 cap_version[] = {
|
|
|
ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr,
|
|
|
char *buf)
|
|
|
{
|
|
|
- u8 data[30];
|
|
|
+ u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(cap_version)), 30)];
|
|
|
ssize_t rc;
|
|
|
char *str = buf;
|
|
|
|
|
@@ -637,6 +822,52 @@ out:
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(tpm_show_caps);
|
|
|
|
|
|
+ssize_t tpm_show_caps_1_2(struct device * dev,
|
|
|
+ struct device_attribute * attr, char *buf)
|
|
|
+{
|
|
|
+ u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(cap_version)), 30)];
|
|
|
+ ssize_t len;
|
|
|
+ char *str = buf;
|
|
|
+
|
|
|
+ struct tpm_chip *chip = dev_get_drvdata(dev);
|
|
|
+ if (chip == NULL)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ memcpy(data, tpm_cap, sizeof(tpm_cap));
|
|
|
+ data[TPM_CAP_IDX] = TPM_CAP_PROP;
|
|
|
+ data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER;
|
|
|
+
|
|
|
+ if ((len = tpm_transmit(chip, data, sizeof(data))) <=
|
|
|
+ TPM_ERROR_SIZE) {
|
|
|
+ dev_dbg(chip->dev, "A TPM error (%d) occurred "
|
|
|
+ "attempting to determine the manufacturer\n",
|
|
|
+ be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ str += sprintf(str, "Manufacturer: 0x%x\n",
|
|
|
+ be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))));
|
|
|
+
|
|
|
+ memcpy(data, cap_version, sizeof(cap_version));
|
|
|
+ data[CAP_VERSION_IDX] = CAP_VERSION_1_2;
|
|
|
+
|
|
|
+ if ((len = tpm_transmit(chip, data, sizeof(data))) <=
|
|
|
+ TPM_ERROR_SIZE) {
|
|
|
+ dev_err(chip->dev, "A TPM error (%d) occurred "
|
|
|
+ "attempting to determine the 1.2 version\n",
|
|
|
+ be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))));
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ str += sprintf(str,
|
|
|
+ "TCG version: %d.%d\nFirmware version: %d.%d\n",
|
|
|
+ (int) data[16], (int) data[17], (int) data[18],
|
|
|
+ (int) data[19]);
|
|
|
+
|
|
|
+out:
|
|
|
+ return str - buf;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);
|
|
|
+
|
|
|
ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr,
|
|
|
const char *buf, size_t count)
|
|
|
{
|