diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/tpm/tpm_tis.c | 77 |
1 files changed, 73 insertions, 4 deletions
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 47517e49d2be..ed97cfed30ea 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c | |||
@@ -306,11 +306,10 @@ MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)"); | |||
306 | * tpm.c can skip polling for the data to be available as the interrupt is | 306 | * tpm.c can skip polling for the data to be available as the interrupt is |
307 | * waited for here | 307 | * waited for here |
308 | */ | 308 | */ |
309 | static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) | 309 | static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) |
310 | { | 310 | { |
311 | int rc, status, burstcnt; | 311 | int rc, status, burstcnt; |
312 | size_t count = 0; | 312 | size_t count = 0; |
313 | u32 ordinal; | ||
314 | 313 | ||
315 | if (request_locality(chip, 0) < 0) | 314 | if (request_locality(chip, 0) < 0) |
316 | return -EBUSY; | 315 | return -EBUSY; |
@@ -345,8 +344,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) | |||
345 | 344 | ||
346 | /* write last byte */ | 345 | /* write last byte */ |
347 | iowrite8(buf[count], | 346 | iowrite8(buf[count], |
348 | chip->vendor.iobase + | 347 | chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality)); |
349 | TPM_DATA_FIFO(chip->vendor.locality)); | ||
350 | wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, | 348 | wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, |
351 | &chip->vendor.int_queue); | 349 | &chip->vendor.int_queue); |
352 | status = tpm_tis_status(chip); | 350 | status = tpm_tis_status(chip); |
@@ -355,6 +353,28 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) | |||
355 | goto out_err; | 353 | goto out_err; |
356 | } | 354 | } |
357 | 355 | ||
356 | return 0; | ||
357 | |||
358 | out_err: | ||
359 | tpm_tis_ready(chip); | ||
360 | release_locality(chip, chip->vendor.locality, 0); | ||
361 | return rc; | ||
362 | } | ||
363 | |||
364 | /* | ||
365 | * If interrupts are used (signaled by an irq set in the vendor structure) | ||
366 | * tpm.c can skip polling for the data to be available as the interrupt is | ||
367 | * waited for here | ||
368 | */ | ||
369 | static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) | ||
370 | { | ||
371 | int rc; | ||
372 | u32 ordinal; | ||
373 | |||
374 | rc = tpm_tis_send_data(chip, buf, len); | ||
375 | if (rc < 0) | ||
376 | return rc; | ||
377 | |||
358 | /* go and do it */ | 378 | /* go and do it */ |
359 | iowrite8(TPM_STS_GO, | 379 | iowrite8(TPM_STS_GO, |
360 | chip->vendor.iobase + TPM_STS(chip->vendor.locality)); | 380 | chip->vendor.iobase + TPM_STS(chip->vendor.locality)); |
@@ -376,6 +396,47 @@ out_err: | |||
376 | return rc; | 396 | return rc; |
377 | } | 397 | } |
378 | 398 | ||
399 | /* | ||
400 | * Early probing for iTPM with STS_DATA_EXPECT flaw. | ||
401 | * Try sending command without itpm flag set and if that | ||
402 | * fails, repeat with itpm flag set. | ||
403 | */ | ||
404 | static int probe_itpm(struct tpm_chip *chip) | ||
405 | { | ||
406 | int rc = 0; | ||
407 | u8 cmd_getticks[] = { | ||
408 | 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a, | ||
409 | 0x00, 0x00, 0x00, 0xf1 | ||
410 | }; | ||
411 | size_t len = sizeof(cmd_getticks); | ||
412 | int rem_itpm = itpm; | ||
413 | |||
414 | itpm = 0; | ||
415 | |||
416 | rc = tpm_tis_send_data(chip, cmd_getticks, len); | ||
417 | if (rc == 0) | ||
418 | goto out; | ||
419 | |||
420 | tpm_tis_ready(chip); | ||
421 | release_locality(chip, chip->vendor.locality, 0); | ||
422 | |||
423 | itpm = 1; | ||
424 | |||
425 | rc = tpm_tis_send_data(chip, cmd_getticks, len); | ||
426 | if (rc == 0) { | ||
427 | dev_info(chip->dev, "Detected an iTPM.\n"); | ||
428 | rc = 1; | ||
429 | } else | ||
430 | rc = -EFAULT; | ||
431 | |||
432 | out: | ||
433 | itpm = rem_itpm; | ||
434 | tpm_tis_ready(chip); | ||
435 | release_locality(chip, chip->vendor.locality, 0); | ||
436 | |||
437 | return rc; | ||
438 | } | ||
439 | |||
379 | static const struct file_operations tis_ops = { | 440 | static const struct file_operations tis_ops = { |
380 | .owner = THIS_MODULE, | 441 | .owner = THIS_MODULE, |
381 | .llseek = no_llseek, | 442 | .llseek = no_llseek, |
@@ -515,6 +576,14 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, | |||
515 | "1.2 TPM (device-id 0x%X, rev-id %d)\n", | 576 | "1.2 TPM (device-id 0x%X, rev-id %d)\n", |
516 | vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); | 577 | vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); |
517 | 578 | ||
579 | if (!itpm) { | ||
580 | itpm = probe_itpm(chip); | ||
581 | if (itpm < 0) { | ||
582 | rc = -ENODEV; | ||
583 | goto out_err; | ||
584 | } | ||
585 | } | ||
586 | |||
518 | if (itpm) | 587 | if (itpm) |
519 | dev_info(dev, "Intel iTPM workaround enabled\n"); | 588 | dev_info(dev, "Intel iTPM workaround enabled\n"); |
520 | 589 | ||