All,
I have been having trouble with a couple of different pieces of hardware
freezing during the tboot initialization and spent some time figuring
out what was wrong.
For reference, I was using tboot-1.8.1 as provided by the CentOS
distribution and having problems with:
1. Dell Latitude e6540 laptop
2. Dell Venue 7140
I have found an issue with how tboot handles writing/reading the status
registers while trying to handle both the TPM 1.2 and 2.0 cases. The
definition of the status register is always 4 bytes long(as defined in
"tboot/common/tpm.c", however TPM 1.2 devices still only have a 3 byte
register. This causes writes/reads to access memory that they should not
be accessing when communicating with 1.2 versioned TPMs.
I implemented a potential fix which has solved the problem for me on my
hardware. Basically, I just create 2 different structures for the status
register and use one or the other based on which type of TPM is present.
I have attached a patch which corrects the problem in a very
straightforward manner as a reference point.
Thanks,
Brent Collins
diff -Nurp tboot-1.8.1/tboot/common/tpm.c tboot-1.8.1.new/tboot/common/tpm.c
--- tboot-1.8.1/tboot/common/tpm.c 2015-03-06 18:21:45.766756609 -0600
+++ tboot-1.8.1.new/tboot/common/tpm.c 2015-03-09 15:48:39.868756544 -0500
@@ -45,6 +45,7 @@
#include <tpm.h>
#include <sha1.h>
+u8 g_tpm_family = 0;
__data struct tpm_if *g_tpm = NULL;
u16 tboot_alg_list[] = {TB_HALG_SHA1,
TB_HALG_SHA256};
@@ -77,7 +78,23 @@ typedef union {
/* TPM_STS_x */
#define TPM_REG_STS 0x18
typedef union {
- u8 _raw[4]; /* 3-byte reg */
+ u8 _raw[3]; /* 3-byte reg */
+ struct __packed {
+ u8 reserved1 : 1;
+ u8 response_retry : 1; /* WO, 1=re-send response */
+ u8 self_test_done : 1; /* RO, only for version 2 */
+ u8 expect : 1; /* RO, 1=more data for command expected */
+ u8 data_avail : 1; /* RO, 0=no more data for response */
+ u8 tpm_go : 1; /* WO, 1=execute sent command */
+ u8 command_ready : 1; /* RW, 1=TPM ready to receive new cmd */
+ u8 sts_valid : 1; /* RO, 1=data_avail and expect bits are
+ valid */
+ u16 burst_count : 16; /* RO, # read/writes bytes before wait */
+ };
+} tpm12_reg_sts_t;
+
+typedef union {
+ u8 _raw[4]; /* 4-byte reg */
struct __packed {
u8 reserved1 : 1;
u8 response_retry : 1; /* WO, 1=re-send response */
@@ -95,7 +112,11 @@ typedef union {
u8 tpm_family : 2;
u8 reserved2 : 4;
};
-} tpm_reg_sts_t;
+} tpm20_reg_sts_t;
+
+/* Global variables for TPM status register */
+tpm12_reg_sts_t g_reg_sts_12;
+tpm20_reg_sts_t g_reg_sts_20;
/* TPM_DATA_FIFO_x */
#define TPM_REG_DATA_FIFO 0x24
@@ -143,7 +164,6 @@ static bool tpm_wait_cmd_ready(uint32_t
{
uint32_t i;
tpm_reg_access_t reg_acc;
- tpm_reg_sts_t reg_sts;
/* ensure the contents of the ACCESS register are valid */
read_tpm_reg(locality, TPM_REG_ACCESS, ®_acc);
@@ -181,18 +201,11 @@ static bool tpm_wait_cmd_ready(uint32_t
#endif
i = 0;
do {
- /* write 1 to TPM_STS_x.commandReady to let TPM enter ready state */
- memset((void *)®_sts, 0, sizeof(reg_sts));
- reg_sts.command_ready = 1;
- write_tpm_reg(locality, TPM_REG_STS, ®_sts);
+ tpm_send_cmd_ready_status(locality);
cpu_relax();
-
/* then see if it has */
- read_tpm_reg(locality, TPM_REG_STS, ®_sts);
-#ifdef TPM_TRACE
- printk(TBOOT_INFO".");
-#endif
- if ( reg_sts.command_ready == 1 )
+
+ if ( tpm_check_cmd_ready_status(locality) )
break;
else
cpu_relax();
@@ -203,10 +216,7 @@ static bool tpm_wait_cmd_ready(uint32_t
#endif
if ( i > TPM_CMD_READY_TIME_OUT ) {
- printk(TBOOT_DETA"TPM: status reg content: %02x %02x %02x\n",
- (uint32_t)reg_sts._raw[0],
- (uint32_t)reg_sts._raw[1],
- (uint32_t)reg_sts._raw[2]);
+ tpm_print_status_register();
printk(TBOOT_INFO"TPM: tpm timeout for command_ready\n");
goto RelinquishControl;
}
@@ -228,7 +238,6 @@ bool tpm_submit_cmd(u32 locality, u8 *in
u32 i, rsp_size, offset;
u16 row_size;
tpm_reg_access_t reg_acc;
- tpm_reg_sts_t reg_sts;
bool ret = true;
if ( locality >= TPM_NR_LOCALITIES ) {
@@ -264,9 +273,8 @@ bool tpm_submit_cmd(u32 locality, u8 *in
do {
i = 0;
do {
- read_tpm_reg(locality, TPM_REG_STS, ®_sts);
/* find out how many bytes the TPM can accept in a row */
- row_size = reg_sts.burst_count;
+ row_size = tpm_get_burst_count(locality);
if ( row_size > 0 )
break;
else
@@ -286,11 +294,7 @@ bool tpm_submit_cmd(u32 locality, u8 *in
i = 0;
do {
- read_tpm_reg(locality,TPM_REG_STS, ®_sts);
-#ifdef TPM_TRACE
- printk(TBOOT_INFO"Wait on Expect = 0, Status register %02x\n", reg_sts._raw[0]);
-#endif
- if ( reg_sts.sts_valid == 1 && reg_sts.expect == 0 )
+ if ( tpm_check_expect_status(locality) )
break;
else
cpu_relax();
@@ -303,18 +307,12 @@ bool tpm_submit_cmd(u32 locality, u8 *in
}
/* command has been written to the TPM, it is time to execute it. */
- memset(®_sts, 0, sizeof(reg_sts));
- reg_sts.tpm_go = 1;
- write_tpm_reg(locality, TPM_REG_STS, ®_sts);
+ tpm_execute_cmd(locality);
/* check for data available */
i = 0;
do {
- read_tpm_reg(locality,TPM_REG_STS, ®_sts);
-#ifdef TPM_TRACE
- printk(TBOOT_INFO"Waiting for DA Flag, Status register %02x\n", reg_sts._raw[0]);
-#endif
- if ( reg_sts.sts_valid == 1 && reg_sts.data_avail == 1 )
+ if ( tpm_check_da_status(locality) )
break;
else
cpu_relax();
@@ -332,8 +330,7 @@ bool tpm_submit_cmd(u32 locality, u8 *in
/* find out how many bytes the TPM returned in a row */
i = 0;
do {
- read_tpm_reg(locality, TPM_REG_STS, ®_sts);
- row_size = reg_sts.burst_count;
+ row_size = tpm_get_burst_count(locality);
if ( row_size > 0 )
break;
else
@@ -376,9 +373,7 @@ bool tpm_submit_cmd(u32 locality, u8 *in
}
#endif
- memset(®_sts, 0, sizeof(reg_sts));
- reg_sts.command_ready = 1;
- write_tpm_reg(locality, TPM_REG_STS, ®_sts);
+ tpm_send_cmd_ready_status(locality);
RelinquishControl:
/* deactivate current locality */
@@ -435,24 +430,19 @@ bool prepare_tpm(void)
bool tpm_detect(void)
{
- tpm_reg_sts_t reg_sts;
-
g_tpm = &tpm_12_if; /* Don't leave g_tpm as NULL*/
if ( !tpm_validate_locality(0) ) {
printk(TBOOT_ERR"TPM: Locality 0 is not open\n");
return false;
}
- /* get TPM family from TPM status register */
- memset((void *)®_sts, 0, sizeof(reg_sts));
- /* write_tpm_reg(0, TPM_REG_STS, ®_sts); */
- /* read_tpm_reg(0, TPM_REG_STS, ®_sts); */
+ /* determine TPM family from command check */
if ( g_tpm->check() )
- reg_sts.tpm_family = 0;
+ g_tpm_family = 0;
else
- reg_sts.tpm_family = 1;
- printk(TBOOT_INFO"TPM: TPM Family 0x%d\n", reg_sts.tpm_family);
- if (reg_sts.tpm_family == 1)
+ g_tpm_family = 1;
+ printk(TBOOT_INFO"TPM: TPM Family 0x%d\n", g_tpm_family);
+ if (g_tpm_family == 1)
g_tpm = &tpm_20_if;
else
g_tpm = &tpm_12_if;
@@ -479,6 +469,164 @@ void tpm_print(struct tpm_if *ti)
ti->timeout.timeout_d);
}
+void tpm_send_cmd_ready_status(uint32_t locality)
+{
+ tpm12_reg_sts_t reg_sts_12;
+ tpm20_reg_sts_t reg_sts_20;
+
+ /* write 1 to TPM_STS_x.commandReady to let TPM enter ready state */
+ if ( g_tpm_family == 0 )
+ {
+ memset((void *)®_sts_12, 0, sizeof(reg_sts_12));
+ reg_sts_12.command_ready = 1;
+ write_tpm_reg(locality, TPM_REG_STS, ®_sts_12);
+ }
+ else
+ {
+ memset((void *)®_sts_20, 0, sizeof(reg_sts_20));
+ reg_sts_20.command_ready = 1;
+ write_tpm_reg(locality, TPM_REG_STS, ®_sts_20);
+ }
+}
+
+bool tpm_check_cmd_ready_status(uint32_t locality)
+{
+ u8 commandReadyValue;
+
+ if ( g_tpm_family == 0 )
+ read_tpm_reg(locality, TPM_REG_STS, &g_reg_sts_12);
+ else
+ read_tpm_reg(locality, TPM_REG_STS, &g_reg_sts_20);
+
+#ifdef TPM_TRACE
+ printk(TBOOT_INFO".");
+#endif
+
+ if ( g_tpm_family == 0 )
+ commandReadyValue = g_reg_sts_12.command_ready;
+ else
+ commandReadyValue = g_reg_sts_20.command_ready;
+
+ if (commandReadyValue == 1)
+ return true;
+ else
+ return false;
+}
+
+void tpm_print_status_register(void)
+{
+ if ( g_tpm_family == 0 )
+ {
+ printk(TBOOT_DETA"TPM: status reg content: %02x %02x %02x\n",
+ (uint32_t)g_reg_sts_12._raw[0],
+ (uint32_t)g_reg_sts_12._raw[1],
+ (uint32_t)g_reg_sts_12._raw[2]);
+ }
+ else
+ {
+ printk(TBOOT_DETA"TPM: status reg content: %02x %02x %02x %02x\n",
+ (uint32_t)g_reg_sts_20._raw[0],
+ (uint32_t)g_reg_sts_20._raw[1],
+ (uint32_t)g_reg_sts_20._raw[2],
+ (uint32_t)g_reg_sts_20._raw[3]);
+ }
+}
+
+u16 tpm_get_burst_count(uint32_t locality)
+{
+ u16 burstCount;
+
+ if ( g_tpm_family == 0 )
+ {
+ read_tpm_reg(locality, TPM_REG_STS, &g_reg_sts_12);
+ burstCount = g_reg_sts_12.burst_count;
+ }
+ else
+ {
+ read_tpm_reg(locality, TPM_REG_STS, &g_reg_sts_20);
+ burstCount = g_reg_sts_20.burst_count;
+ }
+
+ return burstCount;
+}
+
+bool tpm_check_expect_status(uint32_t locality)
+{
+ bool returnValue = false;
+
+ if ( g_tpm_family == 0 )
+ {
+ read_tpm_reg(locality,TPM_REG_STS, &g_reg_sts_12);
+
+#ifdef TPM_TRACE
+ printk(TBOOT_INFO"Wait on Expect = 0, Status register %02x\n", g_reg_sts_12._raw[0]);
+#endif
+
+ if ( g_reg_sts_12.sts_valid == 1 && g_reg_sts_12.expect == 0 )
+ returnValue = true;
+ }
+ else
+ {
+ read_tpm_reg(locality,TPM_REG_STS, &g_reg_sts_20);
+
+#ifdef TPM_TRACE
+ printk(TBOOT_INFO"Wait on Expect = 0, Status register %02x\n", g_reg_sts_20._raw[0]);
+#endif
+
+ if ( g_reg_sts_20.sts_valid == 1 && g_reg_sts_20.expect == 0 )
+ returnValue = true;
+ }
+
+ return returnValue;
+}
+
+bool tpm_check_da_status(uint32_t locality)
+{
+ bool returnValue = false;
+
+ if ( g_tpm_family == 0 )
+ {
+ read_tpm_reg(locality,TPM_REG_STS, &g_reg_sts_12);
+
+#ifdef TPM_TRACE
+ printk(TBOOT_INFO"Waiting for DA Flag, Status register %02x\n", reg_sts_12._raw[0]);
+#endif
+
+ if ( g_reg_sts_12.sts_valid == 1 && g_reg_sts_12.data_avail == 1 )
+ returnValue = true;
+ }
+ else
+ {
+ read_tpm_reg(locality,TPM_REG_STS, &g_reg_sts_20);
+
+#ifdef TPM_TRACE
+ printk(TBOOT_INFO"Waiting for DA Flag, Status register %02x\n", reg_sts_20._raw[0]);
+#endif
+
+ if ( g_reg_sts_20.sts_valid == 1 && g_reg_sts_20.data_avail == 1 )
+ returnValue = true;
+ }
+
+ return returnValue;
+}
+
+
+void tpm_execute_cmd(uint32_t locality)
+{
+ if ( g_tpm_family == 0 )
+ {
+ memset(&g_reg_sts_12, 0, sizeof(g_reg_sts_12));
+ g_reg_sts_12.tpm_go = 1;
+ write_tpm_reg(locality, TPM_REG_STS, &g_reg_sts_12);
+ }
+ else
+ {
+ memset(&g_reg_sts_20, 0, sizeof(g_reg_sts_20));
+ g_reg_sts_20.tpm_go = 1;
+ write_tpm_reg(locality, TPM_REG_STS, &g_reg_sts_20);
+ }
+}
+
/*
* Local variables:
* mode: C
diff -Nurp tboot-1.8.1/tboot/include/tpm.h tboot-1.8.1.new/tboot/include/tpm.h
--- tboot-1.8.1/tboot/include/tpm.h 2015-03-06 18:21:45.815756781 -0600
+++ tboot-1.8.1.new/tboot/include/tpm.h 2015-03-09 11:46:36.030880915 -0500
@@ -273,6 +273,14 @@ extern void tpm_print(struct tpm_if *ti)
extern bool tpm_submit_cmd(u32 locality, u8 *in, u32 in_size,
u8 *out, u32 *out_size);
+extern void tpm_send_cmd_ready_status(uint32_t locality);
+extern bool tpm_check_cmd_ready_status(uint32_t locality);
+extern void tpm_print_status_register(void);
+extern u16 tpm_get_burst_count(uint32_t locality);
+extern bool tpm_check_expect_status(uint32_t locality);
+extern bool tpm_check_da_status(uint32_t locality);
+extern void tpm_execute_cmd(uint32_t locality);
+
//#define TPM_UNIT_TEST 1
------------------------------------------------------------------------------
Dive into the World of Parallel Programming The Go Parallel Website, sponsored
by Intel and developed in partnership with Slashdot Media, is your hub for all
things parallel software development, from weekly thought leadership blogs to
news, videos, case studies, tutorials and more. Take a look and join the
conversation now. http://goparallel.sourceforge.net/
_______________________________________________
tboot-devel mailing list
tboot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/tboot-devel