This patch implements the main part of the TCG BIOS extensions. It provides
the following functionality:

- initialization of the TCPA ACPI table used for logging of measurements
- initialization of the TPM by sending a sequence of commands to it
- proper setup of the TPM once the BIOS hands over control to the bootloader
- support for S3 resume; BIOS sends TPM_Startup(ST_STATE) to TPM
- a utility function called mssleep is added. It waits for a number
  of milliseconds before it returns. I had tried to build a function
  like that based on calc_future_time() and check_timer(), but those
  didn't work once in an S3 resume.

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

---
 src/boot.c    |    2 
 src/post.c    |    5 
 src/resume.c  |    2 
 src/tcgbios.c |  525 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/tcgbios.h |  386 ++++++++++++++++++++++++++++++++++++++++++
 src/util.c    |   18 +
 src/util.h    |    5 
 7 files changed, 943 insertions(+)

Index: seabios/src/tcgbios.c
===================================================================
--- /dev/null
+++ seabios/src/tcgbios.c
@@ -0,0 +1,525 @@
+/*
+ *  Implementation of the TCG BIOS extension according to the specification
+ *  described in
+ *  https://www.trustedcomputinggroup.org/specs/PCClient/TCG_PCClientImplementationforBIOS_1-20_1-00.pdf
+ *
+ *  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,2010,2011
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ */
+
+#include "config.h"
+
+#if CONFIG_TCGBIOS
+
+#include "types.h"
+#include "tpm_drivers.h" // tpm_drivers[]
+#include "util.h" // printf, get_keystroke
+#include "tcgbios.h"// tcpa_*, prototypes
+#include "acpi.h"  // RSDP_SIGNATURE, rsdt_descriptor
+#include "smbios.h" // smbios_entry_point
+
+
+//#define DEBUG_TCGBIOS
+
+static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR };
+static const u8 Startup_ST_STATE[2] = { 0x00, TPM_ST_STATE };
+
+static const u8 PhysicalPresence_CMD_ENABLE[2]  = { 0x00, 0x20 };
+static const u8 PhysicalPresence_CMD_DISABLE[2] = { 0x01, 0x00 };
+static const u8 PhysicalPresence_PRESENT[2]     = { 0x00, 0x08 };
+static const u8 PhysicalPresence_NOT_PRESENT[2] = { 0x00, 0x10 };
+static const u8 PhysicalPresence_LOCK[2]        = { 0x00, 0x04 };
+
+static const u8 CommandFlag_FALSE[1] = { 0x00 };
+static const u8 CommandFlag_TRUE[1]  = { 0x01 };
+
+static const u8 GetCapability_Permanent_Flags[12] = {
+    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04,
+    0x00, 0x00, 0x01, 0x08
+};
+
+static const u8 GetCapability_OwnerAuth[12] = {
+    0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04,
+    0x00, 0x00, 0x01, 0x11
+};
+
+
+#define RSDP_CAST(ptr)   ((struct rsdp_descriptor *)ptr)
+
+
+/* helper functions */
+
+static inline void *input_buf32(struct bregs *regs)
+{
+    return MAKE_FLATPTR(regs->es, regs->di);
+}
+
+static inline void *output_buf32(struct bregs *regs)
+{
+    return MAKE_FLATPTR(regs->ds, regs->si);
+}
+
+
+typedef struct {
+    u8            tpm_probed:1;
+    u8            tpm_found:1;
+    u8            tpm_working:1;
+    u8            if_shutdown:1;
+    u8            tpm_driver_to_use:4;
+} tcpa_state_t;
+
+
+static tcpa_state_t tcpa_state = {
+    .tpm_driver_to_use = TPM_INVALID_DRIVER,
+};
+
+extern struct tpm_driver tpm_drivers[];
+
+
+/********************************************************
+  Extensions for TCG-enabled BIOS
+ *******************************************************/
+
+
+static u32
+is_tpm_present(void)
+{
+    u32 rc = 0;
+    unsigned int i;
+
+    for (i = 0; i < TPM_NUM_DRIVERS; i++) {
+        struct tpm_driver *td = &tpm_drivers[i];
+        if (td->probe() != 0) {
+            td->init();
+            tcpa_state.tpm_driver_to_use = i;
+            rc = 1;
+            break;
+        }
+    }
+
+    return rc;
+}
+
+
+int
+has_working_tpm(void)
+{
+    if (!tcpa_state.tpm_probed) {
+        tcpa_state.tpm_probed = 1;
+        tcpa_state.tpm_found = (is_tpm_present() != 0);
+        tcpa_state.tpm_working = 1;
+    }
+    if (!tcpa_state.tpm_working)
+        return 0;
+
+    return tcpa_state.tpm_found;
+}
+
+
+static u8
+calc_checksum(const u8 *addr, u32 length)
+{
+    u8 sum = 0;
+    u32 ctr;
+
+    for (ctr = 0; ctr < length; ctr++)
+        sum += addr[ctr];
+
+    return sum;
+}
+
+
+/*
+ * Search for the RSDP ACPI table in the memory starting at addr and
+ * ending at addr + len - 1.
+ */
+static struct rsdp_descriptor *
+find_rsdp(u8 *start, unsigned int len)
+{
+    u8 *end = start + len;
+
+    /* scan memory in steps of 16 bytes */
+    while (start < end) {
+        /* check for expected string */
+        if (RSDP_CAST(start)->signature == RSDP_SIGNATURE &&
+            calc_checksum(start,
+                          sizeof(struct rsdp_descriptor)) == 0)
+            return RSDP_CAST(start);
+        start += 0x10;
+    }
+
+    return 0;
+}
+
+
+
+
+static struct tcpa_descriptor_rev2 *
+find_tcpa_by_rsdp(struct rsdp_descriptor *rsdp)
+{
+    u32 ctr = 0;
+    struct tcpa_descriptor_rev2 *tcpa = NULL;
+    struct rsdt_descriptor *rsdt;
+    u32 length;
+    u16 off;
+
+    rsdt   = (struct rsdt_descriptor *)rsdp->rsdt_physical_address;
+    if (!rsdt)
+        return NULL;
+
+    length = rsdt->length;
+    off = offsetof(struct rsdt_descriptor, entry);
+
+    while ((off + sizeof(rsdt->entry[0])) <= length) {
+        /* try all pointers to structures */
+        tcpa = (struct tcpa_descriptor_rev2 *)(int)rsdt->entry[ctr];
+
+        /* valid TCPA ACPI table ? */
+        if (tcpa->signature == TCPA_SIGNATURE &&
+            calc_checksum((u8 *)tcpa, tcpa->length) == 0)
+            break;
+
+        tcpa = NULL;
+        off += sizeof(rsdt->entry[0]);
+        ctr++;
+    }
+
+    return tcpa;
+}
+
+
+static struct tcpa_descriptor_rev2 *
+find_tcpa_table(void)
+{
+    struct tcpa_descriptor_rev2 *tcpa = NULL;
+    struct rsdp_descriptor *rsdp;
+    u16 ebda_seg;
+
+    /* RSDP in EBDA? */
+    ebda_seg = *(u16 *)MAKE_FLATPTR(0x40, 0xe);
+    rsdp = find_rsdp(MAKE_FLATPTR(ebda_seg, 0), 1024);
+
+    if (rsdp)
+        tcpa = find_tcpa_by_rsdp(rsdp);
+
+    if (!tcpa) {
+        rsdp = find_rsdp((u8 *)0xE0000, 0x20000);
+        if (rsdp)
+            tcpa = find_tcpa_by_rsdp(rsdp);
+    }
+
+    if (!rsdp)
+        tcpa_state.if_shutdown = 1;
+
+#ifdef DEBUG_TCGBIOS
+    if (! rsdp )
+        dprintf(1, "TCGBIOS: RSDP was NOT found! -- Disabling interface.\n");
+    else if ( !tcpa )
+        dprintf(1, "TCGBIOS: TCPA ACPI was NOT found!\n");
+#endif
+
+    return tcpa;
+}
+
+
+static u8 *
+get_lasa_base_ptr(u32 *laml)
+{
+    u8 *lasa = 0;
+    struct tcpa_descriptor_rev2 *tcpa = find_tcpa_table();
+
+    if (tcpa) {
+        lasa = (u8 *)(long)tcpa->lasa;
+        if (laml)
+            *laml = tcpa->laml;
+    }
+
+    return lasa;
+}
+
+
+/* clear the ACPI log */
+static void
+reset_acpi_log(void)
+{
+    u32 laml;
+    u8 *lasa = get_lasa_base_ptr(&laml);
+
+    if (lasa)
+        memset(lasa, 0x0, laml);
+}
+
+
+/*
+   initialize the TCPA ACPI subsystem; find the ACPI tables and determine
+   where the TCPA table is.
+ */
+void
+tcpa_acpi_init(void)
+{
+    tcpa_state.if_shutdown = 0;
+    tcpa_state.tpm_probed = 0;
+    tcpa_state.tpm_found = 0;
+    tcpa_state.tpm_working = 0;
+
+    if (!has_working_tpm()) {
+        tcpa_state.if_shutdown = 1;
+        return;
+    }
+
+    reset_acpi_log();
+}
+
+
+static u32
+transmit(u8 locty, const struct iovec iovec[],
+         u8 *respbuffer, u32 *respbufferlen)
+{
+    u32 rc = 0;
+    u32 irc;
+    struct tpm_driver *td;
+    unsigned int i;
+
+    if (tcpa_state.tpm_driver_to_use == TPM_INVALID_DRIVER)
+        return TCG_FATAL_COM_ERROR;
+
+    td = &tpm_drivers[tcpa_state.tpm_driver_to_use];
+
+    irc = td->activate(locty);
+    if (irc != 0) {
+        /* tpm could not be activated */
+        return TCG_FATAL_COM_ERROR;
+    }
+
+    for (i = 0; iovec[i].length; i++) {
+        irc = td->senddata(iovec[i].data,
+                           iovec[i].length);
+        if (irc != 0)
+            return TCG_FATAL_COM_ERROR;
+    }
+
+    irc = td->waitdatavalid();
+    if (irc != 0)
+        return TCG_FATAL_COM_ERROR;
+
+    irc = td->waitrespready(10000);
+    if (irc != 0)
+        return TCG_FATAL_COM_ERROR;
+
+    irc = td->readresp(respbuffer,
+                       respbufferlen);
+    if (irc != 0)
+        return TCG_FATAL_COM_ERROR;
+
+    td->ready();
+
+    return rc;
+}
+
+
+/*
+ * Send a TPM command with the given ordinal. Append the given buffer
+ * containing all data in network byte order to the command (this is
+ * the custom part per command) and expect a response of the given size.
+ * If a buffer is provided, the response will be copied into it.
+ */
+static u32
+build_and_send_cmd_od(u32 ordinal, const u8 *append, u32 append_size,
+                      u8 *resbuffer, u32 return_size, u32 *returnCode,
+                      const u8 *otherdata, u32 otherdata_size)
+{
+#define MAX_APPEND_SIZE   12
+#define MAX_RESPONSE_SIZE sizeof(struct tpm_res_getcap_perm_flags)
+    u32 rc;
+    u8 ibuffer[TPM_REQ_HEADER_SIZE + MAX_APPEND_SIZE];
+    u8 obuffer[MAX_RESPONSE_SIZE];
+    struct tpm_req_header *trqh = (struct tpm_req_header *)ibuffer;
+    struct tpm_rsp_header *trsh = (struct tpm_rsp_header *)obuffer;
+    u8 locty = 0;
+    struct iovec iovec[3];
+    u32 obuffer_len = sizeof(obuffer);
+    u32 idx = 1;
+
+    if (append_size > MAX_APPEND_SIZE ||
+        return_size > MAX_RESPONSE_SIZE) {
+#ifdef DEBUG_TCGBIOS
+        dprintf(1, "TCGBIOS: size of requested buffers too big.");
+#endif
+        return TCG_FIRMWARE_ERROR;
+    }
+
+    iovec[0].data   = trqh;
+    iovec[0].length = TPM_REQ_HEADER_SIZE + append_size;
+
+    if (otherdata) {
+        iovec[1].data   = (void *)otherdata;
+        iovec[1].length = otherdata_size;
+        idx = 2;
+    }
+
+    iovec[idx].data   = NULL;
+    iovec[idx].length = 0;
+
+    memset(ibuffer, 0x0, sizeof(ibuffer));
+    memset(obuffer, 0x0, sizeof(obuffer));
+
+    trqh->tag     = htons(0xc1);
+    trqh->totlen  = htonl(TPM_REQ_HEADER_SIZE + append_size + otherdata_size);
+    trqh->ordinal = htonl(ordinal);
+
+    if (append_size)
+        memcpy((char *)trqh + sizeof(*trqh),
+               append, append_size);
+
+    rc = transmit(locty, iovec, obuffer, &obuffer_len);
+    if (rc)
+        return rc;
+
+    *returnCode = ntohl(trsh->errcode);
+
+    if (resbuffer)
+        memcpy(resbuffer, trsh, return_size);
+
+    return 0;
+}
+
+
+static u32
+build_and_send_cmd(u32 ordinal, const u8 *append, u32 append_size,
+                   u8 *resbuffer, u32 return_size, u32 *returnCode)
+{
+    return build_and_send_cmd_od(ordinal, append, append_size,
+                                 resbuffer, return_size, returnCode,
+                                 NULL, 0);
+}
+
+
+u32
+tcpa_startup(void)
+{
+    u32 rc = 0;
+    u32 returnCode;
+
+    if (!has_working_tpm())
+        return 0;
+
+#ifdef DEBUG_TCGBIOS
+    dprintf(1,"TCGBIOS: Starting with TPM_Startup(ST_CLEAR)\n");
+#endif
+    rc = build_and_send_cmd(TPM_ORD_Startup,
+                            Startup_ST_CLEAR, sizeof(Startup_ST_CLEAR),
+                            NULL, 10, &returnCode);
+#ifdef DEBUG_TCGBIOS
+    dprintf(1,"Return code from TPM_Startup = 0x%08x\n",
+              returnCode);
+#endif
+    if (rc && returnCode)
+        goto err_exit;
+
+    rc = build_and_send_cmd(TPM_ORD_SelfTestFull, NULL, 0,
+                            NULL, 10, &returnCode);
+#ifdef DEBUG_TCGBIOS
+    dprintf(1,"Return code from TPM_SelfTestFull = 0x%08x\n",
+              returnCode);
+#endif
+    if (rc || returnCode)
+        goto err_exit;
+
+    return 0;
+
+err_exit:
+#ifdef DEBUG_TCGBIOS
+    dprintf(1,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
+#endif
+    tcpa_state.tpm_working = 0;
+    if (rc)
+        return rc;
+    return TCG_TCG_COMMAND_ERROR;
+}
+
+
+u32
+tcpa_leave_bios(void)
+{
+    u32 rc = 0;
+    u32 returnCode;
+
+    if (!has_working_tpm())
+        return 0;
+
+    rc = build_and_send_cmd(TPM_ORD_PhysicalPresence,
+                            PhysicalPresence_CMD_ENABLE,
+                            sizeof(PhysicalPresence_CMD_ENABLE),
+                            NULL, 10, &returnCode);
+    if (rc || returnCode)
+        goto err_exit;
+
+    rc = build_and_send_cmd(TPM_ORD_PhysicalPresence,
+                            PhysicalPresence_NOT_PRESENT,
+                            sizeof(PhysicalPresence_NOT_PRESENT),
+                            NULL, 10, &returnCode);
+    if (rc || returnCode)
+        goto err_exit;
+
+    return 0;
+
+err_exit:
+#ifdef DEBUG_TCGBIOS
+    dprintf(1,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
+#endif
+    tcpa_state.tpm_working = 0;
+    if (rc)
+        return rc;
+    return TCG_TCG_COMMAND_ERROR;
+}
+
+
+u32
+tcpa_s3_resume(void)
+{
+    u32 rc = 0;
+    u32 returnCode;
+
+    if (has_working_tpm()) {
+        rc = build_and_send_cmd(TPM_ORD_Startup,
+                                Startup_ST_STATE,
+                                sizeof(Startup_ST_STATE),
+                                NULL, 10, &returnCode);
+#ifdef DEBUG_TCGBIOS
+        dprintf(1,"TCGBIOS: Resuming with TPM_Startup(ST_STATE)\n");
+        dprintf(1,"TCGBIOS: ReturnCode from TPM_Startup = 0x%08x\n",
+                  returnCode);
+#endif
+        if (rc || returnCode)
+            goto err_exit;
+    }
+
+    return 0;
+
+err_exit:
+#ifdef DEBUG_TCGBIOS
+    dprintf(1,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
+#endif
+    tcpa_state.tpm_working = 0;
+    if (rc)
+        return rc;
+    return TCG_TCG_COMMAND_ERROR;
+}
+
+
+#endif /* CONFIG_TCGBIOS */
Index: seabios/src/tcgbios.h
===================================================================
--- /dev/null
+++ seabios/src/tcgbios.h
@@ -0,0 +1,386 @@
+#ifndef TCGBIOS_H
+#define TCGBIOS_H
+
+#include "types.h"
+#include "bregs.h" /* struct bregs */
+
+#define TCG_MAGIC 0x41504354L
+
+/* Define for section 12.3 */
+#define TCG_PC_OK                       0x0
+#define TCG_PC_TPMERROR                 0x1
+#define TCG_PC_LOGOVERFLOW              0x2
+#define TCG_PC_UNSUPPORTED              0x3
+
+#define TPM_ALG_SHA                     0x4
+
+#define TCG_MAGIC                       0x41504354L
+#define TCG_VERSION_MAJOR               1
+#define TCG_VERSION_MINOR               2
+
+#define TPM_OK                          0x0
+#define TPM_RET_BASE                    0x1
+#define TCG_GENERAL_ERROR               (TPM_RET_BASE + 0x0)
+#define TCG_TPM_IS_LOCKED               (TPM_RET_BASE + 0x1)
+#define TCG_NO_RESPONSE                 (TPM_RET_BASE + 0x2)
+#define TCG_INVALID_RESPONSE            (TPM_RET_BASE + 0x3)
+#define TCG_INVALID_ACCESS_REQUEST      (TPM_RET_BASE + 0x4)
+#define TCG_FIRMWARE_ERROR              (TPM_RET_BASE + 0x5)
+#define TCG_INTEGRITY_CHECK_FAILED      (TPM_RET_BASE + 0x6)
+#define TCG_INVALID_DEVICE_ID           (TPM_RET_BASE + 0x7)
+#define TCG_INVALID_VENDOR_ID           (TPM_RET_BASE + 0x8)
+#define TCG_UNABLE_TO_OPEN              (TPM_RET_BASE + 0x9)
+#define TCG_UNABLE_TO_CLOSE             (TPM_RET_BASE + 0xa)
+#define TCG_RESPONSE_TIMEOUT            (TPM_RET_BASE + 0xb)
+#define TCG_INVALID_COM_REQUEST         (TPM_RET_BASE + 0xc)
+#define TCG_INVALID_ADR_REQUEST         (TPM_RET_BASE + 0xd)
+#define TCG_WRITE_BYTE_ERROR            (TPM_RET_BASE + 0xe)
+#define TCG_READ_BYTE_ERROR             (TPM_RET_BASE + 0xf)
+#define TCG_BLOCK_WRITE_TIMEOUT         (TPM_RET_BASE + 0x10)
+#define TCG_CHAR_WRITE_TIMEOUT          (TPM_RET_BASE + 0x11)
+#define TCG_CHAR_READ_TIMEOUT           (TPM_RET_BASE + 0x12)
+#define TCG_BLOCK_READ_TIMEOUT          (TPM_RET_BASE + 0x13)
+#define TCG_TRANSFER_ABORT              (TPM_RET_BASE + 0x14)
+#define TCG_INVALID_DRV_FUNCTION        (TPM_RET_BASE + 0x15)
+#define TCG_OUTPUT_BUFFER_TOO_SHORT     (TPM_RET_BASE + 0x16)
+#define TCG_FATAL_COM_ERROR             (TPM_RET_BASE + 0x17)
+#define TCG_INVALID_INPUT_PARA          (TPM_RET_BASE + 0x18)
+#define TCG_TCG_COMMAND_ERROR           (TPM_RET_BASE + 0x19)
+#define TCG_INTERFACE_SHUTDOWN          (TPM_RET_BASE + 0x20)
+//define TCG_PC_UNSUPPORTED             (TPM_RET_BASE + 0x21)
+#define TCG_PC_TPM_NOT_PRESENT          (TPM_RET_BASE + 0x22)
+#define TCG_PC_TPM_DEACTIVATED          (TPM_RET_BASE + 0x23)
+
+
+#define TPM_INVALID_ADR_REQUEST          TCG_INVALID_ADR_REQUEST
+#define TPM_IS_LOCKED                    TCG_TPM_IS_LOCKED
+#define TPM_INVALID_DEVICE_ID            TCG_INVALID_DEVICE_ID
+#define TPM_INVALID_VENDOR_ID            TCG_INVALID_VENDOR_ID
+//define TPM_RESERVED_REG_INVALID
+#define TPM_FIRMWARE_ERROR               TCG_FIRMWARE_ERROR
+#define TPM_UNABLE_TO_OPEN               TCG_UNABLE_TO_OPEN
+#define TPM_UNABLE_TO_CLOSE              TCG_UNABLE_TO_CLOSE
+#define TPM_INVALID_RESPONSE             TCG_INVALID_RESPONSE
+#define TPM_RESPONSE_TIMEOUT             TCG_RESPONSE_TIMEOUT
+#define TPM_INVALID_ACCESS_REQUEST       TCG_INVALID_ACCESS_REQUEST
+#define TPM_TRANSFER_ABORT               TCG_TRANSFER_ABORT
+#define TPM_GENERAL_ERROR                TCG_GENERAL_ERROR
+
+
+#define TPM_ORD_SelfTestFull             0x00000050
+#define TPM_ORD_ForceClear               0x0000005d
+#define TPM_ORD_GetCapability            0x00000065
+#define TPM_ORD_PhysicalEnable           0x0000006f
+#define TPM_ORD_PhysicalDisable          0x00000070
+#define TPM_ORD_SetOwnerInstall          0x00000071
+#define TPM_ORD_PhysicalSetDeactivated   0x00000072
+#define TPM_ORD_Startup                  0x00000099
+#define TPM_ORD_PhysicalPresence         0x4000000a
+#define TPM_ORD_Extend                   0x00000014
+#define TPM_ORD_SHA1Start                0x000000a0
+#define TPM_ORD_SHA1Update               0x000000a1
+#define TPM_ORD_SHA1Complete             0x000000a2
+
+
+#define TPM_ST_CLEAR                     0x1
+#define TPM_ST_STATE                     0x2
+#define TPM_ST_DEACTIVATED               0x3
+
+
+/* interrupt identifiers (al register) */
+enum irq_ids {
+    TCG_StatusCheck = 0,
+    TCG_HashLogExtendEvent = 1,
+    TCG_PassThroughToTPM = 2,
+    TCG_ShutdownPreBootInterface = 3,
+    TCG_HashLogEvent = 4,
+    TCG_HashAll = 5,
+    TCG_TSS = 6,
+    TCG_CompactHashLogExtendEvent = 7,
+};
+
+/* event types: 10.4.1 / table 11 */
+#define EV_POST_CODE             1
+#define EV_SEPARATOR             4
+#define EV_ACTION                5
+#define EV_EVENT_TAG             6
+#define EV_COMPACT_HASH         12
+#define EV_IPL                  13
+#define EV_IPL_PARTITION_DATA   14
+
+
+#define STATUS_FLAG_SHUTDOWN        (1 << 0)
+
+#define SHA1_BUFSIZE                20
+
+
+struct iovec
+{
+    size_t length;
+    void   *data;
+};
+
+
+/* Input and Output blocks for the TCG BIOS commands */
+
+struct hleei_short
+{
+    u16   ipblength;
+    u16   reserved;
+    const void *hashdataptr;
+    u32   hashdatalen;
+    u32   pcrindex;
+    const void *logdataptr;
+    u32   logdatalen;
+} PACKED;
+
+
+struct hleei_long
+{
+    u16   ipblength;
+    u16   reserved;
+    void *hashdataptr;
+    u32   hashdatalen;
+    u32   pcrindex;
+    u32   reserved2;
+    void *logdataptr;
+    u32   logdatalen;
+} PACKED;
+
+
+struct hleeo
+{
+    u16    opblength;
+    u16    reserved;
+    u32    eventnumber;
+    u8     digest[SHA1_BUFSIZE];
+} PACKED;
+
+
+struct pttti
+{
+    u16    ipblength;
+    u16    reserved;
+    u16    opblength;
+    u16    reserved2;
+    u8     tpmopin[0];
+} PACKED;
+
+
+struct pttto
+{
+    u16    opblength;
+    u16    reserved;
+    u8     tpmopout[0];
+};
+
+
+struct hlei
+{
+    u16    ipblength;
+    u16    reserved;
+    const void  *hashdataptr;
+    u32    hashdatalen;
+    u32    pcrindex;
+    u32    logeventtype;
+    const void  *logdataptr;
+    u32    logdatalen;
+} PACKED;
+
+
+struct hleo
+{
+    u16    opblength;
+    u16    reserved;
+    u32    eventnumber;
+} PACKED;
+
+
+struct hai
+{
+    u16    ipblength;
+    u16    reserved;
+    const void  *hashdataptr;
+    u32    hashdatalen;
+    u32    algorithmid;
+} PACKED;
+
+
+struct ti
+{
+    u16    ipblength;
+    u16    reserved;
+    u16    opblength;
+    u16    reserved2;
+    u8     tssoperandin[0];
+} PACKED;
+
+
+struct to
+{
+    u16    opblength;
+    u16    reserved;
+    u8     tssoperandout[0];
+} PACKED;
+
+
+struct pcpes
+{
+    u32    pcrindex;
+    u32    eventtype;
+    u8     digest[SHA1_BUFSIZE];
+    u32    eventdatasize;
+    u32    event;
+} PACKED;
+
+
+/* 10.4.2.1 */
+struct pcctes
+{
+    u32 eventid;
+    u32 eventdatasize;
+    u8  digest[SHA1_BUFSIZE];
+} PACKED;
+
+/* 10.4.2.1 w/ 10.4.2.2.1 embedded */
+struct pcctes_romex
+{
+    u32 eventid;
+    u32 eventdatasize;
+    u16 reserved;
+    u16 pfa;
+    u8  digest[SHA1_BUFSIZE];
+} PACKED;
+
+
+#define TPM_REQ_HEADER \
+    u16    tag; \
+    u32    totlen; \
+    u32    ordinal;
+
+#define TPM_REQ_HEADER_SIZE  (sizeof(u16) + sizeof(u32) + sizeof(u32))
+
+#define TPM_RSP_HEADER \
+    u16    tag; \
+    u32    totlen; \
+    u32    errcode;
+
+#define TPM_RSP_HEADER_SIZE  (sizeof(u16) + sizeof(u32) + sizeof(u32))
+
+struct tpm_req_header {
+    TPM_REQ_HEADER;
+} PACKED;
+
+
+struct tpm_rsp_header {
+    TPM_RSP_HEADER;
+} PACKED;
+
+
+struct tpm_req_extend {
+    TPM_REQ_HEADER
+    u32    pcrindex;
+    u8     digest[SHA1_BUFSIZE];
+} PACKED;
+
+
+struct tpm_rsp_extend {
+    TPM_RSP_HEADER
+    u8     digest[SHA1_BUFSIZE];
+} PACKED;
+
+
+struct tpm_req_getcap_perm_flags {
+    TPM_REQ_HEADER
+    u32    capArea;
+    u32    subCapSize;
+    u32    subCap;
+} PACKED;
+
+
+struct tpm_permanent_flags {
+    u16    tag;
+    u8     flags[20];
+} PACKED;
+
+
+enum permFlagsIndex {
+    PERM_FLAG_IDX_DISABLE = 0,
+    PERM_FLAG_IDX_OWNERSHIP,
+    PERM_FLAG_IDX_DEACTIVATED,
+    PERM_FLAG_IDX_READPUBEK,
+    PERM_FLAG_IDX_DISABLEOWNERCLEAR,
+    PERM_FLAG_IDX_ALLOW_MAINTENANCE,
+    PERM_FLAG_IDX_PHYSICAL_PRESENCE_LIFETIME_LOCK,
+    PERM_FLAG_IDX_PHYSICAL_PRESENCE_HW_ENABLE,
+};
+
+
+struct tpm_res_getcap_perm_flags {
+    TPM_RSP_HEADER
+    u32    size;
+    struct tpm_permanent_flags perm_flags;
+} PACKED;
+
+
+struct tpm_res_getcap_ownerauth {
+    TPM_RSP_HEADER
+    u32    size;
+    u8     flag;
+} PACKED;
+
+
+struct tpm_res_sha1start {
+    TPM_RSP_HEADER
+    u32    max_num_bytes;
+} PACKED;
+
+
+struct tpm_res_sha1complete {
+    TPM_RSP_HEADER
+    u8     hash[20];
+} PACKED;
+
+struct pttti_extend {
+    struct pttti pttti;
+    struct tpm_req_extend req;
+} PACKED;
+
+
+struct pttto_extend {
+    struct pttto pttto;
+    struct tpm_rsp_extend rsp;
+} PACKED;
+
+
+enum ipltype {
+    IPL_BCV = 0,
+    IPL_EL_TORITO_1,
+    IPL_EL_TORITO_2
+};
+
+#if CONFIG_TCGBIOS
+void tcpa_acpi_init(void);
+int has_working_tpm(void);
+u32 tcpa_startup(void);
+u32 tcpa_leave_bios(void);
+u32 tcpa_s3_resume(void);
+#else
+static inline void tcpa_acpi_init(void) {
+}
+static inline int has_working_tpm(void) {
+    return 0;
+}
+static inline u32 tcpa_startup(void) {
+    return 0;
+}
+static inline u32 tcpa_leave_bios(void) {
+    return 0;
+}
+static inline u32 tcpa_s3_resume(void) {
+    return 0;
+}
+#endif
+void tcpa_menu(void);
+
+#endif /* TCGBIOS_H */
Index: seabios/src/util.c
===================================================================
--- seabios.orig/src/util.c
+++ seabios/src/util.c
@@ -322,3 +322,21 @@ get_keystroke(int msec)
         wait_irq();
     }
 }
+
+
+// wait a given time
+// this function also works in case of a resume
+void
+mssleep(u32 msec)
+{
+    u32 i;
+    u8 x, y = inb(0x61) & 0x10;
+
+    /* Poll the DRAM refresh timer: I/O port 61h, bit 4 toggles every 15us. */
+    msec *= (1000/15); /* Convert milliseconds to multiples of 15us. */
+    for ( i = 0; i < msec; i++ ) {
+        while ( (x = inb(0x61) & 0x10) == y )
+            continue;
+        y = x;
+    }
+}
Index: seabios/src/util.h
===================================================================
--- seabios.orig/src/util.h
+++ seabios/src/util.h
@@ -497,4 +497,9 @@ extern u8 BiosChecksum;
 // version (auto generated file out/version.c)
 extern const char VERSION[];
 
+
+void mssleep(u32 time);
+
+void tcpa_interrupt_handler16(struct bregs *regs);
+
 #endif // util.h
Index: seabios/src/post.c
===================================================================
--- seabios.orig/src/post.c
+++ seabios/src/post.c
@@ -25,6 +25,7 @@
 #include "paravirt.h" // qemu_cfg_port_probe
 #include "ps2port.h" // ps2port_setup
 #include "virtio-blk.h" // virtio_blk_setup
+#include "tcgbios.h" // tcpa_*
 
 
 /****************************************************************
@@ -238,6 +239,10 @@ maininit(void)
     mouse_setup();
     init_bios_tables();
 
+    // Initialize tpm (after acpi tables were written)
+    tcpa_acpi_init();
+    tcpa_startup();
+
     // Run vga option rom
     vga_setup();
 
Index: seabios/src/boot.c
===================================================================
--- seabios.orig/src/boot.c
+++ seabios/src/boot.c
@@ -14,6 +14,7 @@
 #include "cmos.h" // inb_cmos
 #include "paravirt.h" // romfile_loadfile
 #include "pci.h" //pci_bdf_to_*
+#include "tcgbios.h" // tcpa_*
 
 
 /****************************************************************
@@ -449,6 +450,7 @@ boot_prep(void)
     // Allow user to modify BCV/IPL order.
     interactive_bootmenu();
     wait_threads();
+    tcpa_leave_bios();
 
     // Map drives and populate BEV list
     struct bootentry_s *pos = BootList;
Index: seabios/src/resume.c
===================================================================
--- seabios.orig/src/resume.c
+++ seabios/src/resume.c
@@ -10,6 +10,7 @@
 #include "biosvar.h" // struct bios_data_area_s
 #include "bregs.h" // struct bregs
 #include "acpi.h" // find_resume_vector
+#include "tcgbios.h" // tcpa_s3_resume
 
 // Reset DMA controller
 void
@@ -116,6 +117,7 @@ s3_resume(void)
     if (s3_resume_vector) {
         dprintf(1, "Jump to resume vector (%x)\n", s3_resume_vector);
         br.code = FLATPTR_TO_SEGOFF((void*)s3_resume_vector);
+        tcpa_s3_resume();
     } else {
         dprintf(1, "No resume vector set!\n");
         // Jump to the post vector to restart with a normal boot.


