This patch adds an optional test suite (CONFIG_TIS_TEST) for the TIS interface
to SeaBIOS. If compiled into the BIOS, it can be invoked through the
TPM-specific menu item 8.

1. Enable TPM
2. Disable TPM
3. Activate TPM
4. Deactivate TPM
5. Clear ownership
6. Allow installation of owner
7. Prevent installation of owner
8. TIS test

I would like to see this code become part of the SeaBIOS code base
but I understand that a test suite in a BIOS is not the right place...
Nevertheless, for testing the TIS emulation in Qemu, I am posting it here.
The test suite fills up the available BIOS space from 92.6% at the previous
patch to 98.4%.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

---
 Makefile       |    2 
 src/Kconfig    |    7 
 src/tcgbios.c  |   34 +-
 src/tis_test.c |  846 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/tis_test.h |   52 +++
 5 files changed, 932 insertions(+), 9 deletions(-)

Index: seabios/Makefile
===================================================================
--- seabios.orig/Makefile
+++ seabios/Makefile
@@ -20,7 +20,7 @@ SRC16=$(SRCBOTH) system.c disk.c font.c
 SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \
       acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \
       lzmadecode.c bootsplash.c jpeg.c usb-hub.c paravirt.c dev-i440fx.c \
-      pci_region.c tcgbios.c tpm_drivers.c
+      pci_region.c tcgbios.c tpm_drivers.c tis_test.c
 SRC32SEG=util.c output.c pci.c pcibios.c apm.c stacks.c
 
 cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \
Index: seabios/src/tcgbios.c
===================================================================
--- seabios.orig/src/tcgbios.c
+++ seabios/src/tcgbios.c
@@ -33,6 +33,9 @@
 #include "acpi.h"  // RSDP_SIGNATURE, rsdt_descriptor
 #include "smbios.h" // smbios_entry_point
 
+#ifdef CONFIG_TIS_TEST
+#include "tis_test.h"
+#endif
 
 //#define DEBUG_TCGBIOS
 
@@ -927,6 +930,10 @@ pass_through_to_tpm(struct pttti *pttti,
     iovec[1].data   = NULL;
     iovec[1].length = 0;
 
+#ifdef CONFIG_TIS_TEST
+    locty = pttti->reserved;
+#endif
+
     rc = transmit(locty, iovec, pttto->tpmopout, &resbuflen);
     if (rc)
         goto err_exit;
@@ -2022,26 +2029,29 @@ err_exit:
 }
 
 
-static void
+static int
 show_tpm_state(void)
 {
+    int state = 0;
     struct tpm_permanent_flags pf;
     u8 has_owner;
 
     if (read_permanent_flags((char *)&pf, sizeof(pf)) ||
         read_has_owner(&has_owner))
-        return;
+        return ~0;
 
     printf("TPM is ");
 
-    if (pf.flags[PERM_FLAG_IDX_DISABLE])
+    if (pf.flags[PERM_FLAG_IDX_DISABLE]) {
         printf("disabled");
-    else
+        state |= 1 << PERM_FLAG_IDX_DISABLE;
+    } else
         printf("enabled");
 
-    if (pf.flags[PERM_FLAG_IDX_DEACTIVATED])
+    if (pf.flags[PERM_FLAG_IDX_DEACTIVATED]) {
         printf(", deactivated");
-    else
+        state |= 1 << PERM_FLAG_IDX_DEACTIVATED;
+    } else
         printf(", active");
 
     if (has_owner)
@@ -2054,6 +2064,7 @@ show_tpm_state(void)
             printf("and an owner cannot be installed.\n");
     }
 
+    return state;
 }
 
 
@@ -2152,7 +2163,7 @@ void
 tcpa_menu(void)
 {
     int show_menu = 1;
-    int scan_code;
+    int scan_code, state;
     u32 rc;
     tpm_bios_cfg_t cfg = {
         .revision = 1,
@@ -2172,9 +2183,12 @@ tcpa_menu(void)
                    "5. Clear ownership\n"
                    "6. Allow installation of owner\n"
                    "7. Prevent installation of owner\n"
+#ifdef CONFIG_TIS_TEST
+                   "8. TIS test\n"
+#endif
                    "Escape for previous menu.\n");
             show_menu = 0;
-            show_tpm_state();
+            state = show_tpm_state();
         }
 
         cfg.op = 0;
@@ -2194,6 +2208,10 @@ tcpa_menu(void)
         case 7 ... 8:
             cfg.op = scan_code + 1;
             break;
+#ifdef CONFIG_TIS_TEST
+        case 9:
+            tis_test(state);
+#endif
         default:
             continue;
         }
Index: seabios/src/tis_test.c
===================================================================
--- /dev/null
+++ seabios/src/tis_test.c
@@ -0,0 +1,846 @@
+/*
+ *  TIS interface tests
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ * Copyright (C) IBM Corporation, 2006,2011
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ */
+#include "config.h"
+
+#include "types.h"
+#include "util.h" /* read{b,l}, write{b,l} */
+#include "tcgbios.h"
+#include "tpm_drivers.h"
+#include "tis_test.h"
+
+#ifdef CONFIG_TIS_TEST
+
+static int
+tis_check_reg(u32 reg, u8 locty, u32 mask, u32 exp, u32 timeout)
+{
+    u32 val;
+    u32 now = 0;
+
+    while (now <= timeout) {
+        val = readl(TIS_REG(locty, reg));
+
+        if ((val & mask) == exp)
+            return 0;
+
+        mssleep(10);
+
+        now += 10;
+    }
+
+    return 1;
+}
+
+static int
+tis_check_status(u8 locty, u32 mask, u32 exp, u32 timeout)
+{
+    return tis_check_reg(TIS_REG_STS, locty, mask, exp, timeout);
+}
+
+static int
+tis_check_access(u8 locty, u32 mask, u32 exp, u32 timeout)
+{
+    return tis_check_reg(TIS_REG_ACCESS, locty, mask, exp, timeout);
+}
+
+static void
+wait_for_keystroke(void)
+{
+    printf("Press escape to continue.\n");
+    while (1) {
+        switch (get_keystroke(1000)) {
+        case 1:
+            return;
+        }
+    }
+}
+
+static void
+check_access(u8 locty, u8 mask, u8 exp,
+             const char *succ_msg,
+             const char *fail_msg)
+{
+    u8 acc = readb(TIS_REG(locty, TIS_REG_ACCESS));
+    u8 res = acc & mask;
+
+    if (res == exp)
+        printf("%s  -- access reg : %02x\n", succ_msg, acc);
+    else {
+        printf("ERROR: %s\n"
+               "value = %02x, mask = %02x, exp = %02x, actual = %02x, "
+               "bad = %02x\n",
+               fail_msg, acc, mask, exp, res, res ^ exp );
+        wait_for_keystroke();
+    }
+}
+
+static void
+tis_activate_locality(u8 new_locty)
+{
+    int locty;
+
+    /* release locality in use top-downwards */
+    for (locty = 4; locty >= 0; locty--)
+        writeb(TIS_REG(locty, TIS_REG_ACCESS),
+               TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_BEEN_SEIZED);
+
+    printf("Requesting access to locality %d\n", new_locty);
+
+    writeb(TIS_REG(new_locty, TIS_REG_ACCESS),
+           TIS_ACCESS_REQUEST_USE);
+
+    check_access(new_locty,
+                 TIS_ACCESS_ACTIVE_LOCALITY,
+                 TIS_ACCESS_ACTIVE_LOCALITY,
+                 "Switch to the locality",
+                 "Could not get the locality");
+}
+
+/*
+ * Test getting access to localities using the
+ * TIS_ACCESS_ACTIVATE_LOCALITY flag
+ * The test ends with locality 0 being active.
+ */
+static void
+test_active_locality_flag(void)
+{
+    printf("Requesting access to locality 0\n");
+
+    writeb(TIS_REG(0, TIS_REG_ACCESS),
+           TIS_ACCESS_REQUEST_USE|TIS_ACCESS_BEEN_SEIZED);
+
+    check_access(0,
+                 TIS_ACCESS_TPM_ESTABLISHMENT,
+                 TIS_ACCESS_TPM_ESTABLISHMENT,
+                 "Valid TPM Establishment flag in locality 0",
+                 "Invalid TPM Establishment flag in locality 0");
+
+    check_access(0,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY | TIS_ACCESS_BEEN_SEIZED,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY,
+                 "Got locality 0",
+                 "Could not get locality 0");
+
+    printf("Giving up locality 0\n");
+    writeb(TIS_REG(0, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY);
+
+    check_access(0,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY,
+                 0x80,
+                 "Successfully released locality 0",
+                 "Still have locality 0");
+
+    printf("Requesting access to locality 1\n");
+
+    writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE);
+
+    check_access(1,
+                 TIS_ACCESS_ACTIVE_LOCALITY,
+                 TIS_ACCESS_ACTIVE_LOCALITY,
+                 "Got locality 1",
+                 "Could not get locality 1");
+
+    printf("Requesting access to locality 2; seizing locality.\n");
+
+    writeb(TIS_REG(2, TIS_REG_ACCESS), TIS_ACCESS_SEIZE);
+
+    check_access(1,
+                 0x80 | TIS_ACCESS_BEEN_SEIZED | TIS_ACCESS_ACTIVE_LOCALITY,
+                 0x80 | TIS_ACCESS_BEEN_SEIZED,
+                 "Access to locality 1 has been seized",
+                 "Access to locality 1 has not been properly seized");
+
+    check_access(2,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY,
+                 "Got locality 2",
+                 "Could not get locality 2");
+
+    printf("Setting request to use by locality 1\n");
+
+    writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE);
+
+    check_access(2,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY| TIS_ACCESS_PENDING_REQUEST,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY| TIS_ACCESS_PENDING_REQUEST,
+                 "Locality 2 sees pending request by locality 1",
+                 "Locality 2 does not see pending request by locality 1");
+
+    check_access(1,
+                 0x80 | TIS_ACCESS_REQUEST_USE | TIS_ACCESS_ACTIVE_LOCALITY,
+                 0x80 | TIS_ACCESS_REQUEST_USE,
+                 "Locality 1 has request pending",
+                 "Locality 1 does not have request pending");
+
+    printf("Setting request to use by locality 0\n");
+
+    writeb(TIS_REG(0, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE);
+
+    check_access(0,
+                 0x80 | TIS_ACCESS_REQUEST_USE | TIS_ACCESS_ACTIVE_LOCALITY,
+                 0x80 | TIS_ACCESS_REQUEST_USE,
+                 "Locality 0 has request pending",
+                 "Locality 0 does not have request pending");
+
+    check_access(2,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST,
+                 "Locality 2 sees pending request by locality 1",
+                 "Locality 2 does not see pending request by locality 1");
+
+    printf("Setting request to use by locality 3\n");
+
+    writeb(TIS_REG(3, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE);
+
+    check_access(3,
+                 0x80 | TIS_ACCESS_REQUEST_USE | TIS_ACCESS_ACTIVE_LOCALITY,
+                 0x80 | TIS_ACCESS_REQUEST_USE,
+                 "Locality 3 has request pending",
+                 "Locality 3 does not have request pending");
+
+    check_access(2,
+                 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST,
+                 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST,
+                 "Locality 2 sees pending request by localities 0, 1 & 3",
+                 "Locality 2 does not see pending request by localities "
+                 "0, 1 & 3");
+
+    printf("Releasing use by locality 2\n");
+
+    writeb(TIS_REG(2, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY);
+
+    check_access(2,
+                 0x80 | TIS_ACCESS_PENDING_REQUEST|TIS_ACCESS_ACTIVE_LOCALITY,
+                 0x80 | TIS_ACCESS_PENDING_REQUEST,
+                 "Locality 2 successfully relased usage",
+                 "Locality 2 did not completely release usage");
+
+    check_access(3,
+                 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST,
+                 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST,
+                 "Locality 3 is active and sees pending request by localities "
+                 "0 & 1",
+                 "Locality 3 does not see proper flags");
+
+    check_access(0,
+                 0x80|TIS_ACCESS_REQUEST_USE|TIS_ACCESS_PENDING_REQUEST|
+                      TIS_ACCESS_ACTIVE_LOCALITY,
+                 0x80|TIS_ACCESS_REQUEST_USE|TIS_ACCESS_PENDING_REQUEST,
+                 "Locality 0 has request pending and sees other pending request",
+                 "Locality 0 does not have request pending");
+
+    printf("Locality 1 drops request\n");
+
+    writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY);
+
+    printf("Releasing use by locality 3\n");
+
+    writeb(TIS_REG(3, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY);
+
+    check_access(3,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST,
+                 0x80,
+                 "Locality 3 successfully relased usage",
+                 "Locality 3 did not completely release usage");
+
+    check_access(1,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST,
+                 0x80,
+                 "Locality 1 successfully relased usage",
+                 "Locality 1 did not completely release usage");
+
+    check_access(0,
+                 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST,
+                 0x80|TIS_ACCESS_ACTIVE_LOCALITY,
+                 "Locality 0 is active",
+                 "Locality 0 is not avtive");
+}
+
+/*
+ * Test the seize flag. It is assumed that locality 0 is active.
+ * The test ends with locality 2 being active
+ */
+static void
+test_seize_locality_flag(void)
+{
+    printf("Requesting use by locality 1 using the SEIZE bit\n");
+
+    writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_SEIZE);
+
+    check_access(0,
+                 0x80 | TIS_ACCESS_BEEN_SEIZED|TIS_ACCESS_ACTIVE_LOCALITY,
+                 0x80 | TIS_ACCESS_BEEN_SEIZED,
+                 "Access to locality 0 has been seized",
+                 "Access to locality 0 has not been properly seized");
+
+    check_access(1,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY,
+                 "Locality 1 successfully seized usage",
+                 "Locality 1 did not seize usage");
+
+    printf("Requesting use by locality 2 using the SEIZE bit\n");
+
+    writeb(TIS_REG(2, TIS_REG_ACCESS), TIS_ACCESS_SEIZE);
+
+    check_access(1,
+                 0x80 | TIS_ACCESS_BEEN_SEIZED | TIS_ACCESS_ACTIVE_LOCALITY,
+                 0x80 | TIS_ACCESS_BEEN_SEIZED,
+                 "Access to locality 1 has been seized",
+                 "Access to locality 1 has not been properly seized");
+
+    check_access(2,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST,
+                 0x80 | TIS_ACCESS_ACTIVE_LOCALITY,
+                 "Locality 2 successfully seized usage",
+                 "Locality 1 did not seize usage");
+}
+
+#define ENABLED(L4,L3,L2,L1,L0) \
+  ((L4) << 4 | (L3) << 3 | (L2) << 2 | (L1) << 1 | (L0))
+
+static const u8 pcr_resets[] = {
+     ENABLED(1,1,1,1,1), /* PCR 16 */
+     ENABLED(1,0,0,0,0),
+     ENABLED(1,0,0,0,0),
+     ENABLED(1,0,0,0,0),
+     ENABLED(1,0,1,0,0),
+     ENABLED(0,0,1,0,0),
+     ENABLED(0,0,1,0,0),
+     ENABLED(1,1,1,1,1),
+};
+
+static const u8 pcr_extends[] = {
+     ENABLED(1,1,1,1,1), /* PCR 16 */
+     ENABLED(1,1,1,0,0),
+     ENABLED(1,1,1,0,0),
+     ENABLED(0,1,1,0,0),
+     ENABLED(0,1,1,1,0),
+     ENABLED(0,0,1,0,0),
+     ENABLED(0,0,1,0,0),
+     ENABLED(1,1,1,1,1),
+};
+
+
+static u32
+do_pcr_read(u32 pcrindex, u8 *result, u8 locty)
+{
+    u32 rc;
+
+    struct pttto_pcrread pttto;
+    struct pttti_pcrread pttti = {
+        .pttti = {
+            .reserved = locty,
+            .ipblength = sizeof(struct pttti_pcrread),
+            .opblength = sizeof(struct pttto_pcrread),
+        },
+        .req = {
+            .tag      = htons(0xc1),
+            .totlen   = htonl(sizeof(pttti.req)),
+            .ordinal  = htonl(TPM_ORD_PcrRead),
+            .pcrindex = htonl(pcrindex),
+        },
+    };
+    struct bregs regs;
+    regs.es  = FLATPTR_TO_SEG   (&pttti);
+    regs.edi = FLATPTR_TO_OFFSET(&pttti);
+    regs.ds  = FLATPTR_TO_SEG   (&pttto);
+    regs.esi = FLATPTR_TO_OFFSET(&pttto);
+    regs.eax = TCG_PassThroughToTPM;
+
+    tcpa_interrupt_handler32(&regs);
+
+    rc = regs.eax;
+
+    if (rc == 0) {
+        if ((pttto.pttto.opblength < TPM_RSP_HEADER_SIZE) ||
+            ntohs(pttto.rsp.tag) != 0xc4) {
+            rc = TCG_FATAL_COM_ERROR;
+        }
+    }
+
+    if (rc == 0)
+       memcpy(result, pttto.rsp.digest, sizeof(pttto.rsp.digest));
+
+    return rc;
+}
+
+
+static u32
+do_pcr_extend(u8 *hash, u32 pcrindex, u8 locty)
+{
+    u32 rc;
+
+    struct pttto_extend pttto;
+    struct pttti_extend pttti = {
+        .pttti = {
+            .reserved = locty,
+            .ipblength = sizeof(struct pttti_extend),
+            .opblength = sizeof(struct pttto_extend),
+        },
+        .req = {
+            .tag      = htons(0xc1),
+            .totlen   = htonl(sizeof(pttti.req)),
+            .ordinal  = htonl(TPM_ORD_Extend),
+            .pcrindex = htonl(pcrindex),
+        },
+    };
+    struct bregs regs;
+    regs.es  = FLATPTR_TO_SEG(&pttti);
+    regs.edi = FLATPTR_TO_OFFSET(&pttti);
+    regs.ds  = FLATPTR_TO_SEG(&pttto);
+    regs.esi = FLATPTR_TO_OFFSET(&pttto);
+    regs.eax = TCG_PassThroughToTPM;
+
+    memcpy(pttti.req.digest, hash, sizeof(pttti.req.digest));
+
+    tcpa_interrupt_handler32(&regs);
+
+    rc = regs.eax;
+
+    if (rc == 0) {
+        if ((pttto.pttto.opblength < TPM_RSP_HEADER_SIZE) ||
+            ntohs(pttto.rsp.tag) != 0xc4) {
+            rc = TCG_FATAL_COM_ERROR;
+        }
+    }
+
+    return rc;
+}
+
+
+static u32
+do_pcr_reset(u32 pcrindex, u8 locty)
+{
+    u32 rc;
+
+    struct pttto_pcrreset pttto;
+    struct pttti_pcrreset pttti = {
+        .pttti = {
+            .reserved  = locty,
+            .ipblength = sizeof(struct pttti_pcrreset),
+            .opblength = sizeof(struct pttto_pcrreset),
+        },
+        .req = {
+            .tag          = htons(0xc1),
+            .totlen       = htonl(sizeof(pttti.req)),
+            .ordinal      = htonl(TPM_ORD_PCR_Reset),
+            .sizeOfSelect = htons(3),
+        },
+    };
+    struct bregs regs;
+    regs.es  = FLATPTR_TO_SEG(&pttti);
+    regs.edi = FLATPTR_TO_OFFSET(&pttti);
+    regs.ds  = FLATPTR_TO_SEG(&pttto);
+    regs.esi = FLATPTR_TO_OFFSET(&pttto);
+    regs.eax = TCG_PassThroughToTPM;
+
+    pttti.req.pcrSelect[pcrindex >> 3] = 1 << (pcrindex & 0x7);
+
+    tcpa_interrupt_handler32(&regs);
+
+    rc = regs.eax;
+
+    if (rc == 0) {
+        if ((pttto.pttto.opblength < TPM_RSP_HEADER_SIZE) ||
+            ntohs(pttto.rsp.tag) != 0xc4) {
+            rc = TCG_FATAL_COM_ERROR;
+        }
+    }
+
+    return rc;
+}
+
+
+static void
+test_pcr_test(void)
+{
+    u32 pcr_num, res;
+    u8 locty;
+    u8 value_a[20], value_b[20], value_c[20];
+    u8 reset_pcr_0s[20] = { /* all PCRs; 17-22 if TOSPresent = true */
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    };
+    u8 reset_pcr_1s[20] = { /* 17-22 if TOSPresent = false */
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    };
+    u8 value_ext[20] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
+    int extended;
+    int tos_present = !readb(TIS_REG(0, TIS_REG_ACCESS)) &
+                            TIS_ACCESS_TPM_ESTABLISHMENT;
+
+    printf("Testing PCR Extend and Reset\n");
+
+    for (pcr_num = 16; pcr_num <= 23; pcr_num++) {
+
+        printf("PCR %d: ",pcr_num);
+
+        for (locty = 0; locty <= 4; locty++) {
+            if ((res = do_pcr_read(pcr_num, value_a, locty))) {
+                printf("!F0[%d] = 0x%08x", locty, res);
+                continue;
+            }
+            if ((res = do_pcr_extend(value_ext, pcr_num, locty))) {
+                printf("!F1[%d] = 0x%08x", locty, res);
+                continue;
+            }
+            if ((res = do_pcr_read(pcr_num, value_b, locty))) {
+                printf("!F2[%d] = 0x%08x", locty, res);
+                continue;
+            }
+            /* did the PCR change? */
+            if (memcmp(value_a, value_b, sizeof(value_a)) != 0) {
+                /* extend worked */
+                extended = 1;
+                if ((pcr_extends[pcr_num-16] & (1 << locty)) == 0)
+                    printf("!F3[%d] ", locty);
+                else
+                    printf(" P1[%d] ", locty);
+            } else {
+                extended = 0;
+                /* extend did not work */
+                if (pcr_extends[pcr_num-16] & (1 << locty))
+                    printf("!F4[%d] ", locty);
+                else
+                    printf(" P2[%d] ", locty);
+            }
+
+            if (!extended) {
+                /* Need to extend using locality 2; this always works */
+                if ((res = do_pcr_extend(value_ext, pcr_num, 2))) {
+                    printf("!F5[%d] = 0x%08x", locty, res);
+                    continue;
+                }
+            }
+
+            if ((res = do_pcr_reset(pcr_num, locty))) {
+                printf("!F6[%d] = 0x%08x", locty, res);
+                continue;
+            }
+            if (do_pcr_read(pcr_num, value_c, locty)) {
+                printf("!F7[%d] = 0x%08x", locty, res);
+                continue;
+            }
+            /* did reset work? */
+            if ((( pcr_num <= 16 ||
+                   pcr_num == 23 ||
+                  (pcr_num >= 17 && pcr_num <= 22 && tos_present)
+                 ) &&
+                  memcmp(value_c, reset_pcr_0s, sizeof(value_c)) == 0
+                ) ||
+                ( pcr_num >= 17 && pcr_num <= 22 && !tos_present &&
+                  memcmp(value_c, reset_pcr_1s, sizeof(value_c)) == 0
+                )
+               ) {
+                /* reset worked */
+                if ((pcr_resets[pcr_num-16] & (1 << locty)) == 0)
+                    printf("!F8[%d] ", locty);
+                else
+                    printf(" P3[%d] ", locty);
+            } else {
+                /* reset did not work */
+                if ((pcr_resets[pcr_num-16] & (1 << locty)))
+                    printf("!F9[%d] ", locty);
+                else
+                    printf(" P4[%d] ", locty);
+            }
+        }
+        printf("\n");
+    }
+}
+
+
+/* send a command to the TPM without waiting for the response;
+   the caller must have activate the given locality */
+static int
+send_command(u8 locty, const unsigned char *command, u32 cmd_length)
+{
+    u32 c;
+    u32 burst;
+
+    writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY);
+
+    if (tis_check_status(locty,
+                         TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY, 1000)) {
+        printf("Error: Could not bring the TPM "
+               "into ready state in locality %d\n", locty);
+        wait_for_keystroke();
+        return 1;
+    }
+
+    burst = readl(TIS_REG(locty, TIS_REG_STS)) >> 8;
+
+    for (c = 0; c < cmd_length && burst > 0; c++) {
+        writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), command[c]);
+        burst--;
+    }
+
+    if (c != cmd_length) {
+        printf("Error: Could not write data into locality %d FIFO\n", locty);
+        wait_for_keystroke();
+        return 1;
+    }
+
+    writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_TPM_GO);
+
+    return 0;
+}
+
+
+unsigned char pcr_read_cmd[] = {
+    0x00, 0xc1, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00,
+    0x00, 0x15, 0x00, 0x00, 0x00, 0x0a
+};
+
+static void
+test_cmd_aborts(void)
+{
+    printf("Testing command aborts\n");
+
+    /* abort using COMMAND_READY flag */
+
+    tis_activate_locality(0);
+
+    if (send_command(0, pcr_read_cmd, sizeof(pcr_read_cmd))) {
+        printf("Error: Sending command did not work.\n");
+        wait_for_keystroke();
+    } else {
+        writeb(TIS_REG(0, TIS_REG_STS), TIS_STS_COMMAND_READY);
+
+        if (tis_check_status(0,
+                             TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY,
+                             1000)) {
+            printf("After abort, locality %d did not become ready in time.\n",
+                   0);
+        }
+    }
+
+    /* abort using the Seize flag by a higher locality */
+
+    writeb(TIS_REG(0, TIS_REG_ACCESS), TIS_ACCESS_BEEN_SEIZED);
+
+    if (send_command(0, pcr_read_cmd, sizeof(pcr_read_cmd))) {
+        printf("Error: Sending command did not work.\n");
+        wait_for_keystroke();
+    } else {
+        writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_SEIZE);
+
+        if (tis_check_access(1,
+                             TIS_ACCESS_ACTIVE_LOCALITY,
+                             TIS_ACCESS_ACTIVE_LOCALITY,
+                             1000)) {
+            printf("Error: Aborting of locality %d command by locality %d "
+                   "using the SEIZE flag did not work.\n",
+                   0, 1);
+            wait_for_keystroke();
+        } else {
+            check_access(0,
+                         TIS_ACCESS_BEEN_SEIZED|TIS_ACCESS_ACTIVE_LOCALITY,
+                         TIS_ACCESS_BEEN_SEIZED,
+                         "Access to locality 0 has been seized",
+                         "Access to locality 0 has not been properly seized");
+
+            check_access(1,
+                         TIS_ACCESS_ACTIVE_LOCALITY,
+                         TIS_ACCESS_ACTIVE_LOCALITY,
+                         "Got locality 1",
+                         "Could not get locality 1");
+        }
+    }
+
+    /* abort using ACTIVE_LOCALITY flag */
+    tis_activate_locality(1);
+
+    /* put in request by locality 0 */
+    writeb(TIS_REG(0, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE);
+
+    check_access(1,
+                 TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST,
+                 TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST,
+                 "Locality 1 sees pending request",
+                 "Locality 1 does not see pending request");
+
+    if (send_command(1, pcr_read_cmd, sizeof(pcr_read_cmd))) {
+        printf("Error: Sending command did not work.\n");
+        wait_for_keystroke();
+    } else {
+        writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY);
+
+        if (tis_check_access(0,
+                             TIS_ACCESS_ACTIVE_LOCALITY,
+                             TIS_ACCESS_ACTIVE_LOCALITY,
+                             1000)) {
+            printf("Error: Aborting of locality %d command "
+                   "using the ACTIVE_LOCALITY flag did not work.\n",
+                   1);
+            printf("access[0] : %02x\n", readb(TIS_REG(0, TIS_REG_ACCESS)));
+            printf("access[1] : %02x\n", readb(TIS_REG(1, TIS_REG_ACCESS)));
+            wait_for_keystroke();
+        } else {
+            check_access(1,
+                         TIS_ACCESS_ACTIVE_LOCALITY,
+                         0,
+                         "Access to locality 1 relinquished",
+                         "Access to locality 1 not relinquished");
+
+            check_access(0,
+                         TIS_ACCESS_ACTIVE_LOCALITY,
+                         TIS_ACCESS_ACTIVE_LOCALITY,
+                         "Got locality 0",
+                         "Could not get locality 0");
+        }
+    }
+}
+
+
+int
+test_various(u8 locty)
+{
+    int i;
+
+    tis_activate_locality(locty);
+
+    printf("Putting locality %d in ready state.\n", locty);
+
+    writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY);
+
+    if (tis_check_status(locty,
+                         TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY, 1000)) {
+        printf("Error: Could not bring the TPM "
+               "into ready state in locality %d\n",locty);
+        wait_for_keystroke();
+        return 1;
+    }
+
+    printf("Reading data from FIFO. Expecting 0xff.\n");
+
+    if (readb(TIS_REG(locty, TIS_REG_DATA_FIFO)) != 0xff) {
+        printf("Error: Expected to 0xff from FIFO\n");
+        wait_for_keystroke();
+    }
+
+    printf("Writing a byte into the FIFO.\n");
+
+    writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), pcr_read_cmd[0]);
+
+    if (tis_check_status(locty,
+                         TIS_STS_EXPECT, TIS_STS_EXPECT, 0)) {
+        printf("Error: TIS is not expecting data in locality %d\n",locty);
+        wait_for_keystroke();
+        return 1;
+    }
+
+    printf("Sending single byte to be processed.\n");
+
+    /* tpm will process the single byte */
+    writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_TPM_GO);
+
+    if (tis_check_status(locty,
+                         TIS_STS_EXPECT, 0, 0)) {
+        printf("Error: TIS is still expecting data in locality %d [2nd]\n",
+               locty);
+        wait_for_keystroke();
+        return 1;
+    }
+
+    printf("Checking for result.\n");
+
+    if (tis_check_status(locty,
+                         TIS_STS_DATA_AVAILABLE, TIS_STS_DATA_AVAILABLE,
+                         1000)) {
+        printf("Error: No data available in locality %d [2nd]\n",
+               locty);
+        wait_for_keystroke();
+        return 1;
+    }
+
+    printf("Putting locality %d in ready state.\n", locty);
+
+    writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY);
+
+    printf("Writing TPM_PcrRead command into FIFO.\n");
+
+    for (i = 0; i < sizeof(pcr_read_cmd); i++) {
+        if (i > 0 && tis_check_status(0, TIS_STS_EXPECT, TIS_STS_EXPECT, 0)) {
+            printf("Error: TIS is not expecting data anymore [%d/%d].\n",
+                   i,sizeof(pcr_read_cmd)-1);
+            readb(TIS_REG(locty, 0xf90));
+            wait_for_keystroke();
+        }
+        writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), pcr_read_cmd[i]);
+    }
+
+    printf("Checking whether the TPM is still expecting data. "
+           "It should not.\n");
+
+    if (tis_check_status(locty,
+                         TIS_STS_EXPECT, 0, 0)) {
+        printf("Error: TIS is still expecting data in locality %d\n",
+               locty);
+        wait_for_keystroke();
+        return 1;
+    }
+
+    printf("Sending command to put locality %d into ready state.\n", locty);
+
+    writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY);
+
+    printf("Checking that locality %d is in ready state.\n", locty);
+
+    if (tis_check_status(locty,
+                         TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY, 1000)) {
+        printf("Error: Could not bring the TPM "
+               "into ready state in locality %d [2nd]\n", locty);
+        wait_for_keystroke();
+        return 1;
+    }
+
+    return 0;
+}
+
+
+void
+tis_test(int tpm_state)
+{
+    test_active_locality_flag();
+
+    /* locality 0 is active */
+
+    test_seize_locality_flag();
+
+    /* locality 2 is active */
+
+    if (tpm_state == 0)
+        test_pcr_test();
+
+    test_cmd_aborts();
+
+    test_various(0);
+
+    tis_activate_locality(0);
+
+    printf("END OF TEST.\n");
+    wait_for_keystroke();
+}
+
+#endif /* CONFIG_TIS_TEST */
Index: seabios/src/tis_test.h
===================================================================
--- /dev/null
+++ seabios/src/tis_test.h
@@ -0,0 +1,52 @@
+#ifndef TIS_TEST
+#define TIS_TEST
+
+#include "tcgbios.h"
+
+#define TPM_ORD_PCR_Reset       0x000000c8
+#define TPM_ORD_PcrRead         0x00000015
+
+void tis_test(int tpm_state);
+
+struct tpm_req_pcrread {
+    TPM_REQ_HEADER
+    u32    pcrindex;
+} PACKED;
+
+struct tpm_rsp_pcrread {
+    TPM_RSP_HEADER
+    u8     digest[SHA1_BUFSIZE];
+} PACKED;
+
+struct pttti_pcrread {
+    struct pttti pttti;
+    struct tpm_req_pcrread req;
+} PACKED;
+
+struct pttto_pcrread {
+    struct pttto pttto;
+    struct tpm_rsp_pcrread rsp;
+} PACKED;
+
+
+struct tpm_req_pcrreset {
+    TPM_REQ_HEADER
+    u16     sizeOfSelect;
+    u8      pcrSelect[3];
+} PACKED;
+
+struct tpm_rsp_pcrreset {
+    TPM_RSP_HEADER
+} PACKED;
+
+struct pttti_pcrreset {
+    struct pttti pttti;
+    struct tpm_req_pcrreset req;
+} PACKED;
+
+struct pttto_pcrreset {
+    struct pttto pttto;
+    struct tpm_rsp_pcrreset rsp;
+} PACKED;
+
+#endif /* TIS_TEST */
Index: seabios/src/Kconfig
===================================================================
--- seabios.orig/src/Kconfig
+++ seabios/src/Kconfig
@@ -330,6 +330,13 @@ menu "BIOS interfaces"
             Either you may use the TPM for SHA1 calculations or
             use the internal sha1 algorithm to do it (faster).
 
+    config TIS_TEST
+        depends on TCGBIOS
+        bool "TPM TIS test"
+        default n
+        help
+            Test cases for the TPM TIS interface
+
 endmenu
 
 menu "BIOS Tables"


