Merge tag 'tpmdd-next-20190625' of git://git.infradead.org/users/jjs/linux-tpmdd
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 9 Jul 2019 01:47:42 +0000 (18:47 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 9 Jul 2019 01:47:42 +0000 (18:47 -0700)
Pull tpm updates from Jarkko Sakkinen:
 "This contains two critical bug fixes and support for obtaining TPM
  events triggered by ExitBootServices().

  For the latter I have to give a quite verbose explanation not least
  because I had to revisit all the details myself to remember what was
  going on in Matthew's patches.

  The preboot software stack maintains an event log that gets entries
  every time something gets hashed to any of the PCR registers. What
  gets hashed could be a component to be run or perhaps log of some
  actions taken just to give couple of coarse examples. In general,
  anything relevant for the boot process that the preboot software does
  gets hashed and a log entry with a specific event type [1].

  The main application for this is remote attestation and the reason why
  it is useful is nicely put in the very first section of [1]:

     "Attestation is used to provide information about the platform’s
      state to a challenger. However, PCR contents are difficult to
      interpret; therefore, attestation is typically more useful when
      the PCR contents are accompanied by a measurement log. While not
      trusted on their own, the measurement log contains a richer set of
      information than do the PCR contents. The PCR contents are used to
      provide the validation of the measurement log."

  Because EFI_TCG2_PROTOCOL.GetEventLog() is not available after calling
  ExitBootServices(), Linux EFI stub copies the event log to a custom
  configuration table. Unfortunately, ExitBootServices() also generates
  events and obviously these events do not get copied to that table.
  Luckily firmware does this for us by providing a configuration table
  identified by EFI_TCG2_FINAL_EVENTS_TABLE_GUID.

  This essentially contains necessary changes to provide the full event
  log for the use the user space that is concatenated from these two
  partial event logs [2]"

[1] https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/
[2] The final concatenation is done in drivers/char/tpm/eventlog/efi.c

* tag 'tpmdd-next-20190625' of git://git.infradead.org/users/jjs/linux-tpmdd:
  tpm: Don't duplicate events from the final event log in the TCG2 log
  Abstract out support for locating an EFI config table
  tpm: Fix TPM 1.2 Shutdown sequence to prevent future TPM operations
  efi: Attempt to get the TCG2 event log in the boot stub
  tpm: Append the final event log to the TPM event log
  tpm: Reserve the TPM final events table
  tpm: Abstract crypto agile event size calculations
  tpm: Actually fail on TPM errors during "get random"

13 files changed:
drivers/char/tpm/eventlog/efi.c
drivers/char/tpm/eventlog/tpm2.c
drivers/char/tpm/tpm-chip.c
drivers/char/tpm/tpm1-cmd.c
drivers/char/tpm/tpm2-cmd.c
drivers/firmware/efi/efi.c
drivers/firmware/efi/libstub/efi-stub-helper.c
drivers/firmware/efi/libstub/efistub.h
drivers/firmware/efi/libstub/fdt.c
drivers/firmware/efi/libstub/tpm.c
drivers/firmware/efi/tpm.c
include/linux/efi.h
include/linux/tpm_eventlog.h

index 3e44362..6bb023d 100644 (file)
 int tpm_read_log_efi(struct tpm_chip *chip)
 {
 
+       struct efi_tcg2_final_events_table *final_tbl = NULL;
        struct linux_efi_tpm_eventlog *log_tbl;
        struct tpm_bios_log *log;
        u32 log_size;
        u8 tpm_log_version;
+       void *tmp;
+       int ret;
 
        if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
                return -ENODEV;
@@ -47,15 +50,57 @@ int tpm_read_log_efi(struct tpm_chip *chip)
 
        /* malloc EventLog space */
        log->bios_event_log = kmemdup(log_tbl->log, log_size, GFP_KERNEL);
-       if (!log->bios_event_log)
-               goto err_memunmap;
-       log->bios_event_log_end = log->bios_event_log + log_size;
+       if (!log->bios_event_log) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
+       log->bios_event_log_end = log->bios_event_log + log_size;
        tpm_log_version = log_tbl->version;
-       memunmap(log_tbl);
-       return tpm_log_version;
 
-err_memunmap:
+       ret = tpm_log_version;
+
+       if (efi.tpm_final_log == EFI_INVALID_TABLE_ADDR ||
+           efi_tpm_final_log_size == 0 ||
+           tpm_log_version != EFI_TCG2_EVENT_LOG_FORMAT_TCG_2)
+               goto out;
+
+       final_tbl = memremap(efi.tpm_final_log,
+                            sizeof(*final_tbl) + efi_tpm_final_log_size,
+                            MEMREMAP_WB);
+       if (!final_tbl) {
+               pr_err("Could not map UEFI TPM final log\n");
+               kfree(log->bios_event_log);
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       efi_tpm_final_log_size -= log_tbl->final_events_preboot_size;
+
+       tmp = krealloc(log->bios_event_log,
+                      log_size + efi_tpm_final_log_size,
+                      GFP_KERNEL);
+       if (!tmp) {
+               kfree(log->bios_event_log);
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       log->bios_event_log = tmp;
+
+       /*
+        * Copy any of the final events log that didn't also end up in the
+        * main log. Events can be logged in both if events are generated
+        * between GetEventLog() and ExitBootServices().
+        */
+       memcpy((void *)log->bios_event_log + log_size,
+              final_tbl->events + log_tbl->final_events_preboot_size,
+              efi_tpm_final_log_size);
+       log->bios_event_log_end = log->bios_event_log +
+               log_size + efi_tpm_final_log_size;
+
+out:
+       memunmap(final_tbl);
        memunmap(log_tbl);
-       return -ENOMEM;
+       return ret;
 }
index d506362..b9aeda1 100644 (file)
 static size_t calc_tpm2_event_size(struct tcg_pcr_event2_head *event,
                                   struct tcg_pcr_event *event_header)
 {
-       struct tcg_efi_specid_event_head *efispecid;
-       struct tcg_event_field *event_field;
-       void *marker;
-       void *marker_start;
-       u32 halg_size;
-       size_t size;
-       u16 halg;
-       int i;
-       int j;
-
-       marker = event;
-       marker_start = marker;
-       marker = marker + sizeof(event->pcr_idx) + sizeof(event->event_type)
-               + sizeof(event->count);
-
-       efispecid = (struct tcg_efi_specid_event_head *)event_header->event;
-
-       /* Check if event is malformed. */
-       if (event->count > efispecid->num_algs)
-               return 0;
-
-       for (i = 0; i < event->count; i++) {
-               halg_size = sizeof(event->digests[i].alg_id);
-               memcpy(&halg, marker, halg_size);
-               marker = marker + halg_size;
-               for (j = 0; j < efispecid->num_algs; j++) {
-                       if (halg == efispecid->digest_sizes[j].alg_id) {
-                               marker +=
-                                       efispecid->digest_sizes[j].digest_size;
-                               break;
-                       }
-               }
-               /* Algorithm without known length. Such event is unparseable. */
-               if (j == efispecid->num_algs)
-                       return 0;
-       }
-
-       event_field = (struct tcg_event_field *)marker;
-       marker = marker + sizeof(event_field->event_size)
-               + event_field->event_size;
-       size = marker - marker_start;
-
-       if ((event->event_type == 0) && (event_field->event_size == 0))
-               return 0;
-
-       return size;
+       return __calc_tpm2_event_size(event, event_header, false);
 }
 
 static void *tpm2_bios_measurements_start(struct seq_file *m, loff_t *pos)
index 90325e1..d47ad10 100644 (file)
@@ -289,15 +289,15 @@ static int tpm_class_shutdown(struct device *dev)
 {
        struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev);
 
+       down_write(&chip->ops_sem);
        if (chip->flags & TPM_CHIP_FLAG_TPM2) {
-               down_write(&chip->ops_sem);
                if (!tpm_chip_start(chip)) {
                        tpm2_shutdown(chip, TPM2_SU_CLEAR);
                        tpm_chip_stop(chip);
                }
-               chip->ops = NULL;
-               up_write(&chip->ops_sem);
        }
+       chip->ops = NULL;
+       up_write(&chip->ops_sem);
 
        return 0;
 }
index 85dcf26..faacbe1 100644 (file)
@@ -510,7 +510,7 @@ struct tpm1_get_random_out {
  *
  * Return:
  * *  number of bytes read
- * * -errno or a TPM return code otherwise
+ * * -errno (positive TPM return codes are masked to -EIO)
  */
 int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {
@@ -531,8 +531,11 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 
                rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
                                      "attempting get random");
-               if (rc)
+               if (rc) {
+                       if (rc > 0)
+                               rc = -EIO;
                        goto out;
+               }
 
                out = (struct tpm1_get_random_out *)&buf.data[TPM_HEADER_SIZE];
 
index 4de4992..d103545 100644 (file)
@@ -297,7 +297,7 @@ struct tpm2_get_random_out {
  *
  * Return:
  *   size of the buffer on success,
- *   -errno otherwise
+ *   -errno otherwise (positive TPM return codes are masked to -EIO)
  */
 int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {
@@ -324,8 +324,11 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
                                       offsetof(struct tpm2_get_random_out,
                                                buffer),
                                       "attempting get random");
-               if (err)
+               if (err) {
+                       if (err > 0)
+                               err = -EIO;
                        goto out;
+               }
 
                out = (struct tpm2_get_random_out *)
                        &buf.data[TPM_HEADER_SIZE];
index 4b7cf7b..ad3b1f4 100644 (file)
@@ -52,6 +52,7 @@ struct efi __read_mostly efi = {
        .mem_attr_table         = EFI_INVALID_TABLE_ADDR,
        .rng_seed               = EFI_INVALID_TABLE_ADDR,
        .tpm_log                = EFI_INVALID_TABLE_ADDR,
+       .tpm_final_log          = EFI_INVALID_TABLE_ADDR,
        .mem_reserve            = EFI_INVALID_TABLE_ADDR,
 };
 EXPORT_SYMBOL(efi);
@@ -484,6 +485,7 @@ static __initdata efi_config_table_type_t common_tables[] = {
        {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table},
        {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi.rng_seed},
        {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log},
+       {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log},
        {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &efi.mem_reserve},
        {NULL_GUID, NULL, NULL},
 };
index e4610e7..1db780c 100644 (file)
@@ -926,3 +926,18 @@ free_map:
 fail:
        return status;
 }
+
+void *get_efi_config_table(efi_system_table_t *sys_table, efi_guid_t guid)
+{
+       efi_config_table_t *tables = (efi_config_table_t *)sys_table->tables;
+       int i;
+
+       for (i = 0; i < sys_table->nr_tables; i++) {
+               if (efi_guidcmp(tables[i].guid, guid) != 0)
+                       continue;
+
+               return (void *)tables[i].table;
+       }
+
+       return NULL;
+}
index 1b1dfca..7f1556f 100644 (file)
@@ -65,6 +65,8 @@ efi_status_t check_platform_features(efi_system_table_t *sys_table_arg);
 
 efi_status_t efi_random_get_seed(efi_system_table_t *sys_table_arg);
 
+void *get_efi_config_table(efi_system_table_t *sys_table, efi_guid_t guid);
+
 /* Helper macros for the usual case of using simple C variables: */
 #ifndef fdt_setprop_inplace_var
 #define fdt_setprop_inplace_var(fdt, node_offset, name, var) \
index 5440ba1..0bf0190 100644 (file)
@@ -363,26 +363,17 @@ fail:
 
 void *get_fdt(efi_system_table_t *sys_table, unsigned long *fdt_size)
 {
-       efi_guid_t fdt_guid = DEVICE_TREE_GUID;
-       efi_config_table_t *tables;
-       int i;
+       void *fdt;
 
-       tables = (efi_config_table_t *)sys_table->tables;
+       fdt = get_efi_config_table(sys_table, DEVICE_TREE_GUID);
 
-       for (i = 0; i < sys_table->nr_tables; i++) {
-               void *fdt;
+       if (!fdt)
+               return NULL;
 
-               if (efi_guidcmp(tables[i].guid, fdt_guid) != 0)
-                       continue;
-
-               fdt = (void *)tables[i].table;
-               if (fdt_check_header(fdt) != 0) {
-                       pr_efi_err(sys_table, "Invalid header detected on UEFI supplied FDT, ignoring ...\n");
-                       return NULL;
-               }
-               *fdt_size = fdt_totalsize(fdt);
-               return fdt;
+       if (fdt_check_header(fdt) != 0) {
+               pr_efi_err(sys_table, "Invalid header detected on UEFI supplied FDT, ignoring ...\n");
+               return NULL;
        }
-
-       return NULL;
+       *fdt_size = fdt_totalsize(fdt);
+       return fdt;
 }
index 5bd04f7..eb9af83 100644 (file)
@@ -57,31 +57,40 @@ void efi_enable_reset_attack_mitigation(efi_system_table_t *sys_table_arg)
 
 #endif
 
-static void efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg)
+void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table_arg)
 {
        efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID;
        efi_guid_t linux_eventlog_guid = LINUX_EFI_TPM_EVENT_LOG_GUID;
        efi_status_t status;
        efi_physical_addr_t log_location = 0, log_last_entry = 0;
        struct linux_efi_tpm_eventlog *log_tbl = NULL;
+       struct efi_tcg2_final_events_table *final_events_table;
        unsigned long first_entry_addr, last_entry_addr;
        size_t log_size, last_entry_size;
        efi_bool_t truncated;
+       int version = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2;
        void *tcg2_protocol = NULL;
+       int final_events_size = 0;
 
        status = efi_call_early(locate_protocol, &tcg2_guid, NULL,
                                &tcg2_protocol);
        if (status != EFI_SUCCESS)
                return;
 
-       status = efi_call_proto(efi_tcg2_protocol, get_event_log, tcg2_protocol,
-                               EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2,
-                               &log_location, &log_last_entry, &truncated);
-       if (status != EFI_SUCCESS)
-               return;
+       status = efi_call_proto(efi_tcg2_protocol, get_event_log,
+                               tcg2_protocol, version, &log_location,
+                               &log_last_entry, &truncated);
+
+       if (status != EFI_SUCCESS || !log_location) {
+               version = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2;
+               status = efi_call_proto(efi_tcg2_protocol, get_event_log,
+                                       tcg2_protocol, version, &log_location,
+                                       &log_last_entry, &truncated);
+               if (status != EFI_SUCCESS || !log_location)
+                       return;
+
+       }
 
-       if (!log_location)
-               return;
        first_entry_addr = (unsigned long) log_location;
 
        /*
@@ -96,8 +105,23 @@ static void efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg)
                 * We need to calculate its size to deduce the full size of
                 * the logs.
                 */
-               last_entry_size = sizeof(struct tcpa_event) +
-                       ((struct tcpa_event *) last_entry_addr)->event_size;
+               if (version == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) {
+                       /*
+                        * The TCG2 log format has variable length entries,
+                        * and the information to decode the hash algorithms
+                        * back into a size is contained in the first entry -
+                        * pass a pointer to the final entry (to calculate its
+                        * size) and the first entry (so we know how long each
+                        * digest is)
+                        */
+                       last_entry_size =
+                               __calc_tpm2_event_size((void *)last_entry_addr,
+                                                   (void *)(long)log_location,
+                                                   false);
+               } else {
+                       last_entry_size = sizeof(struct tcpa_event) +
+                          ((struct tcpa_event *) last_entry_addr)->event_size;
+               }
                log_size = log_last_entry - log_location + last_entry_size;
        }
 
@@ -112,9 +136,37 @@ static void efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg)
                return;
        }
 
+       /*
+        * Figure out whether any events have already been logged to the
+        * final events structure, and if so how much space they take up
+        */
+       final_events_table = get_efi_config_table(sys_table_arg,
+                                               LINUX_EFI_TPM_FINAL_LOG_GUID);
+       if (final_events_table && final_events_table->nr_events) {
+               struct tcg_pcr_event2_head *header;
+               int offset;
+               void *data;
+               int event_size;
+               int i = final_events_table->nr_events;
+
+               data = (void *)final_events_table;
+               offset = sizeof(final_events_table->version) +
+                       sizeof(final_events_table->nr_events);
+
+               while (i > 0) {
+                       header = data + offset + final_events_size;
+                       event_size = __calc_tpm2_event_size(header,
+                                                  (void *)(long)log_location,
+                                                  false);
+                       final_events_size += event_size;
+                       i--;
+               }
+       }
+
        memset(log_tbl, 0, sizeof(*log_tbl) + log_size);
        log_tbl->size = log_size;
-       log_tbl->version = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2;
+       log_tbl->final_events_preboot_size = final_events_size;
+       log_tbl->version = version;
        memcpy(log_tbl->log, (void *) first_entry_addr, log_size);
 
        status = efi_call_early(install_configuration_table,
@@ -126,9 +178,3 @@ static void efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg)
 err_free:
        efi_call_early(free_pool, log_tbl);
 }
-
-void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table_arg)
-{
-       /* Only try to retrieve the logs in 1.2 format. */
-       efi_retrieve_tpm2_eventlog_1_2(sys_table_arg);
-}
index 3a689b4..1d3f5ca 100644 (file)
@@ -4,11 +4,34 @@
  *     Thiebaud Weksteen <tweek@google.com>
  */
 
+#define TPM_MEMREMAP(start, size) early_memremap(start, size)
+#define TPM_MEMUNMAP(start, size) early_memunmap(start, size)
+
+#include <asm/early_ioremap.h>
 #include <linux/efi.h>
 #include <linux/init.h>
 #include <linux/memblock.h>
+#include <linux/tpm_eventlog.h>
 
-#include <asm/early_ioremap.h>
+int efi_tpm_final_log_size;
+EXPORT_SYMBOL(efi_tpm_final_log_size);
+
+static int tpm2_calc_event_log_size(void *data, int count, void *size_info)
+{
+       struct tcg_pcr_event2_head *header;
+       int event_size, size = 0;
+
+       while (count > 0) {
+               header = data + size;
+               event_size = __calc_tpm2_event_size(header, size_info, true);
+               if (event_size == 0)
+                       return -1;
+               size += event_size;
+               count--;
+       }
+
+       return size;
+}
 
 /*
  * Reserve the memory associated with the TPM Event Log configuration table.
 int __init efi_tpm_eventlog_init(void)
 {
        struct linux_efi_tpm_eventlog *log_tbl;
+       struct efi_tcg2_final_events_table *final_tbl;
        unsigned int tbl_size;
+       int ret = 0;
 
-       if (efi.tpm_log == EFI_INVALID_TABLE_ADDR)
+       if (efi.tpm_log == EFI_INVALID_TABLE_ADDR) {
+               /*
+                * We can't calculate the size of the final events without the
+                * first entry in the TPM log, so bail here.
+                */
                return 0;
+       }
 
        log_tbl = early_memremap(efi.tpm_log, sizeof(*log_tbl));
        if (!log_tbl) {
                pr_err("Failed to map TPM Event Log table @ 0x%lx\n",
-                       efi.tpm_log);
+                      efi.tpm_log);
                efi.tpm_log = EFI_INVALID_TABLE_ADDR;
                return -ENOMEM;
        }
 
        tbl_size = sizeof(*log_tbl) + log_tbl->size;
        memblock_reserve(efi.tpm_log, tbl_size);
+
+       if (efi.tpm_final_log == EFI_INVALID_TABLE_ADDR)
+               goto out;
+
+       final_tbl = early_memremap(efi.tpm_final_log, sizeof(*final_tbl));
+
+       if (!final_tbl) {
+               pr_err("Failed to map TPM Final Event Log table @ 0x%lx\n",
+                      efi.tpm_final_log);
+               efi.tpm_final_log = EFI_INVALID_TABLE_ADDR;
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       tbl_size = tpm2_calc_event_log_size((void *)efi.tpm_final_log
+                                           + sizeof(final_tbl->version)
+                                           + sizeof(final_tbl->nr_events),
+                                           final_tbl->nr_events,
+                                           log_tbl->log);
+       memblock_reserve((unsigned long)final_tbl,
+                        tbl_size + sizeof(*final_tbl));
+       early_memunmap(final_tbl, sizeof(*final_tbl));
+       efi_tpm_final_log_size = tbl_size;
+
+out:
        early_memunmap(log_tbl, sizeof(*log_tbl));
-       return 0;
+       return ret;
 }
 
index 6ebc209..f87fabe 100644 (file)
@@ -689,6 +689,7 @@ void efi_native_runtime_setup(void);
 #define LINUX_EFI_LOADER_ENTRY_GUID            EFI_GUID(0x4a67b082, 0x0a4c, 0x41cf,  0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f)
 #define LINUX_EFI_RANDOM_SEED_TABLE_GUID       EFI_GUID(0x1ce1e5bc, 0x7ceb, 0x42f2,  0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b)
 #define LINUX_EFI_TPM_EVENT_LOG_GUID           EFI_GUID(0xb7799cb0, 0xeca2, 0x4943,  0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa)
+#define LINUX_EFI_TPM_FINAL_LOG_GUID           EFI_GUID(0x1e2ed096, 0x30e2, 0x4254,  0xbd, 0x89, 0x86, 0x3b, 0xbe, 0xf8, 0x23, 0x25)
 #define LINUX_EFI_MEMRESERVE_TABLE_GUID                EFI_GUID(0x888eb0c6, 0x8ede, 0x4ff5,  0xa8, 0xf0, 0x9a, 0xee, 0x5c, 0xb9, 0x77, 0xc2)
 
 typedef struct {
@@ -996,6 +997,7 @@ extern struct efi {
        unsigned long mem_attr_table;   /* memory attributes table */
        unsigned long rng_seed;         /* UEFI firmware random seed */
        unsigned long tpm_log;          /* TPM2 Event Log table */
+       unsigned long tpm_final_log;    /* TPM2 Final Events Log table */
        unsigned long mem_reserve;      /* Linux EFI memreserve table */
        efi_get_time_t *get_time;
        efi_set_time_t *set_time;
@@ -1706,12 +1708,20 @@ struct linux_efi_random_seed {
 
 struct linux_efi_tpm_eventlog {
        u32     size;
+       u32     final_events_preboot_size;
        u8      version;
        u8      log[];
 };
 
 extern int efi_tpm_eventlog_init(void);
 
+struct efi_tcg2_final_events_table {
+       u64 version;
+       u64 nr_events;
+       u8 events[];
+};
+extern int efi_tpm_final_log_size;
+
 /*
  * efi_runtime_service() function identifiers.
  * "NONE" is used by efi_recover_from_page_fault() to check if the page
index 81519f1..63238c8 100644 (file)
@@ -112,4 +112,156 @@ struct tcg_pcr_event2_head {
        struct tpm_digest digests[];
 } __packed;
 
+struct tcg_algorithm_size {
+       u16 algorithm_id;
+       u16 algorithm_size;
+};
+
+struct tcg_algorithm_info {
+       u8 signature[16];
+       u32 platform_class;
+       u8 spec_version_minor;
+       u8 spec_version_major;
+       u8 spec_errata;
+       u8 uintn_size;
+       u32 number_of_algorithms;
+       struct tcg_algorithm_size digest_sizes[];
+};
+
+#ifndef TPM_MEMREMAP
+#define TPM_MEMREMAP(start, size) NULL
+#endif
+
+#ifndef TPM_MEMUNMAP
+#define TPM_MEMUNMAP(start, size) do{} while(0)
+#endif
+
+/**
+ * __calc_tpm2_event_size - calculate the size of a TPM2 event log entry
+ * @event:        Pointer to the event whose size should be calculated
+ * @event_header: Pointer to the initial event containing the digest lengths
+ * @do_mapping:   Whether or not the event needs to be mapped
+ *
+ * The TPM2 event log format can contain multiple digests corresponding to
+ * separate PCR banks, and also contains a variable length of the data that
+ * was measured. This requires knowledge of how long each digest type is,
+ * and this information is contained within the first event in the log.
+ *
+ * We calculate the length by examining the number of events, and then looking
+ * at each event in turn to determine how much space is used for events in
+ * total. Once we've done this we know the offset of the data length field,
+ * and can calculate the total size of the event.
+ *
+ * Return: size of the event on success, <0 on failure
+ */
+
+static inline int __calc_tpm2_event_size(struct tcg_pcr_event2_head *event,
+                                        struct tcg_pcr_event *event_header,
+                                        bool do_mapping)
+{
+       struct tcg_efi_specid_event_head *efispecid;
+       struct tcg_event_field *event_field;
+       void *mapping = NULL;
+       int mapping_size;
+       void *marker;
+       void *marker_start;
+       u32 halg_size;
+       size_t size;
+       u16 halg;
+       int i;
+       int j;
+
+       marker = event;
+       marker_start = marker;
+       marker = marker + sizeof(event->pcr_idx) + sizeof(event->event_type)
+               + sizeof(event->count);
+
+       /* Map the event header */
+       if (do_mapping) {
+               mapping_size = marker - marker_start;
+               mapping = TPM_MEMREMAP((unsigned long)marker_start,
+                                      mapping_size);
+               if (!mapping) {
+                       size = 0;
+                       goto out;
+               }
+       } else {
+               mapping = marker_start;
+       }
+
+       event = (struct tcg_pcr_event2_head *)mapping;
+
+       efispecid = (struct tcg_efi_specid_event_head *)event_header->event;
+
+       /* Check if event is malformed. */
+       if (event->count > efispecid->num_algs) {
+               size = 0;
+               goto out;
+       }
+
+       for (i = 0; i < event->count; i++) {
+               halg_size = sizeof(event->digests[i].alg_id);
+
+               /* Map the digest's algorithm identifier */
+               if (do_mapping) {
+                       TPM_MEMUNMAP(mapping, mapping_size);
+                       mapping_size = halg_size;
+                       mapping = TPM_MEMREMAP((unsigned long)marker,
+                                            mapping_size);
+                       if (!mapping) {
+                               size = 0;
+                               goto out;
+                       }
+               } else {
+                       mapping = marker;
+               }
+
+               memcpy(&halg, mapping, halg_size);
+               marker = marker + halg_size;
+
+               for (j = 0; j < efispecid->num_algs; j++) {
+                       if (halg == efispecid->digest_sizes[j].alg_id) {
+                               marker +=
+                                       efispecid->digest_sizes[j].digest_size;
+                               break;
+                       }
+               }
+               /* Algorithm without known length. Such event is unparseable. */
+               if (j == efispecid->num_algs) {
+                       size = 0;
+                       goto out;
+               }
+       }
+
+       /*
+        * Map the event size - we don't read from the event itself, so
+        * we don't need to map it
+        */
+       if (do_mapping) {
+               TPM_MEMUNMAP(mapping, mapping_size);
+               mapping_size += sizeof(event_field->event_size);
+               mapping = TPM_MEMREMAP((unsigned long)marker,
+                                      mapping_size);
+               if (!mapping) {
+                       size = 0;
+                       goto out;
+               }
+       } else {
+               mapping = marker;
+       }
+
+       event_field = (struct tcg_event_field *)mapping;
+
+       marker = marker + sizeof(event_field->event_size)
+               + event_field->event_size;
+       size = marker - marker_start;
+
+       if ((event->event_type == 0) && (event_field->event_size == 0))
+               size = 0;
+out:
+       if (do_mapping)
+               TPM_MEMUNMAP(mapping, mapping_size);
+       return size;
+}
+
 #endif