rjw | 1f88458 | 2022-01-06 17:20:42 +0800 | [diff] [blame^] | 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 | * |
| 9 | * Copyright (C) 2013 Obsidian Research Corp |
| 10 | * Jason Gunthorpe <jgunthorpe@obsidianresearch.com> |
| 11 | * |
| 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 | struct tpm_readpubek_out { |
| 24 | u8 algorithm[4]; |
| 25 | u8 encscheme[2]; |
| 26 | u8 sigscheme[2]; |
| 27 | __be32 paramsize; |
| 28 | u8 parameters[12]; |
| 29 | __be32 keysize; |
| 30 | u8 modulus[256]; |
| 31 | u8 checksum[20]; |
| 32 | } __packed; |
| 33 | |
| 34 | #define READ_PUBEK_RESULT_MIN_BODY_SIZE (28 + 256) |
| 35 | #define TPM_ORD_READPUBEK 124 |
| 36 | |
| 37 | static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, |
| 38 | char *buf) |
| 39 | { |
| 40 | struct tpm_buf tpm_buf; |
| 41 | struct tpm_readpubek_out *out; |
| 42 | int i; |
| 43 | char *str = buf; |
| 44 | struct tpm_chip *chip = to_tpm_chip(dev); |
| 45 | char anti_replay[20]; |
| 46 | |
| 47 | memset(&anti_replay, 0, sizeof(anti_replay)); |
| 48 | |
| 49 | if (tpm_try_get_ops(chip)) |
| 50 | return 0; |
| 51 | |
| 52 | if (tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK)) |
| 53 | goto out_ops; |
| 54 | |
| 55 | tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay)); |
| 56 | |
| 57 | if (tpm_transmit_cmd(chip, NULL, tpm_buf.data, PAGE_SIZE, |
| 58 | READ_PUBEK_RESULT_MIN_BODY_SIZE, 0, |
| 59 | "attempting to read the PUBEK")) |
| 60 | goto out_buf; |
| 61 | |
| 62 | out = (struct tpm_readpubek_out *)&tpm_buf.data[10]; |
| 63 | str += |
| 64 | sprintf(str, |
| 65 | "Algorithm: %02X %02X %02X %02X\n" |
| 66 | "Encscheme: %02X %02X\n" |
| 67 | "Sigscheme: %02X %02X\n" |
| 68 | "Parameters: %02X %02X %02X %02X " |
| 69 | "%02X %02X %02X %02X " |
| 70 | "%02X %02X %02X %02X\n" |
| 71 | "Modulus length: %d\n" |
| 72 | "Modulus:\n", |
| 73 | out->algorithm[0], out->algorithm[1], out->algorithm[2], |
| 74 | out->algorithm[3], |
| 75 | out->encscheme[0], out->encscheme[1], |
| 76 | out->sigscheme[0], out->sigscheme[1], |
| 77 | out->parameters[0], out->parameters[1], |
| 78 | out->parameters[2], out->parameters[3], |
| 79 | out->parameters[4], out->parameters[5], |
| 80 | out->parameters[6], out->parameters[7], |
| 81 | out->parameters[8], out->parameters[9], |
| 82 | out->parameters[10], out->parameters[11], |
| 83 | be32_to_cpu(out->keysize)); |
| 84 | |
| 85 | for (i = 0; i < 256; i++) { |
| 86 | str += sprintf(str, "%02X ", out->modulus[i]); |
| 87 | if ((i + 1) % 16 == 0) |
| 88 | str += sprintf(str, "\n"); |
| 89 | } |
| 90 | |
| 91 | out_buf: |
| 92 | tpm_buf_destroy(&tpm_buf); |
| 93 | out_ops: |
| 94 | tpm_put_ops(chip); |
| 95 | return str - buf; |
| 96 | } |
| 97 | static DEVICE_ATTR_RO(pubek); |
| 98 | |
| 99 | static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr, |
| 100 | char *buf) |
| 101 | { |
| 102 | cap_t cap; |
| 103 | u8 digest[TPM_DIGEST_SIZE]; |
| 104 | ssize_t rc; |
| 105 | int i, j, num_pcrs; |
| 106 | char *str = buf; |
| 107 | struct tpm_chip *chip = to_tpm_chip(dev); |
| 108 | |
| 109 | if (tpm_try_get_ops(chip)) |
| 110 | return 0; |
| 111 | |
| 112 | if (tpm_getcap(chip, TPM_CAP_PROP_PCR, &cap, |
| 113 | "attempting to determine the number of PCRS", |
| 114 | sizeof(cap.num_pcrs))) { |
| 115 | tpm_put_ops(chip); |
| 116 | return 0; |
| 117 | } |
| 118 | |
| 119 | num_pcrs = be32_to_cpu(cap.num_pcrs); |
| 120 | for (i = 0; i < num_pcrs; i++) { |
| 121 | rc = tpm_pcr_read_dev(chip, i, digest); |
| 122 | if (rc) |
| 123 | break; |
| 124 | str += sprintf(str, "PCR-%02d: ", i); |
| 125 | for (j = 0; j < TPM_DIGEST_SIZE; j++) |
| 126 | str += sprintf(str, "%02X ", digest[j]); |
| 127 | str += sprintf(str, "\n"); |
| 128 | } |
| 129 | tpm_put_ops(chip); |
| 130 | return str - buf; |
| 131 | } |
| 132 | static DEVICE_ATTR_RO(pcrs); |
| 133 | |
| 134 | static ssize_t enabled_show(struct device *dev, struct device_attribute *attr, |
| 135 | char *buf) |
| 136 | { |
| 137 | struct tpm_chip *chip = to_tpm_chip(dev); |
| 138 | ssize_t rc = 0; |
| 139 | cap_t cap; |
| 140 | |
| 141 | if (tpm_try_get_ops(chip)) |
| 142 | return 0; |
| 143 | |
| 144 | if (tpm_getcap(chip, TPM_CAP_FLAG_PERM, &cap, |
| 145 | "attempting to determine the permanent enabled state", |
| 146 | sizeof(cap.perm_flags))) |
| 147 | goto out_ops; |
| 148 | |
| 149 | rc = sprintf(buf, "%d\n", !cap.perm_flags.disable); |
| 150 | out_ops: |
| 151 | tpm_put_ops(chip); |
| 152 | return rc; |
| 153 | } |
| 154 | static DEVICE_ATTR_RO(enabled); |
| 155 | |
| 156 | static ssize_t active_show(struct device *dev, struct device_attribute *attr, |
| 157 | char *buf) |
| 158 | { |
| 159 | struct tpm_chip *chip = to_tpm_chip(dev); |
| 160 | ssize_t rc = 0; |
| 161 | cap_t cap; |
| 162 | |
| 163 | if (tpm_try_get_ops(chip)) |
| 164 | return 0; |
| 165 | |
| 166 | if (tpm_getcap(chip, TPM_CAP_FLAG_PERM, &cap, |
| 167 | "attempting to determine the permanent active state", |
| 168 | sizeof(cap.perm_flags))) |
| 169 | goto out_ops; |
| 170 | |
| 171 | rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated); |
| 172 | out_ops: |
| 173 | tpm_put_ops(chip); |
| 174 | return rc; |
| 175 | } |
| 176 | static DEVICE_ATTR_RO(active); |
| 177 | |
| 178 | static ssize_t owned_show(struct device *dev, struct device_attribute *attr, |
| 179 | char *buf) |
| 180 | { |
| 181 | struct tpm_chip *chip = to_tpm_chip(dev); |
| 182 | ssize_t rc = 0; |
| 183 | cap_t cap; |
| 184 | |
| 185 | if (tpm_try_get_ops(chip)) |
| 186 | return 0; |
| 187 | |
| 188 | if (tpm_getcap(to_tpm_chip(dev), TPM_CAP_PROP_OWNER, &cap, |
| 189 | "attempting to determine the owner state", |
| 190 | sizeof(cap.owned))) |
| 191 | goto out_ops; |
| 192 | |
| 193 | rc = sprintf(buf, "%d\n", cap.owned); |
| 194 | out_ops: |
| 195 | tpm_put_ops(chip); |
| 196 | return rc; |
| 197 | } |
| 198 | static DEVICE_ATTR_RO(owned); |
| 199 | |
| 200 | static ssize_t temp_deactivated_show(struct device *dev, |
| 201 | struct device_attribute *attr, char *buf) |
| 202 | { |
| 203 | struct tpm_chip *chip = to_tpm_chip(dev); |
| 204 | ssize_t rc = 0; |
| 205 | cap_t cap; |
| 206 | |
| 207 | if (tpm_try_get_ops(chip)) |
| 208 | return 0; |
| 209 | |
| 210 | if (tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_VOL, &cap, |
| 211 | "attempting to determine the temporary state", |
| 212 | sizeof(cap.stclear_flags))) |
| 213 | goto out_ops; |
| 214 | |
| 215 | rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated); |
| 216 | out_ops: |
| 217 | tpm_put_ops(chip); |
| 218 | return rc; |
| 219 | } |
| 220 | static DEVICE_ATTR_RO(temp_deactivated); |
| 221 | |
| 222 | static ssize_t caps_show(struct device *dev, struct device_attribute *attr, |
| 223 | char *buf) |
| 224 | { |
| 225 | struct tpm_chip *chip = to_tpm_chip(dev); |
| 226 | ssize_t rc = 0; |
| 227 | char *str = buf; |
| 228 | cap_t cap; |
| 229 | |
| 230 | if (tpm_try_get_ops(chip)) |
| 231 | return 0; |
| 232 | |
| 233 | if (tpm_getcap(chip, TPM_CAP_PROP_MANUFACTURER, &cap, |
| 234 | "attempting to determine the manufacturer", |
| 235 | sizeof(cap.manufacturer_id))) |
| 236 | goto out_ops; |
| 237 | |
| 238 | str += sprintf(str, "Manufacturer: 0x%x\n", |
| 239 | be32_to_cpu(cap.manufacturer_id)); |
| 240 | |
| 241 | /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */ |
| 242 | rc = tpm_getcap(chip, TPM_CAP_VERSION_1_2, &cap, |
| 243 | "attempting to determine the 1.2 version", |
| 244 | sizeof(cap.tpm_version_1_2)); |
| 245 | if (!rc) { |
| 246 | str += sprintf(str, |
| 247 | "TCG version: %d.%d\nFirmware version: %d.%d\n", |
| 248 | cap.tpm_version_1_2.Major, |
| 249 | cap.tpm_version_1_2.Minor, |
| 250 | cap.tpm_version_1_2.revMajor, |
| 251 | cap.tpm_version_1_2.revMinor); |
| 252 | } else { |
| 253 | /* Otherwise just use TPM_STRUCT_VER */ |
| 254 | if (tpm_getcap(chip, TPM_CAP_VERSION_1_1, &cap, |
| 255 | "attempting to determine the 1.1 version", |
| 256 | sizeof(cap.tpm_version))) |
| 257 | goto out_ops; |
| 258 | |
| 259 | str += sprintf(str, |
| 260 | "TCG version: %d.%d\nFirmware version: %d.%d\n", |
| 261 | cap.tpm_version.Major, |
| 262 | cap.tpm_version.Minor, |
| 263 | cap.tpm_version.revMajor, |
| 264 | cap.tpm_version.revMinor); |
| 265 | } |
| 266 | rc = str - buf; |
| 267 | out_ops: |
| 268 | tpm_put_ops(chip); |
| 269 | return rc; |
| 270 | } |
| 271 | static DEVICE_ATTR_RO(caps); |
| 272 | |
| 273 | static ssize_t cancel_store(struct device *dev, struct device_attribute *attr, |
| 274 | const char *buf, size_t count) |
| 275 | { |
| 276 | struct tpm_chip *chip = to_tpm_chip(dev); |
| 277 | |
| 278 | if (tpm_try_get_ops(chip)) |
| 279 | return 0; |
| 280 | |
| 281 | chip->ops->cancel(chip); |
| 282 | tpm_put_ops(chip); |
| 283 | return count; |
| 284 | } |
| 285 | static DEVICE_ATTR_WO(cancel); |
| 286 | |
| 287 | static ssize_t durations_show(struct device *dev, struct device_attribute *attr, |
| 288 | char *buf) |
| 289 | { |
| 290 | struct tpm_chip *chip = to_tpm_chip(dev); |
| 291 | |
| 292 | if (chip->duration[TPM_LONG] == 0) |
| 293 | return 0; |
| 294 | |
| 295 | return sprintf(buf, "%d %d %d [%s]\n", |
| 296 | jiffies_to_usecs(chip->duration[TPM_SHORT]), |
| 297 | jiffies_to_usecs(chip->duration[TPM_MEDIUM]), |
| 298 | jiffies_to_usecs(chip->duration[TPM_LONG]), |
| 299 | chip->duration_adjusted |
| 300 | ? "adjusted" : "original"); |
| 301 | } |
| 302 | static DEVICE_ATTR_RO(durations); |
| 303 | |
| 304 | static ssize_t timeouts_show(struct device *dev, struct device_attribute *attr, |
| 305 | char *buf) |
| 306 | { |
| 307 | struct tpm_chip *chip = to_tpm_chip(dev); |
| 308 | |
| 309 | return sprintf(buf, "%d %d %d %d [%s]\n", |
| 310 | jiffies_to_usecs(chip->timeout_a), |
| 311 | jiffies_to_usecs(chip->timeout_b), |
| 312 | jiffies_to_usecs(chip->timeout_c), |
| 313 | jiffies_to_usecs(chip->timeout_d), |
| 314 | chip->timeout_adjusted |
| 315 | ? "adjusted" : "original"); |
| 316 | } |
| 317 | static DEVICE_ATTR_RO(timeouts); |
| 318 | |
| 319 | static struct attribute *tpm_dev_attrs[] = { |
| 320 | &dev_attr_pubek.attr, |
| 321 | &dev_attr_pcrs.attr, |
| 322 | &dev_attr_enabled.attr, |
| 323 | &dev_attr_active.attr, |
| 324 | &dev_attr_owned.attr, |
| 325 | &dev_attr_temp_deactivated.attr, |
| 326 | &dev_attr_caps.attr, |
| 327 | &dev_attr_cancel.attr, |
| 328 | &dev_attr_durations.attr, |
| 329 | &dev_attr_timeouts.attr, |
| 330 | NULL, |
| 331 | }; |
| 332 | |
| 333 | static const struct attribute_group tpm_dev_group = { |
| 334 | .attrs = tpm_dev_attrs, |
| 335 | }; |
| 336 | |
| 337 | void tpm_sysfs_add_device(struct tpm_chip *chip) |
| 338 | { |
| 339 | /* XXX: If you wish to remove this restriction, you must first update |
| 340 | * tpm_sysfs to explicitly lock chip->ops. |
| 341 | */ |
| 342 | if (chip->flags & TPM_CHIP_FLAG_TPM2) |
| 343 | return; |
| 344 | |
| 345 | /* The sysfs routines rely on an implicit tpm_try_get_ops, device_del |
| 346 | * is called before ops is null'd and the sysfs core synchronizes this |
| 347 | * removal so that no callbacks are running or can run again |
| 348 | */ |
| 349 | WARN_ON(chip->groups_cnt != 0); |
| 350 | chip->groups[chip->groups_cnt++] = &tpm_dev_group; |
| 351 | } |