From: root Date: Sun, 4 Oct 2009 00:19:30 +0000 (+0100) Subject: Backport of tpm from 2.6.30.1, together with a set of patches X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=093219f091b5b8c4958a906f98517b2ebc15cd39;p=xenclient%2Flinux-2.6.27-pq.git Backport of tpm from 2.6.30.1, together with a set of patches from the linux kernel mailing list to support itpm --- diff --git a/master/itpm b/master/itpm index 82014aa..864c17f 100644 --- a/master/itpm +++ b/master/itpm @@ -1,58 +1,1735 @@ +diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig +index 692573e..da96c8e 100644 +--- a/drivers/char/tpm/Kconfig ++++ b/drivers/char/tpm/Kconfig +@@ -23,7 +23,7 @@ if TCG_TPM + + config TCG_TIS + tristate "TPM Interface Specification 1.2 Interface" +- depends on PNP ++ depends on ACPI + ---help--- + If you have a TPM security chip that is compliant with the + TCG TIS 1.2 TPM specification say Yes and it will be accessible +diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c +index 3244389..ccdd828 100644 +--- a/drivers/char/tpm/tpm.c ++++ b/drivers/char/tpm/tpm.c +@@ -429,134 +429,148 @@ out: + #define TPM_DIGEST_SIZE 20 + #define TPM_ERROR_SIZE 10 + #define TPM_RET_CODE_IDX 6 +-#define TPM_GET_CAP_RET_SIZE_IDX 10 +-#define TPM_GET_CAP_RET_UINT32_1_IDX 14 +-#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, ++ TPM_CAP_FLAG = cpu_to_be32(4), ++ TPM_CAP_PROP = cpu_to_be32(5), ++ CAP_VERSION_1_1 = cpu_to_be32(0x06), ++ CAP_VERSION_1_2 = cpu_to_be32(0x1A) + }; + + 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, +-}; +- +-/* +- * This is a semi generic GetCapability command for use +- * with the capability type TPM_CAP_PROP or TPM_CAP_FLAG +- * and their associated sub_capabilities. +- */ ++ TPM_CAP_PROP_PCR = cpu_to_be32(0x101), ++ TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103), ++ TPM_CAP_FLAG_PERM = cpu_to_be32(0x108), ++ TPM_CAP_FLAG_VOL = cpu_to_be32(0x109), ++ TPM_CAP_PROP_OWNER = cpu_to_be32(0x111), ++ TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115), ++ TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120), + +-static const u8 tpm_cap[] = { +- 0, 193, /* TPM_TAG_RQU_COMMAND */ +- 0, 0, 0, 22, /* length */ +- 0, 0, 0, 101, /* TPM_ORD_GetCapability */ +- 0, 0, 0, 0, /* TPM_CAP_ */ +- 0, 0, 0, 4, /* TPM_CAP_SUB_ size */ +- 0, 0, 1, 0 /* TPM_CAP_SUB_ */ + }; + +-static ssize_t transmit_cmd(struct tpm_chip *chip, u8 *data, int len, +- char *desc) ++static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd, ++ int len, const char *desc) + { + int err; + +- len = tpm_transmit(chip, data, len); ++ len = tpm_transmit(chip,(u8 *) cmd, len); + if (len < 0) + return len; + if (len == TPM_ERROR_SIZE) { +- err = be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))); ++ err = be32_to_cpu(cmd->header.out.return_code); + dev_dbg(chip->dev, "A TPM error (%d) occurred %s\n", err, desc); + return err; + } + return 0; + } + ++#define TPM_INTERNAL_RESULT_SIZE 200 ++#define TPM_TAG_RQU_COMMAND cpu_to_be16(193) ++#define TPM_ORD_GET_CAP cpu_to_be32(101) ++ ++static const struct tpm_input_header tpm_getcap_header = { ++ .tag = TPM_TAG_RQU_COMMAND, ++ .length = cpu_to_be32(22), ++ .ordinal = TPM_ORD_GET_CAP ++}; ++ ++ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap, ++ const char *desc) ++{ ++ struct tpm_cmd_t tpm_cmd; ++ int rc; ++ struct tpm_chip *chip = dev_get_drvdata(dev); ++ ++ tpm_cmd.header.in = tpm_getcap_header; ++ if (subcap_id == CAP_VERSION_1_1 || subcap_id == CAP_VERSION_1_2) { ++ tpm_cmd.params.getcap_in.cap = subcap_id; ++ /*subcap field not necessary */ ++ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(0); ++ tpm_cmd.header.in.length -= cpu_to_be32(sizeof(__be32)); ++ } else { ++ if (subcap_id == TPM_CAP_FLAG_PERM || ++ subcap_id == TPM_CAP_FLAG_VOL) ++ tpm_cmd.params.getcap_in.cap = TPM_CAP_FLAG; ++ else ++ tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; ++ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); ++ tpm_cmd.params.getcap_in.subcap = subcap_id; ++ } ++ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc); ++ if (!rc) ++ *cap = tpm_cmd.params.getcap_out.cap; ++ return rc; ++} ++ + void tpm_gen_interrupt(struct tpm_chip *chip) + { +- u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)]; ++ struct tpm_cmd_t tpm_cmd; + 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; ++ tpm_cmd.header.in = tpm_getcap_header; ++ tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; ++ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); ++ tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT; + +- rc = transmit_cmd(chip, data, sizeof(data), ++ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, + "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)]; ++ struct tpm_cmd_t tpm_cmd; ++ struct timeout_t *timeout_cap; ++ struct duration_t *duration_cap; + 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; ++ tpm_cmd.header.in = tpm_getcap_header; ++ tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; ++ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); ++ tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT; + +- rc = transmit_cmd(chip, data, sizeof(data), ++ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, + "attempting to determine the timeouts"); + if (rc) + goto duration; + +- if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX))) ++ if (be32_to_cpu(tpm_cmd.header.out.length) + != 4 * sizeof(u32)) + goto duration; + ++ timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout; + /* Don't overwrite default if value is 0 */ +- timeout = +- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))); ++ timeout = be32_to_cpu(timeout_cap->a); + if (timeout) + chip->vendor.timeout_a = usecs_to_jiffies(timeout); +- timeout = +- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX))); ++ timeout = be32_to_cpu(timeout_cap->b); + if (timeout) + chip->vendor.timeout_b = usecs_to_jiffies(timeout); +- timeout = +- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX))); ++ timeout = be32_to_cpu(timeout_cap->c); + if (timeout) + chip->vendor.timeout_c = usecs_to_jiffies(timeout); +- timeout = +- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_4_IDX))); ++ timeout = be32_to_cpu(timeout_cap->d); + if (timeout) + chip->vendor.timeout_d = usecs_to_jiffies(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; ++ tpm_cmd.header.in = tpm_getcap_header; ++ tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; ++ tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); ++ tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION; + +- rc = transmit_cmd(chip, data, sizeof(data), ++ rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, + "attempting to determine the durations"); + if (rc) + return; + +- if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX))) ++ if (be32_to_cpu(tpm_cmd.header.out.return_code) + != 3 * sizeof(u32)) + return; +- ++ duration_cap = &tpm_cmd.params.getcap_out.cap.duration; + chip->vendor.duration[TPM_SHORT] = +- usecs_to_jiffies(be32_to_cpu +- (*((__be32 *) (data + +- TPM_GET_CAP_RET_UINT32_1_IDX)))); ++ usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short)); + /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above + * value wrong and apparently reports msecs rather than usecs. So we + * fix up the resulting too-small TPM_SHORT value to make things work. +@@ -565,13 +579,9 @@ duration: + chip->vendor.duration[TPM_SHORT] = HZ; + + chip->vendor.duration[TPM_MEDIUM] = +- usecs_to_jiffies(be32_to_cpu +- (*((__be32 *) (data + +- TPM_GET_CAP_RET_UINT32_2_IDX)))); ++ usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium)); + chip->vendor.duration[TPM_LONG] = +- usecs_to_jiffies(be32_to_cpu +- (*((__be32 *) (data + +- TPM_GET_CAP_RET_UINT32_3_IDX)))); ++ usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long)); + } + EXPORT_SYMBOL_GPL(tpm_get_timeouts); + +@@ -587,36 +597,18 @@ void tpm_continue_selftest(struct tpm_chip *chip) + } + EXPORT_SYMBOL_GPL(tpm_continue_selftest); + +-#define TPM_INTERNAL_RESULT_SIZE 200 +- + ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr, + char *buf) + { +- u8 *data; ++ cap_t cap; + ssize_t rc; + +- struct tpm_chip *chip = dev_get_drvdata(dev); +- if (chip == NULL) +- return -ENODEV; +- +- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); +- if (!data) +- return -ENOMEM; +- +- 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, TPM_INTERNAL_RESULT_SIZE, +- "attemtping to determine the permanent enabled state"); +- if (rc) { +- kfree(data); ++ rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap, ++ "attempting to determine the permanent enabled state"); ++ if (rc) + return 0; +- } + +- rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_DISABLE_IDX]); +- +- kfree(data); ++ rc = sprintf(buf, "%d\n", !cap.perm_flags.disable); + return rc; + } + EXPORT_SYMBOL_GPL(tpm_show_enabled); +@@ -624,31 +616,15 @@ EXPORT_SYMBOL_GPL(tpm_show_enabled); + ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr, + char *buf) + { +- u8 *data; ++ cap_t cap; + ssize_t rc; + +- struct tpm_chip *chip = dev_get_drvdata(dev); +- if (chip == NULL) +- return -ENODEV; +- +- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); +- if (!data) +- return -ENOMEM; +- +- 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, TPM_INTERNAL_RESULT_SIZE, +- "attemtping to determine the permanent active state"); +- if (rc) { +- kfree(data); ++ rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap, ++ "attempting to determine the permanent active state"); ++ if (rc) + return 0; +- } +- +- rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_INACTIVE_IDX]); + +- kfree(data); ++ rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated); + return rc; + } + EXPORT_SYMBOL_GPL(tpm_show_active); +@@ -656,31 +632,15 @@ EXPORT_SYMBOL_GPL(tpm_show_active); + ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr, + char *buf) + { +- u8 *data; ++ cap_t cap; + ssize_t rc; + +- struct tpm_chip *chip = dev_get_drvdata(dev); +- if (chip == NULL) +- return -ENODEV; +- +- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); +- if (!data) +- return -ENOMEM; +- +- 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, TPM_INTERNAL_RESULT_SIZE, +- "attempting to determine the owner state"); +- if (rc) { +- kfree(data); ++ rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap, ++ "attempting to determine the owner state"); ++ if (rc) + return 0; +- } + +- rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_RET_BOOL_1_IDX]); +- +- kfree(data); ++ rc = sprintf(buf, "%d\n", cap.owned); + return rc; + } + EXPORT_SYMBOL_GPL(tpm_show_owned); +@@ -688,116 +648,180 @@ EXPORT_SYMBOL_GPL(tpm_show_owned); + ssize_t tpm_show_temp_deactivated(struct device * dev, + struct device_attribute * attr, char *buf) + { +- u8 *data; ++ cap_t cap; + ssize_t rc; + +- struct tpm_chip *chip = dev_get_drvdata(dev); +- if (chip == NULL) +- return -ENODEV; ++ rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap, ++ "attempting to determine the temporary state"); ++ if (rc) ++ return 0; + +- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); +- if (!data) +- return -ENOMEM; ++ rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated); ++ ++/* ++ * tpm_chip_find_get - return tpm_chip for given chip number ++ */ ++static struct tpm_chip *tpm_chip_find_get(int chip_num) ++{ ++ struct tpm_chip *pos, *chip = NULL; + +- memcpy(data, tpm_cap, sizeof(tpm_cap)); +- data[TPM_CAP_IDX] = TPM_CAP_FLAG; +- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_VOL; ++ rcu_read_lock(); ++ list_for_each_entry_rcu(pos, &tpm_chip_list, list) { ++ if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num) ++ continue; + +- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE, +- "attempting to determine the temporary state"); +- if (rc) { +- kfree(data); +- return 0; ++ if (try_module_get(pos->dev->driver->owner)) { ++ chip = pos; ++ break; ++ } + } ++ rcu_read_unlock(); ++ return chip; ++} + +- rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_TEMP_INACTIVE_IDX]); ++#define TPM_ORDINAL_PCRREAD cpu_to_be32(21) ++#define READ_PCR_RESULT_SIZE 30 ++static struct tpm_input_header pcrread_header = { ++ .tag = TPM_TAG_RQU_COMMAND, ++ .length = cpu_to_be32(14), ++ .ordinal = TPM_ORDINAL_PCRREAD ++}; + +- kfree(data); ++int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) ++{ ++ int rc; ++ struct tpm_cmd_t cmd; ++ ++ cmd.header.in = pcrread_header; ++ cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx); ++ BUILD_BUG_ON(cmd.header.in.length > READ_PCR_RESULT_SIZE); ++ rc = transmit_cmd(chip, &cmd, cmd.header.in.length, ++ "attempting to read a pcr value"); ++ ++ if (rc == 0) ++ memcpy(res_buf, cmd.params.pcrread_out.pcr_result, ++ TPM_DIGEST_SIZE); + return rc; + } +-EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated); + +-static const u8 pcrread[] = { +- 0, 193, /* TPM_TAG_RQU_COMMAND */ +- 0, 0, 0, 14, /* length */ +- 0, 0, 0, 21, /* TPM_ORD_PcrRead */ +- 0, 0, 0, 0 /* PCR index */ ++/** ++ * tpm_pcr_read - read a pcr value ++ * @chip_num: tpm idx # or ANY ++ * @pcr_idx: pcr idx to retrieve ++ * @res_buf: TPM_PCR value ++ * size of res_buf is 20 bytes (or NULL if you don't care) ++ * ++ * The TPM driver should be built-in, but for whatever reason it ++ * isn't, protect against the chip disappearing, by incrementing ++ * the module usage count. ++ */ ++int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) ++{ ++ struct tpm_chip *chip; ++ int rc; ++ ++ chip = tpm_chip_find_get(chip_num); ++ if (chip == NULL) ++ return -ENODEV; ++ rc = __tpm_pcr_read(chip, pcr_idx, res_buf); ++ module_put(chip->dev->driver->owner); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(tpm_pcr_read); ++ ++/** ++ * tpm_pcr_extend - extend pcr value with hash ++ * @chip_num: tpm idx # or AN& ++ * @pcr_idx: pcr idx to extend ++ * @hash: hash value used to extend pcr value ++ * ++ * The TPM driver should be built-in, but for whatever reason it ++ * isn't, protect against the chip disappearing, by incrementing ++ * the module usage count. ++ */ ++#define TPM_ORD_PCR_EXTEND cpu_to_be32(20) ++#define EXTEND_PCR_SIZE 34 ++static struct tpm_input_header pcrextend_header = { ++ .tag = TPM_TAG_RQU_COMMAND, ++ .length = cpu_to_be32(34), ++ .ordinal = TPM_ORD_PCR_EXTEND + }; + ++int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) ++{ ++ struct tpm_cmd_t cmd; ++ int rc; ++ struct tpm_chip *chip; ++ ++ chip = tpm_chip_find_get(chip_num); ++ if (chip == NULL) ++ return -ENODEV; ++ ++ cmd.header.in = pcrextend_header; ++ BUILD_BUG_ON(be32_to_cpu(cmd.header.in.length) > EXTEND_PCR_SIZE); ++ cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); ++ memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE); ++ rc = transmit_cmd(chip, &cmd, cmd.header.in.length, ++ "attempting extend a PCR value"); ++ ++ module_put(chip->dev->driver->owner); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(tpm_pcr_extend); ++ + ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr, + char *buf) + { +- u8 *data; ++ cap_t cap; ++ u8 digest[TPM_DIGEST_SIZE]; + ssize_t rc; + int i, j, num_pcrs; +- __be32 index; + char *str = buf; +- + struct tpm_chip *chip = dev_get_drvdata(dev); +- if (chip == NULL) +- return -ENODEV; +- +- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); +- if (!data) +- return -ENOMEM; +- +- memcpy(data, tpm_cap, sizeof(tpm_cap)); +- data[TPM_CAP_IDX] = TPM_CAP_PROP; +- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_PCR; + +- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE, ++ rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap, + "attempting to determine the number of PCRS"); +- if (rc) { +- kfree(data); ++ if (rc) + return 0; +- } + +- num_pcrs = be32_to_cpu(*((__be32 *) (data + 14))); ++ num_pcrs = be32_to_cpu(cap.num_pcrs); + for (i = 0; i < num_pcrs; i++) { +- memcpy(data, pcrread, sizeof(pcrread)); +- index = cpu_to_be32(i); +- memcpy(data + 10, &index, 4); +- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE, +- "attempting to read a PCR"); ++ rc = __tpm_pcr_read(chip, i, digest); + if (rc) +- goto out; ++ break; + str += sprintf(str, "PCR-%02d: ", i); + for (j = 0; j < TPM_DIGEST_SIZE; j++) +- str += sprintf(str, "%02X ", *(data + 10 + j)); ++ str += sprintf(str, "%02X ", digest[j]); + str += sprintf(str, "\n"); + } +-out: +- kfree(data); + return str - buf; + } + EXPORT_SYMBOL_GPL(tpm_show_pcrs); + + #define READ_PUBEK_RESULT_SIZE 314 +-static const u8 readpubek[] = { +- 0, 193, /* TPM_TAG_RQU_COMMAND */ +- 0, 0, 0, 30, /* length */ +- 0, 0, 0, 124, /* TPM_ORD_ReadPubek */ ++#define TPM_ORD_READPUBEK cpu_to_be32(124) ++struct tpm_input_header tpm_readpubek_header = { ++ .tag = TPM_TAG_RQU_COMMAND, ++ .length = cpu_to_be32(30), ++ .ordinal = TPM_ORD_READPUBEK + }; + + ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr, + char *buf) + { + u8 *data; ++ struct tpm_cmd_t tpm_cmd; + ssize_t err; + int i, rc; + char *str = buf; + + struct tpm_chip *chip = dev_get_drvdata(dev); +- if (chip == NULL) +- return -ENODEV; + +- data = kzalloc(READ_PUBEK_RESULT_SIZE, GFP_KERNEL); +- if (!data) +- return -ENOMEM; +- +- memcpy(data, readpubek, sizeof(readpubek)); +- +- err = transmit_cmd(chip, data, READ_PUBEK_RESULT_SIZE, ++ tpm_cmd.header.in = tpm_readpubek_header; ++ err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, + "attempting to read the PUBEK"); + if (err) + goto out; +@@ -812,7 +836,7 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr, + 256 byte modulus + ignore checksum 20 bytes + */ +- ++ data = tpm_cmd.params.readpubek_out_buffer; + str += + sprintf(str, + "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n" +@@ -832,65 +856,33 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr, + } + out: + rc = str - buf; +- kfree(data); + return rc; + } + 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 */ +- 0, 0, 0, 18, /* length */ +- 0, 0, 0, 101, /* TPM_ORD_GetCapability */ +- 0, 0, 0, 0, +- 0, 0, 0, 0 +-}; + + ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr, + char *buf) + { +- u8 *data; ++ cap_t cap; + ssize_t rc; + char *str = buf; + +- struct tpm_chip *chip = dev_get_drvdata(dev); +- if (chip == NULL) +- return -ENODEV; +- +- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); +- if (!data) +- return -ENOMEM; +- +- memcpy(data, tpm_cap, sizeof(tpm_cap)); +- data[TPM_CAP_IDX] = TPM_CAP_PROP; +- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER; +- +- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE, ++ rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap, + "attempting to determine the manufacturer"); +- if (rc) { +- kfree(data); ++ if (rc) + return 0; +- } +- + str += sprintf(str, "Manufacturer: 0x%x\n", +- be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)))); ++ be32_to_cpu(cap.manufacturer_id)); + +- memcpy(data, cap_version, sizeof(cap_version)); +- data[CAP_VERSION_IDX] = CAP_VERSION_1_1; +- rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE, +- "attempting to determine the 1.1 version"); ++ rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap, ++ "attempting to determine the 1.1 version"); + if (rc) +- goto out; +- ++ return 0; + str += sprintf(str, + "TCG version: %d.%d\nFirmware version: %d.%d\n", +- (int) data[14], (int) data[15], (int) data[16], +- (int) data[17]); +- +-out: +- kfree(data); ++ cap.tpm_version.Major, cap.tpm_version.Minor, ++ cap.tpm_version.revMajor, cap.tpm_version.revMinor); + return str - buf; + } + EXPORT_SYMBOL_GPL(tpm_show_caps); +@@ -898,51 +890,25 @@ EXPORT_SYMBOL_GPL(tpm_show_caps); + ssize_t tpm_show_caps_1_2(struct device * dev, + struct device_attribute * attr, char *buf) + { +- u8 *data; +- ssize_t len; ++ cap_t cap; ++ ssize_t rc; + char *str = buf; + +- struct tpm_chip *chip = dev_get_drvdata(dev); +- if (chip == NULL) +- return -ENODEV; +- +- data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL); +- if (!data) +- return -ENOMEM; +- +- memcpy(data, tpm_cap, sizeof(tpm_cap)); +- data[TPM_CAP_IDX] = TPM_CAP_PROP; +- data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER; +- +- len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE); +- if (len <= 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)))); +- kfree(data); ++ rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap, ++ "attempting to determine the manufacturer"); ++ if (rc) + 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; +- +- len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE); +- if (len <= 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; +- } ++ be32_to_cpu(cap.manufacturer_id)); ++ rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap, ++ "attempting to determine the 1.2 version"); ++ if (rc) ++ return 0; + 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: +- kfree(data); ++ cap.tpm_version_1_2.Major, cap.tpm_version_1_2.Minor, ++ cap.tpm_version_1_2.revMajor, ++ cap.tpm_version_1_2.revMinor); + return str - buf; + } + EXPORT_SYMBOL_GPL(tpm_show_caps_1_2); +@@ -961,72 +927,63 @@ EXPORT_SYMBOL_GPL(tpm_store_cancel); + + /* + * Device file system interface to the TPM ++ * ++ * It's assured that the chip will be opened just once, ++ * by the check of is_open variable, which is protected ++ * by driver_lock. + */ + int tpm_open(struct inode *inode, struct file *file) + { +- int rc = 0, minor = iminor(inode); ++ int minor = iminor(inode); + struct tpm_chip *chip = NULL, *pos; + +- lock_kernel(); +- spin_lock(&driver_lock); +- +- list_for_each_entry(pos, &tpm_chip_list, list) { ++ rcu_read_lock(); ++ list_for_each_entry_rcu(pos, &tpm_chip_list, list) { + if (pos->vendor.miscdev.minor == minor) { + chip = pos; ++ get_device(chip->dev); + break; + } + } ++ rcu_read_unlock(); + +- if (chip == NULL) { +- rc = -ENODEV; +- goto err_out; +- } ++ if (!chip) ++ return -ENODEV; + +- if (chip->num_opens) { ++ if (test_and_set_bit(0, &chip->is_open)) { + dev_dbg(chip->dev, "Another process owns this TPM\n"); +- rc = -EBUSY; +- goto err_out; ++ put_device(chip->dev); ++ return -EBUSY; + } + +- chip->num_opens++; +- get_device(chip->dev); +- +- spin_unlock(&driver_lock); +- + chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); + if (chip->data_buffer == NULL) { +- chip->num_opens--; ++ clear_bit(0, &chip->is_open); + put_device(chip->dev); +- unlock_kernel(); + return -ENOMEM; + } + + atomic_set(&chip->data_pending, 0); + + file->private_data = chip; +- unlock_kernel(); + return 0; +- +-err_out: +- spin_unlock(&driver_lock); +- unlock_kernel(); +- return rc; + } + EXPORT_SYMBOL_GPL(tpm_open); + ++/* ++ * Called on file close ++ */ + int tpm_release(struct inode *inode, struct file *file) + { + struct tpm_chip *chip = file->private_data; + ++ del_singleshot_timer_sync(&chip->user_read_timer); + flush_scheduled_work(); +- spin_lock(&driver_lock); + file->private_data = NULL; +- del_singleshot_timer_sync(&chip->user_read_timer); + atomic_set(&chip->data_pending, 0); +- chip->num_opens--; +- put_device(chip->dev); + kfree(chip->data_buffer); +- spin_unlock(&driver_lock); ++ clear_bit(0, &chip->is_open); ++ put_device(chip->dev); + return 0; + } + EXPORT_SYMBOL_GPL(tpm_release); +@@ -1100,13 +1057,11 @@ void tpm_remove_hardware(struct device *dev) + } + + spin_lock(&driver_lock); +- +- list_del(&chip->list); +- ++ list_del_rcu(&chip->list); + spin_unlock(&driver_lock); ++ synchronize_rcu(); + + misc_deregister(&chip->vendor.miscdev); +- + sysfs_remove_group(&dev->kobj, chip->vendor.attr_group); + tpm_bios_log_teardown(chip->bios_dir); + +@@ -1151,25 +1106,33 @@ int tpm_pm_resume(struct device *dev) + } + EXPORT_SYMBOL_GPL(tpm_pm_resume); + ++/* In case vendor provided release function, call it too.*/ ++ ++void tpm_dev_vendor_release(struct tpm_chip *chip) ++{ ++ if (chip->vendor.release) ++ chip->vendor.release(chip->dev); ++ ++ clear_bit(chip->dev_num, dev_mask); ++ kfree(chip->vendor.miscdev.name); ++} ++EXPORT_SYMBOL_GPL(tpm_dev_vendor_release); ++ ++ + /* + * Once all references to platform device are down to 0, + * release all allocated structures. +- * In case vendor provided release function, +- * call it too. + */ +-static void tpm_dev_release(struct device *dev) ++void tpm_dev_release(struct device *dev) + { + struct tpm_chip *chip = dev_get_drvdata(dev); + +- if (chip->vendor.release) +- chip->vendor.release(dev); ++ tpm_dev_vendor_release(chip); + + chip->release(dev); +- +- clear_bit(chip->dev_num, dev_mask); +- kfree(chip->vendor.miscdev.name); + kfree(chip); + } ++EXPORT_SYMBOL_GPL(tpm_dev_release); + + /* + * Called from tpm_.c probe function only for devices +@@ -1178,8 +1141,8 @@ static void tpm_dev_release(struct device *dev) + * upon errant exit from this function specific probe function should call + * pci_disable_device + */ +-struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vendor_specific +- *entry) ++struct tpm_chip *tpm_register_hardware(struct device *dev, ++ const struct tpm_vendor_specific *entry) + { + #define DEVNAME_SIZE 7 + +@@ -1190,11 +1153,8 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + devname = kmalloc(DEVNAME_SIZE, GFP_KERNEL); + +- if (chip == NULL || devname == NULL) { +- kfree(chip); +- kfree(devname); +- return NULL; +- } ++ if (chip == NULL || devname == NULL) ++ goto out_free; + + mutex_init(&chip->buffer_mutex); + mutex_init(&chip->tpm_mutex); +@@ -1211,8 +1171,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend + + if (chip->dev_num >= TPM_NUM_DEVICES) { + dev_err(dev, "No available tpm device numbers\n"); +- kfree(chip); +- return NULL; ++ goto out_free; + } else if (chip->dev_num == 0) + chip->vendor.miscdev.minor = TPM_MINOR; + else +@@ -1238,22 +1197,26 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend + return NULL; + } + +- spin_lock(&driver_lock); +- +- list_add(&chip->list, &tpm_chip_list); +- +- spin_unlock(&driver_lock); +- + if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) { +- list_del(&chip->list); + misc_deregister(&chip->vendor.miscdev); + put_device(chip->dev); ++ + return NULL; + } + + chip->bios_dir = tpm_bios_log_setup(devname); + ++ /* Make chip available */ ++ spin_lock(&driver_lock); ++ list_add_rcu(&chip->list, &tpm_chip_list); ++ spin_unlock(&driver_lock); ++ + return chip; ++ ++out_free: ++ kfree(chip); ++ kfree(devname); ++ return NULL; + } + EXPORT_SYMBOL_GPL(tpm_register_hardware); + +diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h +index 557550a..8e00b4d 100644 +--- a/drivers/char/tpm/tpm.h ++++ b/drivers/char/tpm/tpm.h +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + + enum tpm_timeout { + TPM_TIMEOUT = 5, /* msecs */ +@@ -90,7 +91,7 @@ struct tpm_chip { + struct device *dev; /* Device stuff */ + + int dev_num; /* /dev/tpm# */ +- int num_opens; /* only one allowed */ ++ unsigned long is_open; /* only one allowed */ + int time_expired; + + /* Data passed to and from the tpm via the read/write calls */ +@@ -107,9 +108,6 @@ struct tpm_chip { + struct dentry **bios_dir; + + struct list_head list; +-#ifdef CONFIG_XEN +- void *priv; +-#endif + void (*release) (struct device *); + }; + +@@ -126,18 +124,147 @@ static inline void tpm_write_index(int base, int index, int value) + outb(index, base); + outb(value & 0xFF, base+1); + } ++struct tpm_input_header { ++ __be16 tag; ++ __be32 length; ++ __be32 ordinal; ++}__attribute__((packed)); + +-#ifdef CONFIG_XEN +-static inline void *chip_get_private(const struct tpm_chip *chip) +-{ +- return chip->priv; +-} ++struct tpm_output_header { ++ __be16 tag; ++ __be32 length; ++ __be32 return_code; ++}__attribute__((packed)); + +-static inline void chip_set_private(struct tpm_chip *chip, void *priv) +-{ +- chip->priv = priv; +-} +-#endif ++struct stclear_flags_t { ++ __be16 tag; ++ u8 deactivated; ++ u8 disableForceClear; ++ u8 physicalPresence; ++ u8 physicalPresenceLock; ++ u8 bGlobalLock; ++}__attribute__((packed)); ++ ++struct tpm_version_t { ++ u8 Major; ++ u8 Minor; ++ u8 revMajor; ++ u8 revMinor; ++}__attribute__((packed)); ++ ++struct tpm_version_1_2_t { ++ __be16 tag; ++ u8 Major; ++ u8 Minor; ++ u8 revMajor; ++ u8 revMinor; ++}__attribute__((packed)); ++ ++struct timeout_t { ++ __be32 a; ++ __be32 b; ++ __be32 c; ++ __be32 d; ++}__attribute__((packed)); ++ ++struct duration_t { ++ __be32 tpm_short; ++ __be32 tpm_medium; ++ __be32 tpm_long; ++}__attribute__((packed)); ++ ++struct permanent_flags_t { ++ __be16 tag; ++ u8 disable; ++ u8 ownership; ++ u8 deactivated; ++ u8 readPubek; ++ u8 disableOwnerClear; ++ u8 allowMaintenance; ++ u8 physicalPresenceLifetimeLock; ++ u8 physicalPresenceHWEnable; ++ u8 physicalPresenceCMDEnable; ++ u8 CEKPUsed; ++ u8 TPMpost; ++ u8 TPMpostLock; ++ u8 FIPS; ++ u8 operator; ++ u8 enableRevokeEK; ++ u8 nvLocked; ++ u8 readSRKPub; ++ u8 tpmEstablished; ++ u8 maintenanceDone; ++ u8 disableFullDALogicInfo; ++}__attribute__((packed)); ++ ++typedef union { ++ struct permanent_flags_t perm_flags; ++ struct stclear_flags_t stclear_flags; ++ bool owned; ++ __be32 num_pcrs; ++ struct tpm_version_t tpm_version; ++ struct tpm_version_1_2_t tpm_version_1_2; ++ __be32 manufacturer_id; ++ struct timeout_t timeout; ++ struct duration_t duration; ++} cap_t; ++ ++struct tpm_getcap_params_in { ++ __be32 cap; ++ __be32 subcap_size; ++ __be32 subcap; ++}__attribute__((packed)); ++ ++struct tpm_getcap_params_out { ++ __be32 cap_size; ++ cap_t cap; ++}__attribute__((packed)); ++ ++struct tpm_readpubek_params_out { ++ u8 algorithm[4]; ++ u8 encscheme[2]; ++ u8 sigscheme[2]; ++ u8 parameters[12]; /*assuming RSA*/ ++ __be32 keysize; ++ u8 modulus[256]; ++ u8 checksum[20]; ++}__attribute__((packed)); ++ ++typedef union { ++ struct tpm_input_header in; ++ struct tpm_output_header out; ++} tpm_cmd_header; ++ ++#define TPM_DIGEST_SIZE 20 ++struct tpm_pcrread_out { ++ u8 pcr_result[TPM_DIGEST_SIZE]; ++}__attribute__((packed)); ++ ++struct tpm_pcrread_in { ++ __be32 pcr_idx; ++}__attribute__((packed)); ++ ++struct tpm_pcrextend_in { ++ __be32 pcr_idx; ++ u8 hash[TPM_DIGEST_SIZE]; ++}__attribute__((packed)); ++ ++typedef union { ++ struct tpm_getcap_params_out getcap_out; ++ struct tpm_readpubek_params_out readpubek_out; ++ u8 readpubek_out_buffer[sizeof(struct tpm_readpubek_params_out)]; ++ struct tpm_getcap_params_in getcap_in; ++ struct tpm_pcrread_in pcrread_in; ++ struct tpm_pcrread_out pcrread_out; ++ struct tpm_pcrextend_in pcrextend_in; ++} tpm_cmd_params; ++ ++struct tpm_cmd_t { ++ tpm_cmd_header header; ++ tpm_cmd_params params; ++}__attribute__((packed)); ++ ++ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *); + + extern void tpm_get_timeouts(struct tpm_chip *); + extern void tpm_gen_interrupt(struct tpm_chip *); +@@ -147,6 +274,7 @@ extern struct tpm_chip* tpm_register_hardware(struct device *, + const struct tpm_vendor_specific *); + extern int tpm_open(struct inode *, struct file *); + extern int tpm_release(struct inode *, struct file *); ++extern void tpm_dev_vendor_release(struct tpm_chip *); + extern ssize_t tpm_write(struct file *, const char __user *, size_t, + loff_t *); + extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *); +diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c +index d0e7926..c64a1bc 100644 +--- a/drivers/char/tpm/tpm_atmel.c ++++ b/drivers/char/tpm/tpm_atmel.c +@@ -168,12 +168,22 @@ static void atml_plat_remove(void) + } + } + +-static struct device_driver atml_drv = { +- .name = "tpm_atmel", +- .bus = &platform_bus_type, +- .owner = THIS_MODULE, +- .suspend = tpm_pm_suspend, +- .resume = tpm_pm_resume, ++static int tpm_atml_suspend(struct platform_device *dev, pm_message_t msg) ++{ ++ return tpm_pm_suspend(&dev->dev, msg); ++} ++ ++static int tpm_atml_resume(struct platform_device *dev) ++{ ++ return tpm_pm_resume(&dev->dev); ++} ++static struct platform_driver atml_drv = { ++ .driver = { ++ .name = "tpm_atmel", ++ .owner = THIS_MODULE, ++ }, ++ .suspend = tpm_atml_suspend, ++ .resume = tpm_atml_resume, + }; + + static int __init init_atmel(void) +@@ -184,7 +194,7 @@ static int __init init_atmel(void) + unsigned long base; + struct tpm_chip *chip; + +- rc = driver_register(&atml_drv); ++ rc = platform_driver_register(&atml_drv); + if (rc) + return rc; + +@@ -223,13 +233,13 @@ err_rel_reg: + atmel_release_region(base, + region_size); + err_unreg_drv: +- driver_unregister(&atml_drv); ++ platform_driver_unregister(&atml_drv); + return rc; + } + + static void __exit cleanup_atmel(void) + { +- driver_unregister(&atml_drv); ++ platform_driver_unregister(&atml_drv); + atml_plat_remove(); + } + +diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c +index 68f052b..0c2f55a 100644 +--- a/drivers/char/tpm/tpm_bios.c ++++ b/drivers/char/tpm/tpm_bios.c +@@ -23,8 +23,6 @@ + #include + #include + #include +-#include +-#include + #include "tpm.h" + + #define TCG_EVENT_NAME_LEN_MAX 255 +@@ -214,7 +212,8 @@ static int get_event_name(char *dest, struct tcpa_event *event, + unsigned char * event_entry) + { + const char *name = ""; +- char data[40] = ""; ++ /* 41 so there is room for 40 data and 1 nul */ ++ char data[41] = ""; + int i, n_len = 0, d_len = 0; + struct tcpa_pc_event *pc_event; + +diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c +index 726ee8a..ecba494 100644 +--- a/drivers/char/tpm/tpm_infineon.c ++++ b/drivers/char/tpm/tpm_infineon.c +@@ -4,7 +4,7 @@ + * SLD 9630 TT 1.1 and SLB 9635 TT 1.2 Trusted Platform Module + * Specifications at www.trustedcomputinggroup.org + * +- * Copyright (C) 2005, Marcel Selhorst ++ * Copyright (C) 2005, Marcel Selhorst + * Sirrix AG - security technologies, http://www.sirrix.com and + * Applied Data Security Group, Ruhr-University Bochum, Germany + * Project-Homepage: http://www.prosec.rub.de/tpm +@@ -636,7 +636,7 @@ static void __exit cleanup_inf(void) + module_init(init_inf); + module_exit(cleanup_inf); + +-MODULE_AUTHOR("Marcel Selhorst "); ++MODULE_AUTHOR("Marcel Selhorst "); + MODULE_DESCRIPTION("Driver for Infineon TPM SLD 9630 TT 1.1 / SLB 9635 TT 1.2"); + MODULE_VERSION("1.9"); + MODULE_LICENSE("GPL"); +diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c +index ab18c1e..70efba2 100644 +--- a/drivers/char/tpm/tpm_nsc.c ++++ b/drivers/char/tpm/tpm_nsc.c +@@ -273,12 +273,23 @@ static void tpm_nsc_remove(struct device *dev) + } + } + +-static struct device_driver nsc_drv = { +- .name = "tpm_nsc", +- .bus = &platform_bus_type, +- .owner = THIS_MODULE, +- .suspend = tpm_pm_suspend, +- .resume = tpm_pm_resume, ++static int tpm_nsc_suspend(struct platform_device *dev, pm_message_t msg) ++{ ++ return tpm_pm_suspend(&dev->dev, msg); ++} ++ ++static int tpm_nsc_resume(struct platform_device *dev) ++{ ++ return tpm_pm_resume(&dev->dev); ++} ++ ++static struct platform_driver nsc_drv = { ++ .suspend = tpm_nsc_suspend, ++ .resume = tpm_nsc_resume, ++ .driver = { ++ .name = "tpm_nsc", ++ .owner = THIS_MODULE, ++ }, + }; + + static int __init init_nsc(void) +@@ -297,7 +308,7 @@ static int __init init_nsc(void) + return -ENODEV; + } + +- err = driver_register(&nsc_drv); ++ err = platform_driver_register(&nsc_drv); + if (err) + return err; + +@@ -308,17 +319,15 @@ static int __init init_nsc(void) + /* enable the DPM module */ + tpm_write_index(nscAddrBase, NSC_LDC_INDEX, 0x01); + +- pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); ++ pdev = platform_device_alloc("tpm_nscl0", -1); + if (!pdev) { + rc = -ENOMEM; + goto err_unreg_drv; + } + +- pdev->name = "tpm_nscl0"; +- pdev->id = -1; + pdev->num_resources = 0; ++ pdev->dev.driver = &nsc_drv.driver; + pdev->dev.release = tpm_nsc_remove; +- pdev->dev.driver = &nsc_drv; + + if ((rc = platform_device_register(pdev)) < 0) + goto err_free_dev; +@@ -377,7 +386,7 @@ err_unreg_dev: + err_free_dev: + kfree(pdev); + err_unreg_drv: +- driver_unregister(&nsc_drv); ++ platform_driver_unregister(&nsc_drv); + return rc; + } + +@@ -390,7 +399,7 @@ static void __exit cleanup_nsc(void) + pdev = NULL; + } + +- driver_unregister(&nsc_drv); ++ platform_driver_unregister(&nsc_drv); + } + + module_init(init_nsc); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c -index ed1879c..b83b305 100644 +index ed1879c..22b2a69 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c -@@ -293,7 +293,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) +@@ -21,9 +21,12 @@ + #include + #include + #include +-#include + #include + #include ++ ++#include ++#include ++ + #include "tpm.h" + + #define TPM_HEADER_SIZE 10 +@@ -77,6 +80,14 @@ enum tis_defaults { + static LIST_HEAD(tis_chips); + static DEFINE_SPINLOCK(tis_lock); + ++struct tpm_data { ++ unsigned long tpm_phys_address; ++ void __iomem *tpm_address; ++ int tpm_size; ++ int tpm_irq; ++ int itpm; ++}; ++ + static int check_locality(struct tpm_chip *chip, int l) + { + if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) & +@@ -267,6 +278,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) + int rc, status, burstcnt; + size_t count = 0; + u32 ordinal; ++ struct tpm_data *tpm = to_acpi_device(chip->dev)->driver_data; + + if (request_locality(chip, 0) < 0) + return -EBUSY; +@@ -293,7 +305,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &chip->vendor.int_queue); status = tpm_tis_status(chip); - if ((status & TPM_STS_DATA_EXPECT) == 0) { -+ if ((status & TPM_STS_VALID) == 0) { ++ if (!tpm->itpm && (status & TPM_STS_DATA_EXPECT) == 0) { rc = -EIO; goto out_err; } -@@ -430,7 +430,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) - return IRQ_HANDLED; - } - --static int interrupts = 1; -+static int interrupts = 0; +@@ -434,12 +446,17 @@ static int interrupts = 1; module_param(interrupts, bool, 0444); MODULE_PARM_DESC(interrupts, "Enable interrupts"); -@@ -450,19 +450,19 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, ++static int itpm; ++module_param(itpm, bool, 0444); ++MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)"); ++ + static int tpm_tis_init(struct device *dev, resource_size_t start, + resource_size_t len, unsigned int irq) + { + u32 vendor, intfcaps, intmask; + int rc, i; + struct tpm_chip *chip; ++ struct tpm_data *tpm = to_acpi_device(dev)->driver_data; + + if (!(chip = tpm_register_hardware(dev, &tpm_tis))) + return -ENODEV; +@@ -450,6 +467,12 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, goto out_err; } -- if (request_locality(chip, 0) != 0) { -- rc = -ENODEV; -- goto out_err; -- } -- -- vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); ++ /* Default timeouts */ ++ chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); ++ chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); ++ chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); ++ chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); ++ + if (request_locality(chip, 0) != 0) { + rc = -ENODEV; + goto out_err; +@@ -457,16 +480,15 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, + + vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); + +- /* Default timeouts */ +- chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); +- chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); +- chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); +- chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - - /* Default timeouts */ - chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); - chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + dev_info(dev, +- "1.2 TPM (device-id 0x%X, rev-id %d)\n", ++ "1.2 TPM (%04X:%04X rev %d)\n", vendor & 0xffff, + vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); -+ if (request_locality(chip, 0) != 0) { -+ rc = -ENODEV; -+ goto out_err; ++ if (itpm || vendor == 0x10208086) { ++ dev_info(dev, "Intel iTPM workaround enabled\n"); ++ tpm->itpm = 1; + } + -+ vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); + /* Figure out the capabilities */ + intfcaps = + ioread32(chip->vendor.iobase + +@@ -474,23 +496,23 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, + dev_dbg(dev, "TPM interface capabilities (0x%x):\n", + intfcaps); + if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) +- dev_dbg(dev, "\tBurst Count Static\n"); ++ dev_dbg(dev, " Burst Count Static\n"); + if (intfcaps & TPM_INTF_CMD_READY_INT) +- dev_dbg(dev, "\tCommand Ready Int Support\n"); ++ dev_dbg(dev, " Command Ready Int Support\n"); + if (intfcaps & TPM_INTF_INT_EDGE_FALLING) +- dev_dbg(dev, "\tInterrupt Edge Falling\n"); ++ dev_dbg(dev, " Interrupt Edge Falling\n"); + if (intfcaps & TPM_INTF_INT_EDGE_RISING) +- dev_dbg(dev, "\tInterrupt Edge Rising\n"); ++ dev_dbg(dev, " Interrupt Edge Rising\n"); + if (intfcaps & TPM_INTF_INT_LEVEL_LOW) +- dev_dbg(dev, "\tInterrupt Level Low\n"); ++ dev_dbg(dev, " Interrupt Level Low\n"); + if (intfcaps & TPM_INTF_INT_LEVEL_HIGH) +- dev_dbg(dev, "\tInterrupt Level High\n"); ++ dev_dbg(dev, " Interrupt Level High\n"); + if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT) +- dev_dbg(dev, "\tLocality Change Int Support\n"); ++ dev_dbg(dev, " Locality Change Int Support\n"); + if (intfcaps & TPM_INTF_STS_VALID_INT) +- dev_dbg(dev, "\tSts Valid Int Support\n"); ++ dev_dbg(dev, " Sts Valid Int Support\n"); + if (intfcaps & TPM_INTF_DATA_AVAIL_INT) +- dev_dbg(dev, "\tData Avail Int Support\n"); ++ dev_dbg(dev, " Data Avail Int Support\n"); + + /* INTERRUPT Setup */ + init_waitqueue_head(&chip->vendor.read_queue); +@@ -590,34 +612,114 @@ out_err: + return rc; + } + +-static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, +- const struct pnp_device_id *pnp_id) ++static acpi_status tpm_resources(struct acpi_resource *res, void *data) + { +- resource_size_t start, len; +- unsigned int irq = 0; ++ struct acpi_device *device = data; ++ struct device *dev = &device->dev; ++ struct tpm_data *tpm = device->driver_data; ++ acpi_status status; ++ struct acpi_resource_address64 addr; + - dev_info(dev, - "1.2 TPM (device-id 0x%X, rev-id %d)\n", - vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); -@@ -653,7 +653,7 @@ static struct device_driver tis_drv = { ++ status = acpi_resource_to_address64(res, &addr); ++ ++ if (ACPI_SUCCESS(status)) { ++ dev_info(dev, "found 0x%llx(0x%llx)\n", ++ (long long)addr.minimum, ++ (long long)addr.address_length); ++ tpm->tpm_phys_address = addr.minimum; ++ tpm->tpm_address = ioremap(addr.minimum, addr.address_length); ++ tpm->tpm_size = addr.address_length; ++ } else if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) { ++ struct acpi_resource_fixed_memory32 *fixmem32; ++ ++ fixmem32 = &res->data.fixed_memory32; ++ if (!fixmem32) ++ return AE_NO_MEMORY; ++ ++ dev_info(dev, "found 0x%llx(0x%llx)\n", ++ (long long)fixmem32->address, ++ (long long)TIS_MEM_LEN); ++ tpm->tpm_phys_address = fixmem32->address; ++ tpm->tpm_address = ioremap(fixmem32->address, TIS_MEM_LEN); ++ tpm->tpm_size = fixmem32->address_length; ++ } else if (res->type == ACPI_RESOURCE_TYPE_EXTENDED_IRQ) { ++ struct acpi_resource_extended_irq *irq; ++ ++ irq = &res->data.extended_irq; ++ if (irq->interrupt_count > 0 && irq->interrupts[0] > 0) { ++ dev_info(dev, "IRQ %d (%d, %d)\n", ++ irq->interrupts[0], ++ irq->triggering, irq->polarity); ++ tpm->tpm_irq = irq->interrupts[0]; ++ } ++ } ++ ++ return AE_OK; ++} ++ ++static int tpm_tis_acpi_add(struct acpi_device *device) ++{ ++ acpi_status result; ++ struct tpm_data *tpm = kzalloc(sizeof(*tpm), GFP_KERNEL); ++ int rc = -ENOMEM; ++ ++ if (!tpm) ++ goto out; + +- start = pnp_mem_start(pnp_dev, 0); +- len = pnp_mem_len(pnp_dev, 0); ++ device->driver_data = tpm; + +- if (pnp_irq_valid(pnp_dev, 0)) +- irq = pnp_irq(pnp_dev, 0); +- else ++ result = acpi_walk_resources(device->handle, METHOD_NAME__CRS, ++ tpm_resources, device); ++ ++ rc = -ENODEV; ++ if (ACPI_FAILURE(result)) ++ goto out_free; ++ ++ if (!tpm->tpm_address) { ++ dev_err(&device->dev, "no address found in _CRS\n"); ++ goto out_free; ++ } ++ ++ if (!tpm->tpm_irq) { ++ dev_err(&device->dev, "no IRQ found in _CRS, polling mode\n"); + interrupts = 0; ++ } + +- return tpm_tis_init(&pnp_dev->dev, start, len, irq); ++ return tpm_tis_init(&device->dev, tpm->tpm_phys_address, tpm->tpm_size, ++ tpm->tpm_irq); ++out_free: ++ kfree(tpm); ++out: ++ return rc; + } + +-static int tpm_tis_pnp_suspend(struct pnp_dev *dev, pm_message_t msg) ++static int tpm_tis_acpi_remove(struct acpi_device *device, int type) + { +- return tpm_pm_suspend(&dev->dev, msg); ++ struct tpm_chip *chip = dev_get_drvdata(&device->dev); ++ tpm_remove_hardware(&device->dev); ++ iowrite32(~TPM_GLOBAL_INT_ENABLE & ioread32(chip->vendor.iobase + ++ TPM_INT_ENABLE(chip->vendor.locality)), ++ chip->vendor.iobase + ++ TPM_INT_ENABLE(chip->vendor.locality)); ++ release_locality(chip, chip->vendor.locality, 1); ++ if (chip->vendor.irq) ++ free_irq(chip->vendor.irq, chip); ++ iounmap(chip->vendor.iobase); ++ kfree(device->driver_data); ++ return 0; ++} ++ ++static int tpm_tis_acpi_suspend(struct acpi_device *dev, pm_message_t state) ++{ ++ return tpm_pm_suspend(&dev->dev, state); + } + +-static int tpm_tis_pnp_resume(struct pnp_dev *dev) ++static int tpm_tis_acpi_resume(struct acpi_device *dev) + { + return tpm_pm_resume(&dev->dev); + } + +-static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = { ++static struct acpi_device_id tpm_tis_acpi_tbl[] __devinitdata = { + {"PNP0C31", 0}, /* TPM */ + {"ATM1200", 0}, /* Atmel */ + {"IFX0102", 0}, /* Infineon */ +@@ -625,30 +727,45 @@ static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = { + {"BCM0102", 0}, /* Broadcom */ + {"NSC1200", 0}, /* National */ + {"ICO0102", 0}, /* Intel */ ++ {"INTC0102", 0}, /* TPM spec says to look for this in _CID */ + /* Add new here */ + {"", 0}, /* User Specified */ + {"", 0} /* Terminator */ + }; ++MODULE_DEVICE_TABLE(acpi, tpm_tis_acpi_tbl); + +-static struct pnp_driver tis_pnp_driver = { ++static struct acpi_driver tis_acpi_driver = { + .name = "tpm_tis", +- .id_table = tpm_pnp_tbl, +- .probe = tpm_tis_pnp_init, +- .suspend = tpm_tis_pnp_suspend, +- .resume = tpm_tis_pnp_resume, ++ .ids = tpm_tis_acpi_tbl, ++ .ops = { ++ .add = tpm_tis_acpi_add, ++ .remove = tpm_tis_acpi_remove, ++ .suspend = tpm_tis_acpi_suspend, ++ .resume = tpm_tis_acpi_resume, ++ }, + }; + +-#define TIS_HID_USR_IDX sizeof(tpm_pnp_tbl)/sizeof(struct pnp_device_id) -2 +-module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id, +- sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444); ++#define TIS_HID_USR_IDX (ARRAY_SIZE(tpm_tis_acpi_tbl) - 2) ++module_param_string(hid, tpm_tis_acpi_tbl[TIS_HID_USR_IDX].id, ++ sizeof(tpm_tis_acpi_tbl[TIS_HID_USR_IDX].id), 0444); + MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe"); + +-static struct device_driver tis_drv = { +- .name = "tpm_tis", +- .bus = &platform_bus_type, +- .owner = THIS_MODULE, +- .suspend = tpm_pm_suspend, +- .resume = tpm_pm_resume, ++static int tpm_tis_suspend(struct platform_device *dev, pm_message_t msg) ++{ ++ return tpm_pm_suspend(&dev->dev, msg); ++} ++ ++static int tpm_tis_resume(struct platform_device *dev) ++{ ++ return tpm_pm_resume(&dev->dev); ++} ++static struct platform_driver tis_drv = { ++ .driver = { ++ .name = "tpm_tis", ++ .owner = THIS_MODULE, ++ }, ++ .suspend = tpm_tis_suspend, ++ .resume = tpm_tis_resume, + }; static struct platform_device *pdev; +@@ -661,19 +778,19 @@ static int __init init_tis(void) + int rc; + + if (force) { +- rc = driver_register(&tis_drv); ++ rc = platform_driver_register(&tis_drv); + if (rc < 0) + return rc; + if (IS_ERR(pdev=platform_device_register_simple("tpm_tis", -1, NULL, 0))) + return PTR_ERR(pdev); + if((rc=tpm_tis_init(&pdev->dev, TIS_MEM_BASE, TIS_MEM_LEN, 0)) != 0) { + platform_device_unregister(pdev); +- driver_unregister(&tis_drv); ++ platform_driver_unregister(&tis_drv); + } + return rc; + } + +- return pnp_register_driver(&tis_pnp_driver); ++ return acpi_bus_register_driver(&tis_acpi_driver); + } + + static void __exit cleanup_tis(void) +@@ -683,6 +800,7 @@ static void __exit cleanup_tis(void) + spin_lock(&tis_lock); + list_for_each_entry_safe(i, j, &tis_chips, list) { + chip = to_tpm_chip(i); ++ tpm_remove_hardware(chip->dev); + iowrite32(~TPM_GLOBAL_INT_ENABLE & + ioread32(chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor. +@@ -694,14 +812,14 @@ static void __exit cleanup_tis(void) + free_irq(chip->vendor.irq, chip); + iounmap(i->iobase); + list_del(&i->list); +- tpm_remove_hardware(chip->dev); + } + spin_unlock(&tis_lock); ++ + if (force) { + platform_device_unregister(pdev); +- driver_unregister(&tis_drv); ++ platform_driver_unregister(&tis_drv); + } else +- pnp_unregister_driver(&tis_pnp_driver); ++ acpi_bus_unregister_driver(&tis_acpi_driver); + } --static int force; -+static int force = 1; - module_param(force, bool, 0444); - MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry"); - static int __init init_tis(void) + module_init(init_tis);