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