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 | static void tpm_bios_log_free(void *data) |
67 | { |
68 | kvfree(addr: data); |
69 | } |
70 | |
71 | /* read binary bios log */ |
72 | int tpm_read_log_acpi(struct tpm_chip *chip) |
73 | { |
74 | struct acpi_tcpa *buff; |
75 | acpi_status status; |
76 | void __iomem *virt; |
77 | u64 len, start; |
78 | struct tpm_bios_log *log; |
79 | struct acpi_table_tpm2 *tbl; |
80 | struct acpi_tpm2_phy *tpm2_phy; |
81 | int format; |
82 | int ret; |
83 | |
84 | log = &chip->log; |
85 | |
86 | /* Unfortuntely ACPI does not associate the event log with a specific |
87 | * TPM, like PPI. Thus all ACPI TPMs will read the same log. |
88 | */ |
89 | if (!chip->acpi_dev_handle) |
90 | return -ENODEV; |
91 | |
92 | if (chip->flags & TPM_CHIP_FLAG_TPM2) { |
93 | status = acpi_get_table(signature: "TPM2" , instance: 1, |
94 | out_table: (struct acpi_table_header **)&tbl); |
95 | if (ACPI_FAILURE(status)) |
96 | return -ENODEV; |
97 | |
98 | if (tbl->header.length < |
99 | sizeof(*tbl) + sizeof(struct acpi_tpm2_phy)) { |
100 | acpi_put_table(table: (struct acpi_table_header *)tbl); |
101 | return -ENODEV; |
102 | } |
103 | |
104 | tpm2_phy = (void *)tbl + sizeof(*tbl); |
105 | len = tpm2_phy->log_area_minimum_length; |
106 | |
107 | start = tpm2_phy->log_area_start_address; |
108 | if (!start || !len) { |
109 | acpi_put_table(table: (struct acpi_table_header *)tbl); |
110 | return -ENODEV; |
111 | } |
112 | |
113 | acpi_put_table(table: (struct acpi_table_header *)tbl); |
114 | format = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2; |
115 | } else { |
116 | /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */ |
117 | status = acpi_get_table(ACPI_SIG_TCPA, instance: 1, |
118 | out_table: (struct acpi_table_header **)&buff); |
119 | if (ACPI_FAILURE(status)) |
120 | return -ENODEV; |
121 | |
122 | switch (buff->platform_class) { |
123 | case BIOS_SERVER: |
124 | len = buff->server.log_max_len; |
125 | start = buff->server.log_start_addr; |
126 | break; |
127 | case BIOS_CLIENT: |
128 | default: |
129 | len = buff->client.log_max_len; |
130 | start = buff->client.log_start_addr; |
131 | break; |
132 | } |
133 | |
134 | acpi_put_table(table: (struct acpi_table_header *)buff); |
135 | format = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2; |
136 | } |
137 | |
138 | if (!len) { |
139 | dev_warn(&chip->dev, "%s: TCPA log area empty\n" , __func__); |
140 | return -EIO; |
141 | } |
142 | |
143 | /* malloc EventLog space */ |
144 | log->bios_event_log = kvmalloc(len, GFP_KERNEL); |
145 | if (!log->bios_event_log) |
146 | return -ENOMEM; |
147 | |
148 | log->bios_event_log_end = log->bios_event_log + len; |
149 | |
150 | virt = acpi_os_map_iomem(phys: start, size: len); |
151 | if (!virt) { |
152 | dev_warn(&chip->dev, "%s: Failed to map ACPI memory\n" , __func__); |
153 | /* try EFI log next */ |
154 | ret = -ENODEV; |
155 | goto err; |
156 | } |
157 | |
158 | memcpy_fromio(log->bios_event_log, virt, len); |
159 | |
160 | acpi_os_unmap_iomem(virt, size: len); |
161 | |
162 | if (chip->flags & TPM_CHIP_FLAG_TPM2 && |
163 | !tpm_is_tpm2_log(bios_event_log: log->bios_event_log, len)) { |
164 | /* try EFI log next */ |
165 | ret = -ENODEV; |
166 | goto err; |
167 | } |
168 | |
169 | ret = devm_add_action(&chip->dev, tpm_bios_log_free, log->bios_event_log); |
170 | if (ret) { |
171 | log->bios_event_log = NULL; |
172 | goto err; |
173 | } |
174 | |
175 | return format; |
176 | |
177 | err: |
178 | tpm_bios_log_free(data: log->bios_event_log); |
179 | log->bios_event_log = NULL; |
180 | return ret; |
181 | } |
182 | |