diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 0a62c19937b6..46caadca916a 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -81,42 +81,66 @@ void tpm_put_ops(struct tpm_chip *chip) EXPORT_SYMBOL_GPL(tpm_put_ops); /** - * tpm_chip_find_get() - find and reserve a TPM chip + * tpm_default_chip() - find a TPM chip and get a reference to it + */ +struct tpm_chip *tpm_default_chip(void) +{ + struct tpm_chip *chip, *res = NULL; + int chip_num = 0; + int chip_prev; + + mutex_lock(&idr_lock); + + do { + chip_prev = chip_num; + chip = idr_get_next(&dev_nums_idr, &chip_num); + if (chip) { + get_device(&chip->dev); + res = chip; + break; + } + } while (chip_prev != chip_num); + + mutex_unlock(&idr_lock); + + return res; +} +EXPORT_SYMBOL_GPL(tpm_default_chip); + +/** + * tpm_find_get_ops() - find and reserve a TPM chip * @chip: a &struct tpm_chip instance, %NULL for the default chip * * Finds a TPM chip and reserves its class device and operations. The chip must - * be released with tpm_chip_put_ops() after use. + * be released with tpm_put_ops() after use. + * This function is for internal use only. It supports existing TPM callers + * by accepting NULL, but those callers should be converted to pass in a chip + * directly. * * Return: * A reserved &struct tpm_chip instance. * %NULL if a chip is not found. * %NULL if the chip is not available. */ -struct tpm_chip *tpm_chip_find_get(struct tpm_chip *chip) +struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip) { - struct tpm_chip *res = NULL; - int chip_num = 0; - int chip_prev; + int rc; - mutex_lock(&idr_lock); - - if (!chip) { - do { - chip_prev = chip_num; - chip = idr_get_next(&dev_nums_idr, &chip_num); - if (chip && !tpm_try_get_ops(chip)) { - res = chip; - break; - } - } while (chip_prev != chip_num); - } else { + if (chip) { if (!tpm_try_get_ops(chip)) - res = chip; + return chip; + return NULL; } - mutex_unlock(&idr_lock); - - return res; + chip = tpm_default_chip(); + if (!chip) + return NULL; + rc = tpm_try_get_ops(chip); + /* release additional reference we got from tpm_default_chip() */ + put_device(&chip->dev); + if (rc) + return NULL; + return chip; } /** diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index e32f6e85dc6d..1a803b0cf980 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include "tpm.h" @@ -369,10 +368,13 @@ err_len: return -EINVAL; } -static int tpm_request_locality(struct tpm_chip *chip) +static int tpm_request_locality(struct tpm_chip *chip, unsigned int flags) { int rc; + if (flags & TPM_TRANSMIT_NESTED) + return 0; + if (!chip->ops->request_locality) return 0; @@ -385,10 +387,13 @@ static int tpm_request_locality(struct tpm_chip *chip) return 0; } -static void tpm_relinquish_locality(struct tpm_chip *chip) +static void tpm_relinquish_locality(struct tpm_chip *chip, unsigned int flags) { int rc; + if (flags & TPM_TRANSMIT_NESTED) + return; + if (!chip->ops->relinquish_locality) return; @@ -399,6 +404,28 @@ static void tpm_relinquish_locality(struct tpm_chip *chip) chip->locality = -1; } +static int tpm_cmd_ready(struct tpm_chip *chip, unsigned int flags) +{ + if (flags & TPM_TRANSMIT_NESTED) + return 0; + + if (!chip->ops->cmd_ready) + return 0; + + return chip->ops->cmd_ready(chip); +} + +static int tpm_go_idle(struct tpm_chip *chip, unsigned int flags) +{ + if (flags & TPM_TRANSMIT_NESTED) + return 0; + + if (!chip->ops->go_idle) + return 0; + + return chip->ops->go_idle(chip); +} + static ssize_t tpm_try_transmit(struct tpm_chip *chip, struct tpm_space *space, u8 *buf, size_t bufsiz, @@ -423,7 +450,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, header->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); header->return_code = cpu_to_be32(TPM2_RC_COMMAND_CODE | TSS2_RESMGR_TPM_RC_LAYER); - return bufsiz; + return sizeof(*header); } if (bufsiz > TPM_BUFSIZE) @@ -439,24 +466,24 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, return -E2BIG; } - if (!(flags & TPM_TRANSMIT_UNLOCKED)) + if (!(flags & TPM_TRANSMIT_UNLOCKED) && !(flags & TPM_TRANSMIT_NESTED)) mutex_lock(&chip->tpm_mutex); - if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, true); /* Store the decision as chip->locality will be changed. */ need_locality = chip->locality == -1; - if (!(flags & TPM_TRANSMIT_RAW) && need_locality) { - rc = tpm_request_locality(chip); + if (need_locality) { + rc = tpm_request_locality(chip, flags); if (rc < 0) goto out_no_locality; } - if (chip->dev.parent) - pm_runtime_get_sync(chip->dev.parent); + rc = tpm_cmd_ready(chip, flags); + if (rc) + goto out; rc = tpm2_prepare_space(chip, space, ordinal, buf); if (rc) @@ -516,19 +543,22 @@ out_recv: } rc = tpm2_commit_space(chip, space, ordinal, buf, &len); + if (rc) + dev_err(&chip->dev, "tpm2_commit_space: error %d\n", rc); out: - if (chip->dev.parent) - pm_runtime_put_sync(chip->dev.parent); + rc = tpm_go_idle(chip, flags); + if (rc) + goto out; if (need_locality) - tpm_relinquish_locality(chip); + tpm_relinquish_locality(chip, flags); out_no_locality: if (chip->ops->clk_enable != NULL) chip->ops->clk_enable(chip, false); - if (!(flags & TPM_TRANSMIT_UNLOCKED)) + if (!(flags & TPM_TRANSMIT_UNLOCKED) && !(flags & TPM_TRANSMIT_NESTED)) mutex_unlock(&chip->tpm_mutex); return rc ? rc : len; } @@ -930,7 +960,7 @@ int tpm_is_tpm2(struct tpm_chip *chip) { int rc; - chip = tpm_chip_find_get(chip); + chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; @@ -954,7 +984,7 @@ int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) { int rc; - chip = tpm_chip_find_get(chip); + chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; if (chip->flags & TPM_CHIP_FLAG_TPM2) @@ -1013,7 +1043,7 @@ int tpm_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash) u32 count = 0; int i; - chip = tpm_chip_find_get(chip); + chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; @@ -1142,7 +1172,7 @@ int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen) { int rc; - chip = tpm_chip_find_get(chip); + chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; @@ -1262,7 +1292,7 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max) if (!out || !num_bytes || max > TPM_MAX_RNG_DATA) return -EINVAL; - chip = tpm_chip_find_get(chip); + chip = tpm_find_get_ops(chip); if (!chip) return -ENODEV; @@ -1324,7 +1354,7 @@ int tpm_seal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, { int rc; - chip = tpm_chip_find_get(chip); + chip = tpm_find_get_ops(chip); if (!chip || !(chip->flags & TPM_CHIP_FLAG_TPM2)) return -ENODEV; @@ -1352,7 +1382,7 @@ int tpm_unseal_trusted(struct tpm_chip *chip, { int rc; - chip = tpm_chip_find_get(chip); + chip = tpm_find_get_ops(chip); if (!chip || !(chip->flags & TPM_CHIP_FLAG_TPM2)) return -ENODEV; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 4426649e431c..f3501d05264f 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -424,23 +424,24 @@ struct tpm_buf { u8 *data; }; -static inline int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal) +static inline void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal) { struct tpm_input_header *head; + head = (struct tpm_input_header *)buf->data; + head->tag = cpu_to_be16(tag); + head->length = cpu_to_be32(sizeof(*head)); + head->ordinal = cpu_to_be32(ordinal); +} +static inline int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal) +{ buf->data_page = alloc_page(GFP_HIGHUSER); if (!buf->data_page) return -ENOMEM; buf->flags = 0; buf->data = kmap(buf->data_page); - - head = (struct tpm_input_header *) buf->data; - - head->tag = cpu_to_be16(tag); - head->length = cpu_to_be32(sizeof(*head)); - head->ordinal = cpu_to_be32(ordinal); - + tpm_buf_reset(buf, tag, ordinal); return 0; } @@ -511,9 +512,17 @@ extern const struct file_operations tpm_fops; extern const struct file_operations tpmrm_fops; extern struct idr dev_nums_idr; +/** + * enum tpm_transmit_flags - flags for tpm_transmit() + * + * @TPM_TRANSMIT_UNLOCKED: do not lock the chip + * @TPM_TRANSMIT_NESTED: discard setup steps (power management, + * locality) including locking (i.e. implicit + * UNLOCKED) + */ enum tpm_transmit_flags { TPM_TRANSMIT_UNLOCKED = BIT(0), - TPM_TRANSMIT_RAW = BIT(1), + TPM_TRANSMIT_NESTED = BIT(1), }; ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, @@ -538,7 +547,7 @@ static inline void tpm_msleep(unsigned int delay_msec) delay_msec * 1000); }; -struct tpm_chip *tpm_chip_find_get(struct tpm_chip *chip); +struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip); __must_check int tpm_try_get_ops(struct tpm_chip *chip); void tpm_put_ops(struct tpm_chip *chip); @@ -569,7 +578,7 @@ static inline u32 tpm2_rc_value(u32 rc) 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, u32 count, struct tpm2_digest *digests); -int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max); +int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max); void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, unsigned int flags); int tpm2_seal_trusted(struct tpm_chip *chip, diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index d31b09099216..c31b490bd41d 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -27,46 +27,6 @@ enum tpm2_session_attributes { TPM2_SA_CONTINUE_SESSION = BIT(0), }; -struct tpm2_startup_in { - __be16 startup_type; -} __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_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; - struct tpm2_hash { unsigned int crypto_id; unsigned int tpm_id; @@ -321,82 +281,72 @@ int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, u32 count, } -#define TPM2_GETRANDOM_IN_SIZE \ - (sizeof(struct tpm_input_header) + \ - sizeof(struct tpm2_get_random_in)) - -static const struct tpm_input_header tpm2_getrandom_header = { - .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), - .length = cpu_to_be32(TPM2_GETRANDOM_IN_SIZE), - .ordinal = cpu_to_be32(TPM2_CC_GET_RANDOM) -}; +struct tpm2_get_random_out { + __be16 size; + u8 buffer[TPM_MAX_RNG_DATA]; +} __packed; /** * 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 + * @chip: a &tpm_chip instance + * @dest: destination buffer + * @max: the max number of random bytes to pull * * Return: - * Size of the output buffer, or -EIO on error. + * size of the buffer on success, + * -errno otherwise */ -int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max) +int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max) { - struct tpm2_cmd cmd; - u32 recd, rlength; - u32 num_bytes; + struct tpm2_get_random_out *out; + struct tpm_buf buf; + u32 recd; + u32 num_bytes = max; int err; int total = 0; int retries = 5; - u8 *dest = out; + u8 *dest_ptr = dest; - num_bytes = min_t(u32, max, sizeof(cmd.params.getrandom_out.buffer)); - - if (!out || !num_bytes || - max > sizeof(cmd.params.getrandom_out.buffer)) + if (!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_buf_init(&buf, 0, 0); + if (err) + return err; - err = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), + do { + tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_RANDOM); + tpm_buf_append_u16(&buf, num_bytes); + err = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, offsetof(struct tpm2_get_random_out, buffer), 0, "attempting get random"); if (err) - break; + goto out; - recd = min_t(u32, be16_to_cpu(cmd.params.getrandom_out.size), - num_bytes); - rlength = be32_to_cpu(cmd.header.out.length); - if (rlength < offsetof(struct tpm2_get_random_out, buffer) + - recd) - return -EFAULT; - memcpy(dest, cmd.params.getrandom_out.buffer, recd); + out = (struct tpm2_get_random_out *) + &buf.data[TPM_HEADER_SIZE]; + recd = min_t(u32, be16_to_cpu(out->size), num_bytes); + if (tpm_buf_length(&buf) < + offsetof(struct tpm2_get_random_out, buffer) + recd) { + err = -EFAULT; + goto out; + } + memcpy(dest_ptr, out->buffer, recd); - dest += recd; + dest_ptr += recd; total += recd; num_bytes -= recd; } while (retries-- && total < max); + tpm_buf_destroy(&buf); return total ? total : -EIO; +out: + tpm_buf_destroy(&buf); + return err; } -#define TPM2_GET_TPM_PT_IN_SIZE \ - (sizeof(struct tpm_input_header) + \ - sizeof(struct tpm2_get_tpm_pt_in)) - -#define TPM2_GET_TPM_PT_OUT_BODY_SIZE \ - sizeof(struct tpm2_get_tpm_pt_out) - -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_flush_context_cmd() - execute a TPM2_FlushContext command * @chip: TPM chip to use @@ -471,7 +421,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, { unsigned int blob_len; struct tpm_buf buf; - u32 hash, rlength; + u32 hash; int i; int rc; @@ -546,8 +496,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, rc = -E2BIG; goto out; } - rlength = be32_to_cpu(((struct tpm2_cmd *)&buf)->header.out.length); - if (rlength < TPM_HEADER_SIZE + 4 + blob_len) { + if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) { rc = -EFAULT; goto out; } @@ -657,7 +606,6 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, u16 data_len; u8 *data; int rc; - u32 rlength; rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); if (rc) @@ -685,9 +633,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, goto out; } - rlength = be32_to_cpu(((struct tpm2_cmd *)&buf) - ->header.out.length); - if (rlength < TPM_HEADER_SIZE + 6 + data_len) { + if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 6 + data_len) { rc = -EFAULT; goto out; } @@ -733,69 +679,71 @@ out: return rc; } +struct tpm2_get_cap_out { + u8 more_data; + __be32 subcap_id; + __be32 property_cnt; + __be32 property_id; + __be32 value; +} __packed; + /** * tpm2_get_tpm_pt() - get value of a TPM_CAP_TPM_PROPERTIES type property - * @chip: TPM chip to use. + * @chip: a &tpm_chip instance * @property_id: property ID. * @value: output variable. * @desc: passed to tpm_transmit_cmd() * - * Return: Same as with tpm_transmit_cmd. + * Return: + * 0 on success, + * -errno or a TPM return code otherwise */ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, const char *desc) { - struct tpm2_cmd cmd; + struct tpm2_get_cap_out *out; + struct tpm_buf buf; 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, NULL, &cmd, sizeof(cmd), - TPM2_GET_TPM_PT_OUT_BODY_SIZE, 0, desc); - if (!rc) - *value = be32_to_cpu(cmd.params.get_tpm_pt_out.value); - + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY); + if (rc) + return rc; + tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES); + tpm_buf_append_u32(&buf, property_id); + tpm_buf_append_u32(&buf, 1); + rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, NULL); + if (!rc) { + out = (struct tpm2_get_cap_out *) + &buf.data[TPM_HEADER_SIZE]; + *value = be32_to_cpu(out->value); + } + tpm_buf_destroy(&buf); return rc; } EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt); -#define TPM2_SHUTDOWN_IN_SIZE \ - (sizeof(struct tpm_input_header) + \ - sizeof(struct tpm2_startup_in)) - -static const struct tpm_input_header tpm2_shutdown_header = { - .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), - .length = cpu_to_be32(TPM2_SHUTDOWN_IN_SIZE), - .ordinal = cpu_to_be32(TPM2_CC_SHUTDOWN) -}; - /** - * tpm2_shutdown() - send shutdown command to the TPM chip + * tpm2_shutdown() - send a TPM shutdown command * - * @chip: TPM chip to use. - * @shutdown_type: shutdown type. The value is either - * TPM_SU_CLEAR or TPM_SU_STATE. + * Sends a TPM shutdown command. The shutdown command is used in call + * sites where the system is going down. If it fails, there is not much + * that can be done except print an error message. + * + * @chip: a &tpm_chip instance + * @shutdown_type: TPM_SU_CLEAR or TPM_SU_STATE. */ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type) { - struct tpm2_cmd cmd; + struct tpm_buf buf; int rc; - cmd.header.in = tpm2_shutdown_header; - cmd.params.startup_in.startup_type = cpu_to_be16(shutdown_type); - - rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0, - "stopping the TPM"); - - /* In places where shutdown command is sent there's no much we can do - * except print the error code on a system failure. - */ - if (rc < 0 && rc != -EPIPE) - dev_warn(&chip->dev, "transmit returned %d while stopping the TPM", - rc); + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN); + if (rc) + return; + tpm_buf_append_u16(&buf, shutdown_type); + tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, + "stopping the TPM"); + tpm_buf_destroy(&buf); } /* @@ -863,31 +811,37 @@ static int tpm2_do_selftest(struct tpm_chip *chip) } /** - * tpm2_probe() - probe TPM 2.0 - * @chip: TPM chip to use + * tpm2_probe() - probe for the TPM 2.0 protocol + * @chip: a &tpm_chip instance * - * Return: < 0 error and 0 on success. + * Send an idempotent TPM 2.0 command and see whether there is TPM2 chip in the + * other end based on the response tag. The flag TPM_CHIP_FLAG_TPM2 is set by + * this function if this is the case. * - * Send idempotent TPM 2.0 command and see whether TPM 2.0 chip replied based on - * the reply tag. + * Return: + * 0 on success, + * -errno otherwise */ int tpm2_probe(struct tpm_chip *chip) { - struct tpm2_cmd cmd; + struct tpm_output_header *out; + struct tpm_buf buf; 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(0x100); - cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1); - - rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0, NULL); - if (rc < 0) + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY); + if (rc) return rc; - - if (be16_to_cpu(cmd.header.out.tag) == TPM2_ST_NO_SESSIONS) - chip->flags |= TPM_CHIP_FLAG_TPM2; - + tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES); + tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS); + tpm_buf_append_u32(&buf, 1); + rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, NULL); + /* We ignore TPM return codes on purpose. */ + if (rc >= 0) { + out = (struct tpm_output_header *)buf.data; + if (be16_to_cpu(out->tag) == TPM2_ST_NO_SESSIONS) + chip->flags |= TPM_CHIP_FLAG_TPM2; + } + tpm_buf_destroy(&buf); return 0; } EXPORT_SYMBOL_GPL(tpm2_probe); diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 6122d3276f72..d2e101b32482 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -39,7 +39,7 @@ static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space) for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) { if (space->session_tbl[i]) tpm2_flush_context_cmd(chip, space->session_tbl[i], - TPM_TRANSMIT_UNLOCKED); + TPM_TRANSMIT_NESTED); } } @@ -84,7 +84,7 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf, tpm_buf_append(&tbuf, &buf[*offset], body_size); rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 4, - TPM_TRANSMIT_UNLOCKED, NULL); + TPM_TRANSMIT_NESTED, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", __func__, rc); @@ -133,7 +133,7 @@ static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf, tpm_buf_append_u32(&tbuf, handle); rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 0, - TPM_TRANSMIT_UNLOCKED, NULL); + TPM_TRANSMIT_NESTED, NULL); if (rc < 0) { dev_warn(&chip->dev, "%s: failed with a system error %d\n", __func__, rc); @@ -170,7 +170,7 @@ static void tpm2_flush_space(struct tpm_chip *chip) for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) if (space->context_tbl[i] && ~space->context_tbl[i]) tpm2_flush_context_cmd(chip, space->context_tbl[i], - TPM_TRANSMIT_UNLOCKED); + TPM_TRANSMIT_NESTED); tpm2_flush_sessions(chip, space); } @@ -377,7 +377,7 @@ static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp, return 0; out_no_slots: - tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_UNLOCKED); + tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_NESTED); dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__, phandle); return -ENOMEM; @@ -465,7 +465,7 @@ static int tpm2_save_space(struct tpm_chip *chip) return rc; tpm2_flush_context_cmd(chip, space->context_tbl[i], - TPM_TRANSMIT_UNLOCKED); + TPM_TRANSMIT_NESTED); space->context_tbl[i] = ~0; } diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 34fbc6cb097b..36952ef98f90 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -132,7 +132,7 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, } /** - * crb_go_idle - request tpm crb device to go the idle state + * __crb_go_idle - request tpm crb device to go the idle state * * @dev: crb device * @priv: crb private data @@ -147,7 +147,7 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, * * Return: 0 always */ -static int crb_go_idle(struct device *dev, struct crb_priv *priv) +static int __crb_go_idle(struct device *dev, struct crb_priv *priv) { if ((priv->sm == ACPI_TPM2_START_METHOD) || (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) || @@ -163,11 +163,20 @@ static int crb_go_idle(struct device *dev, struct crb_priv *priv) dev_warn(dev, "goIdle timed out\n"); return -ETIME; } + return 0; } +static int crb_go_idle(struct tpm_chip *chip) +{ + struct device *dev = &chip->dev; + struct crb_priv *priv = dev_get_drvdata(dev); + + return __crb_go_idle(dev, priv); +} + /** - * crb_cmd_ready - request tpm crb device to enter ready state + * __crb_cmd_ready - request tpm crb device to enter ready state * * @dev: crb device * @priv: crb private data @@ -181,7 +190,7 @@ static int crb_go_idle(struct device *dev, struct crb_priv *priv) * * Return: 0 on success -ETIME on timeout; */ -static int crb_cmd_ready(struct device *dev, struct crb_priv *priv) +static int __crb_cmd_ready(struct device *dev, struct crb_priv *priv) { if ((priv->sm == ACPI_TPM2_START_METHOD) || (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) || @@ -200,6 +209,14 @@ static int crb_cmd_ready(struct device *dev, struct crb_priv *priv) return 0; } +static int crb_cmd_ready(struct tpm_chip *chip) +{ + struct device *dev = &chip->dev; + struct crb_priv *priv = dev_get_drvdata(dev); + + return __crb_cmd_ready(dev, priv); +} + static int __crb_request_locality(struct device *dev, struct crb_priv *priv, int loc) { @@ -401,6 +418,8 @@ static const struct tpm_class_ops tpm_crb = { .send = crb_send, .cancel = crb_cancel, .req_canceled = crb_req_canceled, + .go_idle = crb_go_idle, + .cmd_ready = crb_cmd_ready, .request_locality = crb_request_locality, .relinquish_locality = crb_relinquish_locality, .req_complete_mask = CRB_DRV_STS_COMPLETE, @@ -520,7 +539,7 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, * PTT HW bug w/a: wake up the device to access * possibly not retained registers. */ - ret = crb_cmd_ready(dev, priv); + ret = __crb_cmd_ready(dev, priv); if (ret) goto out_relinquish_locality; @@ -565,7 +584,7 @@ out: if (!ret) priv->cmd_size = cmd_size; - crb_go_idle(dev, priv); + __crb_go_idle(dev, priv); out_relinquish_locality: @@ -628,32 +647,7 @@ static int crb_acpi_add(struct acpi_device *device) chip->acpi_dev_handle = device->handle; chip->flags = TPM_CHIP_FLAG_TPM2; - rc = __crb_request_locality(dev, priv, 0); - if (rc) - return rc; - - rc = crb_cmd_ready(dev, priv); - if (rc) - goto out; - - pm_runtime_get_noresume(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - - rc = tpm_chip_register(chip); - if (rc) { - crb_go_idle(dev, priv); - pm_runtime_put_noidle(dev); - pm_runtime_disable(dev); - goto out; - } - - pm_runtime_put_sync(dev); - -out: - __crb_relinquish_locality(dev, priv, 0); - - return rc; + return tpm_chip_register(chip); } static int crb_acpi_remove(struct acpi_device *device) @@ -663,52 +657,11 @@ static int crb_acpi_remove(struct acpi_device *device) tpm_chip_unregister(chip); - pm_runtime_disable(dev); - return 0; } -static int __maybe_unused crb_pm_runtime_suspend(struct device *dev) -{ - struct tpm_chip *chip = dev_get_drvdata(dev); - struct crb_priv *priv = dev_get_drvdata(&chip->dev); - - return crb_go_idle(dev, priv); -} - -static int __maybe_unused crb_pm_runtime_resume(struct device *dev) -{ - struct tpm_chip *chip = dev_get_drvdata(dev); - struct crb_priv *priv = dev_get_drvdata(&chip->dev); - - return crb_cmd_ready(dev, priv); -} - -static int __maybe_unused crb_pm_suspend(struct device *dev) -{ - int ret; - - ret = tpm_pm_suspend(dev); - if (ret) - return ret; - - return crb_pm_runtime_suspend(dev); -} - -static int __maybe_unused crb_pm_resume(struct device *dev) -{ - int ret; - - ret = crb_pm_runtime_resume(dev); - if (ret) - return ret; - - return tpm_pm_resume(dev); -} - static const struct dev_pm_ops crb_pm = { - SET_SYSTEM_SLEEP_PM_OPS(crb_pm_suspend, crb_pm_resume) - SET_RUNTIME_PM_OPS(crb_pm_runtime_suspend, crb_pm_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(tpm_pm_suspend, tpm_pm_resume) }; static const struct acpi_device_id crb_device_ids[] = { diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 6116cd05e228..9086edc9066b 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -117,7 +117,7 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) /* Lock the adapter for the duration of the whole sequence. */ if (!tpm_dev.client->adapter->algo->master_xfer) return -EOPNOTSUPP; - i2c_lock_adapter(tpm_dev.client->adapter); + i2c_lock_bus(tpm_dev.client->adapter, I2C_LOCK_SEGMENT); if (tpm_dev.chip_type == SLB9645) { /* use a combined read for newer chips @@ -192,7 +192,7 @@ static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) } out: - i2c_unlock_adapter(tpm_dev.client->adapter); + i2c_unlock_bus(tpm_dev.client->adapter, I2C_LOCK_SEGMENT); /* take care of 'guard time' */ usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); @@ -224,7 +224,7 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, if (!tpm_dev.client->adapter->algo->master_xfer) return -EOPNOTSUPP; - i2c_lock_adapter(tpm_dev.client->adapter); + i2c_lock_bus(tpm_dev.client->adapter, I2C_LOCK_SEGMENT); /* prepend the 'register address' to the buffer */ tpm_dev.buf[0] = addr; @@ -243,7 +243,7 @@ static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, usleep_range(sleep_low, sleep_hi); } - i2c_unlock_adapter(tpm_dev.client->adapter); + i2c_unlock_bus(tpm_dev.client->adapter, I2C_LOCK_SEGMENT); /* take care of 'guard time' */ usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 8b46aaa9e049..d2345d9fd7b5 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -875,6 +875,8 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, chip->acpi_dev_handle = acpi_dev_handle; #endif + chip->hwrng.quality = priv->rng_quality; + /* Maximum timeouts */ chip->timeout_a = msecs_to_jiffies(TIS_TIMEOUT_A_MAX); chip->timeout_b = msecs_to_jiffies(TIS_TIMEOUT_B_MAX); diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index f6e1dbe212a7..f48125f1e6e0 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -99,6 +99,7 @@ struct tpm_tis_data { wait_queue_head_t int_queue; wait_queue_head_t read_queue; const struct tpm_tis_phy_ops *phy_ops; + unsigned short rng_quality; }; struct tpm_tis_phy_ops { diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c index 424ff2fde1f2..9914f6973463 100644 --- a/drivers/char/tpm/tpm_tis_spi.c +++ b/drivers/char/tpm/tpm_tis_spi.c @@ -199,6 +199,7 @@ static const struct tpm_tis_phy_ops tpm_spi_phy_ops = { static int tpm_tis_spi_probe(struct spi_device *dev) { struct tpm_tis_spi_phy *phy; + int irq; phy = devm_kzalloc(&dev->dev, sizeof(struct tpm_tis_spi_phy), GFP_KERNEL); @@ -211,7 +212,13 @@ static int tpm_tis_spi_probe(struct spi_device *dev) if (!phy->iobuf) return -ENOMEM; - return tpm_tis_core_init(&dev->dev, &phy->priv, -1, &tpm_spi_phy_ops, + /* If the SPI device has an IRQ then use that */ + if (dev->irq > 0) + irq = dev->irq; + else + irq = -1; + + return tpm_tis_core_init(&dev->dev, &phy->priv, irq, &tpm_spi_phy_ops, NULL); } diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c index e4f79f920450..87a0ce47f201 100644 --- a/drivers/char/tpm/tpm_vtpm_proxy.c +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -418,7 +418,7 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality) proxy_dev->state |= STATE_DRIVER_COMMAND; rc = tpm_transmit_cmd(chip, NULL, buf.data, tpm_buf_length(&buf), 0, - TPM_TRANSMIT_UNLOCKED | TPM_TRANSMIT_RAW, + TPM_TRANSMIT_NESTED, "attempting to set locality"); proxy_dev->state &= ~STATE_DRIVER_COMMAND; diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 06639fb6ab85..4609b94142d4 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -43,6 +43,8 @@ struct tpm_class_ops { u8 (*status) (struct tpm_chip *chip); bool (*update_timeouts)(struct tpm_chip *chip, unsigned long *timeout_cap); + int (*go_idle)(struct tpm_chip *chip); + int (*cmd_ready)(struct tpm_chip *chip); int (*request_locality)(struct tpm_chip *chip, int loc); int (*relinquish_locality)(struct tpm_chip *chip, int loc); void (*clk_enable)(struct tpm_chip *chip, bool value); @@ -61,6 +63,7 @@ extern int tpm_seal_trusted(struct tpm_chip *chip, extern int tpm_unseal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, struct trusted_key_options *options); +extern struct tpm_chip *tpm_default_chip(void); #else static inline int tpm_is_tpm2(struct tpm_chip *chip) { @@ -96,5 +99,9 @@ static inline int tpm_unseal_trusted(struct tpm_chip *chip, { return -ENODEV; } +static inline struct tpm_chip *tpm_default_chip(void) +{ + return NULL; +} #endif #endif diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index a597b2795d2d..67db9d9454ca 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -53,9 +53,9 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; extern int ima_policy_flag; /* set during initialization */ -extern int ima_used_chip; extern int ima_hash_algo; extern int ima_appraise; +extern struct tpm_chip *ima_tpm_chip; /* IMA event related data */ struct ima_event_data { diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 4e085a17124f..7e7e7e7c250a 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -631,10 +631,10 @@ int ima_calc_buffer_hash(const void *buf, loff_t len, static void __init ima_pcrread(int idx, u8 *pcr) { - if (!ima_used_chip) + if (!ima_tpm_chip) return; - if (tpm_pcr_read(NULL, idx, pcr) != 0) + if (tpm_pcr_read(ima_tpm_chip, idx, pcr) != 0) pr_err("Error Communicating to TPM chip\n"); } diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 29b72cd2502e..faac9ecaa0ae 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -26,7 +26,7 @@ /* name for boot aggregate entry */ static const char *boot_aggregate_name = "boot_aggregate"; -int ima_used_chip; +struct tpm_chip *ima_tpm_chip; /* Add the boot aggregate to the IMA measurement list and extend * the PCR register. @@ -64,7 +64,7 @@ static int __init ima_add_boot_aggregate(void) iint->ima_hash->algo = HASH_ALGO_SHA1; iint->ima_hash->length = SHA1_DIGEST_SIZE; - if (ima_used_chip) { + if (ima_tpm_chip) { result = ima_calc_boot_aggregate(&hash.hdr); if (result < 0) { audit_cause = "hashing_error"; @@ -106,17 +106,11 @@ void __init ima_load_x509(void) int __init ima_init(void) { - u8 pcr_i[TPM_DIGEST_SIZE]; int rc; - ima_used_chip = 0; - rc = tpm_pcr_read(NULL, 0, pcr_i); - if (rc == 0) - ima_used_chip = 1; - - if (!ima_used_chip) - pr_info("No TPM chip found, activating TPM-bypass! (rc=%d)\n", - rc); + ima_tpm_chip = tpm_default_chip(); + if (!ima_tpm_chip) + pr_info("No TPM chip found, activating TPM-bypass!\n"); rc = integrity_init_keyring(INTEGRITY_KEYRING_IMA); if (rc) diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 418f35e38015..b186819bd5aa 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -142,10 +142,10 @@ static int ima_pcr_extend(const u8 *hash, int pcr) { int result = 0; - if (!ima_used_chip) + if (!ima_tpm_chip) return result; - result = tpm_pcr_extend(NULL, pcr, hash); + result = tpm_pcr_extend(ima_tpm_chip, pcr, hash); if (result != 0) pr_err("Error Communicating to TPM chip, result: %d\n", result); return result;