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, &reg_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 *)&reg_sts, 0, sizeof(reg_sts));
-        reg_sts.command_ready = 1;
-        write_tpm_reg(locality, TPM_REG_STS, &reg_sts);
+        tpm_send_cmd_ready_status(locality);
         cpu_relax();
-
         /* then see if it has */
-        read_tpm_reg(locality, TPM_REG_STS, &reg_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, &reg_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, &reg_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(&reg_sts, 0,  sizeof(reg_sts));
-    reg_sts.tpm_go = 1;
-    write_tpm_reg(locality, TPM_REG_STS, &reg_sts);
+    tpm_execute_cmd(locality);
 
     /* check for data available */
     i = 0;
     do {
-        read_tpm_reg(locality,TPM_REG_STS, &reg_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, &reg_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(&reg_sts, 0, sizeof(reg_sts));
-    reg_sts.command_ready = 1;
-    write_tpm_reg(locality, TPM_REG_STS, &reg_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 *)&reg_sts, 0, sizeof(reg_sts));
-    /* write_tpm_reg(0, TPM_REG_STS, &reg_sts); */
-    /* read_tpm_reg(0, TPM_REG_STS, &reg_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 *)&reg_sts_12, 0, sizeof(reg_sts_12));
+        reg_sts_12.command_ready = 1;
+        write_tpm_reg(locality, TPM_REG_STS, &reg_sts_12);
+    }
+    else
+    {
+        memset((void *)&reg_sts_20, 0, sizeof(reg_sts_20));
+        reg_sts_20.command_ready = 1;
+        write_tpm_reg(locality, TPM_REG_STS, &reg_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

Reply via email to