Re: [PATCH] tpm_tis: work around status register bug in STMicroelectronics TPM

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Apr 15, 2020 at 05:16:05PM -0700, Omar Sandoval wrote:
> On Wed, Apr 15, 2020 at 04:51:39PM -0700, James Bottomley wrote:
> > On Wed, 2020-04-15 at 15:45 -0700, Omar Sandoval wrote:
> > > From: Omar Sandoval <osandov@xxxxxx>
> > > 
> > > We've encountered a particular model of STMicroelectronics TPM that
> > > transiently returns a bad value in the status register. This causes
> > > the kernel to believe that the TPM is ready to receive a command when
> > > it actually isn't, which in turn causes the send to time out in
> > > get_burstcount(). In testing, reading the status register one extra
> > > time convinces the TPM to return a valid value.
> > 
> > Interesting, I've got a very early upgradeable nuvoton that seems to be
> > behaving like this.
> 
> I'll attach the userspace reproducer I used to figure this out. I'd be
> interested to see if it times out on your TPM, too. Note that it bangs
> on /dev/mem and assumes that the MMIO address is 0xfed40000. That seems
> to be the hard-coded address for x86 in the kernel, but just to be safe
> you might want to check `grep MSFT0101 /proc/iomem`.

Forgot to attach it, of course...
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

enum tis_access {
	TPM_ACCESS_VALID = 0x80,
	TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
	TPM_ACCESS_REQUEST_PENDING = 0x04,
	TPM_ACCESS_REQUEST_USE = 0x02,
};

enum tis_status {
	TPM_STS_VALID = 0x80,
	TPM_STS_COMMAND_READY = 0x40,
	TPM_STS_GO = 0x20,
	TPM_STS_DATA_AVAIL = 0x10,
	TPM_STS_DATA_EXPECT = 0x08,
};

#define TPM_ACCESS(l)                   (0x0000 | ((l) << 12))
#define TPM_STS(l)                      (0x0018 | ((l) << 12))

int main(void)
{
	int fd;
	void *map;
	volatile uint8_t *access;
	volatile uint8_t *sts;
	unsigned long long i;

	fd = open("/dev/mem", O_RDWR | O_DSYNC);
	if (fd == -1) {
		perror("open");
		return EXIT_FAILURE;
	}
	map = mmap(NULL, 0x5000, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
		   0xfed40000);
	if (map == MAP_FAILED) {
		perror("mmap");
		return EXIT_FAILURE;
	}
	access = (uint8_t *)map + TPM_ACCESS(0);
	sts = (uint8_t *)map + TPM_STS(0);

	i = 0;
	for (;;) {
		struct timespec stop, now;
		uint32_t burstcnt;
		uint8_t sts_read;

		*access = TPM_ACCESS_REQUEST_USE;
		while ((*access & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) !=
		       (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
			;

		sts_read = *sts;
#if 0
		if (sts_read == 0xff)
			sts_read = *sts;
#endif
		if (!(sts_read & TPM_STS_COMMAND_READY)) {
			*sts = TPM_STS_COMMAND_READY;
			while (!(*sts & TPM_STS_COMMAND_READY))
				;
		}

		clock_gettime(CLOCK_MONOTONIC, &stop);
		stop.tv_sec += 1;
		for (;;) {
			burstcnt = ((*(volatile uint32_t *)sts) >> 8) & 0xffff;
			if (burstcnt)
				break;
			clock_gettime(CLOCK_MONOTONIC, &now);
			if (now.tv_sec > stop.tv_sec ||
			    (now.tv_sec == stop.tv_sec &&
			     now.tv_nsec >= stop.tv_nsec)) {
				fprintf(stderr, "Timed out after %llu iterations\n", i);
				i = 0;
				break;
			}
		}

		*access = TPM_ACCESS_ACTIVE_LOCALITY;
		while ((*access & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) !=
		       TPM_ACCESS_VALID)
			;
		i++;
	}

	return EXIT_SUCCESS;
}

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux Kernel]     [Linux Kernel Hardening]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux