Message ID | 1415713513-16524-8-git-send-email-jarkko.sakkinen@linux.intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 11/11/2014 08:45 AM, Jarkko Sakkinen wrote: > TPM 2.0 devices are separated by adding a field 'flags' to struct > tpm_chip and defining a flag TPM_CHIP_FLAG_TPM2 for tagging them. > > This patch adds the following internal functions: > > - tpm2_get_random() > - tpm2_get_tpm_pt() > - tpm2_pcr_extend() > - tpm2_pcr_read() > - tpm2_startup() > > Additionally, the following exported functions are implemented for > implementing TPM 2.0 device drivers: > > - tpm2_do_selftest() > - tpm2_calc_ordinal_durations() > - tpm2_gen_interrupt() > > The existing functions that are exported for the use for existing > subsystems have been changed to check the flags field in struct > tpm_chip and use appropriate TPM 2.0 counterpart if > TPM_CHIP_FLAG_TPM2 is est. > > The code for tpm2_calc_ordinal_duration() and tpm2_startup() were > originally written by Will Arthur. > > Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> > Signed-off-by: Will Arthur <will.c.arthur@intel.com> > --- > drivers/char/tpm/Makefile | 2 +- > drivers/char/tpm/tpm-chip.c | 21 +- > drivers/char/tpm/tpm-interface.c | 24 +- > drivers/char/tpm/tpm.h | 67 +++++ > drivers/char/tpm/tpm2-cmd.c | 566 +++++++++++++++++++++++++++++++++++++++ > 5 files changed, 668 insertions(+), 12 deletions(-) > create mode 100644 drivers/char/tpm/tpm2-cmd.c > > diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile > index 837da04..ae56af9 100644 > --- a/drivers/char/tpm/Makefile > +++ b/drivers/char/tpm/Makefile > @@ -2,7 +2,7 @@ > # Makefile for the kernel tpm device drivers. > # > obj-$(CONFIG_TCG_TPM) += tpm.o > -tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o > +tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o > tpm-$(CONFIG_ACPI) += tpm_ppi.o > > ifdef CONFIG_ACPI > diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c > index 5d268ac..4d25b24 100644 > --- a/drivers/char/tpm/tpm-chip.c > +++ b/drivers/char/tpm/tpm-chip.c > @@ -213,11 +213,14 @@ int tpm_chip_register(struct tpm_chip *chip) > if (rc) > return rc; > > - rc = tpm_add_ppi(chip); > - if (rc) > - goto out_err; > + /* Populate sysfs for TPM1 devices. */ > + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { > + rc = tpm_add_ppi(chip); > + if (rc) > + goto out_err; > > - chip->bios_dir = tpm_bios_log_setup(chip->devname); > + chip->bios_dir = tpm_bios_log_setup(chip->devname); > + } > > /* Make the chip available. */ > spin_lock(&driver_lock); > @@ -248,10 +251,12 @@ void tpm_chip_unregister(struct tpm_chip *chip) > spin_unlock(&driver_lock); > synchronize_rcu(); > > - tpm_remove_ppi(chip); > - > - if (chip->bios_dir) > - tpm_bios_log_teardown(chip->bios_dir); > + /* Clean up sysfs for TPM1 devices. */ > + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { > + if (chip->bios_dir) > + tpm_bios_log_teardown(chip->bios_dir); > + tpm_remove_ppi(chip); > + } > > tpm_dev_del_device(chip); > } > diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c > index 9e4ce4d..e62b835 100644 > --- a/drivers/char/tpm/tpm-interface.c > +++ b/drivers/char/tpm/tpm-interface.c > @@ -360,7 +360,10 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, > if (chip->vendor.irq) > goto out_recv; > > - stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); > + if (chip->flags & TPM_CHIP_FLAG_TPM2) > + stop = jiffies + tpm2_calc_ordinal_duration(chip, ordinal); > + else > + stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); > do { > u8 status = chip->ops->status(chip); > if ((status & chip->ops->req_complete_mask) == > @@ -483,7 +486,7 @@ static const struct tpm_input_header tpm_startup_header = { > static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) > { > struct tpm_cmd_t start_cmd; > - start_cmd.header.in = tpm_startup_header; > + > start_cmd.params.startup_in.startup_type = startup_type; > return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, > "attempting to start the TPM"); > @@ -680,7 +683,10 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) > chip = tpm_chip_find_get(chip_num); > if (chip == NULL) > return -ENODEV; > - rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf); > + if (chip->flags & TPM_CHIP_FLAG_TPM2) > + rc = tpm2_pcr_read(chip, pcr_idx, res_buf); > + else > + rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf); > tpm_chip_put(chip); > return rc; > } > @@ -714,6 +720,12 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) > if (chip == NULL) > return -ENODEV; > > + if (chip->flags & TPM_CHIP_FLAG_TPM2) { > + rc = tpm2_pcr_extend(chip, pcr_idx, hash); > + tpm_chip_put(chip); > + return rc; > + } > + > cmd.header.in = pcrextend_header; > cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); > memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE); > @@ -974,6 +986,12 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) > if (chip == NULL) > return -ENODEV; > > + if (chip->flags & TPM_CHIP_FLAG_TPM2) { > + err = tpm2_get_random(chip, out, max); > + tpm_chip_put(chip); > + return err; > + } > + > do { > tpm_cmd.header.in = tpm_getrandom_header; > tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes); > diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h > index 9d062e6..8a434d2 100644 > --- a/drivers/char/tpm/tpm.h > +++ b/drivers/char/tpm/tpm.h > @@ -62,6 +62,57 @@ enum tpm_duration { > #define TPM_ERR_INVALID_POSTINIT 38 > > #define TPM_HEADER_SIZE 10 > + > +enum tpm2_const { > + TPM2_PLATFORM_PCR = 24, > + TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8), > + TPM2_TIMEOUT_A = 750 * 1000, > + TPM2_TIMEOUT_B = 2000 * 1000, > + TPM2_TIMEOUT_C = 200 * 1000, > + TPM2_TIMEOUT_D = 30 * 1000, > + TPM2_DURATION_SHORT = 20 * 1000, > + TPM2_DURATION_MEDIUM = 750 * 1000, > + TPM2_DURATION_LONG = 2000 * 1000, > +}; > + > +enum tpm2_structures { > + TPM2_ST_NO_SESSIONS = 0x8001, > + TPM2_ST_SESSIONS = 0x8002, > +}; > + > +enum tpm2_return_codes { > + TPM2_RC_TESTING = 0x090A, > + TPM2_RC_DISABLED = 0x0120, > +}; > + > +enum tpm2_algorithms { > + TPM2_ALG_SHA1 = 0x0004, > +}; > + > +enum tpm2_command_codes { > + TPM2_CC_FIRST = 0x011F, > + TPM2_CC_SELF_TEST = 0x0143, > + TPM2_CC_STARTUP = 0x0144, > + TPM2_CC_GET_CAPABILITY = 0x017A, > + TPM2_CC_GET_RANDOM = 0x017B, > + TPM2_CC_PCR_READ = 0x017E, > + TPM2_CC_PCR_EXTEND = 0x0182, > + TPM2_CC_LAST = 0x018F, > +}; > + > +enum tpm2_permanent_handles { > + TPM2_RS_PW = 0x40000009, > +}; > + > +enum tpm2_capabilities { > + TPM2_CAP_TPM_PROPERTIES = 6, > +}; > + > +enum tpm2_startup_types { > + TPM2_SU_CLEAR = 0x0000, > + TPM2_SU_STATE = 0x0001, > +}; > + > struct tpm_chip; > > struct tpm_vendor_specific { > @@ -96,12 +147,17 @@ struct tpm_vendor_specific { > > #define TPM_PPI_VERSION_LEN 3 > > +enum tpm_chip_flags { > + TPM_CHIP_FLAG_TPM2 = BIT(0), > +}; > + > struct tpm_chip { > struct device *pdev; /* Device stuff */ > struct device dev; > struct cdev cdev; > > const struct tpm_class_ops *ops; > + unsigned int flags; > > int dev_num; /* /dev/tpm# */ > char devname[7]; > @@ -362,3 +418,14 @@ static inline void tpm_remove_ppi(struct tpm_chip *chip) > { > } > #endif > + > +int tpm2_startup(struct tpm_chip *chip, __be16 startup_type); > +int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf); > +int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash); > +int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max); > + > +extern ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, > + u32 *value, const char *desc); > +extern unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *, u32); > +extern int tpm2_do_selftest(struct tpm_chip *chip); > +extern int tpm2_gen_interrupt(struct tpm_chip *chip); > diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c > new file mode 100644 > index 0000000..458a17d > --- /dev/null > +++ b/drivers/char/tpm/tpm2-cmd.c > @@ -0,0 +1,566 @@ > +/* > + * Copyright (C) 2014 Intel Corporation > + * > + * Authors: > + * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> > + * > + * Maintained by: <tpmdd-devel@lists.sourceforge.net> > + * > + * This file contains TPM2 protocol implementations of the commands > + * used by the kernel internally. > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; version 2 > + * of the License. > + */ > + > +#include "tpm.h" > + > +struct tpm2_startup_in { > + __be16 startup_type; > +} __packed; > + > +struct tpm2_self_test_in { > + u8 full_test; > +} __packed; > + > +struct tpm2_pcr_read_in { > + __be32 pcr_selects_cnt; > + __be16 hash_alg; > + u8 pcr_select_size; > + u8 pcr_select[TPM2_PCR_SELECT_MIN]; > +} __packed; > + > +struct tpm2_pcr_read_out { > + __be32 update_cnt; > + __be32 pcr_selects_cnt; > + __be16 hash_alg; > + u8 pcr_select_size; > + u8 pcr_select[TPM2_PCR_SELECT_MIN]; > + __be32 digests_cnt; > + __be16 digest_size; > + u8 digest[TPM_DIGEST_SIZE]; > +} __packed; > + > +struct tpm2_null_auth_area { > + __be32 handle; > + __be16 nonce_size; > + u8 attributes; > + __be16 auth_size; > +} __packed; > + > +struct tpm2_pcr_extend_in { > + __be32 pcr_idx; > + __be32 auth_area_size; > + struct tpm2_null_auth_area auth_area; > + __be32 digest_cnt; > + __be16 hash_alg; > + u8 digest[TPM_DIGEST_SIZE]; > +} __packed; > + > +struct tpm2_get_tpm_pt_in { > + __be32 cap_id; > + __be32 property_id; > + __be32 property_cnt; > +} __packed; > + > +struct tpm2_get_tpm_pt_out { > + u8 more_data; > + __be32 subcap_id; > + __be32 property_cnt; > + __be32 property_id; > + __be32 value; > +} __packed; > + > +struct tpm2_get_random_in { > + __be16 size; > +} __packed; > + > +struct tpm2_get_random_out { > + __be16 size; > + u8 buffer[TPM_MAX_RNG_DATA]; > +} __packed; > + > +union tpm2_cmd_params { > + struct tpm2_startup_in startup_in; > + struct tpm2_self_test_in selftest_in; > + struct tpm2_pcr_read_in pcrread_in; > + struct tpm2_pcr_read_out pcrread_out; > + struct tpm2_pcr_extend_in pcrextend_in; > + struct tpm2_get_tpm_pt_in get_tpm_pt_in; > + struct tpm2_get_tpm_pt_out get_tpm_pt_out; > + struct tpm2_get_random_in getrandom_in; > + struct tpm2_get_random_out getrandom_out; > +}; > + > +struct tpm2_cmd { > + tpm_cmd_header header; > + union tpm2_cmd_params params; > +} __packed; > + > +/* > + * Array with one entry per ordinal defining the maximum amount > + * of time the chip could take to return the result. The values > + * of the SHORT, MEDIUM, and LONG durations are taken from the > + * PC Client Profile (PTP) specification. > + */ > +static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = { > + TPM_UNDEFINED, /* 11F */ > + TPM_UNDEFINED, /* 120 */ > + TPM_LONG, /* 121 */ > + TPM_UNDEFINED, /* 122 */ > + TPM_UNDEFINED, /* 123 */ > + TPM_UNDEFINED, /* 124 */ > + TPM_UNDEFINED, /* 125 */ > + TPM_UNDEFINED, /* 126 */ > + TPM_UNDEFINED, /* 127 */ > + TPM_UNDEFINED, /* 128 */ > + TPM_LONG, /* 129 */ > + TPM_UNDEFINED, /* 12a */ > + TPM_UNDEFINED, /* 12b */ > + TPM_UNDEFINED, /* 12c */ > + TPM_UNDEFINED, /* 12d */ > + TPM_UNDEFINED, /* 12e */ > + TPM_UNDEFINED, /* 12f */ > + TPM_UNDEFINED, /* 130 */ > + TPM_UNDEFINED, /* 131 */ > + TPM_UNDEFINED, /* 132 */ > + TPM_UNDEFINED, /* 133 */ > + TPM_UNDEFINED, /* 134 */ > + TPM_UNDEFINED, /* 135 */ > + TPM_UNDEFINED, /* 136 */ > + TPM_UNDEFINED, /* 137 */ > + TPM_UNDEFINED, /* 138 */ > + TPM_UNDEFINED, /* 139 */ > + TPM_UNDEFINED, /* 13a */ > + TPM_UNDEFINED, /* 13b */ > + TPM_UNDEFINED, /* 13c */ > + TPM_UNDEFINED, /* 13d */ > + TPM_MEDIUM, /* 13e */ > + TPM_UNDEFINED, /* 13f */ > + TPM_UNDEFINED, /* 140 */ > + TPM_UNDEFINED, /* 141 */ > + TPM_UNDEFINED, /* 142 */ > + TPM_LONG, /* 143 */ > + TPM_MEDIUM, /* 144 */ > + TPM_UNDEFINED, /* 145 */ > + TPM_UNDEFINED, /* 146 */ > + TPM_UNDEFINED, /* 147 */ > + TPM_UNDEFINED, /* 148 */ > + TPM_UNDEFINED, /* 149 */ > + TPM_UNDEFINED, /* 14a */ > + TPM_UNDEFINED, /* 14b */ > + TPM_UNDEFINED, /* 14c */ > + TPM_UNDEFINED, /* 14d */ > + TPM_LONG, /* 14e */ > + TPM_UNDEFINED, /* 14f */ > + TPM_UNDEFINED, /* 150 */ > + TPM_UNDEFINED, /* 151 */ > + TPM_UNDEFINED, /* 152 */ > + TPM_UNDEFINED, /* 153 */ > + TPM_UNDEFINED, /* 154 */ > + TPM_UNDEFINED, /* 155 */ > + TPM_UNDEFINED, /* 156 */ > + TPM_UNDEFINED, /* 157 */ > + TPM_UNDEFINED, /* 158 */ > + TPM_UNDEFINED, /* 159 */ > + TPM_UNDEFINED, /* 15a */ > + TPM_UNDEFINED, /* 15b */ > + TPM_MEDIUM, /* 15c */ > + TPM_UNDEFINED, /* 15d */ > + TPM_UNDEFINED, /* 15e */ > + TPM_UNDEFINED, /* 15f */ > + TPM_UNDEFINED, /* 160 */ > + TPM_UNDEFINED, /* 161 */ > + TPM_UNDEFINED, /* 162 */ > + TPM_UNDEFINED, /* 163 */ > + TPM_UNDEFINED, /* 164 */ > + TPM_UNDEFINED, /* 165 */ > + TPM_UNDEFINED, /* 166 */ > + TPM_UNDEFINED, /* 167 */ > + TPM_UNDEFINED, /* 168 */ > + TPM_UNDEFINED, /* 169 */ > + TPM_UNDEFINED, /* 16a */ > + TPM_UNDEFINED, /* 16b */ > + TPM_UNDEFINED, /* 16c */ > + TPM_UNDEFINED, /* 16d */ > + TPM_UNDEFINED, /* 16e */ > + TPM_UNDEFINED, /* 16f */ > + TPM_UNDEFINED, /* 170 */ > + TPM_UNDEFINED, /* 171 */ > + TPM_UNDEFINED, /* 172 */ > + TPM_UNDEFINED, /* 173 */ > + TPM_UNDEFINED, /* 174 */ > + TPM_UNDEFINED, /* 175 */ > + TPM_UNDEFINED, /* 176 */ > + TPM_LONG, /* 177 */ > + TPM_UNDEFINED, /* 178 */ > + TPM_UNDEFINED, /* 179 */ > + TPM_MEDIUM, /* 17a */ > + TPM_LONG, /* 17b */ > + TPM_UNDEFINED, /* 17c */ > + TPM_UNDEFINED, /* 17d */ > + TPM_UNDEFINED, /* 17e */ > + TPM_UNDEFINED, /* 17f */ > + TPM_UNDEFINED, /* 180 */ > + TPM_UNDEFINED, /* 181 */ > + TPM_MEDIUM, /* 182 */ > + TPM_UNDEFINED, /* 183 */ > + TPM_UNDEFINED, /* 184 */ > + TPM_MEDIUM, /* 185 */ > + TPM_MEDIUM, /* 186 */ > + TPM_UNDEFINED, /* 187 */ > + TPM_UNDEFINED, /* 188 */ > + TPM_UNDEFINED, /* 189 */ > + TPM_UNDEFINED, /* 18a */ > + TPM_UNDEFINED, /* 18b */ > + TPM_UNDEFINED, /* 18c */ > + TPM_UNDEFINED, /* 18d */ > + TPM_UNDEFINED, /* 18e */ > + TPM_UNDEFINED /* 18f */ > +}; > + > +static const struct tpm_input_header tpm2_startup_header = { > + .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), > + .length = cpu_to_be32(12), 12 -> sizeof(struct tpm_input_header) + sizeof(struct pm2_startup_in) > + .ordinal = cpu_to_be32(TPM2_CC_STARTUP) > +}; > + > +/** > + * tpm2_startup() - send startup command to the TPM chip > + * @chip: TPM chip to use. > + * @startup_type startup type. The value is either > + * TPM_SU_CLEAR or TPM_SU_STATE. > + * > + * 0 is returned when the operation is successful. When a negative number is > + * returned it remarks a POSIX error code. When a positive number is returned > + * it remarks a TPM error. Replace 'When's with 'If's. (when being 'temporal') > + */ > +int tpm2_startup(struct tpm_chip *chip, __be16 startup_type) > +{ > + struct tpm2_cmd cmd; > + > + cmd.header.in = tpm2_startup_header; > + > + cmd.params.startup_in.startup_type = startup_type; > + return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), > + "attempting to start the TPM"); > +} > + > +#define TPM2_PCR_READ_IN_SIZE \ > + (sizeof(struct tpm_input_header) + \ > + sizeof(struct tpm2_pcr_read_in)) > + Ah! You could also use a #define above! > +static const struct tpm_input_header tpm2_pcrread_header = { > + .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), > + .length = cpu_to_be32(TPM2_PCR_READ_IN_SIZE), > + .ordinal = cpu_to_be32(TPM2_CC_PCR_READ) > +}; > + > +/** > + * tpm2_pcr_read() - read a PCR value > + * @chip: TPM chip to use. > + * @pcr_idx: index of the PCR to read. > + * @ref_buf: buffer to store the resulting hash, > + * > + * 0 is returned when the operation is successful. When a negative number is > + * returned it remarks a POSIX error code. When a positive number is returned > + * it remarks a TPM error. > + */ Replace 'When's with 'If's. Also further below. > +int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) > +{ > + int rc; > + struct tpm2_cmd cmd; > + u8 *buf; > + int i, j; > + > + if (pcr_idx >= TPM2_PLATFORM_PCR) > + return -EINVAL; > + > + cmd.header.in = tpm2_pcrread_header; > + cmd.params.pcrread_in.pcr_selects_cnt = cpu_to_be32(1); > + cmd.params.pcrread_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1); > + cmd.params.pcrread_in.pcr_select_size = TPM2_PCR_SELECT_MIN; > + > + for (i = 0; i < TPM2_PCR_SELECT_MIN; i++) { > + j = pcr_idx - i * 8; > + > + cmd.params.pcrread_in.pcr_select[i] = > + (j >= 0 && j < 8) ? 1 << j : 0; > + } Umpf - what's this? You need to set the PCR index as an index in the bitfield? pcr_idx >> 3 gives you the index into the array, assuming that [0] holds bits for PCR0 to 7. 1 << (pcr_idx & 0x7) gives you the bit to set, assuming bit 0 is to be set for PCR 0 1 << (7- (pcr_idx & 0x7)) gives you the bit to set, assuming bit 7 is to be set for PCR 0. memset(cmd.params.pcrread_in.pcr_select, 0, sizeof(cmd.params.pcrread_in.pcr_select)); cmd.params.pcrread_in.pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7); > + > + rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), > + "attempting to read a pcr value"); > + > + if (rc == 0) { > + buf = cmd.params.pcrread_out.digest; > + memcpy(res_buf, buf, TPM_DIGEST_SIZE); > + } > + > + return rc; > +} > + > +/** > + * tpm2_pcr_extend() - extend a PCR value > + * @chip: TPM chip to use. > + * @pcr_idx: index of the PCR. > + * @hash: hash value to use for the extend operation. > + * > + * 0 is returned when the operation is successful. When a negative number is > + * returned it remarks a POSIX error code. When a positive number is returned > + * it remarks a TPM error. > + */ > +static const struct tpm_input_header tpm2_pcrextend_header = { > + .tag = cpu_to_be16(TPM2_ST_SESSIONS), > + .length = cpu_to_be32(sizeof(struct tpm_input_header) + > + sizeof(struct tpm2_pcr_extend_in)), > + .ordinal = cpu_to_be32(TPM2_CC_PCR_EXTEND) > +}; > + > +int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash) > +{ > + struct tpm2_cmd cmd; > + int rc; > + > + cmd.header.in = tpm2_pcrextend_header; > + cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); > + cmd.params.pcrextend_in.auth_area_size = > + cpu_to_be32(sizeof(struct tpm2_null_auth_area)); > + cmd.params.pcrextend_in.auth_area.handle = > + cpu_to_be32(TPM2_RS_PW); > + cmd.params.pcrextend_in.auth_area.nonce_size = 0; > + cmd.params.pcrextend_in.auth_area.attributes = 0; > + cmd.params.pcrextend_in.auth_area.auth_size = 0; > + cmd.params.pcrextend_in.digest_cnt = cpu_to_be32(1); > + cmd.params.pcrextend_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1); > + memcpy(cmd.params.pcrextend_in.digest, hash, TPM_DIGEST_SIZE); > + > + rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), > + "attempting extend a PCR value"); > + > + return rc; > +} > + > +static const struct tpm_input_header tpm2_getrandom_header = { > + .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), > + .length = cpu_to_be32(sizeof(struct tpm_input_header) + > + sizeof(struct tpm2_get_random_in)), > + .ordinal = cpu_to_be32(TPM2_CC_GET_RANDOM) > +}; > + > +/** > + * tpm2_get_random() - get random bytes from the TPM RNG > + * @chip: TPM chip to use > + * @out: destination buffer for the random bytes > + * @max: the max number of bytes to write to @out > + * > + * 0 is returned when the operation is successful. When a negative number is > + * returned it remarks a POSIX error code. When a positive number is returned > + * it remarks a TPM error. > + */ > +int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max) > +{ > + struct tpm2_cmd cmd; > + u32 recd, num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA); num_bytes = min_t(u32, max, sizeof(tpm2_cmd.params.getrandom_out.buffer); This way you tie it to the actual buffer size of the buffer the TPM can fill > + int err, total = 0, retries = 5; > + u8 *dest = out; > + > + if (!out || !num_bytes || max > TPM_MAX_RNG_DATA) ... || max > sizeof((tpm2_cmd.params.getrandom_out.buffer) > + return -EINVAL; > + > + do { > + cmd.header.in = tpm2_getrandom_header; > + cmd.params.getrandom_in.size = cpu_to_be16(num_bytes); > + > + err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), > + "attempting get random"); > + if (err) > + break; > + > + recd = be16_to_cpu(cmd.params.getrandom_out.size); > + memcpy(dest, cmd.params.getrandom_out.buffer, recd); to be on the safe side, I would do /* never accept more bytes than we asked for */ recd = min(recd, num_bytes); memcpy(dest, cmd.params.getrandom_out.buffer, recd); so that the destination buffer can never be overwritten beyond its boundaries by a TPM that just ends up sending you more bytes than you asked for (firmware bug). > + > + dest += recd; > + total += recd; > + num_bytes -= recd; > + } while (retries-- && total < max); > + > + return total ? total : -EIO; > +} > + > +#define TPM2_GET_TPM_PT_IN_SIZE \ > + (sizeof(struct tpm_input_header) + \ > + sizeof(struct tpm2_get_tpm_pt_in)) > + > +static const struct tpm_input_header tpm2_get_tpm_pt_header = { > + .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), > + .length = cpu_to_be32(TPM2_GET_TPM_PT_IN_SIZE), > + .ordinal = cpu_to_be32(TPM2_CC_GET_CAPABILITY) > +}; > + > +/** > + * tpm2_get_tpm_pt() - get value of a TPM_CAP_TPM_PROPERTIES type property > + * @chip: TPM chip to use. > + * @property_id: property ID. > + * @value: output variable. > + * @desc: passed to tpm_transmit_cmd() > + * > + * 0 is returned when the operation is successful. When a negative number is > + * returned it remarks a POSIX error code. When a positive number is returned > + * it remarks a TPM error. > + */ > +ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, > + const char *desc) > +{ > + struct tpm2_cmd cmd; > + int rc; > + > + cmd.header.in = tpm2_get_tpm_pt_header; > + cmd.params.get_tpm_pt_in.cap_id = cpu_to_be32(TPM2_CAP_TPM_PROPERTIES); > + cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(property_id); > + cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1); > + > + rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), desc); > + if (!rc) > + *value = cmd.params.get_tpm_pt_out.value; > + > + return rc; > +} > +EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt); > + > +/* > + * tpm2_calc_ordinal_duration() - maximum duration for a command > + * @chip: TPM chip to use. > + * @ordinal: command code number. > + * > + * 0 is returned when the operation is successful. When a negative number is > + * returned it remarks a POSIX error code. When a positive number is returned > + * it remarks a TPM error. > + */ > +unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) > +{ > + int index = TPM_UNDEFINED; > + int duration = 0; > + > + if (ordinal >= TPM2_CC_FIRST && ordinal <= TPM2_CC_LAST) > + index = tpm2_ordinal_duration[ordinal - TPM2_CC_FIRST]; > + > + if (index != TPM_UNDEFINED) > + duration = chip->vendor.duration[index]; > + if (duration <= 0) > + return 2 * 60 * HZ; > + else > + return duration; > +} > +EXPORT_SYMBOL_GPL(tpm2_calc_ordinal_duration); > + > +static const struct tpm_input_header tpm2_selftest_header = { > + .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), > + .length = cpu_to_be32(sizeof(struct tpm_input_header) + > + sizeof(struct tpm2_self_test_in)), > + .ordinal = cpu_to_be32(TPM2_CC_SELF_TEST) > +}; > + > +#define TPM2_SELF_TEST_IN_SIZE \ > + (sizeof(struct tpm_input_header) + sizeof(struct tpm2_self_test_in)) > + > +/** > + * tpm2_continue_selftest() - start a self test > + * @chip: TPM chip to use > + * @full: test all commands instead of testing only those that were not > + * previously tested. > + * > + * 0 is returned when the operation is successful. When a negative number is > + * returned it remarks a POSIX error code. When a positive number is returned > + * it remarks a TPM error. > + */ > +static int tpm2_start_selftest(struct tpm_chip *chip, bool full) > +{ > + int rc; > + struct tpm2_cmd cmd; > + > + cmd.header.in = tpm2_selftest_header; > + cmd.params.selftest_in.full_test = full; > + > + rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE, > + "continue selftest"); > + > + return rc; > +} > + > +/** > + * tpm2_do_selftest() - run a full self test > + * @chip: TPM chip to use > + * > + * During the self test TPM2 commands return with the error code RC_TESTING. > + * Waiting is done by issuing PCR read until it executes successfully. > + * > + * 0 is returned when the operation is successful. When a negative number is > + * returned it remarks a POSIX error code. When a positive number is returned > + * it remarks a TPM error. > + */ > +int tpm2_do_selftest(struct tpm_chip *chip) > +{ > + int rc; > + unsigned int loops; > + unsigned int delay_msec = 100; > + unsigned long duration; > + struct tpm2_cmd cmd; > + int i; > + > + duration = tpm2_calc_ordinal_duration(chip, TPM2_CC_SELF_TEST); > + > + loops = jiffies_to_msecs(duration) / delay_msec; > + > + rc = tpm2_start_selftest(chip, true); > + if (rc) > + return rc; > + > + for (i = 0; i < loops; i++) { > + /* Attempt to read a PCR value */ > + cmd.header.in = tpm2_pcrread_header; > + cmd.params.pcrread_in.pcr_selects_cnt = cpu_to_be32(1); > + cmd.params.pcrread_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1); > + cmd.params.pcrread_in.pcr_select_size = TPM2_PCR_SELECT_MIN; > + cmd.params.pcrread_in.pcr_select[0] = 0x01; > + cmd.params.pcrread_in.pcr_select[1] = 0x00; > + cmd.params.pcrread_in.pcr_select[2] = 0x00; > + > + rc = tpm_transmit_cmd(chip, (u8 *) &cmd, sizeof(cmd), NULL); > + if (rc < 0) > + break; > + > + rc = be32_to_cpu(cmd.header.out.return_code); > + if (rc != TPM2_RC_TESTING) > + break; > + > + msleep(delay_msec); > + } > + > + return rc; > +} > +EXPORT_SYMBOL_GPL(tpm2_do_selftest); > + > +/** > + * tpm2_gen_interrupt() - generate an interrupt > + * @chip: TPM chip to use > + * > + * 0 is returned when the operation is successful. When a negative number is > + * returned it remarks a POSIX error code. When a positive number is returned > + * it remarks a TPM error. > + */ > + > +int tpm2_gen_interrupt(struct tpm_chip *chip) > +{ > + u32 dummy; > + int rc; > + > + rc = tpm2_get_tpm_pt(chip, > + TPM2_CAP_TPM_PROPERTIES, > + &dummy, > + "attempting to generate an interrupt"); > + > + return rc; > +} > +EXPORT_SYMBOL_GPL(tpm2_gen_interrupt); Stefan ------------------------------------------------------------------------------ Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server from Actuate! Instantly Supercharge Your Business Reports and Dashboards with Interactivity, Sharing, Native Excel Exports, App Integration & more Get technology previously reserved for billion-dollar corporations, FREE http://pubads.g.doubleclick.net/gampad/clk?id=157005751&iu=/4140/ostg.clktrk
On Tue, Nov 25, 2014 at 07:42:25PM -0500, Stefan Berger wrote: > On 11/11/2014 08:45 AM, Jarkko Sakkinen wrote: > >TPM 2.0 devices are separated by adding a field 'flags' to struct > >tpm_chip and defining a flag TPM_CHIP_FLAG_TPM2 for tagging them. > > > >This patch adds the following internal functions: > > > >- tpm2_get_random() > >- tpm2_get_tpm_pt() > >- tpm2_pcr_extend() > >- tpm2_pcr_read() > >- tpm2_startup() > > > >Additionally, the following exported functions are implemented for > >implementing TPM 2.0 device drivers: > > > >- tpm2_do_selftest() > >- tpm2_calc_ordinal_durations() > >- tpm2_gen_interrupt() > > > >The existing functions that are exported for the use for existing > >subsystems have been changed to check the flags field in struct > >tpm_chip and use appropriate TPM 2.0 counterpart if > >TPM_CHIP_FLAG_TPM2 is est. > > > >The code for tpm2_calc_ordinal_duration() and tpm2_startup() were > >originally written by Will Arthur. > > > >Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> > >Signed-off-by: Will Arthur <will.c.arthur@intel.com> > >--- > > drivers/char/tpm/Makefile | 2 +- > > drivers/char/tpm/tpm-chip.c | 21 +- > > drivers/char/tpm/tpm-interface.c | 24 +- > > drivers/char/tpm/tpm.h | 67 +++++ > > drivers/char/tpm/tpm2-cmd.c | 566 +++++++++++++++++++++++++++++++++++++++ > > 5 files changed, 668 insertions(+), 12 deletions(-) > > create mode 100644 drivers/char/tpm/tpm2-cmd.c > > > >diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile > >index 837da04..ae56af9 100644 > >--- a/drivers/char/tpm/Makefile > >+++ b/drivers/char/tpm/Makefile > >@@ -2,7 +2,7 @@ > > # Makefile for the kernel tpm device drivers. > > # > > obj-$(CONFIG_TCG_TPM) += tpm.o > >-tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o > >+tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o > > tpm-$(CONFIG_ACPI) += tpm_ppi.o > > > > ifdef CONFIG_ACPI > >diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c > >index 5d268ac..4d25b24 100644 > >--- a/drivers/char/tpm/tpm-chip.c > >+++ b/drivers/char/tpm/tpm-chip.c > >@@ -213,11 +213,14 @@ int tpm_chip_register(struct tpm_chip *chip) > > if (rc) > > return rc; > > > >- rc = tpm_add_ppi(chip); > >- if (rc) > >- goto out_err; > >+ /* Populate sysfs for TPM1 devices. */ > >+ if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { > >+ rc = tpm_add_ppi(chip); > >+ if (rc) > >+ goto out_err; > > > >- chip->bios_dir = tpm_bios_log_setup(chip->devname); > >+ chip->bios_dir = tpm_bios_log_setup(chip->devname); > >+ } > > > > /* Make the chip available. */ > > spin_lock(&driver_lock); > >@@ -248,10 +251,12 @@ void tpm_chip_unregister(struct tpm_chip *chip) > > spin_unlock(&driver_lock); > > synchronize_rcu(); > > > >- tpm_remove_ppi(chip); > >- > >- if (chip->bios_dir) > >- tpm_bios_log_teardown(chip->bios_dir); > >+ /* Clean up sysfs for TPM1 devices. */ > >+ if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { > >+ if (chip->bios_dir) > >+ tpm_bios_log_teardown(chip->bios_dir); > >+ tpm_remove_ppi(chip); > >+ } > > > > tpm_dev_del_device(chip); > > } > >diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c > >index 9e4ce4d..e62b835 100644 > >--- a/drivers/char/tpm/tpm-interface.c > >+++ b/drivers/char/tpm/tpm-interface.c > >@@ -360,7 +360,10 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, > > if (chip->vendor.irq) > > goto out_recv; > > > >- stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); > >+ if (chip->flags & TPM_CHIP_FLAG_TPM2) > >+ stop = jiffies + tpm2_calc_ordinal_duration(chip, ordinal); > >+ else > >+ stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); > > do { > > u8 status = chip->ops->status(chip); > > if ((status & chip->ops->req_complete_mask) == > >@@ -483,7 +486,7 @@ static const struct tpm_input_header tpm_startup_header = { > > static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) > > { > > struct tpm_cmd_t start_cmd; > >- start_cmd.header.in = tpm_startup_header; > >+ > > start_cmd.params.startup_in.startup_type = startup_type; > > return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, > > "attempting to start the TPM"); > >@@ -680,7 +683,10 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) > > chip = tpm_chip_find_get(chip_num); > > if (chip == NULL) > > return -ENODEV; > >- rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf); > >+ if (chip->flags & TPM_CHIP_FLAG_TPM2) > >+ rc = tpm2_pcr_read(chip, pcr_idx, res_buf); > >+ else > >+ rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf); > > tpm_chip_put(chip); > > return rc; > > } > >@@ -714,6 +720,12 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) > > if (chip == NULL) > > return -ENODEV; > > > >+ if (chip->flags & TPM_CHIP_FLAG_TPM2) { > >+ rc = tpm2_pcr_extend(chip, pcr_idx, hash); > >+ tpm_chip_put(chip); > >+ return rc; > >+ } > >+ > > cmd.header.in = pcrextend_header; > > cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); > > memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE); > >@@ -974,6 +986,12 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) > > if (chip == NULL) > > return -ENODEV; > > > >+ if (chip->flags & TPM_CHIP_FLAG_TPM2) { > >+ err = tpm2_get_random(chip, out, max); > >+ tpm_chip_put(chip); > >+ return err; > >+ } > >+ > > do { > > tpm_cmd.header.in = tpm_getrandom_header; > > tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes); > >diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h > >index 9d062e6..8a434d2 100644 > >--- a/drivers/char/tpm/tpm.h > >+++ b/drivers/char/tpm/tpm.h > >@@ -62,6 +62,57 @@ enum tpm_duration { > > #define TPM_ERR_INVALID_POSTINIT 38 > > > > #define TPM_HEADER_SIZE 10 > >+ > >+enum tpm2_const { > >+ TPM2_PLATFORM_PCR = 24, > >+ TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8), > >+ TPM2_TIMEOUT_A = 750 * 1000, > >+ TPM2_TIMEOUT_B = 2000 * 1000, > >+ TPM2_TIMEOUT_C = 200 * 1000, > >+ TPM2_TIMEOUT_D = 30 * 1000, > >+ TPM2_DURATION_SHORT = 20 * 1000, > >+ TPM2_DURATION_MEDIUM = 750 * 1000, > >+ TPM2_DURATION_LONG = 2000 * 1000, > >+}; > >+ > >+enum tpm2_structures { > >+ TPM2_ST_NO_SESSIONS = 0x8001, > >+ TPM2_ST_SESSIONS = 0x8002, > >+}; > >+ > >+enum tpm2_return_codes { > >+ TPM2_RC_TESTING = 0x090A, > >+ TPM2_RC_DISABLED = 0x0120, > >+}; > >+ > >+enum tpm2_algorithms { > >+ TPM2_ALG_SHA1 = 0x0004, > >+}; > >+ > >+enum tpm2_command_codes { > >+ TPM2_CC_FIRST = 0x011F, > >+ TPM2_CC_SELF_TEST = 0x0143, > >+ TPM2_CC_STARTUP = 0x0144, > >+ TPM2_CC_GET_CAPABILITY = 0x017A, > >+ TPM2_CC_GET_RANDOM = 0x017B, > >+ TPM2_CC_PCR_READ = 0x017E, > >+ TPM2_CC_PCR_EXTEND = 0x0182, > >+ TPM2_CC_LAST = 0x018F, > >+}; > >+ > >+enum tpm2_permanent_handles { > >+ TPM2_RS_PW = 0x40000009, > >+}; > >+ > >+enum tpm2_capabilities { > >+ TPM2_CAP_TPM_PROPERTIES = 6, > >+}; > >+ > >+enum tpm2_startup_types { > >+ TPM2_SU_CLEAR = 0x0000, > >+ TPM2_SU_STATE = 0x0001, > >+}; > >+ > > struct tpm_chip; > > > > struct tpm_vendor_specific { > >@@ -96,12 +147,17 @@ struct tpm_vendor_specific { > > > > #define TPM_PPI_VERSION_LEN 3 > > > >+enum tpm_chip_flags { > >+ TPM_CHIP_FLAG_TPM2 = BIT(0), > >+}; > >+ > > struct tpm_chip { > > struct device *pdev; /* Device stuff */ > > struct device dev; > > struct cdev cdev; > > > > const struct tpm_class_ops *ops; > >+ unsigned int flags; > > > > int dev_num; /* /dev/tpm# */ > > char devname[7]; > >@@ -362,3 +418,14 @@ static inline void tpm_remove_ppi(struct tpm_chip *chip) > > { > > } > > #endif > >+ > >+int tpm2_startup(struct tpm_chip *chip, __be16 startup_type); > >+int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf); > >+int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash); > >+int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max); > >+ > >+extern ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, > >+ u32 *value, const char *desc); > >+extern unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *, u32); > >+extern int tpm2_do_selftest(struct tpm_chip *chip); > >+extern int tpm2_gen_interrupt(struct tpm_chip *chip); > >diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c > >new file mode 100644 > >index 0000000..458a17d > >--- /dev/null > >+++ b/drivers/char/tpm/tpm2-cmd.c > >@@ -0,0 +1,566 @@ > >+/* > >+ * Copyright (C) 2014 Intel Corporation > >+ * > >+ * Authors: > >+ * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> > >+ * > >+ * Maintained by: <tpmdd-devel@lists.sourceforge.net> > >+ * > >+ * This file contains TPM2 protocol implementations of the commands > >+ * used by the kernel internally. > >+ * > >+ * This program is free software; you can redistribute it and/or > >+ * modify it under the terms of the GNU General Public License > >+ * as published by the Free Software Foundation; version 2 > >+ * of the License. > >+ */ > >+ > >+#include "tpm.h" > >+ > >+struct tpm2_startup_in { > >+ __be16 startup_type; > >+} __packed; > >+ > >+struct tpm2_self_test_in { > >+ u8 full_test; > >+} __packed; > >+ > >+struct tpm2_pcr_read_in { > >+ __be32 pcr_selects_cnt; > >+ __be16 hash_alg; > >+ u8 pcr_select_size; > >+ u8 pcr_select[TPM2_PCR_SELECT_MIN]; > >+} __packed; > >+ > >+struct tpm2_pcr_read_out { > >+ __be32 update_cnt; > >+ __be32 pcr_selects_cnt; > >+ __be16 hash_alg; > >+ u8 pcr_select_size; > >+ u8 pcr_select[TPM2_PCR_SELECT_MIN]; > >+ __be32 digests_cnt; > >+ __be16 digest_size; > >+ u8 digest[TPM_DIGEST_SIZE]; > >+} __packed; > >+ > >+struct tpm2_null_auth_area { > >+ __be32 handle; > >+ __be16 nonce_size; > >+ u8 attributes; > >+ __be16 auth_size; > >+} __packed; > >+ > >+struct tpm2_pcr_extend_in { > >+ __be32 pcr_idx; > >+ __be32 auth_area_size; > >+ struct tpm2_null_auth_area auth_area; > >+ __be32 digest_cnt; > >+ __be16 hash_alg; > >+ u8 digest[TPM_DIGEST_SIZE]; > >+} __packed; > >+ > >+struct tpm2_get_tpm_pt_in { > >+ __be32 cap_id; > >+ __be32 property_id; > >+ __be32 property_cnt; > >+} __packed; > >+ > >+struct tpm2_get_tpm_pt_out { > >+ u8 more_data; > >+ __be32 subcap_id; > >+ __be32 property_cnt; > >+ __be32 property_id; > >+ __be32 value; > >+} __packed; > >+ > >+struct tpm2_get_random_in { > >+ __be16 size; > >+} __packed; > >+ > >+struct tpm2_get_random_out { > >+ __be16 size; > >+ u8 buffer[TPM_MAX_RNG_DATA]; > >+} __packed; > >+ > >+union tpm2_cmd_params { > >+ struct tpm2_startup_in startup_in; > >+ struct tpm2_self_test_in selftest_in; > >+ struct tpm2_pcr_read_in pcrread_in; > >+ struct tpm2_pcr_read_out pcrread_out; > >+ struct tpm2_pcr_extend_in pcrextend_in; > >+ struct tpm2_get_tpm_pt_in get_tpm_pt_in; > >+ struct tpm2_get_tpm_pt_out get_tpm_pt_out; > >+ struct tpm2_get_random_in getrandom_in; > >+ struct tpm2_get_random_out getrandom_out; > >+}; > >+ > >+struct tpm2_cmd { > >+ tpm_cmd_header header; > >+ union tpm2_cmd_params params; > >+} __packed; > >+ > >+/* > >+ * Array with one entry per ordinal defining the maximum amount > >+ * of time the chip could take to return the result. The values > >+ * of the SHORT, MEDIUM, and LONG durations are taken from the > >+ * PC Client Profile (PTP) specification. > >+ */ > >+static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = { > >+ TPM_UNDEFINED, /* 11F */ > >+ TPM_UNDEFINED, /* 120 */ > >+ TPM_LONG, /* 121 */ > >+ TPM_UNDEFINED, /* 122 */ > >+ TPM_UNDEFINED, /* 123 */ > >+ TPM_UNDEFINED, /* 124 */ > >+ TPM_UNDEFINED, /* 125 */ > >+ TPM_UNDEFINED, /* 126 */ > >+ TPM_UNDEFINED, /* 127 */ > >+ TPM_UNDEFINED, /* 128 */ > >+ TPM_LONG, /* 129 */ > >+ TPM_UNDEFINED, /* 12a */ > >+ TPM_UNDEFINED, /* 12b */ > >+ TPM_UNDEFINED, /* 12c */ > >+ TPM_UNDEFINED, /* 12d */ > >+ TPM_UNDEFINED, /* 12e */ > >+ TPM_UNDEFINED, /* 12f */ > >+ TPM_UNDEFINED, /* 130 */ > >+ TPM_UNDEFINED, /* 131 */ > >+ TPM_UNDEFINED, /* 132 */ > >+ TPM_UNDEFINED, /* 133 */ > >+ TPM_UNDEFINED, /* 134 */ > >+ TPM_UNDEFINED, /* 135 */ > >+ TPM_UNDEFINED, /* 136 */ > >+ TPM_UNDEFINED, /* 137 */ > >+ TPM_UNDEFINED, /* 138 */ > >+ TPM_UNDEFINED, /* 139 */ > >+ TPM_UNDEFINED, /* 13a */ > >+ TPM_UNDEFINED, /* 13b */ > >+ TPM_UNDEFINED, /* 13c */ > >+ TPM_UNDEFINED, /* 13d */ > >+ TPM_MEDIUM, /* 13e */ > >+ TPM_UNDEFINED, /* 13f */ > >+ TPM_UNDEFINED, /* 140 */ > >+ TPM_UNDEFINED, /* 141 */ > >+ TPM_UNDEFINED, /* 142 */ > >+ TPM_LONG, /* 143 */ > >+ TPM_MEDIUM, /* 144 */ > >+ TPM_UNDEFINED, /* 145 */ > >+ TPM_UNDEFINED, /* 146 */ > >+ TPM_UNDEFINED, /* 147 */ > >+ TPM_UNDEFINED, /* 148 */ > >+ TPM_UNDEFINED, /* 149 */ > >+ TPM_UNDEFINED, /* 14a */ > >+ TPM_UNDEFINED, /* 14b */ > >+ TPM_UNDEFINED, /* 14c */ > >+ TPM_UNDEFINED, /* 14d */ > >+ TPM_LONG, /* 14e */ > >+ TPM_UNDEFINED, /* 14f */ > >+ TPM_UNDEFINED, /* 150 */ > >+ TPM_UNDEFINED, /* 151 */ > >+ TPM_UNDEFINED, /* 152 */ > >+ TPM_UNDEFINED, /* 153 */ > >+ TPM_UNDEFINED, /* 154 */ > >+ TPM_UNDEFINED, /* 155 */ > >+ TPM_UNDEFINED, /* 156 */ > >+ TPM_UNDEFINED, /* 157 */ > >+ TPM_UNDEFINED, /* 158 */ > >+ TPM_UNDEFINED, /* 159 */ > >+ TPM_UNDEFINED, /* 15a */ > >+ TPM_UNDEFINED, /* 15b */ > >+ TPM_MEDIUM, /* 15c */ > >+ TPM_UNDEFINED, /* 15d */ > >+ TPM_UNDEFINED, /* 15e */ > >+ TPM_UNDEFINED, /* 15f */ > >+ TPM_UNDEFINED, /* 160 */ > >+ TPM_UNDEFINED, /* 161 */ > >+ TPM_UNDEFINED, /* 162 */ > >+ TPM_UNDEFINED, /* 163 */ > >+ TPM_UNDEFINED, /* 164 */ > >+ TPM_UNDEFINED, /* 165 */ > >+ TPM_UNDEFINED, /* 166 */ > >+ TPM_UNDEFINED, /* 167 */ > >+ TPM_UNDEFINED, /* 168 */ > >+ TPM_UNDEFINED, /* 169 */ > >+ TPM_UNDEFINED, /* 16a */ > >+ TPM_UNDEFINED, /* 16b */ > >+ TPM_UNDEFINED, /* 16c */ > >+ TPM_UNDEFINED, /* 16d */ > >+ TPM_UNDEFINED, /* 16e */ > >+ TPM_UNDEFINED, /* 16f */ > >+ TPM_UNDEFINED, /* 170 */ > >+ TPM_UNDEFINED, /* 171 */ > >+ TPM_UNDEFINED, /* 172 */ > >+ TPM_UNDEFINED, /* 173 */ > >+ TPM_UNDEFINED, /* 174 */ > >+ TPM_UNDEFINED, /* 175 */ > >+ TPM_UNDEFINED, /* 176 */ > >+ TPM_LONG, /* 177 */ > >+ TPM_UNDEFINED, /* 178 */ > >+ TPM_UNDEFINED, /* 179 */ > >+ TPM_MEDIUM, /* 17a */ > >+ TPM_LONG, /* 17b */ > >+ TPM_UNDEFINED, /* 17c */ > >+ TPM_UNDEFINED, /* 17d */ > >+ TPM_UNDEFINED, /* 17e */ > >+ TPM_UNDEFINED, /* 17f */ > >+ TPM_UNDEFINED, /* 180 */ > >+ TPM_UNDEFINED, /* 181 */ > >+ TPM_MEDIUM, /* 182 */ > >+ TPM_UNDEFINED, /* 183 */ > >+ TPM_UNDEFINED, /* 184 */ > >+ TPM_MEDIUM, /* 185 */ > >+ TPM_MEDIUM, /* 186 */ > >+ TPM_UNDEFINED, /* 187 */ > >+ TPM_UNDEFINED, /* 188 */ > >+ TPM_UNDEFINED, /* 189 */ > >+ TPM_UNDEFINED, /* 18a */ > >+ TPM_UNDEFINED, /* 18b */ > >+ TPM_UNDEFINED, /* 18c */ > >+ TPM_UNDEFINED, /* 18d */ > >+ TPM_UNDEFINED, /* 18e */ > >+ TPM_UNDEFINED /* 18f */ > >+}; > >+ > >+static const struct tpm_input_header tpm2_startup_header = { > >+ .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), > >+ .length = cpu_to_be32(12), > > 12 -> sizeof(struct tpm_input_header) + sizeof(struct pm2_startup_in) > > >+ .ordinal = cpu_to_be32(TPM2_CC_STARTUP) > >+}; > >+ > >+/** > >+ * tpm2_startup() - send startup command to the TPM chip > >+ * @chip: TPM chip to use. > >+ * @startup_type startup type. The value is either > >+ * TPM_SU_CLEAR or TPM_SU_STATE. > >+ * > >+ * 0 is returned when the operation is successful. When a negative number is > >+ * returned it remarks a POSIX error code. When a positive number is returned > >+ * it remarks a TPM error. > > Replace 'When's with 'If's. (when being 'temporal') > > >+ */ > >+int tpm2_startup(struct tpm_chip *chip, __be16 startup_type) > >+{ > >+ struct tpm2_cmd cmd; > >+ > >+ cmd.header.in = tpm2_startup_header; > >+ > >+ cmd.params.startup_in.startup_type = startup_type; > >+ return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), > >+ "attempting to start the TPM"); > >+} > >+ > >+#define TPM2_PCR_READ_IN_SIZE \ > >+ (sizeof(struct tpm_input_header) + \ > >+ sizeof(struct tpm2_pcr_read_in)) > >+ > > Ah! You could also use a #define above! > > >+static const struct tpm_input_header tpm2_pcrread_header = { > >+ .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), > >+ .length = cpu_to_be32(TPM2_PCR_READ_IN_SIZE), > >+ .ordinal = cpu_to_be32(TPM2_CC_PCR_READ) > >+}; > >+ > >+/** > >+ * tpm2_pcr_read() - read a PCR value > >+ * @chip: TPM chip to use. > >+ * @pcr_idx: index of the PCR to read. > >+ * @ref_buf: buffer to store the resulting hash, > >+ * > >+ * 0 is returned when the operation is successful. When a negative number is > >+ * returned it remarks a POSIX error code. When a positive number is returned > >+ * it remarks a TPM error. > >+ */ > > Replace 'When's with 'If's. Also further below. > > >+int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) > >+{ > >+ int rc; > >+ struct tpm2_cmd cmd; > >+ u8 *buf; > >+ int i, j; > >+ > >+ if (pcr_idx >= TPM2_PLATFORM_PCR) > >+ return -EINVAL; > >+ > >+ cmd.header.in = tpm2_pcrread_header; > >+ cmd.params.pcrread_in.pcr_selects_cnt = cpu_to_be32(1); > >+ cmd.params.pcrread_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1); > >+ cmd.params.pcrread_in.pcr_select_size = TPM2_PCR_SELECT_MIN; > >+ > >+ for (i = 0; i < TPM2_PCR_SELECT_MIN; i++) { > >+ j = pcr_idx - i * 8; > >+ > >+ cmd.params.pcrread_in.pcr_select[i] = > >+ (j >= 0 && j < 8) ? 1 << j : 0; > >+ } > > Umpf - what's this? You need to set the PCR index as an index in the > bitfield? > > pcr_idx >> 3 gives you the index into the array, assuming that [0] holds > bits for PCR0 to 7. > > 1 << (pcr_idx & 0x7) gives you the bit to set, assuming bit 0 is to be set > for PCR 0 > 1 << (7- (pcr_idx & 0x7)) gives you the bit to set, assuming bit 7 is to be > set for PCR 0. > > memset(cmd.params.pcrread_in.pcr_select, 0, > sizeof(cmd.params.pcrread_in.pcr_select)); > cmd.params.pcrread_in.pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7); Just sloppy and ugly code that nobody has commented so far and since it has worked I haven't really give it much thought :) Will definitely clean that mess, thanks. /Jarkko ------------------------------------------------------------------------------ Download BIRT iHub F-Type - The Free Enterprise-Grade BIRT Server from Actuate! Instantly Supercharge Your Business Reports and Dashboards with Interactivity, Sharing, Native Excel Exports, App Integration & more Get technology previously reserved for billion-dollar corporations, FREE http://pubads.g.doubleclick.net/gampad/clk?id=157005751&iu=/4140/ostg.clktrk
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 837da04..ae56af9 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -2,7 +2,7 @@ # Makefile for the kernel tpm device drivers. # obj-$(CONFIG_TCG_TPM) += tpm.o -tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o +tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o tpm-$(CONFIG_ACPI) += tpm_ppi.o ifdef CONFIG_ACPI diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 5d268ac..4d25b24 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -213,11 +213,14 @@ int tpm_chip_register(struct tpm_chip *chip) if (rc) return rc; - rc = tpm_add_ppi(chip); - if (rc) - goto out_err; + /* Populate sysfs for TPM1 devices. */ + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { + rc = tpm_add_ppi(chip); + if (rc) + goto out_err; - chip->bios_dir = tpm_bios_log_setup(chip->devname); + chip->bios_dir = tpm_bios_log_setup(chip->devname); + } /* Make the chip available. */ spin_lock(&driver_lock); @@ -248,10 +251,12 @@ void tpm_chip_unregister(struct tpm_chip *chip) spin_unlock(&driver_lock); synchronize_rcu(); - tpm_remove_ppi(chip); - - if (chip->bios_dir) - tpm_bios_log_teardown(chip->bios_dir); + /* Clean up sysfs for TPM1 devices. */ + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { + if (chip->bios_dir) + tpm_bios_log_teardown(chip->bios_dir); + tpm_remove_ppi(chip); + } tpm_dev_del_device(chip); } diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 9e4ce4d..e62b835 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -360,7 +360,10 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, if (chip->vendor.irq) goto out_recv; - stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); + if (chip->flags & TPM_CHIP_FLAG_TPM2) + stop = jiffies + tpm2_calc_ordinal_duration(chip, ordinal); + else + stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); do { u8 status = chip->ops->status(chip); if ((status & chip->ops->req_complete_mask) == @@ -483,7 +486,7 @@ static const struct tpm_input_header tpm_startup_header = { static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) { struct tpm_cmd_t start_cmd; - start_cmd.header.in = tpm_startup_header; + start_cmd.params.startup_in.startup_type = startup_type; return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, "attempting to start the TPM"); @@ -680,7 +683,10 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) chip = tpm_chip_find_get(chip_num); if (chip == NULL) return -ENODEV; - rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf); + if (chip->flags & TPM_CHIP_FLAG_TPM2) + rc = tpm2_pcr_read(chip, pcr_idx, res_buf); + else + rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf); tpm_chip_put(chip); return rc; } @@ -714,6 +720,12 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) if (chip == NULL) return -ENODEV; + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + rc = tpm2_pcr_extend(chip, pcr_idx, hash); + tpm_chip_put(chip); + return rc; + } + cmd.header.in = pcrextend_header; cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE); @@ -974,6 +986,12 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) if (chip == NULL) return -ENODEV; + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + err = tpm2_get_random(chip, out, max); + tpm_chip_put(chip); + return err; + } + do { tpm_cmd.header.in = tpm_getrandom_header; tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 9d062e6..8a434d2 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -62,6 +62,57 @@ enum tpm_duration { #define TPM_ERR_INVALID_POSTINIT 38 #define TPM_HEADER_SIZE 10 + +enum tpm2_const { + TPM2_PLATFORM_PCR = 24, + TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8), + TPM2_TIMEOUT_A = 750 * 1000, + TPM2_TIMEOUT_B = 2000 * 1000, + TPM2_TIMEOUT_C = 200 * 1000, + TPM2_TIMEOUT_D = 30 * 1000, + TPM2_DURATION_SHORT = 20 * 1000, + TPM2_DURATION_MEDIUM = 750 * 1000, + TPM2_DURATION_LONG = 2000 * 1000, +}; + +enum tpm2_structures { + TPM2_ST_NO_SESSIONS = 0x8001, + TPM2_ST_SESSIONS = 0x8002, +}; + +enum tpm2_return_codes { + TPM2_RC_TESTING = 0x090A, + TPM2_RC_DISABLED = 0x0120, +}; + +enum tpm2_algorithms { + TPM2_ALG_SHA1 = 0x0004, +}; + +enum tpm2_command_codes { + TPM2_CC_FIRST = 0x011F, + TPM2_CC_SELF_TEST = 0x0143, + TPM2_CC_STARTUP = 0x0144, + TPM2_CC_GET_CAPABILITY = 0x017A, + TPM2_CC_GET_RANDOM = 0x017B, + TPM2_CC_PCR_READ = 0x017E, + TPM2_CC_PCR_EXTEND = 0x0182, + TPM2_CC_LAST = 0x018F, +}; + +enum tpm2_permanent_handles { + TPM2_RS_PW = 0x40000009, +}; + +enum tpm2_capabilities { + TPM2_CAP_TPM_PROPERTIES = 6, +}; + +enum tpm2_startup_types { + TPM2_SU_CLEAR = 0x0000, + TPM2_SU_STATE = 0x0001, +}; + struct tpm_chip; struct tpm_vendor_specific { @@ -96,12 +147,17 @@ struct tpm_vendor_specific { #define TPM_PPI_VERSION_LEN 3 +enum tpm_chip_flags { + TPM_CHIP_FLAG_TPM2 = BIT(0), +}; + struct tpm_chip { struct device *pdev; /* Device stuff */ struct device dev; struct cdev cdev; const struct tpm_class_ops *ops; + unsigned int flags; int dev_num; /* /dev/tpm# */ char devname[7]; @@ -362,3 +418,14 @@ static inline void tpm_remove_ppi(struct tpm_chip *chip) { } #endif + +int tpm2_startup(struct tpm_chip *chip, __be16 startup_type); +int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf); +int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash); +int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max); + +extern ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, + u32 *value, const char *desc); +extern unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *, u32); +extern int tpm2_do_selftest(struct tpm_chip *chip); +extern int tpm2_gen_interrupt(struct tpm_chip *chip); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c new file mode 100644 index 0000000..458a17d --- /dev/null +++ b/drivers/char/tpm/tpm2-cmd.c @@ -0,0 +1,566 @@ +/* + * Copyright (C) 2014 Intel Corporation + * + * Authors: + * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> + * + * Maintained by: <tpmdd-devel@lists.sourceforge.net> + * + * This file contains TPM2 protocol implementations of the commands + * used by the kernel internally. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include "tpm.h" + +struct tpm2_startup_in { + __be16 startup_type; +} __packed; + +struct tpm2_self_test_in { + u8 full_test; +} __packed; + +struct tpm2_pcr_read_in { + __be32 pcr_selects_cnt; + __be16 hash_alg; + u8 pcr_select_size; + u8 pcr_select[TPM2_PCR_SELECT_MIN]; +} __packed; + +struct tpm2_pcr_read_out { + __be32 update_cnt; + __be32 pcr_selects_cnt; + __be16 hash_alg; + u8 pcr_select_size; + u8 pcr_select[TPM2_PCR_SELECT_MIN]; + __be32 digests_cnt; + __be16 digest_size; + u8 digest[TPM_DIGEST_SIZE]; +} __packed; + +struct tpm2_null_auth_area { + __be32 handle; + __be16 nonce_size; + u8 attributes; + __be16 auth_size; +} __packed; + +struct tpm2_pcr_extend_in { + __be32 pcr_idx; + __be32 auth_area_size; + struct tpm2_null_auth_area auth_area; + __be32 digest_cnt; + __be16 hash_alg; + u8 digest[TPM_DIGEST_SIZE]; +} __packed; + +struct tpm2_get_tpm_pt_in { + __be32 cap_id; + __be32 property_id; + __be32 property_cnt; +} __packed; + +struct tpm2_get_tpm_pt_out { + u8 more_data; + __be32 subcap_id; + __be32 property_cnt; + __be32 property_id; + __be32 value; +} __packed; + +struct tpm2_get_random_in { + __be16 size; +} __packed; + +struct tpm2_get_random_out { + __be16 size; + u8 buffer[TPM_MAX_RNG_DATA]; +} __packed; + +union tpm2_cmd_params { + struct tpm2_startup_in startup_in; + struct tpm2_self_test_in selftest_in; + struct tpm2_pcr_read_in pcrread_in; + struct tpm2_pcr_read_out pcrread_out; + struct tpm2_pcr_extend_in pcrextend_in; + struct tpm2_get_tpm_pt_in get_tpm_pt_in; + struct tpm2_get_tpm_pt_out get_tpm_pt_out; + struct tpm2_get_random_in getrandom_in; + struct tpm2_get_random_out getrandom_out; +}; + +struct tpm2_cmd { + tpm_cmd_header header; + union tpm2_cmd_params params; +} __packed; + +/* + * Array with one entry per ordinal defining the maximum amount + * of time the chip could take to return the result. The values + * of the SHORT, MEDIUM, and LONG durations are taken from the + * PC Client Profile (PTP) specification. + */ +static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = { + TPM_UNDEFINED, /* 11F */ + TPM_UNDEFINED, /* 120 */ + TPM_LONG, /* 121 */ + TPM_UNDEFINED, /* 122 */ + TPM_UNDEFINED, /* 123 */ + TPM_UNDEFINED, /* 124 */ + TPM_UNDEFINED, /* 125 */ + TPM_UNDEFINED, /* 126 */ + TPM_UNDEFINED, /* 127 */ + TPM_UNDEFINED, /* 128 */ + TPM_LONG, /* 129 */ + TPM_UNDEFINED, /* 12a */ + TPM_UNDEFINED, /* 12b */ + TPM_UNDEFINED, /* 12c */ + TPM_UNDEFINED, /* 12d */ + TPM_UNDEFINED, /* 12e */ + TPM_UNDEFINED, /* 12f */ + TPM_UNDEFINED, /* 130 */ + TPM_UNDEFINED, /* 131 */ + TPM_UNDEFINED, /* 132 */ + TPM_UNDEFINED, /* 133 */ + TPM_UNDEFINED, /* 134 */ + TPM_UNDEFINED, /* 135 */ + TPM_UNDEFINED, /* 136 */ + TPM_UNDEFINED, /* 137 */ + TPM_UNDEFINED, /* 138 */ + TPM_UNDEFINED, /* 139 */ + TPM_UNDEFINED, /* 13a */ + TPM_UNDEFINED, /* 13b */ + TPM_UNDEFINED, /* 13c */ + TPM_UNDEFINED, /* 13d */ + TPM_MEDIUM, /* 13e */ + TPM_UNDEFINED, /* 13f */ + TPM_UNDEFINED, /* 140 */ + TPM_UNDEFINED, /* 141 */ + TPM_UNDEFINED, /* 142 */ + TPM_LONG, /* 143 */ + TPM_MEDIUM, /* 144 */ + TPM_UNDEFINED, /* 145 */ + TPM_UNDEFINED, /* 146 */ + TPM_UNDEFINED, /* 147 */ + TPM_UNDEFINED, /* 148 */ + TPM_UNDEFINED, /* 149 */ + TPM_UNDEFINED, /* 14a */ + TPM_UNDEFINED, /* 14b */ + TPM_UNDEFINED, /* 14c */ + TPM_UNDEFINED, /* 14d */ + TPM_LONG, /* 14e */ + TPM_UNDEFINED, /* 14f */ + TPM_UNDEFINED, /* 150 */ + TPM_UNDEFINED, /* 151 */ + TPM_UNDEFINED, /* 152 */ + TPM_UNDEFINED, /* 153 */ + TPM_UNDEFINED, /* 154 */ + TPM_UNDEFINED, /* 155 */ + TPM_UNDEFINED, /* 156 */ + TPM_UNDEFINED, /* 157 */ + TPM_UNDEFINED, /* 158 */ + TPM_UNDEFINED, /* 159 */ + TPM_UNDEFINED, /* 15a */ + TPM_UNDEFINED, /* 15b */ + TPM_MEDIUM, /* 15c */ + TPM_UNDEFINED, /* 15d */ + TPM_UNDEFINED, /* 15e */ + TPM_UNDEFINED, /* 15f */ + TPM_UNDEFINED, /* 160 */ + TPM_UNDEFINED, /* 161 */ + TPM_UNDEFINED, /* 162 */ + TPM_UNDEFINED, /* 163 */ + TPM_UNDEFINED, /* 164 */ + TPM_UNDEFINED, /* 165 */ + TPM_UNDEFINED, /* 166 */ + TPM_UNDEFINED, /* 167 */ + TPM_UNDEFINED, /* 168 */ + TPM_UNDEFINED, /* 169 */ + TPM_UNDEFINED, /* 16a */ + TPM_UNDEFINED, /* 16b */ + TPM_UNDEFINED, /* 16c */ + TPM_UNDEFINED, /* 16d */ + TPM_UNDEFINED, /* 16e */ + TPM_UNDEFINED, /* 16f */ + TPM_UNDEFINED, /* 170 */ + TPM_UNDEFINED, /* 171 */ + TPM_UNDEFINED, /* 172 */ + TPM_UNDEFINED, /* 173 */ + TPM_UNDEFINED, /* 174 */ + TPM_UNDEFINED, /* 175 */ + TPM_UNDEFINED, /* 176 */ + TPM_LONG, /* 177 */ + TPM_UNDEFINED, /* 178 */ + TPM_UNDEFINED, /* 179 */ + TPM_MEDIUM, /* 17a */ + TPM_LONG, /* 17b */ + TPM_UNDEFINED, /* 17c */ + TPM_UNDEFINED, /* 17d */ + TPM_UNDEFINED, /* 17e */ + TPM_UNDEFINED, /* 17f */ + TPM_UNDEFINED, /* 180 */ + TPM_UNDEFINED, /* 181 */ + TPM_MEDIUM, /* 182 */ + TPM_UNDEFINED, /* 183 */ + TPM_UNDEFINED, /* 184 */ + TPM_MEDIUM, /* 185 */ + TPM_MEDIUM, /* 186 */ + TPM_UNDEFINED, /* 187 */ + TPM_UNDEFINED, /* 188 */ + TPM_UNDEFINED, /* 189 */ + TPM_UNDEFINED, /* 18a */ + TPM_UNDEFINED, /* 18b */ + TPM_UNDEFINED, /* 18c */ + TPM_UNDEFINED, /* 18d */ + TPM_UNDEFINED, /* 18e */ + TPM_UNDEFINED /* 18f */ +}; + +static const struct tpm_input_header tpm2_startup_header = { + .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .length = cpu_to_be32(12), + .ordinal = cpu_to_be32(TPM2_CC_STARTUP) +}; + +/** + * tpm2_startup() - send startup command to the TPM chip + * @chip: TPM chip to use. + * @startup_type startup type. The value is either + * TPM_SU_CLEAR or TPM_SU_STATE. + * + * 0 is returned when the operation is successful. When a negative number is + * returned it remarks a POSIX error code. When a positive number is returned + * it remarks a TPM error. + */ +int tpm2_startup(struct tpm_chip *chip, __be16 startup_type) +{ + struct tpm2_cmd cmd; + + cmd.header.in = tpm2_startup_header; + + cmd.params.startup_in.startup_type = startup_type; + return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), + "attempting to start the TPM"); +} + +#define TPM2_PCR_READ_IN_SIZE \ + (sizeof(struct tpm_input_header) + \ + sizeof(struct tpm2_pcr_read_in)) + +static const struct tpm_input_header tpm2_pcrread_header = { + .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .length = cpu_to_be32(TPM2_PCR_READ_IN_SIZE), + .ordinal = cpu_to_be32(TPM2_CC_PCR_READ) +}; + +/** + * tpm2_pcr_read() - read a PCR value + * @chip: TPM chip to use. + * @pcr_idx: index of the PCR to read. + * @ref_buf: buffer to store the resulting hash, + * + * 0 is returned when the operation is successful. When a negative number is + * returned it remarks a POSIX error code. When a positive number is returned + * it remarks a TPM error. + */ +int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) +{ + int rc; + struct tpm2_cmd cmd; + u8 *buf; + int i, j; + + if (pcr_idx >= TPM2_PLATFORM_PCR) + return -EINVAL; + + cmd.header.in = tpm2_pcrread_header; + cmd.params.pcrread_in.pcr_selects_cnt = cpu_to_be32(1); + cmd.params.pcrread_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1); + cmd.params.pcrread_in.pcr_select_size = TPM2_PCR_SELECT_MIN; + + for (i = 0; i < TPM2_PCR_SELECT_MIN; i++) { + j = pcr_idx - i * 8; + + cmd.params.pcrread_in.pcr_select[i] = + (j >= 0 && j < 8) ? 1 << j : 0; + } + + rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), + "attempting to read a pcr value"); + + if (rc == 0) { + buf = cmd.params.pcrread_out.digest; + memcpy(res_buf, buf, TPM_DIGEST_SIZE); + } + + return rc; +} + +/** + * tpm2_pcr_extend() - extend a PCR value + * @chip: TPM chip to use. + * @pcr_idx: index of the PCR. + * @hash: hash value to use for the extend operation. + * + * 0 is returned when the operation is successful. When a negative number is + * returned it remarks a POSIX error code. When a positive number is returned + * it remarks a TPM error. + */ +static const struct tpm_input_header tpm2_pcrextend_header = { + .tag = cpu_to_be16(TPM2_ST_SESSIONS), + .length = cpu_to_be32(sizeof(struct tpm_input_header) + + sizeof(struct tpm2_pcr_extend_in)), + .ordinal = cpu_to_be32(TPM2_CC_PCR_EXTEND) +}; + +int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash) +{ + struct tpm2_cmd cmd; + int rc; + + cmd.header.in = tpm2_pcrextend_header; + cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); + cmd.params.pcrextend_in.auth_area_size = + cpu_to_be32(sizeof(struct tpm2_null_auth_area)); + cmd.params.pcrextend_in.auth_area.handle = + cpu_to_be32(TPM2_RS_PW); + cmd.params.pcrextend_in.auth_area.nonce_size = 0; + cmd.params.pcrextend_in.auth_area.attributes = 0; + cmd.params.pcrextend_in.auth_area.auth_size = 0; + cmd.params.pcrextend_in.digest_cnt = cpu_to_be32(1); + cmd.params.pcrextend_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1); + memcpy(cmd.params.pcrextend_in.digest, hash, TPM_DIGEST_SIZE); + + rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), + "attempting extend a PCR value"); + + return rc; +} + +static const struct tpm_input_header tpm2_getrandom_header = { + .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .length = cpu_to_be32(sizeof(struct tpm_input_header) + + sizeof(struct tpm2_get_random_in)), + .ordinal = cpu_to_be32(TPM2_CC_GET_RANDOM) +}; + +/** + * tpm2_get_random() - get random bytes from the TPM RNG + * @chip: TPM chip to use + * @out: destination buffer for the random bytes + * @max: the max number of bytes to write to @out + * + * 0 is returned when the operation is successful. When a negative number is + * returned it remarks a POSIX error code. When a positive number is returned + * it remarks a TPM error. + */ +int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max) +{ + struct tpm2_cmd cmd; + u32 recd, num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA); + int err, total = 0, retries = 5; + u8 *dest = out; + + if (!out || !num_bytes || max > TPM_MAX_RNG_DATA) + return -EINVAL; + + do { + cmd.header.in = tpm2_getrandom_header; + cmd.params.getrandom_in.size = cpu_to_be16(num_bytes); + + err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), + "attempting get random"); + if (err) + break; + + recd = be16_to_cpu(cmd.params.getrandom_out.size); + memcpy(dest, cmd.params.getrandom_out.buffer, recd); + + dest += recd; + total += recd; + num_bytes -= recd; + } while (retries-- && total < max); + + return total ? total : -EIO; +} + +#define TPM2_GET_TPM_PT_IN_SIZE \ + (sizeof(struct tpm_input_header) + \ + sizeof(struct tpm2_get_tpm_pt_in)) + +static const struct tpm_input_header tpm2_get_tpm_pt_header = { + .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .length = cpu_to_be32(TPM2_GET_TPM_PT_IN_SIZE), + .ordinal = cpu_to_be32(TPM2_CC_GET_CAPABILITY) +}; + +/** + * tpm2_get_tpm_pt() - get value of a TPM_CAP_TPM_PROPERTIES type property + * @chip: TPM chip to use. + * @property_id: property ID. + * @value: output variable. + * @desc: passed to tpm_transmit_cmd() + * + * 0 is returned when the operation is successful. When a negative number is + * returned it remarks a POSIX error code. When a positive number is returned + * it remarks a TPM error. + */ +ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, + const char *desc) +{ + struct tpm2_cmd cmd; + int rc; + + cmd.header.in = tpm2_get_tpm_pt_header; + cmd.params.get_tpm_pt_in.cap_id = cpu_to_be32(TPM2_CAP_TPM_PROPERTIES); + cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(property_id); + cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1); + + rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), desc); + if (!rc) + *value = cmd.params.get_tpm_pt_out.value; + + return rc; +} +EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt); + +/* + * tpm2_calc_ordinal_duration() - maximum duration for a command + * @chip: TPM chip to use. + * @ordinal: command code number. + * + * 0 is returned when the operation is successful. When a negative number is + * returned it remarks a POSIX error code. When a positive number is returned + * it remarks a TPM error. + */ +unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) +{ + int index = TPM_UNDEFINED; + int duration = 0; + + if (ordinal >= TPM2_CC_FIRST && ordinal <= TPM2_CC_LAST) + index = tpm2_ordinal_duration[ordinal - TPM2_CC_FIRST]; + + if (index != TPM_UNDEFINED) + duration = chip->vendor.duration[index]; + if (duration <= 0) + return 2 * 60 * HZ; + else + return duration; +} +EXPORT_SYMBOL_GPL(tpm2_calc_ordinal_duration); + +static const struct tpm_input_header tpm2_selftest_header = { + .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .length = cpu_to_be32(sizeof(struct tpm_input_header) + + sizeof(struct tpm2_self_test_in)), + .ordinal = cpu_to_be32(TPM2_CC_SELF_TEST) +}; + +#define TPM2_SELF_TEST_IN_SIZE \ + (sizeof(struct tpm_input_header) + sizeof(struct tpm2_self_test_in)) + +/** + * tpm2_continue_selftest() - start a self test + * @chip: TPM chip to use + * @full: test all commands instead of testing only those that were not + * previously tested. + * + * 0 is returned when the operation is successful. When a negative number is + * returned it remarks a POSIX error code. When a positive number is returned + * it remarks a TPM error. + */ +static int tpm2_start_selftest(struct tpm_chip *chip, bool full) +{ + int rc; + struct tpm2_cmd cmd; + + cmd.header.in = tpm2_selftest_header; + cmd.params.selftest_in.full_test = full; + + rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE, + "continue selftest"); + + return rc; +} + +/** + * tpm2_do_selftest() - run a full self test + * @chip: TPM chip to use + * + * During the self test TPM2 commands return with the error code RC_TESTING. + * Waiting is done by issuing PCR read until it executes successfully. + * + * 0 is returned when the operation is successful. When a negative number is + * returned it remarks a POSIX error code. When a positive number is returned + * it remarks a TPM error. + */ +int tpm2_do_selftest(struct tpm_chip *chip) +{ + int rc; + unsigned int loops; + unsigned int delay_msec = 100; + unsigned long duration; + struct tpm2_cmd cmd; + int i; + + duration = tpm2_calc_ordinal_duration(chip, TPM2_CC_SELF_TEST); + + loops = jiffies_to_msecs(duration) / delay_msec; + + rc = tpm2_start_selftest(chip, true); + if (rc) + return rc; + + for (i = 0; i < loops; i++) { + /* Attempt to read a PCR value */ + cmd.header.in = tpm2_pcrread_header; + cmd.params.pcrread_in.pcr_selects_cnt = cpu_to_be32(1); + cmd.params.pcrread_in.hash_alg = cpu_to_be16(TPM2_ALG_SHA1); + cmd.params.pcrread_in.pcr_select_size = TPM2_PCR_SELECT_MIN; + cmd.params.pcrread_in.pcr_select[0] = 0x01; + cmd.params.pcrread_in.pcr_select[1] = 0x00; + cmd.params.pcrread_in.pcr_select[2] = 0x00; + + rc = tpm_transmit_cmd(chip, (u8 *) &cmd, sizeof(cmd), NULL); + if (rc < 0) + break; + + rc = be32_to_cpu(cmd.header.out.return_code); + if (rc != TPM2_RC_TESTING) + break; + + msleep(delay_msec); + } + + return rc; +} +EXPORT_SYMBOL_GPL(tpm2_do_selftest); + +/** + * tpm2_gen_interrupt() - generate an interrupt + * @chip: TPM chip to use + * + * 0 is returned when the operation is successful. When a negative number is + * returned it remarks a POSIX error code. When a positive number is returned + * it remarks a TPM error. + */ + +int tpm2_gen_interrupt(struct tpm_chip *chip) +{ + u32 dummy; + int rc; + + rc = tpm2_get_tpm_pt(chip, + TPM2_CAP_TPM_PROPERTIES, + &dummy, + "attempting to generate an interrupt"); + + return rc; +} +EXPORT_SYMBOL_GPL(tpm2_gen_interrupt);