|  | // SPDX-License-Identifier: GPL-2.0-or-later | 
|  | /* | 
|  | * Copyright (C) 2017 Google | 
|  | * | 
|  | * Authors: | 
|  | *      Thiebaud Weksteen <tweek@google.com> | 
|  | */ | 
|  |  | 
|  | #include <linux/efi.h> | 
|  | #include <linux/tpm_eventlog.h> | 
|  |  | 
|  | #include "../tpm.h" | 
|  | #include "common.h" | 
|  |  | 
|  | /* read binary bios log from EFI configuration table */ | 
|  | int tpm_read_log_efi(struct tpm_chip *chip) | 
|  | { | 
|  |  | 
|  | struct efi_tcg2_final_events_table *final_tbl = NULL; | 
|  | int final_events_log_size = efi_tpm_final_log_size; | 
|  | 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; | 
|  |  | 
|  | if (efi.tpm_log == EFI_INVALID_TABLE_ADDR) | 
|  | return -ENODEV; | 
|  |  | 
|  | log = &chip->log; | 
|  |  | 
|  | log_tbl = memremap(efi.tpm_log, sizeof(*log_tbl), MEMREMAP_WB); | 
|  | if (!log_tbl) { | 
|  | pr_err("Could not map UEFI TPM log table !\n"); | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | log_size = log_tbl->size; | 
|  | memunmap(log_tbl); | 
|  |  | 
|  | if (!log_size) { | 
|  | pr_warn("UEFI TPM log area empty\n"); | 
|  | return -EIO; | 
|  | } | 
|  |  | 
|  | log_tbl = memremap(efi.tpm_log, sizeof(*log_tbl) + log_size, | 
|  | MEMREMAP_WB); | 
|  | if (!log_tbl) { | 
|  | pr_err("Could not map UEFI TPM log table payload!\n"); | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | /* malloc EventLog space */ | 
|  | log->bios_event_log = kmemdup(log_tbl->log, log_size, GFP_KERNEL); | 
|  | 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; | 
|  |  | 
|  | ret = tpm_log_version; | 
|  |  | 
|  | if (efi.tpm_final_log == EFI_INVALID_TABLE_ADDR || | 
|  | final_events_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) + final_events_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; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * The 'final events log' size excludes the 'final events preboot log' | 
|  | * at its beginning. | 
|  | */ | 
|  | final_events_log_size -= log_tbl->final_events_preboot_size; | 
|  |  | 
|  | /* | 
|  | * Allocate memory for the 'combined log' where we will append the | 
|  | * 'final events log' to. | 
|  | */ | 
|  | tmp = krealloc(log->bios_event_log, | 
|  | log_size + final_events_log_size, | 
|  | GFP_KERNEL); | 
|  | if (!tmp) { | 
|  | kfree(log->bios_event_log); | 
|  | ret = -ENOMEM; | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | log->bios_event_log = tmp; | 
|  |  | 
|  | /* | 
|  | * Append 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, | 
|  | final_events_log_size); | 
|  | /* | 
|  | * The size of the 'combined log' is the size of the 'main log' plus | 
|  | * the size of the 'final events log'. | 
|  | */ | 
|  | log->bios_event_log_end = log->bios_event_log + | 
|  | log_size + final_events_log_size; | 
|  |  | 
|  | out: | 
|  | memunmap(final_tbl); | 
|  | memunmap(log_tbl); | 
|  | return ret; | 
|  | } |