Commit | Line | Data |
---|---|---|
000a07b0 JG |
1 | /* |
2 | * Copyright (C) 2004 IBM Corporation | |
3 | * Authors: | |
4 | * Leendert van Doorn <leendert@watson.ibm.com> | |
5 | * Dave Safford <safford@watson.ibm.com> | |
6 | * Reiner Sailer <sailer@watson.ibm.com> | |
7 | * Kylene Hall <kjhall@us.ibm.com> | |
8 | * | |
1e3b73a9 JG |
9 | * Copyright (C) 2013 Obsidian Research Corp |
10 | * Jason Gunthorpe <jgunthorpe@obsidianresearch.com> | |
11 | * | |
000a07b0 JG |
12 | * sysfs filesystem inspection interface to the TPM |
13 | * | |
14 | * This program is free software; you can redistribute it and/or | |
15 | * modify it under the terms of the GNU General Public License as | |
16 | * published by the Free Software Foundation, version 2 of the | |
17 | * License. | |
18 | * | |
19 | */ | |
20 | #include <linux/device.h> | |
21 | #include "tpm.h" | |
22 | ||
23 | /* XXX for now this helper is duplicated in tpm-interface.c */ | |
24 | static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd, | |
25 | int len, const char *desc) | |
26 | { | |
27 | int err; | |
28 | ||
29 | len = tpm_transmit(chip, (u8 *) cmd, len); | |
30 | if (len < 0) | |
31 | return len; | |
32 | else if (len < TPM_HEADER_SIZE) | |
33 | return -EFAULT; | |
34 | ||
35 | err = be32_to_cpu(cmd->header.out.return_code); | |
36 | if (err != 0 && desc) | |
37 | dev_err(chip->dev, "A TPM error (%d) occurred %s\n", err, desc); | |
38 | ||
39 | return err; | |
40 | } | |
41 | ||
42 | #define READ_PUBEK_RESULT_SIZE 314 | |
43 | #define TPM_ORD_READPUBEK cpu_to_be32(124) | |
44 | static struct tpm_input_header tpm_readpubek_header = { | |
45 | .tag = TPM_TAG_RQU_COMMAND, | |
46 | .length = cpu_to_be32(30), | |
47 | .ordinal = TPM_ORD_READPUBEK | |
48 | }; | |
1e3b73a9 JG |
49 | static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, |
50 | char *buf) | |
000a07b0 JG |
51 | { |
52 | u8 *data; | |
53 | struct tpm_cmd_t tpm_cmd; | |
54 | ssize_t err; | |
55 | int i, rc; | |
56 | char *str = buf; | |
57 | ||
58 | struct tpm_chip *chip = dev_get_drvdata(dev); | |
59 | ||
60 | tpm_cmd.header.in = tpm_readpubek_header; | |
61 | err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, | |
62 | "attempting to read the PUBEK"); | |
63 | if (err) | |
64 | goto out; | |
65 | ||
66 | /* | |
67 | ignore header 10 bytes | |
68 | algorithm 32 bits (1 == RSA ) | |
69 | encscheme 16 bits | |
70 | sigscheme 16 bits | |
71 | parameters (RSA 12->bytes: keybit, #primes, expbit) | |
72 | keylenbytes 32 bits | |
73 | 256 byte modulus | |
74 | ignore checksum 20 bytes | |
75 | */ | |
76 | data = tpm_cmd.params.readpubek_out_buffer; | |
77 | str += | |
78 | sprintf(str, | |
79 | "Algorithm: %02X %02X %02X %02X\n" | |
80 | "Encscheme: %02X %02X\n" | |
81 | "Sigscheme: %02X %02X\n" | |
82 | "Parameters: %02X %02X %02X %02X " | |
83 | "%02X %02X %02X %02X " | |
84 | "%02X %02X %02X %02X\n" | |
85 | "Modulus length: %d\n" | |
86 | "Modulus:\n", | |
87 | data[0], data[1], data[2], data[3], | |
88 | data[4], data[5], | |
89 | data[6], data[7], | |
90 | data[12], data[13], data[14], data[15], | |
91 | data[16], data[17], data[18], data[19], | |
92 | data[20], data[21], data[22], data[23], | |
93 | be32_to_cpu(*((__be32 *) (data + 24)))); | |
94 | ||
95 | for (i = 0; i < 256; i++) { | |
96 | str += sprintf(str, "%02X ", data[i + 28]); | |
97 | if ((i + 1) % 16 == 0) | |
98 | str += sprintf(str, "\n"); | |
99 | } | |
100 | out: | |
101 | rc = str - buf; | |
102 | return rc; | |
103 | } | |
1e3b73a9 | 104 | static DEVICE_ATTR_RO(pubek); |
000a07b0 | 105 | |
1e3b73a9 JG |
106 | static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr, |
107 | char *buf) | |
000a07b0 JG |
108 | { |
109 | cap_t cap; | |
110 | u8 digest[TPM_DIGEST_SIZE]; | |
111 | ssize_t rc; | |
112 | int i, j, num_pcrs; | |
113 | char *str = buf; | |
114 | struct tpm_chip *chip = dev_get_drvdata(dev); | |
115 | ||
116 | rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap, | |
117 | "attempting to determine the number of PCRS"); | |
118 | if (rc) | |
119 | return 0; | |
120 | ||
121 | num_pcrs = be32_to_cpu(cap.num_pcrs); | |
122 | for (i = 0; i < num_pcrs; i++) { | |
123 | rc = tpm_pcr_read_dev(chip, i, digest); | |
124 | if (rc) | |
125 | break; | |
126 | str += sprintf(str, "PCR-%02d: ", i); | |
127 | for (j = 0; j < TPM_DIGEST_SIZE; j++) | |
128 | str += sprintf(str, "%02X ", digest[j]); | |
129 | str += sprintf(str, "\n"); | |
130 | } | |
131 | return str - buf; | |
132 | } | |
1e3b73a9 | 133 | static DEVICE_ATTR_RO(pcrs); |
000a07b0 | 134 | |
1e3b73a9 JG |
135 | static ssize_t enabled_show(struct device *dev, struct device_attribute *attr, |
136 | char *buf) | |
000a07b0 JG |
137 | { |
138 | cap_t cap; | |
139 | ssize_t rc; | |
140 | ||
141 | rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap, | |
142 | "attempting to determine the permanent enabled state"); | |
143 | if (rc) | |
144 | return 0; | |
145 | ||
146 | rc = sprintf(buf, "%d\n", !cap.perm_flags.disable); | |
147 | return rc; | |
148 | } | |
1e3b73a9 | 149 | static DEVICE_ATTR_RO(enabled); |
000a07b0 | 150 | |
1e3b73a9 JG |
151 | ssize_t active_show(struct device *dev, struct device_attribute *attr, |
152 | char *buf) | |
000a07b0 JG |
153 | { |
154 | cap_t cap; | |
155 | ssize_t rc; | |
156 | ||
157 | rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap, | |
158 | "attempting to determine the permanent active state"); | |
159 | if (rc) | |
160 | return 0; | |
161 | ||
162 | rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated); | |
163 | return rc; | |
164 | } | |
1e3b73a9 | 165 | static DEVICE_ATTR_RO(active); |
000a07b0 | 166 | |
1e3b73a9 JG |
167 | static ssize_t owned_show(struct device *dev, struct device_attribute *attr, |
168 | char *buf) | |
000a07b0 JG |
169 | { |
170 | cap_t cap; | |
171 | ssize_t rc; | |
172 | ||
173 | rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap, | |
174 | "attempting to determine the owner state"); | |
175 | if (rc) | |
176 | return 0; | |
177 | ||
178 | rc = sprintf(buf, "%d\n", cap.owned); | |
179 | return rc; | |
180 | } | |
1e3b73a9 | 181 | static DEVICE_ATTR_RO(owned); |
000a07b0 | 182 | |
1e3b73a9 JG |
183 | static ssize_t temp_deactivated_show(struct device *dev, |
184 | struct device_attribute *attr, char *buf) | |
000a07b0 JG |
185 | { |
186 | cap_t cap; | |
187 | ssize_t rc; | |
188 | ||
189 | rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap, | |
190 | "attempting to determine the temporary state"); | |
191 | if (rc) | |
192 | return 0; | |
193 | ||
194 | rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated); | |
195 | return rc; | |
196 | } | |
1e3b73a9 | 197 | static DEVICE_ATTR_RO(temp_deactivated); |
000a07b0 | 198 | |
1e3b73a9 JG |
199 | static ssize_t caps_show(struct device *dev, struct device_attribute *attr, |
200 | char *buf) | |
000a07b0 JG |
201 | { |
202 | cap_t cap; | |
203 | ssize_t rc; | |
204 | char *str = buf; | |
205 | ||
206 | rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap, | |
207 | "attempting to determine the manufacturer"); | |
208 | if (rc) | |
209 | return 0; | |
210 | str += sprintf(str, "Manufacturer: 0x%x\n", | |
211 | be32_to_cpu(cap.manufacturer_id)); | |
212 | ||
213 | /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */ | |
214 | rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap, | |
215 | "attempting to determine the 1.2 version"); | |
216 | if (!rc) { | |
217 | str += sprintf(str, | |
218 | "TCG version: %d.%d\nFirmware version: %d.%d\n", | |
219 | cap.tpm_version_1_2.Major, | |
220 | cap.tpm_version_1_2.Minor, | |
221 | cap.tpm_version_1_2.revMajor, | |
222 | cap.tpm_version_1_2.revMinor); | |
223 | } else { | |
224 | /* Otherwise just use TPM_STRUCT_VER */ | |
225 | rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap, | |
226 | "attempting to determine the 1.1 version"); | |
227 | if (rc) | |
228 | return 0; | |
229 | str += sprintf(str, | |
230 | "TCG version: %d.%d\nFirmware version: %d.%d\n", | |
231 | cap.tpm_version.Major, | |
232 | cap.tpm_version.Minor, | |
233 | cap.tpm_version.revMajor, | |
234 | cap.tpm_version.revMinor); | |
235 | } | |
236 | ||
237 | return str - buf; | |
238 | } | |
1e3b73a9 | 239 | static DEVICE_ATTR_RO(caps); |
000a07b0 | 240 | |
1e3b73a9 JG |
241 | static ssize_t cancel_store(struct device *dev, struct device_attribute *attr, |
242 | const char *buf, size_t count) | |
000a07b0 JG |
243 | { |
244 | struct tpm_chip *chip = dev_get_drvdata(dev); | |
245 | if (chip == NULL) | |
246 | return 0; | |
247 | ||
5f82e9f0 | 248 | chip->ops->cancel(chip); |
000a07b0 JG |
249 | return count; |
250 | } | |
1e3b73a9 | 251 | static DEVICE_ATTR_WO(cancel); |
000a07b0 | 252 | |
1e3b73a9 JG |
253 | static ssize_t durations_show(struct device *dev, struct device_attribute *attr, |
254 | char *buf) | |
000a07b0 JG |
255 | { |
256 | struct tpm_chip *chip = dev_get_drvdata(dev); | |
257 | ||
258 | if (chip->vendor.duration[TPM_LONG] == 0) | |
259 | return 0; | |
260 | ||
261 | return sprintf(buf, "%d %d %d [%s]\n", | |
262 | jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]), | |
263 | jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]), | |
264 | jiffies_to_usecs(chip->vendor.duration[TPM_LONG]), | |
265 | chip->vendor.duration_adjusted | |
266 | ? "adjusted" : "original"); | |
267 | } | |
1e3b73a9 | 268 | static DEVICE_ATTR_RO(durations); |
000a07b0 | 269 | |
1e3b73a9 JG |
270 | static ssize_t timeouts_show(struct device *dev, struct device_attribute *attr, |
271 | char *buf) | |
000a07b0 JG |
272 | { |
273 | struct tpm_chip *chip = dev_get_drvdata(dev); | |
274 | ||
275 | return sprintf(buf, "%d %d %d %d [%s]\n", | |
276 | jiffies_to_usecs(chip->vendor.timeout_a), | |
277 | jiffies_to_usecs(chip->vendor.timeout_b), | |
278 | jiffies_to_usecs(chip->vendor.timeout_c), | |
279 | jiffies_to_usecs(chip->vendor.timeout_d), | |
280 | chip->vendor.timeout_adjusted | |
281 | ? "adjusted" : "original"); | |
282 | } | |
1e3b73a9 JG |
283 | static DEVICE_ATTR_RO(timeouts); |
284 | ||
285 | static struct attribute *tpm_dev_attrs[] = { | |
286 | &dev_attr_pubek.attr, | |
287 | &dev_attr_pcrs.attr, | |
288 | &dev_attr_enabled.attr, | |
289 | &dev_attr_active.attr, | |
290 | &dev_attr_owned.attr, | |
291 | &dev_attr_temp_deactivated.attr, | |
292 | &dev_attr_caps.attr, | |
293 | &dev_attr_cancel.attr, | |
294 | &dev_attr_durations.attr, | |
295 | &dev_attr_timeouts.attr, | |
296 | NULL, | |
297 | }; | |
298 | ||
299 | static const struct attribute_group tpm_dev_group = { | |
300 | .attrs = tpm_dev_attrs, | |
301 | }; | |
302 | ||
303 | int tpm_sysfs_add_device(struct tpm_chip *chip) | |
304 | { | |
305 | int err; | |
306 | err = sysfs_create_group(&chip->dev->kobj, | |
307 | &tpm_dev_group); | |
308 | ||
309 | if (err) | |
310 | dev_err(chip->dev, | |
311 | "failed to create sysfs attributes, %d\n", err); | |
312 | return err; | |
313 | } | |
314 | ||
315 | void tpm_sysfs_del_device(struct tpm_chip *chip) | |
316 | { | |
317 | sysfs_remove_group(&chip->dev->kobj, &tpm_dev_group); | |
318 | } |