Index: root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm.c =================================================================== --- root.orig/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm.c +++ root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm.c @@ -30,15 +30,295 @@ enum tpm_const { TPM_MINOR = 224, /* officially assigned */ - TPM_MIN_BUFSIZE = 2048, - TPM_MAX_BUFSIZE = 64 * 1024, +#ifndef CONFIG_XEN + TPM_BUFSIZE = 2048, +#endif TPM_NUM_DEVICES = 256, - TPM_NUM_MASK_ENTRIES = TPM_NUM_DEVICES / (8 * sizeof(int)) }; +enum tpm_duration { + TPM_SHORT = 0, + TPM_MEDIUM = 1, + TPM_LONG = 2, + TPM_UNDEFINED, +}; + +#define TPM_MAX_ORDINAL 243 +#define TPM_MAX_PROTECTED_ORDINAL 12 +#define TPM_PROTECTED_ORDINAL_MASK 0xFF + static LIST_HEAD(tpm_chip_list); static DEFINE_SPINLOCK(driver_lock); -static int dev_mask[TPM_NUM_MASK_ENTRIES]; +static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES); + +/* + * Array with one entry per ordinal defining the maximum amount + * of time the chip could take to return the result. The ordinal + * designation of short, medium or long is defined in a table in + * TCG Specification TPM Main Part 2 TPM Structures Section 17. The + * values of the SHORT, MEDIUM, and LONG durations are retrieved + * from the chip during initialization with a call to tpm_get_timeouts. + */ +static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = { + TPM_UNDEFINED, /* 0 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 5 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 10 */ + TPM_SHORT, +}; + +static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = { + TPM_UNDEFINED, /* 0 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 5 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 10 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_LONG, + TPM_LONG, + TPM_MEDIUM, /* 15 */ + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, + TPM_LONG, + TPM_SHORT, /* 20 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_SHORT, /* 25 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, /* 30 */ + TPM_LONG, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 35 */ + TPM_MEDIUM, + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 40 */ + TPM_LONG, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 45 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_LONG, + TPM_MEDIUM, /* 50 */ + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 55 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 60 */ + TPM_MEDIUM, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, /* 65 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 70 */ + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 75 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_LONG, /* 80 */ + TPM_UNDEFINED, + TPM_MEDIUM, + TPM_LONG, + TPM_SHORT, + TPM_UNDEFINED, /* 85 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 90 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, /* 95 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 100 */ + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 105 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 110 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 115 */ + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_LONG, /* 120 */ + TPM_LONG, + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_SHORT, + TPM_SHORT, /* 125 */ + TPM_SHORT, + TPM_LONG, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, /* 130 */ + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_SHORT, + TPM_MEDIUM, + TPM_UNDEFINED, /* 135 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 140 */ + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 145 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 150 */ + TPM_MEDIUM, + TPM_MEDIUM, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, /* 155 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 160 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 165 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_LONG, /* 170 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 175 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_MEDIUM, /* 180 */ + TPM_SHORT, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, /* 185 */ + TPM_SHORT, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 190 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 195 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 200 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, + TPM_SHORT, /* 205 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_MEDIUM, /* 210 */ + TPM_UNDEFINED, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_MEDIUM, + TPM_UNDEFINED, /* 215 */ + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, + TPM_SHORT, /* 220 */ + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_SHORT, + TPM_UNDEFINED, /* 225 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 230 */ + TPM_LONG, + TPM_MEDIUM, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, /* 235 */ + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_UNDEFINED, + TPM_SHORT, /* 240 */ + TPM_UNDEFINED, + TPM_MEDIUM, +}; static void user_reader_timeout(unsigned long ptr) { @@ -47,28 +327,58 @@ static void user_reader_timeout(unsigned schedule_work(&chip->work); } -static void timeout_work(void * ptr) +static void timeout_work(void *ptr) { struct tpm_chip *chip = ptr; down(&chip->buffer_mutex); atomic_set(&chip->data_pending, 0); +#ifndef CONFIG_XEN + memset(chip->data_buffer, 0, TPM_BUFSIZE); +#else memset(chip->data_buffer, 0, get_chip_buffersize(chip)); +#endif up(&chip->buffer_mutex); } /* + * Returns max number of jiffies to wait + */ +unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, + u32 ordinal) +{ + int duration_idx = TPM_UNDEFINED; + int duration = 0; + + if (ordinal < TPM_MAX_ORDINAL) + duration_idx = tpm_ordinal_duration[ordinal]; + else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) < + TPM_MAX_PROTECTED_ORDINAL) + duration_idx = + tpm_protected_ordinal_duration[ordinal & + TPM_PROTECTED_ORDINAL_MASK]; + + if (duration_idx != TPM_UNDEFINED) + duration = chip->vendor.duration[duration_idx]; + if (duration <= 0) + return 2 * 60 * HZ; + else + return duration; +} +EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); + +/* * Internal kernel interface to transmit TPM commands */ -static ssize_t tpm_transmit(struct tpm_chip * chip, const char *buf, +static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, size_t bufsiz) { ssize_t rc; - u32 count; + u32 count, ordinal; unsigned long stop; count = be32_to_cpu(*((__be32 *) (buf + 2))); - + ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); if (count == 0) return -ENODATA; if (count > bufsiz) { @@ -79,21 +389,23 @@ static ssize_t tpm_transmit(struct tpm_c down(&chip->tpm_mutex); - if ((rc = chip->vendor->send(chip, (u8 *) buf, count)) < 0) { + if ((rc = chip->vendor.send(chip, (u8 *) buf, count)) < 0) { dev_err(chip->dev, "tpm_transmit: tpm_send: error %zd\n", rc); goto out; } - stop = jiffies + 2 * 60 * HZ; + if (chip->vendor.irq) + goto out_recv; + + stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); do { - u8 status = chip->vendor->status(chip); - if ((status & chip->vendor->req_complete_mask) == - chip->vendor->req_complete_val) { + u8 status = chip->vendor.status(chip); + if ((status & chip->vendor.req_complete_mask) == + chip->vendor.req_complete_val) goto out_recv; - } - if ((status == chip->vendor->req_canceled)) { + if ((status == chip->vendor.req_canceled)) { dev_err(chip->dev, "Operation Canceled\n"); rc = -ECANCELED; goto out; @@ -103,14 +415,13 @@ static ssize_t tpm_transmit(struct tpm_c rmb(); } while (time_before(jiffies, stop)); - - chip->vendor->cancel(chip); + chip->vendor.cancel(chip); dev_err(chip->dev, "Operation Timed out\n"); rc = -ETIME; goto out; out_recv: - rc = chip->vendor->recv(chip, (u8 *) buf, bufsiz); + rc = chip->vendor.recv(chip, (u8 *) buf, bufsiz); if (rc < 0) dev_err(chip->dev, "tpm_transmit: tpm_recv: error %zd\n", rc); @@ -120,17 +431,247 @@ out: } #define TPM_DIGEST_SIZE 20 -#define CAP_PCR_RESULT_SIZE 18 -static const u8 cap_pcr[] = { +#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, +}; + +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. + */ + +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, 5, - 0, 0, 0, 4, - 0, 0, 1, 1 + 0, 0, 0, 0, /* TPM_CAP_ */ + 0, 0, 0, 4, /* TPM_CAP_SUB_ size */ + 0, 0, 1, 0 /* TPM_CAP_SUB_ */ }; -#define READ_PCR_RESULT_SIZE 30 +static ssize_t transmit_cmd(struct tpm_chip *chip, u8 *data, int len, + char *desc) +{ + int err; + + len = tpm_transmit(chip, data, len); + if (len < 0) + return len; + if (len == TPM_ERROR_SIZE) { + err = be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))); + dev_dbg(chip->dev, "A TPM error (%d) occurred %s\n", err, desc); + return err; + } + 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 = msecs_to_jiffies(timeout); + timeout = + be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX))); + if (timeout) + chip->vendor.timeout_b = msecs_to_jiffies(timeout); + timeout = + be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX))); + if (timeout) + chip->vendor.timeout_c = msecs_to_jiffies(timeout); + timeout = + be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_4_IDX))); + if (timeout) + chip->vendor.timeout_d = msecs_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; + + 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] = + msecs_to_jiffies(be32_to_cpu + (*((__be32 *) (data + + TPM_GET_CAP_RET_UINT32_1_IDX)))); + chip->vendor.duration[TPM_MEDIUM] = + msecs_to_jiffies(be32_to_cpu + (*((__be32 *) (data + + TPM_GET_CAP_RET_UINT32_2_IDX)))); + chip->vendor.duration[TPM_LONG] = + msecs_to_jiffies(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 */ @@ -141,8 +682,8 @@ static const u8 pcrread[] = { ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr, char *buf) { - u8 data[READ_PCR_RESULT_SIZE]; - ssize_t len; + u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(pcrread)), 30)]; + ssize_t rc; int i, j, num_pcrs; __be32 index; char *str = buf; @@ -151,29 +692,24 @@ ssize_t tpm_show_pcrs(struct device *dev if (chip == NULL) return -ENODEV; - memcpy(data, cap_pcr, sizeof(cap_pcr)); - if ((len = tpm_transmit(chip, data, sizeof(data))) - < CAP_PCR_RESULT_SIZE) { - dev_dbg(chip->dev, "A TPM error (%d) occurred " - "attempting to determine the number of PCRS\n", - be32_to_cpu(*((__be32 *) (data + 6)))); + 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, sizeof(data), + "attempting to determine the number of PCRS"); + if (rc) return 0; - } num_pcrs = be32_to_cpu(*((__be32 *) (data + 14))); - for (i = 0; i < num_pcrs; i++) { memcpy(data, pcrread, sizeof(pcrread)); index = cpu_to_be32(i); memcpy(data + 10, &index, 4); - if ((len = tpm_transmit(chip, data, sizeof(data))) - < READ_PCR_RESULT_SIZE){ - dev_dbg(chip->dev, "A TPM error (%d) occurred" - " attempting to read PCR %d of %d\n", - be32_to_cpu(*((__be32 *) (data + 6))), - i, num_pcrs); + rc = transmit_cmd(chip, data, sizeof(data), + "attempting to read a PCR"); + if (rc) goto out; - } str += sprintf(str, "PCR-%02d: ", i); for (j = 0; j < TPM_DIGEST_SIZE; j++) str += sprintf(str, "%02X ", *(data + 10 + j)); @@ -195,7 +731,7 @@ ssize_t tpm_show_pubek(struct device *de char *buf) { u8 *data; - ssize_t len; + ssize_t err; int i, rc; char *str = buf; @@ -209,14 +745,10 @@ ssize_t tpm_show_pubek(struct device *de memcpy(data, readpubek, sizeof(readpubek)); - if ((len = tpm_transmit(chip, data, READ_PUBEK_RESULT_SIZE)) < - READ_PUBEK_RESULT_SIZE) { - dev_dbg(chip->dev, "A TPM error (%d) occurred " - "attempting to read the PUBEK\n", - be32_to_cpu(*((__be32 *) (data + 6)))); - rc = 0; + err = transmit_cmd(chip, data, READ_PUBEK_RESULT_SIZE, + "attempting to read the PUBEK"); + if (err) goto out; - } /* ignore header 10 bytes @@ -246,36 +778,68 @@ ssize_t tpm_show_pubek(struct device *de if ((i + 1) % 16 == 0) str += sprintf(str, "\n"); } - rc = str - buf; out: + rc = str - buf; kfree(data); return rc; } EXPORT_SYMBOL_GPL(tpm_show_pubek); -#define CAP_VER_RESULT_SIZE 18 +#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, 6, + 0, 0, 0, 0, 0, 0, 0, 0 }; -#define CAP_MANUFACTURER_RESULT_SIZE 18 -static const u8 cap_manufacturer[] = { - 0, 193, /* TPM_TAG_RQU_COMMAND */ - 0, 0, 0, 22, /* length */ - 0, 0, 0, 101, /* TPM_ORD_GetCapability */ - 0, 0, 0, 5, - 0, 0, 0, 4, - 0, 0, 1, 3 -}; - ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr, char *buf) { - u8 data[sizeof(cap_manufacturer)]; + u8 data[max_t(int, max(ARRAY_SIZE(tpm_cap), ARRAY_SIZE(cap_version)), 30)]; + ssize_t rc; + 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; + + rc = transmit_cmd(chip, data, sizeof(data), + "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_1; + rc = transmit_cmd(chip, data, sizeof(data), + "attempting to determine the 1.1 version"); + if (rc) + goto out; + + 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: + return str - buf; +} +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; @@ -283,29 +847,40 @@ ssize_t tpm_show_caps(struct device *dev if (chip == NULL) return -ENODEV; - memcpy(data, cap_manufacturer, sizeof(cap_manufacturer)); + 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))) < - CAP_MANUFACTURER_RESULT_SIZE) - return len; + 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 + 14)))); + 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))) < - CAP_VER_RESULT_SIZE) - return len; - - str += - sprintf(str, "TCG version: %d.%d\nFirmware version: %d.%d\n", - (int) data[14], (int) data[15], (int) data[16], - (int) data[17]); + 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); +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) @@ -314,7 +889,7 @@ ssize_t tpm_store_cancel(struct device * if (chip == NULL) return 0; - chip->vendor->cancel(chip); + chip->vendor.cancel(chip); return count; } EXPORT_SYMBOL_GPL(tpm_store_cancel); @@ -330,7 +905,7 @@ int tpm_open(struct inode *inode, struct spin_lock(&driver_lock); list_for_each_entry(pos, &tpm_chip_list, list) { - if (pos->vendor->miscdev.minor == minor) { + if (pos->vendor.miscdev.minor == minor) { chip = pos; break; } @@ -352,7 +927,12 @@ int tpm_open(struct inode *inode, struct spin_unlock(&driver_lock); - chip->data_buffer = kmalloc(get_chip_buffersize(chip) * sizeof(u8), GFP_KERNEL); +#ifndef CONFIG_XEN + chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); +#else + chip->data_buffer = kmalloc(get_chip_buffersize(chip) * sizeof(u8), + GFP_KERNEL); +#endif if (chip->data_buffer == NULL) { chip->num_opens--; put_device(chip->dev); @@ -388,7 +968,7 @@ int tpm_release(struct inode *inode, str EXPORT_SYMBOL_GPL(tpm_release); ssize_t tpm_write(struct file *file, const char __user *buf, - size_t size, loff_t * off) + size_t size, loff_t *off) { struct tpm_chip *chip = file->private_data; int in_size = size, out_size; @@ -400,8 +980,13 @@ ssize_t tpm_write(struct file *file, con down(&chip->buffer_mutex); +#ifndef CONFIG_XEN + if (in_size > TPM_BUFSIZE) + in_size = TPM_BUFSIZE; +#else if (in_size > get_chip_buffersize(chip)) in_size = get_chip_buffersize(chip); +#endif if (copy_from_user (chip->data_buffer, (void __user *) buf, in_size)) { @@ -410,11 +995,17 @@ ssize_t tpm_write(struct file *file, con } /* atomic tpm command send and result receive */ - out_size = tpm_transmit(chip, chip->data_buffer, +#ifndef CONFIG_XEN + out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE); +#else + out_size = tpm_transmit(chip, chip->data_buffer, get_chip_buffersize(chip)); +#endif atomic_set(&chip->data_pending, out_size); +#ifdef CONFIG_XEN atomic_set(&chip->data_position, 0); +#endif up(&chip->buffer_mutex); /* Set a timeout by which the reader must come claim the result */ @@ -422,42 +1013,59 @@ ssize_t tpm_write(struct file *file, con return in_size; } - EXPORT_SYMBOL_GPL(tpm_write); -ssize_t tpm_read(struct file * file, char __user *buf, - size_t size, loff_t * off) +ssize_t tpm_read(struct file *file, char __user *buf, + size_t size, loff_t *off) { struct tpm_chip *chip = file->private_data; int ret_size; +#ifdef CONFIG_XEN int pos, pending = 0; +#endif +#ifndef CONFIG_XEN + del_singleshot_timer_sync(&chip->user_read_timer); + flush_scheduled_work(); +#endif ret_size = atomic_read(&chip->data_pending); +#ifndef CONFIG_XEN + atomic_set(&chip->data_pending, 0); +#endif if (ret_size > 0) { /* relay data */ if (size < ret_size) ret_size = size; +#ifdef CONFIG_XEN pos = atomic_read(&chip->data_position); - +#endif down(&chip->buffer_mutex); +#ifndef CONFIG_XEN + if (copy_to_user(buf, chip->data_buffer, ret_size)) +#else if (copy_to_user(buf, &chip->data_buffer[pos], ret_size)) { +#endif ret_size = -EFAULT; +#ifdef CONFIG_XEN } else { pending = atomic_read(&chip->data_pending) - ret_size; if ( pending ) { - atomic_set( &chip->data_pending, pending ); - atomic_set( &chip->data_position, pos+ret_size ); + atomic_set(&chip->data_pending, pending); + atomic_set(&chip->data_position, + pos+ret_size); } } +#endif up(&chip->buffer_mutex); } - - if ( ret_size <= 0 || pending == 0 ) { - atomic_set( &chip->data_pending, 0 ); + +#ifdef CONFIG_XEN + if ( ret_size <= 0 || pending == 0 ) { + atomic_set(&chip->data_pending, 0); del_singleshot_timer_sync(&chip->user_read_timer); flush_scheduled_work(); } - +#endif return ret_size; } EXPORT_SYMBOL_GPL(tpm_read); @@ -478,14 +1086,13 @@ void tpm_remove_hardware(struct device * spin_unlock(&driver_lock); dev_set_drvdata(dev, NULL); - misc_deregister(&chip->vendor->miscdev); - kfree(chip->vendor->miscdev.name); + misc_deregister(&chip->vendor.miscdev); + kfree(chip->vendor.miscdev.name); - sysfs_remove_group(&dev->kobj, chip->vendor->attr_group); + sysfs_remove_group(&dev->kobj, chip->vendor.attr_group); tpm_bios_log_teardown(chip->bios_dir); - dev_mask[chip->dev_num / TPM_NUM_MASK_ENTRIES ] &= - ~(1 << (chip->dev_num % TPM_NUM_MASK_ENTRIES)); + clear_bit(chip->dev_num, dev_mask); kfree(chip); @@ -536,18 +1143,18 @@ EXPORT_SYMBOL_GPL(tpm_pm_resume); * upon errant exit from this function specific probe function should call * pci_disable_device */ -int tpm_register_hardware(struct device *dev, struct tpm_vendor_specific *entry) +struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vendor_specific + *entry) { #define DEVNAME_SIZE 7 char *devname; struct tpm_chip *chip; - int i, j; /* Driver specific per-device data */ chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (chip == NULL) - return -ENOMEM; + return NULL; init_MUTEX(&chip->buffer_mutex); init_MUTEX(&chip->tpm_mutex); @@ -559,51 +1166,37 @@ int tpm_register_hardware(struct device chip->user_read_timer.function = user_reader_timeout; chip->user_read_timer.data = (unsigned long) chip; - chip->vendor = entry; - - if (entry->buffersize < TPM_MIN_BUFSIZE) { - entry->buffersize = TPM_MIN_BUFSIZE; - } else if (entry->buffersize > TPM_MAX_BUFSIZE) { - entry->buffersize = TPM_MAX_BUFSIZE; - } - - chip->dev_num = -1; - - for (i = 0; i < TPM_NUM_MASK_ENTRIES; i++) - for (j = 0; j < 8 * sizeof(int); j++) - if ((dev_mask[i] & (1 << j)) == 0) { - chip->dev_num = - i * TPM_NUM_MASK_ENTRIES + j; - dev_mask[i] |= 1 << j; - goto dev_num_search_complete; - } + memcpy(&chip->vendor, entry, sizeof(struct tpm_vendor_specific)); + + chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES); -dev_num_search_complete: - if (chip->dev_num < 0) { + if (chip->dev_num >= TPM_NUM_DEVICES) { dev_err(dev, "No available tpm device numbers\n"); kfree(chip); - return -ENODEV; + return NULL; } else if (chip->dev_num == 0) - chip->vendor->miscdev.minor = TPM_MINOR; + chip->vendor.miscdev.minor = TPM_MINOR; else - chip->vendor->miscdev.minor = MISC_DYNAMIC_MINOR; + chip->vendor.miscdev.minor = MISC_DYNAMIC_MINOR; + + set_bit(chip->dev_num, dev_mask); devname = kmalloc(DEVNAME_SIZE, GFP_KERNEL); scnprintf(devname, DEVNAME_SIZE, "%s%d", "tpm", chip->dev_num); - chip->vendor->miscdev.name = devname; + chip->vendor.miscdev.name = devname; - chip->vendor->miscdev.dev = dev; + chip->vendor.miscdev.dev = dev; chip->dev = get_device(dev); - if (misc_register(&chip->vendor->miscdev)) { + if (misc_register(&chip->vendor.miscdev)) { dev_err(chip->dev, "unable to misc_register %s, minor %d\n", - chip->vendor->miscdev.name, - chip->vendor->miscdev.minor); + chip->vendor.miscdev.name, + chip->vendor.miscdev.minor); put_device(dev); + clear_bit(chip->dev_num, dev_mask); kfree(chip); - dev_mask[i] &= !(1 << j); - return -ENODEV; + return NULL; } spin_lock(&driver_lock); @@ -614,11 +1207,11 @@ dev_num_search_complete: spin_unlock(&driver_lock); - sysfs_create_group(&dev->kobj, chip->vendor->attr_group); + sysfs_create_group(&dev->kobj, chip->vendor.attr_group); chip->bios_dir = tpm_bios_log_setup(devname); - return 0; + return chip; } EXPORT_SYMBOL_GPL(tpm_register_hardware); Index: root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm.h =================================================================== --- root.orig/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm.h +++ root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm.h @@ -24,6 +24,14 @@ #include #include #include +#include + +#ifdef CONFIG_XEN +enum tpm_bufsize { + TPM_MIN_BUFFERSIZE = 2048, + TPM_MAX_BUFFERSIZE = 64 * 1024, +}; +#endif enum tpm_timeout { TPM_TIMEOUT = 5, /* msecs */ @@ -41,19 +49,33 @@ extern ssize_t tpm_show_pcrs(struct devi char *); extern ssize_t tpm_show_caps(struct device *, struct device_attribute *attr, char *); +extern ssize_t tpm_show_caps_1_2(struct device *, struct device_attribute *attr, + char *); extern ssize_t tpm_store_cancel(struct device *, struct device_attribute *attr, const char *, size_t); +extern ssize_t tpm_show_enabled(struct device *, struct device_attribute *attr, + char *); +extern ssize_t tpm_show_active(struct device *, struct device_attribute *attr, + char *); +extern ssize_t tpm_show_owned(struct device *, struct device_attribute *attr, + char *); +extern ssize_t tpm_show_temp_deactivated(struct device *, + struct device_attribute *attr, char *); struct tpm_chip; struct tpm_vendor_specific { - u8 req_complete_mask; - u8 req_complete_val; - u8 req_canceled; + const u8 req_complete_mask; + const u8 req_complete_val; + const u8 req_canceled; +#ifdef CONFIG_XEN u32 buffersize; +#endif void __iomem *iobase; /* ioremapped address */ unsigned long base; /* TPM base address */ + int irq; + int region_size; int have_region; @@ -63,6 +85,13 @@ struct tpm_vendor_specific { u8 (*status) (struct tpm_chip *); struct miscdevice miscdev; struct attribute_group *attr_group; + struct list_head list; + int locality; + unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */ + unsigned long duration[3]; /* jiffies */ + + wait_queue_head_t read_queue; + wait_queue_head_t int_queue; }; struct tpm_chip { @@ -75,20 +104,27 @@ struct tpm_chip { /* Data passed to and from the tpm via the read/write calls */ u8 *data_buffer; atomic_t data_pending; +#ifdef CONFIG_XEN atomic_t data_position; +#endif struct semaphore buffer_mutex; struct timer_list user_read_timer; /* user needs to claim result */ struct work_struct work; struct semaphore tpm_mutex; /* tpm is processing */ - struct tpm_vendor_specific *vendor; + struct tpm_vendor_specific vendor; struct dentry **bios_dir; struct list_head list; +#ifdef CONFIG_XEN + void *priv; +#endif }; +#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor) + static inline int tpm_read_index(int base, int index) { outb(index, base); @@ -101,13 +137,35 @@ static inline void tpm_write_index(int b outb(value & 0xFF, base+1); } +#ifdef CONFIG_XEN static inline u32 get_chip_buffersize(struct tpm_chip *chip) { - return chip->vendor->buffersize; + u32 size = chip->vendor.buffersize; + if (size > TPM_MAX_BUFFERSIZE) { + return TPM_MAX_BUFFERSIZE; + } else if (size < TPM_MIN_BUFFERSIZE) { + return TPM_MIN_BUFFERSIZE; + } + return size; +} + +static inline void *chip_get_private(const struct tpm_chip *chip) +{ + return chip->priv; } -extern int tpm_register_hardware(struct device *, - struct tpm_vendor_specific *); +static inline void chip_set_private(struct tpm_chip *chip, void *priv) +{ + chip->priv = priv; +} +#endif + +extern void tpm_get_timeouts(struct tpm_chip *); +extern void tpm_gen_interrupt(struct tpm_chip *); +extern void tpm_continue_selftest(struct tpm_chip *); +extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32); +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 ssize_t tpm_write(struct file *, const char __user *, size_t, @@ -121,7 +179,7 @@ extern int tpm_pm_resume(struct device * extern struct dentry ** tpm_bios_log_setup(char *); extern void tpm_bios_log_teardown(struct dentry **); #else -static inline struct dentry* tpm_bios_log_setup(char *name) +static inline struct dentry ** tpm_bios_log_setup(char *name) { return NULL; } Index: root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm_vtpm.c =================================================================== --- root.orig/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm_vtpm.c +++ root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm_vtpm.c @@ -29,8 +29,6 @@ enum { STATUS_READY = 0x04 }; -#define MIN(x,y) ((x) < (y)) ? (x) : (y) - struct transmission { struct list_head next; @@ -49,26 +47,6 @@ enum { TRANSMISSION_FLAG_WAS_QUEUED = 0x1 }; -struct vtpm_state { - struct transmission *current_request; - spinlock_t req_list_lock; - wait_queue_head_t req_wait_queue; - - struct list_head queued_requests; - - struct transmission *current_response; - spinlock_t resp_list_lock; - wait_queue_head_t resp_wait_queue; // processes waiting for responses - - struct transmission *req_cancelled; // if a cancellation was encounterd - - u8 vd_status; - u8 flags; - - unsigned long disconnect_time; - - struct tpm_virtual_device *tpmvd; -}; enum { DATAEX_FLAG_QUEUED_ONLY = 0x1 @@ -76,7 +54,6 @@ enum { /* local variables */ -static struct vtpm_state *vtpms; /* local function prototypes */ static int _vtpm_send_queued(struct tpm_chip *chip); @@ -160,11 +137,16 @@ static inline void transmission_free(str /* * Lower layer uses this function to make a response available. */ -int vtpm_vd_recv(const unsigned char *buffer, size_t count, const void *ptr) +int vtpm_vd_recv(const struct tpm_chip *chip, + const unsigned char *buffer, size_t count, + void *ptr) { unsigned long flags; int ret_size = 0; struct transmission *t; + struct vtpm_state *vtpms; + + vtpms = (struct vtpm_state *)chip_get_private(chip); /* * The list with requests must contain one request @@ -173,26 +155,11 @@ int vtpm_vd_recv(const unsigned char *bu */ spin_lock_irqsave(&vtpms->resp_list_lock, flags); if (vtpms->current_request != ptr) { - printk("WARNING: The request pointer is different than the " - "pointer the shared memory driver returned to me. " - "%p != %p\n", - vtpms->current_request, ptr); - } - - /* - * If the request has been cancelled, just quit here - */ - if (vtpms->req_cancelled == (struct transmission *)ptr) { - if (vtpms->current_request == vtpms->req_cancelled) { - vtpms->current_request = NULL; - } - transmission_free(vtpms->req_cancelled); - vtpms->req_cancelled = NULL; spin_unlock_irqrestore(&vtpms->resp_list_lock, flags); return 0; } - if (NULL != (t = vtpms->current_request)) { + if ((t = vtpms->current_request)) { transmission_free(t); vtpms->current_request = NULL; } @@ -217,8 +184,12 @@ int vtpm_vd_recv(const unsigned char *bu /* * Lower layer indicates its status (connected/disconnected) */ -void vtpm_vd_status(u8 vd_status) +void vtpm_vd_status(const struct tpm_chip *chip, u8 vd_status) { + struct vtpm_state *vtpms; + + vtpms = (struct vtpm_state *)chip_get_private(chip); + vtpms->vd_status = vd_status; if ((vtpms->vd_status & TPM_VD_STATUS_CONNECTED) == 0) { vtpms->disconnect_time = jiffies; @@ -233,6 +204,9 @@ static int vtpm_recv(struct tpm_chip *ch { int rc = 0; unsigned long flags; + struct vtpm_state *vtpms; + + vtpms = (struct vtpm_state *)chip_get_private(chip); /* * Check if the previous operation only queued the command @@ -251,7 +225,7 @@ static int vtpm_recv(struct tpm_chip *ch * Return a response of up to 30 '0's. */ - count = MIN(count, 30); + count = min_t(size_t, count, 30); memset(buf, 0x0, count); return count; } @@ -270,7 +244,7 @@ static int vtpm_recv(struct tpm_chip *ch if (vtpms->current_response) { struct transmission *t = vtpms->current_response; vtpms->current_response = NULL; - rc = MIN(count, t->response_len); + rc = min(count, t->response_len); memcpy(buf, t->response, rc); transmission_free(t); } @@ -284,6 +258,9 @@ static int vtpm_send(struct tpm_chip *ch int rc = 0; unsigned long flags; struct transmission *t = transmission_alloc(); + struct vtpm_state *vtpms; + + vtpms = (struct vtpm_state *)chip_get_private(chip); if (!t) return -ENOMEM; @@ -327,8 +304,7 @@ static int vtpm_send(struct tpm_chip *ch vtpms->current_request = t; - rc = vtpm_vd_send(chip, - vtpms->tpmvd->tpm_private, + rc = vtpm_vd_send(vtpms->tpm_private, buf, count, t); @@ -373,6 +349,8 @@ static int _vtpm_send_queued(struct tpm_ int error = 0; long flags; unsigned char buffer[1]; + struct vtpm_state *vtpms; + vtpms = (struct vtpm_state *)chip_get_private(chip); spin_lock_irqsave(&vtpms->req_list_lock, flags); @@ -387,8 +365,7 @@ static int _vtpm_send_queued(struct tpm_ vtpms->current_request = qt; spin_unlock_irqrestore(&vtpms->req_list_lock, flags); - rc = vtpm_vd_send(chip, - vtpms->tpmvd->tpm_private, + rc = vtpm_vd_send(vtpms->tpm_private, qt->request, qt->request_len, qt); @@ -427,9 +404,21 @@ static int _vtpm_send_queued(struct tpm_ static void vtpm_cancel(struct tpm_chip *chip) { unsigned long flags; + struct vtpm_state *vtpms = (struct vtpm_state *)chip_get_private(chip); + spin_lock_irqsave(&vtpms->resp_list_lock,flags); - vtpms->req_cancelled = vtpms->current_request; + if (!vtpms->current_response && vtpms->current_request) { + spin_unlock_irqrestore(&vtpms->resp_list_lock, flags); + interruptible_sleep_on(&vtpms->resp_wait_queue); + spin_lock_irqsave(&vtpms->resp_list_lock,flags); + } + + if (vtpms->current_response) { + struct transmission *t = vtpms->current_response; + vtpms->current_response = NULL; + transmission_free(t); + } spin_unlock_irqrestore(&vtpms->resp_list_lock,flags); } @@ -438,6 +427,9 @@ static u8 vtpm_status(struct tpm_chip *c { u8 rc = 0; unsigned long flags; + struct vtpm_state *vtpms; + + vtpms = (struct vtpm_state *)chip_get_private(chip); spin_lock_irqsave(&vtpms->resp_list_lock, flags); /* @@ -449,7 +441,10 @@ static u8 vtpm_status(struct tpm_chip *c if (vtpms->current_response || 0 != (vtpms->flags & DATAEX_FLAG_QUEUED_ONLY)) { rc = STATUS_DATA_AVAIL; + } else if (!vtpms->current_response && !vtpms->current_request) { + rc = STATUS_READY; } + spin_unlock_irqrestore(&vtpms->resp_list_lock, flags); return rc; } @@ -465,12 +460,21 @@ static struct file_operations vtpm_ops = static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); +static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); +static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); +static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); +static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, + NULL); static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL); static DEVICE_ATTR(cancel, S_IWUSR |S_IWGRP, NULL, tpm_store_cancel); static struct attribute *vtpm_attrs[] = { &dev_attr_pubek.attr, &dev_attr_pcrs.attr, + &dev_attr_enabled.attr, + &dev_attr_active.attr, + &dev_attr_owned.attr, + &dev_attr_temp_deactivated.attr, &dev_attr_caps.attr, &dev_attr_cancel.attr, NULL, @@ -478,6 +482,8 @@ static struct attribute *vtpm_attrs[] = static struct attribute_group vtpm_attr_grp = { .attrs = vtpm_attrs }; +#define TPM_LONG_TIMEOUT (10 * 60 * HZ) + static struct tpm_vendor_specific tpm_vtpm = { .recv = vtpm_recv, .send = vtpm_send, @@ -486,61 +492,56 @@ static struct tpm_vendor_specific tpm_vt .req_complete_mask = STATUS_BUSY | STATUS_DATA_AVAIL, .req_complete_val = STATUS_DATA_AVAIL, .req_canceled = STATUS_READY, - .base = 0, .attr_group = &vtpm_attr_grp, .miscdev = { .fops = &vtpm_ops, }, + .duration = { + TPM_LONG_TIMEOUT, + TPM_LONG_TIMEOUT, + TPM_LONG_TIMEOUT, + }, }; -static struct platform_device *pdev; - -int __init init_vtpm(struct tpm_virtual_device *tvd) -{ - int rc; - - /* vtpms is global - only allow one user */ - if (vtpms) - return -EBUSY; +struct tpm_chip *init_vtpm(struct device *dev, + struct tpm_virtual_device *tvd, + struct tpm_private *tp) +{ + long rc; + struct tpm_chip *chip; + struct vtpm_state *vtpms; vtpms = kzalloc(sizeof(struct vtpm_state), GFP_KERNEL); if (!vtpms) - return -ENOMEM; + return ERR_PTR(-ENOMEM); vtpm_state_init(vtpms); vtpms->tpmvd = tvd; - - pdev = platform_device_register_simple("tpm_vtpm", -1, NULL, 0); - if (IS_ERR(pdev)) { - rc = PTR_ERR(pdev); - goto err_free_mem; - } + vtpms->tpm_private = tp; if (tvd) tpm_vtpm.buffersize = tvd->max_tx_size; - if ((rc = tpm_register_hardware(&pdev->dev, &tpm_vtpm)) < 0) { - goto err_unreg_pdev; + chip = tpm_register_hardware(dev, &tpm_vtpm); + if (!chip) { + rc = -ENODEV; + goto err_free_mem; } - return 0; + chip_set_private(chip, vtpms); + + return chip; -err_unreg_pdev: - platform_device_unregister(pdev); err_free_mem: kfree(vtpms); - vtpms = NULL; - return rc; + return ERR_PTR(rc); } -void __exit cleanup_vtpm(void) +void cleanup_vtpm(struct device *dev) { - struct tpm_chip *chip = dev_get_drvdata(&pdev->dev); - if (chip) { - tpm_remove_hardware(chip->dev); - platform_device_unregister(pdev); - } + struct tpm_chip *chip = dev_get_drvdata(dev); + struct vtpm_state *vtpms = (struct vtpm_state*)chip_get_private(chip); + tpm_remove_hardware(dev); kfree(vtpms); - vtpms = NULL; } Index: root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm_xen.c =================================================================== --- root.orig/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm_xen.c +++ root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm_xen.c @@ -34,6 +34,7 @@ */ #include +#include #include #include #include @@ -41,12 +42,15 @@ #include #include #include +#include "tpm.h" #include "tpm_vtpm.h" #undef DEBUG /* local structures */ struct tpm_private { + struct tpm_chip *chip; + tpmif_tx_interface_t *tx; atomic_t refcnt; unsigned int evtchn; @@ -60,6 +64,7 @@ struct tpm_private { atomic_t tx_busy; void *tx_remember; + domid_t backend_id; wait_queue_head_t wait_q; @@ -95,6 +100,7 @@ static int tpm_xmit(struct tpm_private * const u8 * buf, size_t count, int userbuffer, void *remember); static void destroy_tpmring(struct tpm_private *tp); +void __exit tpmif_exit(void); #define DPRINTK(fmt, args...) \ pr_debug("xen_tpm_fr (%s:%d) " fmt, __FUNCTION__, __LINE__, ##args) @@ -199,8 +205,7 @@ static DEFINE_MUTEX(suspend_lock); /* * Send data via this module by calling this function */ -int vtpm_vd_send(struct tpm_chip *chip, - struct tpm_private *tp, +int vtpm_vd_send(struct tpm_private *tp, const u8 * buf, size_t count, void *ptr) { int sent; @@ -331,7 +336,7 @@ out: static void backend_changed(struct xenbus_device *dev, enum xenbus_state backend_state) { - struct tpm_private *tp = dev->dev.driver_data; + struct tpm_private *tp = tpm_private_from_dev(&dev->dev); DPRINTK("\n"); switch (backend_state) { @@ -358,6 +363,9 @@ static void backend_changed(struct xenbu } } +struct tpm_virtual_device tvd = { + .max_tx_size = PAGE_SIZE * TPMIF_TX_RING_SIZE, +}; static int tpmfront_probe(struct xenbus_device *dev, const struct xenbus_device_id *id) @@ -369,6 +377,12 @@ static int tpmfront_probe(struct xenbus_ if (!tp) return -ENOMEM; + tp->chip = init_vtpm(&dev->dev, &tvd, tp); + + if (IS_ERR(tp->chip)) { + return PTR_ERR(tp->chip); + } + err = xenbus_scanf(XBT_NIL, dev->nodename, "handle", "%i", &handle); if (XENBUS_EXIST_ERR(err)) @@ -380,12 +394,10 @@ static int tpmfront_probe(struct xenbus_ } tp->dev = dev; - dev->dev.driver_data = tp; err = talk_to_backend(dev, tp); if (err) { tpm_private_put(); - dev->dev.driver_data = NULL; return err; } return 0; @@ -394,16 +406,16 @@ static int tpmfront_probe(struct xenbus_ static int tpmfront_remove(struct xenbus_device *dev) { - struct tpm_private *tp = (struct tpm_private *)dev->dev.driver_data; + struct tpm_private *tp = tpm_private_from_dev(&dev->dev); destroy_tpmring(tp); + cleanup_vtpm(&dev->dev); return 0; } static int tpmfront_suspend(struct xenbus_device *dev) { - struct tpm_private *tp = (struct tpm_private *)dev->dev.driver_data; + struct tpm_private *tp = tpm_private_from_dev(&dev->dev); u32 ctr; - /* lock, so no app can send */ mutex_lock(&suspend_lock); tp->is_suspended = 1; @@ -431,7 +443,7 @@ static int tpmfront_suspend(struct xenbu static int tpmfront_resume(struct xenbus_device *dev) { - struct tpm_private *tp = (struct tpm_private *)dev->dev.driver_data; + struct tpm_private *tp = tpm_private_from_dev(&dev->dev); destroy_tpmring(tp); return talk_to_backend(dev, tp); } @@ -548,7 +560,7 @@ static void tpmif_rx_action(unsigned lon offset += tocopy; } - vtpm_vd_recv(buffer, received, tp->tx_remember); + vtpm_vd_recv(tp->chip, buffer, received, tp->tx_remember); kfree(buffer); exit: @@ -638,6 +650,7 @@ static int tpm_xmit(struct tpm_private * atomic_set(&tp->tx_busy, 1); tp->tx_remember = remember; + mb(); DPRINTK("Notifying backend via event channel %d\n", @@ -657,9 +670,9 @@ static void tpmif_notify_upperlayer(stru * to the BE. */ if (tp->is_connected) { - vtpm_vd_status(TPM_VD_STATUS_CONNECTED); + vtpm_vd_status(tp->chip, TPM_VD_STATUS_CONNECTED); } else { - vtpm_vd_status(TPM_VD_STATUS_DISCONNECTED); + vtpm_vd_status(tp->chip, TPM_VD_STATUS_DISCONNECTED); } } @@ -699,13 +712,10 @@ static void tpmif_set_connected_state(st * ================================================================= */ -struct tpm_virtual_device tvd = { - .max_tx_size = PAGE_SIZE * TPMIF_TX_RING_SIZE, -}; static int __init tpmif_init(void) { - int rc; + long rc = 0; struct tpm_private *tp; if ((xen_start_info->flags & SIF_INITDOMAIN)) { @@ -718,11 +728,6 @@ static int __init tpmif_init(void) goto failexit; } - tvd.tpm_private = tp; - rc = init_vtpm(&tvd); - if (rc) - goto init_vtpm_failed; - IPRINTK("Initialising the vTPM driver.\n"); if ( gnttab_alloc_grant_references ( TPMIF_TX_RING_SIZE, &gref_head ) < 0) { @@ -734,19 +739,16 @@ static int __init tpmif_init(void) return 0; gnttab_alloc_failed: - cleanup_vtpm(); -init_vtpm_failed: tpm_private_put(); failexit: - return rc; + return (int)rc; } -static void __exit tpmif_exit(void) +void __exit tpmif_exit(void) { exit_tpm_xenbus(); - cleanup_vtpm(); tpm_private_put(); gnttab_free_grant_references(gref_head); } Index: root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm_vtpm.h =================================================================== --- root.orig/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm_vtpm.h +++ root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/tpm_vtpm.h @@ -12,6 +12,26 @@ struct tpm_virtual_device { * for allocation of buffers. */ unsigned int max_tx_size; +}; + +struct vtpm_state { + struct transmission *current_request; + spinlock_t req_list_lock; + wait_queue_head_t req_wait_queue; + + struct list_head queued_requests; + + struct transmission *current_response; + spinlock_t resp_list_lock; + wait_queue_head_t resp_wait_queue; // processes waiting for responses + + u8 vd_status; + u8 flags; + + unsigned long disconnect_time; + + struct tpm_virtual_device *tpmvd; + /* * The following is a private structure of the underlying * driver. It is passed as parameter in the send function. @@ -19,20 +39,30 @@ struct tpm_virtual_device { struct tpm_private *tpm_private; }; + enum vdev_status { TPM_VD_STATUS_DISCONNECTED = 0x0, TPM_VD_STATUS_CONNECTED = 0x1 }; /* this function is called from tpm_vtpm.c */ -int vtpm_vd_send(struct tpm_chip *tc, - struct tpm_private * tp, +int vtpm_vd_send(struct tpm_private * tp, const u8 * buf, size_t count, void *ptr); /* these functions are offered by tpm_vtpm.c */ -int __init init_vtpm(struct tpm_virtual_device *); -void __exit cleanup_vtpm(void); -int vtpm_vd_recv(const unsigned char *buffer, size_t count, const void *ptr); -void vtpm_vd_status(u8 status); +struct tpm_chip *init_vtpm(struct device *, + struct tpm_virtual_device *, + struct tpm_private *); +void cleanup_vtpm(struct device *); +int vtpm_vd_recv(const struct tpm_chip* chip, + const unsigned char *buffer, size_t count, void *ptr); +void vtpm_vd_status(const struct tpm_chip *, u8 status); + +static inline struct tpm_private *tpm_private_from_dev(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct vtpm_state *vtpms = chip_get_private(chip); + return vtpms->tpm_private; +} #endif Index: root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/Kconfig =================================================================== --- root.orig/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/Kconfig +++ root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/Kconfig @@ -20,9 +20,18 @@ config TCG_TPM Note: For more TPM drivers enable CONFIG_PNP, CONFIG_ACPI and CONFIG_PNPACPI. +config TCG_TIS + tristate "TPM Interface Specification 1.2 Interface" + depends on TCG_TPM + ---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 + from within Linux. To compile this driver as a module, choose + M here; the module will be called tpm_tis. + config TCG_NSC tristate "National Semiconductor TPM Interface" - depends on TCG_TPM && !XEN_UNPRIVILEGED_GUEST + depends on TCG_TPM && PNPACPI ---help--- If you have a TPM security chip from National Semicondutor say Yes and it will be accessible from within Linux. To @@ -31,7 +40,7 @@ config TCG_NSC config TCG_ATMEL tristate "Atmel TPM Interface" - depends on TCG_TPM && !XEN_UNPRIVILEGED_GUEST + depends on TCG_TPM ---help--- If you have a TPM security chip from Atmel say Yes and it will be accessible from within Linux. To compile this driver Index: root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/Makefile =================================================================== --- root.orig/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/Makefile +++ root/xen-unstable.hg/linux-2.6-xen-sparse/drivers/char/tpm/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_TCG_TPM) += tpm.o ifdef CONFIG_ACPI obj-$(CONFIG_TCG_TPM) += tpm_bios.o endif +obj-$(CONFIG_TCG_TIS) += tpm_tis.o obj-$(CONFIG_TCG_NSC) += tpm_nsc.o obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o Index: root/xen-unstable.hg/buildconfigs/linux-defconfig_xen_x86_32 =================================================================== --- root.orig/xen-unstable.hg/buildconfigs/linux-defconfig_xen_x86_32 +++ root/xen-unstable.hg/buildconfigs/linux-defconfig_xen_x86_32 @@ -1902,6 +1902,7 @@ CONFIG_HANGCHECK_TIMER=m # TPM devices # CONFIG_TCG_TPM=m +CONFIG_TCG_TIS=m CONFIG_TCG_NSC=m CONFIG_TCG_ATMEL=m CONFIG_TCG_INFINEON=m Index: root/xen-unstable.hg/buildconfigs/linux-defconfig_xen_x86_64 =================================================================== --- root.orig/xen-unstable.hg/buildconfigs/linux-defconfig_xen_x86_64 +++ root/xen-unstable.hg/buildconfigs/linux-defconfig_xen_x86_64 @@ -1765,6 +1765,7 @@ CONFIG_HANGCHECK_TIMER=m # TPM devices # CONFIG_TCG_TPM=m +CONFIG_TCG_TIS=m CONFIG_TCG_NSC=m CONFIG_TCG_ATMEL=m CONFIG_TCG_INFINEON=m