* tpm_pcr_read - read a PCR value from SHA1 bank
  * @chip:      a &struct tpm_chip instance, %NULL for the default chip
  * @pcr_idx:   the PCR to be retrieved
- * @res_buf:   the value of the PCR
+ * @digest:    the PCR bank and buffer current PCR value is written to
  *
  * Return: same as with tpm_transmit_cmd()
  */
-int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
+int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
+                struct tpm_digest *digest)
 {
        int rc;
 
                return -ENODEV;
 
        if (chip->flags & TPM_CHIP_FLAG_TPM2)
-               rc = tpm2_pcr_read(chip, pcr_idx, res_buf);
+               rc = tpm2_pcr_read(chip, pcr_idx, digest, NULL);
        else
-               rc = tpm1_pcr_read(chip, pcr_idx, res_buf);
+               rc = tpm1_pcr_read(chip, pcr_idx, digest->digest);
 
        tpm_put_ops(chip);
        return rc;
  * @pcr_idx:   the PCR to be retrieved
  * @hash:      the hash value used to extend the PCR value
  *
- * Note: with TPM 2.0 extends also those banks with a known digest size to the
- * cryto subsystem in order to prevent malicious use of those PCR banks. In the
- * future we should dynamically determine digest sizes.
+ * Note: with TPM 2.0 extends also those banks for which no digest was
+ * specified in order to prevent malicious use of those PCR banks.
  *
  * Return: same as with tpm_transmit_cmd()
  */
                        return -ENOMEM;
 
                for (i = 0; i < chip->nr_allocated_banks; i++) {
-                       digest_list[i].alg_id = chip->allocated_banks[i];
+                       digest_list[i].alg_id = chip->allocated_banks[i].alg_id;
                        memcpy(digest_list[i].digest, hash, TPM_DIGEST_SIZE);
                }
 
 
        unsigned int groups_cnt;
 
        u32 nr_allocated_banks;
-       u16 *allocated_banks;
+       struct tpm_bank_info *allocated_banks;
 #ifdef CONFIG_ACPI
        acpi_handle acpi_dev_handle;
        char ppi_version[TPM_PPI_VERSION_LEN + 1];
 }
 
 int tpm2_get_timeouts(struct tpm_chip *chip);
-int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf);
+int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
+                 struct tpm_digest *digest, u16 *digest_size_ptr);
 int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, u32 count,
                    struct tpm_digest *digests);
 int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max);
 
                goto out;
        }
 
-       chip->allocated_banks[0] = TPM_ALG_SHA1;
+       chip->allocated_banks[0].alg_id = TPM_ALG_SHA1;
+       chip->allocated_banks[0].digest_size = hash_digest_size[HASH_ALGO_SHA1];
+       chip->allocated_banks[0].crypto_id = HASH_ALGO_SHA1;
        chip->nr_allocated_banks = 1;
 
        return rc;
 
  * tpm2_pcr_read() - read a PCR value
  * @chip:      TPM chip to use.
  * @pcr_idx:   index of the PCR to read.
- * @res_buf:   buffer to store the resulting hash.
+ * @digest:    PCR bank and buffer current PCR value is written to.
+ * @digest_size_ptr:   pointer to variable that stores the digest size.
  *
  * Return: Same as with tpm_transmit_cmd.
  */
-int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
+int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
+                 struct tpm_digest *digest, u16 *digest_size_ptr)
 {
+       int i;
        int rc;
        struct tpm_buf buf;
        struct tpm2_pcr_read_out *out;
        u8 pcr_select[TPM2_PCR_SELECT_MIN] = {0};
+       u16 digest_size;
+       u16 expected_digest_size = 0;
 
        if (pcr_idx >= TPM2_PLATFORM_PCR)
                return -EINVAL;
 
+       if (!digest_size_ptr) {
+               for (i = 0; i < chip->nr_allocated_banks &&
+                    chip->allocated_banks[i].alg_id != digest->alg_id; i++)
+                       ;
+
+               if (i == chip->nr_allocated_banks)
+                       return -EINVAL;
+
+               expected_digest_size = chip->allocated_banks[i].digest_size;
+       }
+
        rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
        if (rc)
                return rc;
        pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
 
        tpm_buf_append_u32(&buf, 1);
-       tpm_buf_append_u16(&buf, TPM_ALG_SHA1);
+       tpm_buf_append_u16(&buf, digest->alg_id);
        tpm_buf_append_u8(&buf, TPM2_PCR_SELECT_MIN);
        tpm_buf_append(&buf, (const unsigned char *)pcr_select,
                       sizeof(pcr_select));
 
-       rc = tpm_transmit_cmd(chip, &buf, 0, res_buf ?
-                             "attempting to read a pcr value" : NULL);
-       if (rc == 0 && res_buf) {
-               out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE];
-               memcpy(res_buf, out->digest, SHA1_DIGEST_SIZE);
+       rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to read a pcr value");
+       if (rc)
+               goto out;
+
+       out = (struct tpm2_pcr_read_out *)&buf.data[TPM_HEADER_SIZE];
+       digest_size = be16_to_cpu(out->digest_size);
+       if (digest_size > sizeof(digest->digest) ||
+           (!digest_size_ptr && digest_size != expected_digest_size)) {
+               rc = -EINVAL;
+               goto out;
        }
 
+       if (digest_size_ptr)
+               *digest_size_ptr = digest_size;
+
+       memcpy(digest->digest, out->digest, digest_size);
+out:
        tpm_buf_destroy(&buf);
        return rc;
 }
        struct tpm2_null_auth_area auth_area;
        int rc;
        int i;
-       int j;
 
        if (count > chip->nr_allocated_banks)
                return -EINVAL;
        tpm_buf_append_u32(&buf, count);
 
        for (i = 0; i < count; i++) {
-               for (j = 0; j < ARRAY_SIZE(tpm2_hash_map); j++) {
-                       if (digests[i].alg_id != tpm2_hash_map[j].tpm_id)
-                               continue;
-                       tpm_buf_append_u16(&buf, digests[i].alg_id);
-                       tpm_buf_append(&buf, (const unsigned char
-                                             *)&digests[i].digest,
-                              hash_digest_size[tpm2_hash_map[j].crypto_id]);
-               }
+               tpm_buf_append_u16(&buf, digests[i].alg_id);
+               tpm_buf_append(&buf, (const unsigned char *)&digests[i].digest,
+                              chip->allocated_banks[i].digest_size);
        }
 
        rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
 }
 EXPORT_SYMBOL_GPL(tpm2_probe);
 
+static int tpm2_init_bank_info(struct tpm_chip *chip, u32 bank_index)
+{
+       struct tpm_bank_info *bank = chip->allocated_banks + bank_index;
+       struct tpm_digest digest = { .alg_id = bank->alg_id };
+       int i;
+
+       /*
+        * Avoid unnecessary PCR read operations to reduce overhead
+        * and obtain identifiers of the crypto subsystem.
+        */
+       for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) {
+               enum hash_algo crypto_algo = tpm2_hash_map[i].crypto_id;
+
+               if (bank->alg_id != tpm2_hash_map[i].tpm_id)
+                       continue;
+
+               bank->digest_size = hash_digest_size[crypto_algo];
+               bank->crypto_id = crypto_algo;
+               return 0;
+       }
+
+       return tpm2_pcr_read(chip, 0, &digest, &bank->digest_size);
+}
+
 struct tpm2_pcr_selection {
        __be16  hash_alg;
        u8  size_of_select;
                pcr_select_offset = memchr_inv(pcr_selection.pcr_select, 0,
                                               pcr_selection.size_of_select);
                if (pcr_select_offset) {
-                       chip->allocated_banks[nr_alloc_banks] = hash_alg;
+                       chip->allocated_banks[nr_alloc_banks].alg_id = hash_alg;
+
+                       rc = tpm2_init_bank_info(chip, nr_alloc_banks);
+                       if (rc < 0)
+                               break;
+
                        nr_alloc_banks++;
                }
 
 
        u8 digest[TPM_MAX_DIGEST_SIZE];
 } __packed;
 
+struct tpm_bank_info {
+       u16 alg_id;
+       u16 digest_size;
+       u16 crypto_id;
+};
+
 enum TPM_OPS_FLAGS {
        TPM_OPS_AUTO_STARTUP = BIT(0),
 };
 #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE)
 
 extern int tpm_is_tpm2(struct tpm_chip *chip);
-extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf);
+extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
+                       struct tpm_digest *digest);
 extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash);
 extern int tpm_send(struct tpm_chip *chip, void *cmd, size_t buflen);
 extern int tpm_get_random(struct tpm_chip *chip, u8 *data, size_t max);
        return -ENODEV;
 }
 
-static inline int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
+static inline int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx,
+                              struct tpm_digest *digest)
 {
        return -ENODEV;
 }
 
        return calc_buffer_shash(buf, len, hash);
 }
 
-static void __init ima_pcrread(u32 idx, u8 *pcr)
+static void __init ima_pcrread(u32 idx, struct tpm_digest *d)
 {
        if (!ima_tpm_chip)
                return;
 
-       if (tpm_pcr_read(ima_tpm_chip, idx, pcr) != 0)
+       if (tpm_pcr_read(ima_tpm_chip, idx, d) != 0)
                pr_err("Error Communicating to TPM chip\n");
 }
 
 static int __init ima_calc_boot_aggregate_tfm(char *digest,
                                              struct crypto_shash *tfm)
 {
-       u8 pcr_i[TPM_DIGEST_SIZE];
+       struct tpm_digest d = { .alg_id = TPM_ALG_SHA1, .digest = {0} };
        int rc;
        u32 i;
        SHASH_DESC_ON_STACK(shash, tfm);
 
        /* cumulative sha1 over tpm registers 0-7 */
        for (i = TPM_PCR0; i < TPM_PCR8; i++) {
-               ima_pcrread(i, pcr_i);
+               ima_pcrread(i, &d);
                /* now accumulate with current aggregate */
-               rc = crypto_shash_update(shash, pcr_i, TPM_DIGEST_SIZE);
+               rc = crypto_shash_update(shash, d.digest, TPM_DIGEST_SIZE);
        }
        if (!rc)
                crypto_shash_final(shash, digest);