firmware: tpm2_lite: Implement TlclGetRandom()

Implement support for getting random bytes from the TPM in the tpm2
library. The intent is to use this to seed the kaslr-seed DT property on
ARM devices.

BRANCH=None
BUG=None
TEST=Generate some random bytes in depthcharge using this API,
and 'stop trunksd; tpmc rand <size>' with sizes (0, 1, 0xf0, and
0xf1) on the device and see the last one fail

Change-Id: Ied0dc1ead70ac4daa2cee315516160ec100039be
Signed-off-by: Stephen Boyd <swboyd@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1327187
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Reviewed-by: Andrey Pronin <apronin@chromium.org>
diff --git a/firmware/include/tpm2_tss_constants.h b/firmware/include/tpm2_tss_constants.h
index cb920c0..162993e 100644
--- a/firmware/include/tpm2_tss_constants.h
+++ b/firmware/include/tpm2_tss_constants.h
@@ -34,6 +34,7 @@
 #define TPM2_NV_ReadLock       ((TPM_CC)0x0000014F)
 #define TPM2_NV_ReadPublic     ((TPM_CC)0x00000169)
 #define TPM2_GetCapability     ((TPM_CC)0x0000017A)
+#define TPM2_GetRandom         ((TPM_CC)0x0000017B)
 
 #define HR_SHIFT               24
 #define TPM_HT_NV_INDEX        0x01
@@ -207,6 +208,10 @@
 	uint32_t property_count;
 };
 
+struct tpm2_get_random_cmd {
+	uint16_t bytes_requested;
+};
+
 struct tpm2_self_test_cmd {
 	TPMI_YES_NO full_test;
 };
@@ -258,6 +263,10 @@
 	TPMS_CAPABILITY_DATA capability_data;
 } __attribute__((packed));
 
+struct get_random_response {
+	TPM2B_DIGEST random_bytes;
+} __attribute__((packed));
+
 struct nv_read_public_response {
 	TPMS_NV_PUBLIC nvPublic;
 	TPM2B_NAME nvName;
@@ -269,6 +278,7 @@
 		struct nv_read_response nvr;
 		struct tpm2_session_header def_space;
 		struct get_capability_response cap;
+		struct get_random_response random;
 		struct nv_read_public_response nv_read_public;
 	};
 };
diff --git a/firmware/lib/tpm2_lite/marshaling.c b/firmware/lib/tpm2_lite/marshaling.c
index df93866..e20bcda 100644
--- a/firmware/lib/tpm2_lite/marshaling.c
+++ b/firmware/lib/tpm2_lite/marshaling.c
@@ -261,6 +261,11 @@
 	unmarshal_TPMS_CAPABILITY_DATA(buffer, size, &cap->capability_data);
 }
 
+static void unmarshal_get_random(void **buffer, int *size,
+				 struct get_random_response *random)
+{
+	unmarshal_TPM2B(buffer, size, &random->random_bytes);
+}
 
 /*
  * Each marshaling function receives a pointer to the buffer to marshal into,
@@ -620,6 +625,15 @@
 	marshal_u32(buffer, command_body->property_count, buffer_space);
 }
 
+static void marshal_get_random(void **buffer, struct tpm2_get_random_cmd
+				       *command_body,
+				   int *buffer_space)
+{
+	tpm_tag = TPM_ST_NO_SESSIONS;
+
+	marshal_u16(buffer, command_body->bytes_requested, buffer_space);
+}
+
 static void marshal_clear(void **buffer,
 			  void *command_body,
 			  int *buffer_space)
@@ -709,6 +723,10 @@
 		marshal_get_capability(&cmd_body, tpm_command_body, &body_size);
 		break;
 
+	case TPM2_GetRandom:
+		marshal_get_random(&cmd_body, tpm_command_body, &body_size);
+		break;
+
 	case TPM2_Clear:
 		marshal_clear(&cmd_body, tpm_command_body, &body_size);
 		break;
@@ -781,6 +799,11 @@
 					 &response->cap);
 		break;
 
+	case TPM2_GetRandom:
+		unmarshal_get_random(&response_body, &cr_size,
+				     &response->random);
+		break;
+
 	case TPM2_Hierarchy_Control:
 	case TPM2_NV_Write:
 	case TPM2_NV_WriteLock:
diff --git a/firmware/lib/tpm2_lite/tlcl.c b/firmware/lib/tpm2_lite/tlcl.c
index 8e5fbea..61c4c41 100644
--- a/firmware/lib/tpm2_lite/tlcl.c
+++ b/firmware/lib/tpm2_lite/tlcl.c
@@ -572,9 +572,30 @@
 
 uint32_t TlclGetRandom(uint8_t *data, uint32_t length, uint32_t *size)
 {
-	*size = 0;
-	VB2_DEBUG("NOT YET IMPLEMENTED\n");
-	return TPM_E_IOERROR;
+	uint32_t rv;
+	struct tpm2_get_random_cmd random;
+	struct get_random_response *response = &tpm2_resp.random;
+	size_t max_len, offset;
+
+	offset = offsetof(struct tpm2_response, random.random_bytes.buffer);
+	max_len = TPM_BUFFER_SIZE - offset;
+
+	if (length > max_len)
+		return TPM_E_BUFFER_SIZE;
+
+	random.bytes_requested = length;
+
+	rv = tpm_send_receive(TPM2_GetRandom, &random, &tpm2_resp);
+	if (rv != TPM_SUCCESS)
+		return rv;
+
+	*size = response->random_bytes.size;
+	if (*size > length)
+		return TPM_E_RESPONSE_TOO_LARGE;
+
+	memcpy(data, response->random_bytes.buffer, *size);
+
+	return rv;
 }
 
 // Converts TPM_PT_VENDOR_STRING_x |value| to an array of bytes in |buf|.