feat: add single PCR read functionality

The `tpm_pcr_read_single` function required the following params:
- PCR index value
- The hash algorithm value (TPM_ALG_<TYPE> as seen in TCG TPM2 structure
  spec)
- `pcr_digest_read` buffer that returns the digest read back from the
  PCR.
- `pcr_digest_read_len` length of the byte buffer passed in to the read
  the digest

Change-Id: I7f37ea58a7578e1d715f16fd8d4977f34a48507d
Signed-off-by: Mudit Sharma <mudit.sharma@arm.com>
diff --git a/include/tpm2.h b/include/tpm2.h
index 57af444..1dad4f2 100644
--- a/include/tpm2.h
+++ b/include/tpm2.h
@@ -15,6 +15,8 @@
 #define TPM_SU_CLEAR 0x0000U
 #define TPM_SU_STATE 0x0001U
 
+#define TPM_ALG_SHA256	((uint16_t)0x000BU)
+
 /* Return values */
 enum tpm_ret_value {
 	TPM_SUCCESS = 0,
@@ -38,4 +40,9 @@
 		   uint16_t algorithm, const uint8_t *digest,
 		   uint32_t digest_len);
 
+int tpm_pcr_read_single(struct tpm_chip_data *chip_data, uint32_t index,
+			uint16_t algorithm,
+			uint8_t *pcr_digest_read,
+			size_t pcr_digest_read_len);
+
 #endif /* TPM2_H */
diff --git a/include/tpm2_private.h b/include/tpm2_private.h
index 37f22af..88cb17b 100644
--- a/include/tpm2_private.h
+++ b/include/tpm2_private.h
@@ -37,6 +37,8 @@
 #define TPM_ATTRIBUTES_DISABLE	((uint32_t)0U)
 #define TPM_ZERO_HMAC_SIZE	((uint32_t)0U)
 #define TPM_SINGLE_HASH_COUNT	((uint32_t)1U)
+/* 24 PCRs bit-mask with 3 bytes */
+#define TPM_PCR_SELECT_SIZE	((uint8_t)0x3U)
 
 /* Commands (16-bit) */
 #define TPM_CMD_STARTUP	((uint16_t)0x0144U)
@@ -65,6 +67,7 @@
 #define TPM_HEADER_SIZE	((uint32_t)10U)
 #define MAX_SIZE_CMDBUF	((uint32_t)256U)
 #define MAX_CMD_DATA	((uint32_t)(MAX_SIZE_CMDBUF - TPM_HEADER_SIZE))
+#define MAX_DIGEST_SIZE	((uint32_t)32U)
 
 /*
  * Provide a backward-compatible implementation of static_assert.
@@ -91,6 +94,16 @@
 } __attribute__((__packed__));
 typedef struct tpm_cmd tpm_cmd;
 
+struct tpm_pcr_single_read_res {
+	uint32_t pcr_update_ctr;
+	uint32_t tpml_pcr_selection_count;
+	uint16_t tpms_pcr_selection_hash;
+	uint32_t tpms_pcr_selection_num_octate_bitmap;
+	uint32_t tpml_digest_count;
+	uint16_t tpml_digest_size;
+	uint8_t digest[MAX_DIGEST_SIZE];
+} __attribute__((packed));
+
 TPM_STATIC_ASSERT(sizeof(tpm_cmd_hdr) == TPM_HEADER_SIZE,
 		  tpm_cmd_hdr__incorrect_size);
 
diff --git a/src/tpm2_cmds.c b/src/tpm2_cmds.c
index f77ae9c..8cd3324 100644
--- a/src/tpm2_cmds.c
+++ b/src/tpm2_cmds.c
@@ -252,3 +252,95 @@
 
 	return TPM_SUCCESS;
 }
+
+int tpm_pcr_read_single(struct tpm_chip_data *chip_data, uint32_t index,
+			uint16_t algorithm,
+			uint8_t *pcr_digest_read,
+			size_t pcr_digest_read_len)
+{
+	tpm_cmd pcr_read_cmd, pcr_read_res;
+	uint32_t tpm_rc;
+	int ret;
+	size_t data_size;
+	uint16_t digest_len;
+	struct tpm_pcr_single_read_res tpm_read_response;
+
+	if (index >= TPM_PCR_SELECT_SIZE * 8) {
+		ERROR("%s: PCR index out of range\n", __func__);
+		return TPM_INVALID_PARAM;
+	}
+	uint32_t pcr_bitmask = 1U << index;
+
+	if (pcr_digest_read == NULL) {
+		return TPM_INVALID_PARAM;
+	}
+
+	memset(&pcr_read_cmd, 0, sizeof(pcr_read_cmd));
+	memset(pcr_digest_read, 0, pcr_digest_read_len);
+
+	pcr_read_cmd.header.tag = htobe16(TPM_ST_NO_SESSIONS);
+	pcr_read_cmd.header.cmd_size = htobe32(sizeof(tpm_cmd_hdr));
+	pcr_read_cmd.header.cmd_code = htobe32(TPM_CMD_PCR_READ);
+
+	/* TPML_PCR_SELECTION (count) */
+	ret = tpm_update_buffer(&pcr_read_cmd, 1,
+				sizeof(uint32_t)); /* Read 1 PCR only */
+	if (ret < 0) {
+		return ret;
+	}
+
+	/* TPMS_PCR_SELECTION (Hash algorithm) */
+	ret = tpm_update_buffer(&pcr_read_cmd, algorithm, sizeof(algorithm));
+	if (ret < 0) {
+		return ret;
+	}
+
+	/* TPMS_PCR_SELECTION (PCR_SELECT_MIN) */
+	ret = tpm_update_buffer(&pcr_read_cmd, TPM_PCR_SELECT_SIZE, sizeof(uint8_t));
+	if (ret < 0) {
+		return ret;
+	}
+
+	/* TPMS_PCR_SELECTION (pcrSelect) */
+	/* NOT stored in big endian */
+	tpm_update_buffer(&pcr_read_cmd, pcr_bitmask & 0xFF, sizeof(uint8_t));
+	tpm_update_buffer(&pcr_read_cmd, (pcr_bitmask >> 8) & 0xFF, sizeof(uint8_t));
+	tpm_update_buffer(&pcr_read_cmd, (pcr_bitmask >> 16) & 0xFF, sizeof(uint8_t));
+
+	ret = tpm_xfer(chip_data, &pcr_read_cmd, &pcr_read_res);
+	if (ret < 0) {
+		return ret;
+	}
+
+	tpm_rc = be32toh(pcr_read_res.header.cmd_code);
+	if (tpm_rc != TPM_RESPONSE_SUCCESS) {
+		ERROR("%s: response code contains error = %x\n", __func__,
+		      tpm_rc);
+		return TPM_ERR_RESPONSE;
+	}
+
+	data_size = be32toh(pcr_read_res.header.cmd_size) - TPM_HEADER_SIZE;
+	if (data_size > sizeof(struct tpm_pcr_single_read_res) ||
+		data_size < offsetof(struct tpm_pcr_single_read_res, digest)) {
+		ERROR("%s: Abnormal command size reported\n", __func__);
+		return TPM_ERR_RESPONSE;
+	}
+
+	memcpy(&tpm_read_response, pcr_read_res.data, sizeof(struct tpm_pcr_single_read_res));
+
+	digest_len = be16toh(tpm_read_response.tpml_digest_size);
+	if (digest_len > MAX_DIGEST_SIZE || digest_len > pcr_digest_read_len) {
+		ERROR("%s: Buffer passed for returning digest has insufficient space\n", __func__);
+		return TPM_INVALID_PARAM;
+	}
+
+	if (offsetof(struct tpm_pcr_single_read_res, digest) + digest_len > data_size) {
+		ERROR("%s: Insufficient data read\n", __func__);
+		return TPM_ERR_RESPONSE;
+	}
+
+	/* Copy digest returned from PCR read  */
+	memcpy(pcr_digest_read, tpm_read_response.digest, digest_len);
+
+	return TPM_SUCCESS;
+}