1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2005, 2012 IBM Corporation |
4 | * |
5 | * Authors: |
6 | * Kent Yoder <key@linux.vnet.ibm.com> |
7 | * Seiji Munetoh <munetoh@jp.ibm.com> |
8 | * Stefan Berger <stefanb@us.ibm.com> |
9 | * Reiner Sailer <sailer@watson.ibm.com> |
10 | * Kylene Hall <kjhall@us.ibm.com> |
11 | * Nayna Jain <nayna@linux.vnet.ibm.com> |
12 | * |
13 | * Access to the event log created by a system's firmware / BIOS |
14 | */ |
15 | |
16 | #include <linux/seq_file.h> |
17 | #include <linux/fs.h> |
18 | #include <linux/security.h> |
19 | #include <linux/module.h> |
20 | #include <linux/tpm_eventlog.h> |
21 | |
22 | #include "../tpm.h" |
23 | #include "common.h" |
24 | |
25 | static int tpm_bios_measurements_open(struct inode *inode, |
26 | struct file *file) |
27 | { |
28 | int err; |
29 | struct seq_file *seq; |
30 | struct tpm_chip_seqops *chip_seqops; |
31 | const struct seq_operations *seqops; |
32 | struct tpm_chip *chip; |
33 | |
34 | inode_lock(inode); |
35 | if (!inode->i_private) { |
36 | inode_unlock(inode); |
37 | return -ENODEV; |
38 | } |
39 | chip_seqops = inode->i_private; |
40 | seqops = chip_seqops->seqops; |
41 | chip = chip_seqops->chip; |
42 | get_device(dev: &chip->dev); |
43 | inode_unlock(inode); |
44 | |
45 | /* now register seq file */ |
46 | err = seq_open(file, seqops); |
47 | if (!err) { |
48 | seq = file->private_data; |
49 | seq->private = chip; |
50 | } |
51 | |
52 | return err; |
53 | } |
54 | |
55 | static int tpm_bios_measurements_release(struct inode *inode, |
56 | struct file *file) |
57 | { |
58 | struct seq_file *seq = file->private_data; |
59 | struct tpm_chip *chip = seq->private; |
60 | |
61 | put_device(dev: &chip->dev); |
62 | |
63 | return seq_release(inode, file); |
64 | } |
65 | |
66 | static const struct file_operations tpm_bios_measurements_ops = { |
67 | .owner = THIS_MODULE, |
68 | .open = tpm_bios_measurements_open, |
69 | .read = seq_read, |
70 | .llseek = seq_lseek, |
71 | .release = tpm_bios_measurements_release, |
72 | }; |
73 | |
74 | static int tpm_read_log(struct tpm_chip *chip) |
75 | { |
76 | int rc; |
77 | |
78 | if (chip->log.bios_event_log != NULL) { |
79 | dev_dbg(&chip->dev, |
80 | "%s: ERROR - event log already initialized\n" , |
81 | __func__); |
82 | return -EFAULT; |
83 | } |
84 | |
85 | rc = tpm_read_log_acpi(chip); |
86 | if (rc != -ENODEV) |
87 | return rc; |
88 | |
89 | rc = tpm_read_log_efi(chip); |
90 | if (rc != -ENODEV) |
91 | return rc; |
92 | |
93 | return tpm_read_log_of(chip); |
94 | } |
95 | |
96 | /* |
97 | * tpm_bios_log_setup() - Read the event log from the firmware |
98 | * @chip: TPM chip to use. |
99 | * |
100 | * If an event log is found then the securityfs files are setup to |
101 | * export it to userspace, otherwise nothing is done. |
102 | */ |
103 | void tpm_bios_log_setup(struct tpm_chip *chip) |
104 | { |
105 | const char *name = dev_name(dev: &chip->dev); |
106 | unsigned int cnt; |
107 | int log_version; |
108 | int rc = 0; |
109 | |
110 | if (chip->flags & TPM_CHIP_FLAG_VIRTUAL) |
111 | return; |
112 | |
113 | rc = tpm_read_log(chip); |
114 | if (rc < 0) |
115 | return; |
116 | log_version = rc; |
117 | |
118 | cnt = 0; |
119 | chip->bios_dir[cnt] = securityfs_create_dir(name, NULL); |
120 | /* NOTE: securityfs_create_dir can return ENODEV if securityfs is |
121 | * compiled out. The caller should ignore the ENODEV return code. |
122 | */ |
123 | if (IS_ERR(ptr: chip->bios_dir[cnt])) |
124 | goto err; |
125 | cnt++; |
126 | |
127 | chip->bin_log_seqops.chip = chip; |
128 | if (log_version == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) |
129 | chip->bin_log_seqops.seqops = |
130 | &tpm2_binary_b_measurements_seqops; |
131 | else |
132 | chip->bin_log_seqops.seqops = |
133 | &tpm1_binary_b_measurements_seqops; |
134 | |
135 | |
136 | chip->bios_dir[cnt] = |
137 | securityfs_create_file(name: "binary_bios_measurements" , |
138 | mode: 0440, parent: chip->bios_dir[0], |
139 | data: (void *)&chip->bin_log_seqops, |
140 | fops: &tpm_bios_measurements_ops); |
141 | if (IS_ERR(ptr: chip->bios_dir[cnt])) |
142 | goto err; |
143 | cnt++; |
144 | |
145 | if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { |
146 | |
147 | chip->ascii_log_seqops.chip = chip; |
148 | chip->ascii_log_seqops.seqops = |
149 | &tpm1_ascii_b_measurements_seqops; |
150 | |
151 | chip->bios_dir[cnt] = |
152 | securityfs_create_file(name: "ascii_bios_measurements" , |
153 | mode: 0440, parent: chip->bios_dir[0], |
154 | data: (void *)&chip->ascii_log_seqops, |
155 | fops: &tpm_bios_measurements_ops); |
156 | if (IS_ERR(ptr: chip->bios_dir[cnt])) |
157 | goto err; |
158 | cnt++; |
159 | } |
160 | |
161 | return; |
162 | |
163 | err: |
164 | chip->bios_dir[cnt] = NULL; |
165 | tpm_bios_log_teardown(chip); |
166 | return; |
167 | } |
168 | |
169 | void tpm_bios_log_teardown(struct tpm_chip *chip) |
170 | { |
171 | int i; |
172 | struct inode *inode; |
173 | |
174 | /* securityfs_remove currently doesn't take care of handling sync |
175 | * between removal and opening of pseudo files. To handle this, a |
176 | * workaround is added by making i_private = NULL here during removal |
177 | * and to check it during open(), both within inode_lock()/unlock(). |
178 | * This design ensures that open() either safely gets kref or fails. |
179 | */ |
180 | for (i = (TPM_NUM_EVENT_LOG_FILES - 1); i >= 0; i--) { |
181 | if (chip->bios_dir[i]) { |
182 | inode = d_inode(dentry: chip->bios_dir[i]); |
183 | inode_lock(inode); |
184 | inode->i_private = NULL; |
185 | inode_unlock(inode); |
186 | securityfs_remove(dentry: chip->bios_dir[i]); |
187 | } |
188 | } |
189 | } |
190 | |