tpm: Append to TPM2 log the hashes used for PCR extension

Modify the function that writes the TPM logs to take the same digest
passed to tpm_extend.  Update the tpm2 acpi log header to describe the
digest format.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
diff --git a/src/std/tcg.h b/src/std/tcg.h
index 401b20b..3234524 100644
--- a/src/std/tcg.h
+++ b/src/std/tcg.h
@@ -518,22 +518,20 @@
     struct tpm2_digest_value digest[0];
 } PACKED;
 
+// Each entry in the TPM log contains: a tpm_log_header, a variable
+// length digest, a tpm_log_trailer, and a variable length event.  The
+// 'digest' matches what is sent to the TPM hardware via the Extend
+// command.  On TPM1.2 the digest is a SHA1 hash; on TPM2.0 the digest
+// contains a tpm2_digest_values struct followed by a variable number
+// of tpm2_digest_value structs (as specified by the hardware via the
+// TPM2_CAP_PCRS request).
 struct tpm_log_header {
     u32 pcrindex;
     u32 eventtype;
     u8 digest[0];
 } PACKED;
 
-struct tpml_digest_values_sha1 {
-    u32 count; /* number of digests */
-    u16 hashtype;
-    u8 sha1[SHA1_BUFSIZE];
-} PACKED;
-
-struct tcg_pcr_event2_sha1 {
-    u32 pcrindex;
-    u32 eventtype;
-    struct tpml_digest_values_sha1 digest;
+struct tpm_log_trailer {
     u32 eventdatasize;
     u8 event[0];
 } PACKED;
@@ -549,10 +547,12 @@
     struct TCG_EfiSpecIdEventAlgorithmSize {
         u16 algorithmId;
         u16 digestSize;
-    } digestSizes[1];
+    } digestSizes[0];
+    /*
     u8 vendorInfoSize;
     u8 vendorInfo[0];
-};
+    */
+} PACKED;
 
 #define TPM_TCPA_ACPI_CLASS_CLIENT 0
 
diff --git a/src/tcgbios.c b/src/tcgbios.c
index 57131d7..4cff4ce 100644
--- a/src/tcgbios.c
+++ b/src/tcgbios.c
@@ -137,15 +137,17 @@
  * Extend the ACPI log with the given entry by copying the
  * entry data into the log.
  * Input
- *  pcpes : Pointer to the event 'header' to be copied into the log
- *  event : Pointer to the event 'body' to be copied into the log
+ *  entry : The header data to use (including the variable length digest)
+ *  digest_len : Length of the digest in 'entry'
+ *  event : Pointer to the event body to be copied into the log
+ *  event_len : Length of 'event'
  *
  * Output:
  *  Returns an error code in case of faiure, 0 in case of success
  */
 static int
-tpm_log_event(struct tcg_pcr_event2_sha1 *entry, const void *event
-              , TPMVersion tpm_version)
+tpm_log_event(struct tpm_log_header *entry, int digest_len
+              , const void *event, int event_len)
 {
     dprintf(DEBUG_tcg, "TCGBIOS: LASA = %p, next entry = %p\n",
             tpm_state.log_area_start_address, tpm_state.log_area_next_entry);
@@ -153,7 +155,8 @@
     if (tpm_state.log_area_next_entry == NULL)
         return -1;
 
-    u32 size = sizeof(*entry) + entry->eventdatasize;
+    u32 size = (sizeof(*entry) + digest_len
+                + sizeof(struct tpm_log_trailer) + event_len);
     u32 logsize = (tpm_state.log_area_next_entry + size
                    - tpm_state.log_area_start_address);
     if (logsize > tpm_state.log_area_minimum_length) {
@@ -161,22 +164,11 @@
         return -1;
     }
 
-    switch (tpm_version) {
-    case TPM_VERSION_1_2: ;
-        struct pcpes *pcpes = (void*)tpm_state.log_area_next_entry;
-        pcpes->pcrindex = entry->pcrindex;
-        pcpes->eventtype = entry->eventtype;
-        memcpy(pcpes->digest, entry->digest.sha1, sizeof(pcpes->digest));
-        pcpes->eventdatasize = entry->eventdatasize;
-        memcpy(pcpes->event, event, entry->eventdatasize);
-        size = sizeof(*pcpes) + entry->eventdatasize;
-        break;
-    case TPM_VERSION_2: ;
-        struct tcg_pcr_event2_sha1 *e = (void*)tpm_state.log_area_next_entry;
-        memcpy(e, entry, sizeof(*e));
-        memcpy(e->event, event, entry->eventdatasize);
-        break;
-    }
+    void *dest = tpm_state.log_area_next_entry;
+    memcpy(dest, entry, sizeof(*entry) + digest_len);
+    struct tpm_log_trailer *t = dest + sizeof(*entry) + digest_len;
+    t->eventdatasize = event_len;
+    memcpy(t->event, event, event_len);
 
     tpm_state.log_area_last_entry = tpm_state.log_area_next_entry;
     tpm_state.log_area_next_entry += size;
@@ -185,41 +177,6 @@
     return 0;
 }
 
-/*
- * Initialize the log; a TPM2 log needs a special TPM 1.2 log entry
- * as the first entry serving identification purposes
- */
-static void
-tpm_log_init(void)
-{
-    struct TCG_EfiSpecIdEventStruct event = {
-        .signature = "Spec ID Event03",
-        .platformClass = TPM_TCPA_ACPI_CLASS_CLIENT,
-        .specVersionMinor = 0,
-        .specVersionMajor = 2,
-        .specErrata = 0,
-        .uintnSize = 2,
-        .numberOfAlgorithms = 1,
-        .digestSizes[0] = {
-             .algorithmId = TPM2_ALG_SHA1,
-             .digestSize = SHA1_BUFSIZE,
-        },
-        .vendorInfoSize = 0,
-    };
-    struct tcg_pcr_event2_sha1 entry = {
-        .eventtype = EV_NO_ACTION,
-        .eventdatasize = sizeof(event),
-    };
-
-    switch (TPM_version) {
-    case TPM_VERSION_1_2:
-        break;
-    case TPM_VERSION_2:
-        /* write a 1.2 type of entry */
-        tpm_log_event(&entry, &event, TPM_VERSION_1_2);
-    }
-}
-
 
 /****************************************************************
  * Helper functions
@@ -276,6 +233,74 @@
     }
 }
 
+// Add an entry at the start of the log describing digest formats
+static int
+tpm20_write_EfiSpecIdEventStruct(void)
+{
+    if (!tpm20_pcr_selection)
+        return -1;
+
+    struct {
+        struct TCG_EfiSpecIdEventStruct hdr;
+        u8 pad[256];
+    } event = {
+        .hdr.signature = "Spec ID Event03",
+        .hdr.platformClass = TPM_TCPA_ACPI_CLASS_CLIENT,
+        .hdr.specVersionMinor = 0,
+        .hdr.specVersionMajor = 2,
+        .hdr.specErrata = 0,
+        .hdr.uintnSize = 2,
+    };
+
+    struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections;
+    void *nsel, *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size;
+
+    u32 count;
+    for (count = 0; count < be32_to_cpu(tpm20_pcr_selection->count); count++) {
+        u8 sizeOfSelect = sel->sizeOfSelect;
+
+        nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
+        if (nsel > end)
+            break;
+
+        int hsize = tpm20_get_hash_buffersize(be16_to_cpu(sel->hashAlg));
+        if (hsize < 0) {
+            dprintf(DEBUG_tcg, "TPM is using an unsupported hash: %d\n",
+                    be16_to_cpu(sel->hashAlg));
+            return -1;
+        }
+
+        int event_size = offsetof(struct TCG_EfiSpecIdEventStruct
+                                  , digestSizes[count+1]);
+        if (event_size > sizeof(event) - sizeof(u32)) {
+            dprintf(DEBUG_tcg, "EfiSpecIdEventStruct pad too small\n");
+            return -1;
+        }
+
+        event.hdr.digestSizes[count].algorithmId = be16_to_cpu(sel->hashAlg);
+        event.hdr.digestSizes[count].digestSize = hsize;
+
+        sel = nsel;
+    }
+
+    if (sel != end) {
+        dprintf(DEBUG_tcg, "Malformed pcr selection structure fron TPM\n");
+        return -1;
+    }
+
+    event.hdr.numberOfAlgorithms = count;
+    int event_size = offsetof(struct TCG_EfiSpecIdEventStruct
+                              , digestSizes[count]);
+    u32 *vendorInfoSize = (void*)&event + event_size;
+    *vendorInfoSize = 0;
+    event_size += sizeof(*vendorInfoSize);
+
+    struct tpm_log_entry le = {
+        .hdr.eventtype = EV_NO_ACTION,
+    };
+    return tpm_log_event(&le.hdr, SHA1_BUFSIZE, &event, event_size);
+}
+
 /*
  * Build the TPM2 tpm2_digest_values data structure from the given hash.
  * Follow the PCR bank configuration of the TPM and write the same hash
@@ -677,22 +702,14 @@
     if (!tpm_is_working())
         return;
 
-    struct tcg_pcr_event2_sha1 entry = {
-        .pcrindex = pcrindex,
-        .eventtype = event_type,
-        .eventdatasize = event_length,
-        .digest = {
-            .count = 1,
-            .hashtype  = TPM2_ALG_SHA1,
-        }
-    };
-    sha1(hashdata, hashdata_length, entry.digest.sha1);
+    u8 hash[SHA1_BUFSIZE];
+    sha1(hashdata, hashdata_length, hash);
 
     struct tpm_log_entry le = {
         .hdr.pcrindex = pcrindex,
         .hdr.eventtype = event_type,
     };
-    int digest_len = tpm_build_digest(&le, entry.digest.sha1);
+    int digest_len = tpm_build_digest(&le, hash);
     if (digest_len < 0)
         return;
     int ret = tpm_extend(&le, digest_len);
@@ -700,7 +717,7 @@
         tpm_set_failure();
         return;
     }
-    tpm_log_event(&entry, event, TPM_version);
+    tpm_log_event(&le.hdr, digest_len, event, event_length);
 }
 
 
@@ -887,6 +904,10 @@
     if (ret)
         goto err_exit;
 
+    ret = tpm20_write_EfiSpecIdEventStruct();
+    if (ret)
+        goto err_exit;
+
     return 0;
 
 err_exit:
@@ -926,8 +947,6 @@
     if (ret)
         return;
 
-    tpm_log_init();
-
     TPM_working = 1;
 
     if (runningOnXen())
@@ -1238,17 +1257,8 @@
         if (ret)
             return TCG_TCG_COMMAND_ERROR;
     }
-    struct tcg_pcr_event2_sha1 entry = {
-        .pcrindex = pcpes->pcrindex,
-        .eventtype = pcpes->eventtype,
-        .eventdatasize = pcpes->eventdatasize,
-        .digest = {
-            .count = 1,
-            .hashtype = TPM2_ALG_SHA1,
-        }
-    };
-    memcpy(entry.digest.sha1, pcpes->digest, sizeof(entry.digest.sha1));
-    int ret = tpm_log_event(&entry, pcpes->event, TPM_version);
+    int ret = tpm_log_event(&le.hdr, digest_len
+                            , pcpes->event, pcpes->eventdatasize);
     if (ret)
         return TCG_PC_LOGOVERFLOW;
     return 0;