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 | ||
000a07b0 | 23 | #define READ_PUBEK_RESULT_SIZE 314 |
c659af78 | 24 | #define READ_PUBEK_RESULT_MIN_BODY_SIZE (28 + 256) |
a69faebf | 25 | #define TPM_ORD_READPUBEK 124 |
0014777f | 26 | static const struct tpm_input_header tpm_readpubek_header = { |
06e93279 | 27 | .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), |
000a07b0 | 28 | .length = cpu_to_be32(30), |
a69faebf | 29 | .ordinal = cpu_to_be32(TPM_ORD_READPUBEK) |
000a07b0 | 30 | }; |
1e3b73a9 JG |
31 | static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, |
32 | char *buf) | |
000a07b0 JG |
33 | { |
34 | u8 *data; | |
35 | struct tpm_cmd_t tpm_cmd; | |
36 | ssize_t err; | |
37 | int i, rc; | |
38 | char *str = buf; | |
39 | ||
062807f2 | 40 | struct tpm_chip *chip = to_tpm_chip(dev); |
000a07b0 JG |
41 | |
42 | tpm_cmd.header.in = tpm_readpubek_header; | |
745b361e | 43 | err = tpm_transmit_cmd(chip, NULL, &tpm_cmd, READ_PUBEK_RESULT_SIZE, |
c659af78 | 44 | READ_PUBEK_RESULT_MIN_BODY_SIZE, 0, |
87155b73 | 45 | "attempting to read the PUBEK"); |
000a07b0 JG |
46 | if (err) |
47 | goto out; | |
48 | ||
49 | /* | |
50 | ignore header 10 bytes | |
51 | algorithm 32 bits (1 == RSA ) | |
52 | encscheme 16 bits | |
53 | sigscheme 16 bits | |
54 | parameters (RSA 12->bytes: keybit, #primes, expbit) | |
55 | keylenbytes 32 bits | |
56 | 256 byte modulus | |
57 | ignore checksum 20 bytes | |
58 | */ | |
59 | data = tpm_cmd.params.readpubek_out_buffer; | |
60 | str += | |
61 | sprintf(str, | |
62 | "Algorithm: %02X %02X %02X %02X\n" | |
63 | "Encscheme: %02X %02X\n" | |
64 | "Sigscheme: %02X %02X\n" | |
65 | "Parameters: %02X %02X %02X %02X " | |
66 | "%02X %02X %02X %02X " | |
67 | "%02X %02X %02X %02X\n" | |
68 | "Modulus length: %d\n" | |
69 | "Modulus:\n", | |
70 | data[0], data[1], data[2], data[3], | |
71 | data[4], data[5], | |
72 | data[6], data[7], | |
73 | data[12], data[13], data[14], data[15], | |
74 | data[16], data[17], data[18], data[19], | |
75 | data[20], data[21], data[22], data[23], | |
76 | be32_to_cpu(*((__be32 *) (data + 24)))); | |
77 | ||
78 | for (i = 0; i < 256; i++) { | |
79 | str += sprintf(str, "%02X ", data[i + 28]); | |
80 | if ((i + 1) % 16 == 0) | |
81 | str += sprintf(str, "\n"); | |
82 | } | |
83 | out: | |
84 | rc = str - buf; | |
85 | return rc; | |
86 | } | |
1e3b73a9 | 87 | static DEVICE_ATTR_RO(pubek); |
000a07b0 | 88 | |
1e3b73a9 JG |
89 | static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr, |
90 | char *buf) | |
000a07b0 JG |
91 | { |
92 | cap_t cap; | |
93 | u8 digest[TPM_DIGEST_SIZE]; | |
94 | ssize_t rc; | |
95 | int i, j, num_pcrs; | |
96 | char *str = buf; | |
062807f2 | 97 | struct tpm_chip *chip = to_tpm_chip(dev); |
000a07b0 | 98 | |
062807f2 | 99 | rc = tpm_getcap(chip, TPM_CAP_PROP_PCR, &cap, |
c659af78 SB |
100 | "attempting to determine the number of PCRS", |
101 | sizeof(cap.num_pcrs)); | |
000a07b0 JG |
102 | if (rc) |
103 | return 0; | |
104 | ||
105 | num_pcrs = be32_to_cpu(cap.num_pcrs); | |
106 | for (i = 0; i < num_pcrs; i++) { | |
107 | rc = tpm_pcr_read_dev(chip, i, digest); | |
108 | if (rc) | |
109 | break; | |
110 | str += sprintf(str, "PCR-%02d: ", i); | |
111 | for (j = 0; j < TPM_DIGEST_SIZE; j++) | |
112 | str += sprintf(str, "%02X ", digest[j]); | |
113 | str += sprintf(str, "\n"); | |
114 | } | |
115 | return str - buf; | |
116 | } | |
1e3b73a9 | 117 | static DEVICE_ATTR_RO(pcrs); |
000a07b0 | 118 | |
1e3b73a9 JG |
119 | static ssize_t enabled_show(struct device *dev, struct device_attribute *attr, |
120 | char *buf) | |
000a07b0 JG |
121 | { |
122 | cap_t cap; | |
123 | ssize_t rc; | |
124 | ||
062807f2 | 125 | rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap, |
c659af78 SB |
126 | "attempting to determine the permanent enabled state", |
127 | sizeof(cap.perm_flags)); | |
000a07b0 JG |
128 | if (rc) |
129 | return 0; | |
130 | ||
131 | rc = sprintf(buf, "%d\n", !cap.perm_flags.disable); | |
132 | return rc; | |
133 | } | |
1e3b73a9 | 134 | static DEVICE_ATTR_RO(enabled); |
000a07b0 | 135 | |
5f64822d | 136 | static ssize_t active_show(struct device *dev, struct device_attribute *attr, |
1e3b73a9 | 137 | char *buf) |
000a07b0 JG |
138 | { |
139 | cap_t cap; | |
140 | ssize_t rc; | |
141 | ||
062807f2 | 142 | rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap, |
c659af78 SB |
143 | "attempting to determine the permanent active state", |
144 | sizeof(cap.perm_flags)); | |
000a07b0 JG |
145 | if (rc) |
146 | return 0; | |
147 | ||
148 | rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated); | |
149 | return rc; | |
150 | } | |
1e3b73a9 | 151 | static DEVICE_ATTR_RO(active); |
000a07b0 | 152 | |
1e3b73a9 JG |
153 | static ssize_t owned_show(struct device *dev, struct device_attribute *attr, |
154 | char *buf) | |
000a07b0 JG |
155 | { |
156 | cap_t cap; | |
157 | ssize_t rc; | |
158 | ||
062807f2 | 159 | rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_PROP_OWNER, &cap, |
c659af78 SB |
160 | "attempting to determine the owner state", |
161 | sizeof(cap.owned)); | |
000a07b0 JG |
162 | if (rc) |
163 | return 0; | |
164 | ||
165 | rc = sprintf(buf, "%d\n", cap.owned); | |
166 | return rc; | |
167 | } | |
1e3b73a9 | 168 | static DEVICE_ATTR_RO(owned); |
000a07b0 | 169 | |
1e3b73a9 JG |
170 | static ssize_t temp_deactivated_show(struct device *dev, |
171 | struct device_attribute *attr, char *buf) | |
000a07b0 JG |
172 | { |
173 | cap_t cap; | |
174 | ssize_t rc; | |
175 | ||
062807f2 | 176 | rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_VOL, &cap, |
c659af78 SB |
177 | "attempting to determine the temporary state", |
178 | sizeof(cap.stclear_flags)); | |
000a07b0 JG |
179 | if (rc) |
180 | return 0; | |
181 | ||
182 | rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated); | |
183 | return rc; | |
184 | } | |
1e3b73a9 | 185 | static DEVICE_ATTR_RO(temp_deactivated); |
000a07b0 | 186 | |
1e3b73a9 JG |
187 | static ssize_t caps_show(struct device *dev, struct device_attribute *attr, |
188 | char *buf) | |
000a07b0 | 189 | { |
062807f2 | 190 | struct tpm_chip *chip = to_tpm_chip(dev); |
000a07b0 JG |
191 | cap_t cap; |
192 | ssize_t rc; | |
193 | char *str = buf; | |
194 | ||
062807f2 | 195 | rc = tpm_getcap(chip, TPM_CAP_PROP_MANUFACTURER, &cap, |
c659af78 SB |
196 | "attempting to determine the manufacturer", |
197 | sizeof(cap.manufacturer_id)); | |
000a07b0 JG |
198 | if (rc) |
199 | return 0; | |
200 | str += sprintf(str, "Manufacturer: 0x%x\n", | |
201 | be32_to_cpu(cap.manufacturer_id)); | |
202 | ||
203 | /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */ | |
84fda152 | 204 | rc = tpm_getcap(chip, TPM_CAP_VERSION_1_2, &cap, |
c659af78 SB |
205 | "attempting to determine the 1.2 version", |
206 | sizeof(cap.tpm_version_1_2)); | |
000a07b0 JG |
207 | if (!rc) { |
208 | str += sprintf(str, | |
209 | "TCG version: %d.%d\nFirmware version: %d.%d\n", | |
210 | cap.tpm_version_1_2.Major, | |
211 | cap.tpm_version_1_2.Minor, | |
212 | cap.tpm_version_1_2.revMajor, | |
213 | cap.tpm_version_1_2.revMinor); | |
214 | } else { | |
215 | /* Otherwise just use TPM_STRUCT_VER */ | |
84fda152 | 216 | rc = tpm_getcap(chip, TPM_CAP_VERSION_1_1, &cap, |
c659af78 SB |
217 | "attempting to determine the 1.1 version", |
218 | sizeof(cap.tpm_version)); | |
000a07b0 JG |
219 | if (rc) |
220 | return 0; | |
221 | str += sprintf(str, | |
222 | "TCG version: %d.%d\nFirmware version: %d.%d\n", | |
223 | cap.tpm_version.Major, | |
224 | cap.tpm_version.Minor, | |
225 | cap.tpm_version.revMajor, | |
226 | cap.tpm_version.revMinor); | |
227 | } | |
228 | ||
229 | return str - buf; | |
230 | } | |
1e3b73a9 | 231 | static DEVICE_ATTR_RO(caps); |
000a07b0 | 232 | |
1e3b73a9 JG |
233 | static ssize_t cancel_store(struct device *dev, struct device_attribute *attr, |
234 | const char *buf, size_t count) | |
000a07b0 | 235 | { |
062807f2 | 236 | struct tpm_chip *chip = to_tpm_chip(dev); |
000a07b0 JG |
237 | if (chip == NULL) |
238 | return 0; | |
239 | ||
5f82e9f0 | 240 | chip->ops->cancel(chip); |
000a07b0 JG |
241 | return count; |
242 | } | |
1e3b73a9 | 243 | static DEVICE_ATTR_WO(cancel); |
000a07b0 | 244 | |
1e3b73a9 JG |
245 | static ssize_t durations_show(struct device *dev, struct device_attribute *attr, |
246 | char *buf) | |
000a07b0 | 247 | { |
062807f2 | 248 | struct tpm_chip *chip = to_tpm_chip(dev); |
000a07b0 | 249 | |
af782f33 | 250 | if (chip->duration[TPM_LONG] == 0) |
000a07b0 JG |
251 | return 0; |
252 | ||
253 | return sprintf(buf, "%d %d %d [%s]\n", | |
af782f33 CR |
254 | jiffies_to_usecs(chip->duration[TPM_SHORT]), |
255 | jiffies_to_usecs(chip->duration[TPM_MEDIUM]), | |
256 | jiffies_to_usecs(chip->duration[TPM_LONG]), | |
257 | chip->duration_adjusted | |
000a07b0 JG |
258 | ? "adjusted" : "original"); |
259 | } | |
1e3b73a9 | 260 | static DEVICE_ATTR_RO(durations); |
000a07b0 | 261 | |
1e3b73a9 JG |
262 | static ssize_t timeouts_show(struct device *dev, struct device_attribute *attr, |
263 | char *buf) | |
000a07b0 | 264 | { |
062807f2 | 265 | struct tpm_chip *chip = to_tpm_chip(dev); |
000a07b0 JG |
266 | |
267 | return sprintf(buf, "%d %d %d %d [%s]\n", | |
af782f33 CR |
268 | jiffies_to_usecs(chip->timeout_a), |
269 | jiffies_to_usecs(chip->timeout_b), | |
270 | jiffies_to_usecs(chip->timeout_c), | |
271 | jiffies_to_usecs(chip->timeout_d), | |
272 | chip->timeout_adjusted | |
000a07b0 JG |
273 | ? "adjusted" : "original"); |
274 | } | |
1e3b73a9 JG |
275 | static DEVICE_ATTR_RO(timeouts); |
276 | ||
277 | static struct attribute *tpm_dev_attrs[] = { | |
278 | &dev_attr_pubek.attr, | |
279 | &dev_attr_pcrs.attr, | |
280 | &dev_attr_enabled.attr, | |
281 | &dev_attr_active.attr, | |
282 | &dev_attr_owned.attr, | |
283 | &dev_attr_temp_deactivated.attr, | |
284 | &dev_attr_caps.attr, | |
285 | &dev_attr_cancel.attr, | |
286 | &dev_attr_durations.attr, | |
287 | &dev_attr_timeouts.attr, | |
288 | NULL, | |
289 | }; | |
290 | ||
291 | static const struct attribute_group tpm_dev_group = { | |
292 | .attrs = tpm_dev_attrs, | |
293 | }; | |
294 | ||
062807f2 | 295 | void tpm_sysfs_add_device(struct tpm_chip *chip) |
1e3b73a9 | 296 | { |
7518a21a JS |
297 | if (chip->flags & TPM_CHIP_FLAG_TPM2) |
298 | return; | |
299 | ||
062807f2 JG |
300 | /* The sysfs routines rely on an implicit tpm_try_get_ops, device_del |
301 | * is called before ops is null'd and the sysfs core synchronizes this | |
302 | * removal so that no callbacks are running or can run again | |
4e26195f | 303 | */ |
062807f2 JG |
304 | WARN_ON(chip->groups_cnt != 0); |
305 | chip->groups[chip->groups_cnt++] = &tpm_dev_group; | |
1e3b73a9 | 306 | } |