Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2005 IBM Corporation * * Authors: * Seiji Munetoh <munetoh@jp.ibm.com> * Stefan Berger <stefanb@us.ibm.com> * Reiner Sailer <sailer@watson.ibm.com> * Kylene Hall <kjhall@us.ibm.com> * Nayna Jain <nayna@linux.vnet.ibm.com> * * Maintained by: <tpmdd-devel@lists.sourceforge.net> * * Access to the event log extended by the TCG BIOS of PC platform */ #include <linux/device.h> #include <linux/seq_file.h> #include <linux/fs.h> #include <linux/security.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/acpi.h> #include <linux/tpm_eventlog.h> #include "../tpm.h" #include "common.h" struct acpi_tcpa { struct acpi_table_header hdr; u16 platform_class; union { struct client_hdr { u32 log_max_len __packed; u64 log_start_addr __packed; } client; struct server_hdr { u16 reserved; u64 log_max_len __packed; u64 log_start_addr __packed; } server; }; }; /* Check that the given log is indeed a TPM2 log. */ static bool tpm_is_tpm2_log(void *bios_event_log, u64 len) { struct tcg_efi_specid_event_head *efispecid; struct tcg_pcr_event *event_header; int n; if (len < sizeof(*event_header)) return false; len -= sizeof(*event_header); event_header = bios_event_log; if (len < sizeof(*efispecid)) return false; efispecid = (struct tcg_efi_specid_event_head *)event_header->event; n = memcmp(efispecid->signature, TCG_SPECID_SIG, sizeof(TCG_SPECID_SIG)); return n == 0; } /* read binary bios log */ int tpm_read_log_acpi(struct tpm_chip *chip) { struct acpi_tcpa *buff; acpi_status status; void __iomem *virt; u64 len, start; struct tpm_bios_log *log; struct acpi_table_tpm2 *tbl; struct acpi_tpm2_phy *tpm2_phy; int format; int ret; log = &chip->log; /* Unfortuntely ACPI does not associate the event log with a specific * TPM, like PPI. Thus all ACPI TPMs will read the same log. */ if (!chip->acpi_dev_handle) return -ENODEV; if (chip->flags & TPM_CHIP_FLAG_TPM2) { status = acpi_get_table("TPM2", 1, (struct acpi_table_header **)&tbl); if (ACPI_FAILURE(status)) return -ENODEV; if (tbl->header.length < sizeof(*tbl) + sizeof(struct acpi_tpm2_phy)) { acpi_put_table((struct acpi_table_header *)tbl); return -ENODEV; } tpm2_phy = (void *)tbl + sizeof(*tbl); len = tpm2_phy->log_area_minimum_length; start = tpm2_phy->log_area_start_address; if (!start || !len) { acpi_put_table((struct acpi_table_header *)tbl); return -ENODEV; } acpi_put_table((struct acpi_table_header *)tbl); format = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2; } else { /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */ status = acpi_get_table(ACPI_SIG_TCPA, 1, (struct acpi_table_header **)&buff); if (ACPI_FAILURE(status)) return -ENODEV; switch (buff->platform_class) { case BIOS_SERVER: len = buff->server.log_max_len; start = buff->server.log_start_addr; break; case BIOS_CLIENT: default: len = buff->client.log_max_len; start = buff->client.log_start_addr; break; } acpi_put_table((struct acpi_table_header *)buff); format = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2; } if (!len) { dev_warn(&chip->dev, "%s: TCPA log area empty\n", __func__); return -EIO; } /* malloc EventLog space */ log->bios_event_log = devm_kmalloc(&chip->dev, len, GFP_KERNEL); if (!log->bios_event_log) return -ENOMEM; log->bios_event_log_end = log->bios_event_log + len; ret = -EIO; virt = acpi_os_map_iomem(start, len); if (!virt) { dev_warn(&chip->dev, "%s: Failed to map ACPI memory\n", __func__); /* try EFI log next */ ret = -ENODEV; goto err; } memcpy_fromio(log->bios_event_log, virt, len); acpi_os_unmap_iomem(virt, len); if (chip->flags & TPM_CHIP_FLAG_TPM2 && !tpm_is_tpm2_log(log->bios_event_log, len)) { /* try EFI log next */ ret = -ENODEV; goto err; } return format; err: devm_kfree(&chip->dev, log->bios_event_log); log->bios_event_log = NULL; return ret; } |