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 | } else { |
51 | put_device(dev: &chip->dev); |
52 | } |
53 | |
54 | return err; |
55 | } |
56 | |
57 | static int tpm_bios_measurements_release(struct inode *inode, |
58 | struct file *file) |
59 | { |
60 | struct seq_file *seq = file->private_data; |
61 | struct tpm_chip *chip = seq->private; |
62 | |
63 | put_device(dev: &chip->dev); |
64 | |
65 | return seq_release(inode, file); |
66 | } |
67 | |
68 | static const struct file_operations tpm_bios_measurements_ops = { |
69 | .owner = THIS_MODULE, |
70 | .open = tpm_bios_measurements_open, |
71 | .read = seq_read, |
72 | .llseek = seq_lseek, |
73 | .release = tpm_bios_measurements_release, |
74 | }; |
75 | |
76 | static int tpm_read_log(struct tpm_chip *chip) |
77 | { |
78 | int rc; |
79 | |
80 | if (chip->log.bios_event_log != NULL) { |
81 | dev_dbg(&chip->dev, |
82 | "%s: ERROR - event log already initialized\n" , |
83 | __func__); |
84 | return -EFAULT; |
85 | } |
86 | |
87 | rc = tpm_read_log_acpi(chip); |
88 | if (rc != -ENODEV) |
89 | return rc; |
90 | |
91 | rc = tpm_read_log_efi(chip); |
92 | if (rc != -ENODEV) |
93 | return rc; |
94 | |
95 | return tpm_read_log_of(chip); |
96 | } |
97 | |
98 | /* |
99 | * tpm_bios_log_setup() - Read the event log from the firmware |
100 | * @chip: TPM chip to use. |
101 | * |
102 | * If an event log is found then the securityfs files are setup to |
103 | * export it to userspace, otherwise nothing is done. |
104 | */ |
105 | void tpm_bios_log_setup(struct tpm_chip *chip) |
106 | { |
107 | const char *name = dev_name(dev: &chip->dev); |
108 | unsigned int cnt; |
109 | int log_version; |
110 | int rc = 0; |
111 | |
112 | if (chip->flags & TPM_CHIP_FLAG_VIRTUAL) |
113 | return; |
114 | |
115 | rc = tpm_read_log(chip); |
116 | if (rc < 0) |
117 | return; |
118 | log_version = rc; |
119 | |
120 | cnt = 0; |
121 | chip->bios_dir[cnt] = securityfs_create_dir(name, NULL); |
122 | /* NOTE: securityfs_create_dir can return ENODEV if securityfs is |
123 | * compiled out. The caller should ignore the ENODEV return code. |
124 | */ |
125 | if (IS_ERR(ptr: chip->bios_dir[cnt])) |
126 | goto err; |
127 | cnt++; |
128 | |
129 | chip->bin_log_seqops.chip = chip; |
130 | if (log_version == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) |
131 | chip->bin_log_seqops.seqops = |
132 | &tpm2_binary_b_measurements_seqops; |
133 | else |
134 | chip->bin_log_seqops.seqops = |
135 | &tpm1_binary_b_measurements_seqops; |
136 | |
137 | |
138 | chip->bios_dir[cnt] = |
139 | securityfs_create_file(name: "binary_bios_measurements" , |
140 | mode: 0440, parent: chip->bios_dir[0], |
141 | data: (void *)&chip->bin_log_seqops, |
142 | fops: &tpm_bios_measurements_ops); |
143 | if (IS_ERR(ptr: chip->bios_dir[cnt])) |
144 | goto err; |
145 | cnt++; |
146 | |
147 | if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { |
148 | |
149 | chip->ascii_log_seqops.chip = chip; |
150 | chip->ascii_log_seqops.seqops = |
151 | &tpm1_ascii_b_measurements_seqops; |
152 | |
153 | chip->bios_dir[cnt] = |
154 | securityfs_create_file(name: "ascii_bios_measurements" , |
155 | mode: 0440, parent: chip->bios_dir[0], |
156 | data: (void *)&chip->ascii_log_seqops, |
157 | fops: &tpm_bios_measurements_ops); |
158 | if (IS_ERR(ptr: chip->bios_dir[cnt])) |
159 | goto err; |
160 | cnt++; |
161 | } |
162 | |
163 | return; |
164 | |
165 | err: |
166 | chip->bios_dir[cnt] = NULL; |
167 | tpm_bios_log_teardown(chip); |
168 | return; |
169 | } |
170 | |
171 | void tpm_bios_log_teardown(struct tpm_chip *chip) |
172 | { |
173 | int i; |
174 | struct inode *inode; |
175 | |
176 | /* securityfs_remove currently doesn't take care of handling sync |
177 | * between removal and opening of pseudo files. To handle this, a |
178 | * workaround is added by making i_private = NULL here during removal |
179 | * and to check it during open(), both within inode_lock()/unlock(). |
180 | * This design ensures that open() either safely gets kref or fails. |
181 | */ |
182 | for (i = (TPM_NUM_EVENT_LOG_FILES - 1); i >= 0; i--) { |
183 | if (chip->bios_dir[i]) { |
184 | inode = d_inode(dentry: chip->bios_dir[i]); |
185 | inode_lock(inode); |
186 | inode->i_private = NULL; |
187 | inode_unlock(inode); |
188 | securityfs_remove(dentry: chip->bios_dir[i]); |
189 | } |
190 | } |
191 | } |
192 | |