Patchwork [V2,4/8] Implementation of the TCG BIOS extensions

login
register
mail settings
Submitter Stefan Berger
Date April 5, 2011, 1:29 a.m.
Message ID <20110405013013.683353499@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/89749/
State New
Headers show

Comments

Stefan Berger - April 5, 2011, 1:29 a.m.
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
- enable configuration of SeaBIOS to be built with TCGBIOS extensions
  depending on COREBOOT not being selected
  All TCG BIOS extensions are activated with CONFIG_TCGBIOS.

Structures that are needed in subsequent patches are also included in
tcgbios.h at this point.

-v2:
 - replace mssleep() with calls to msleep()
 - Moving Kconfig patch to this file
 - converting code to call dprintf(DEBUG_tcg, ...)
 - use the get_rsdp call to get hold of the RSDP
 - use util.c:checksum()
 - Adapting tcgbios.c to be under LGPLv3
 - using if (!CONFIG_TCGBIOS) everywhere

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

---
 src/Kconfig   |    8 +
 src/boot.c    |    2 
 src/config.h  |    1 
 src/post.c    |    5 
 src/resume.c  |    2 
 src/tcgbios.c |  394 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/tcgbios.h |  312 +++++++++++++++++++++++++++++++++++++++++++++
 src/util.h    |    3 
 8 files changed, 727 insertions(+)

Patch

Index: seabios/src/util.h
===================================================================
--- seabios.orig/src/util.h
+++ seabios/src/util.h
@@ -497,4 +497,7 @@  extern u8 BiosChecksum;
 // version (auto generated file out/version.c)
 extern const char VERSION[];
 
+// TCG BIOS extensions
+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.
Index: seabios/src/config.h
===================================================================
--- seabios.orig/src/config.h
+++ seabios/src/config.h
@@ -119,5 +119,6 @@ 
 #define DEBUG_unimplemented 2
 #define DEBUG_invalid 3
 #define DEBUG_thread 2
+#define DEBUG_tcg 20
 
 #endif // config.h
Index: seabios/src/Kconfig
===================================================================
--- seabios.orig/src/Kconfig
+++ seabios/src/Kconfig
@@ -314,6 +314,14 @@  menu "BIOS interfaces"
         default n
         help
             Disable A20 on 16bit boot.
+
+    config TCGBIOS
+        depends on !COREBOOT
+        bool "TPM support and TCG BIOS extensions"
+        default y
+        help
+            Provide TPM support along with TCG BIOS extensions
+
 endmenu
 
 menu "BIOS Tables"
Index: seabios/src/tcgbios.h
===================================================================
--- seabios.orig/src/tcgbios.h
+++ seabios/src/tcgbios.h
@@ -1,6 +1,22 @@ 
 #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
@@ -51,7 +67,303 @@ 
 #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
+};
+
+void tcpa_acpi_init(void);
 int has_working_tpm(void);
+u32 tcpa_startup(void);
+u32 tcpa_leave_bios(void);
+u32 tcpa_s3_resume(void);
 
 
 #endif /* TCGBIOS_H */
Index: seabios/src/tcgbios.c
===================================================================
--- seabios.orig/src/tcgbios.c
+++ seabios/src/tcgbios.c
@@ -12,6 +12,48 @@ 
 
 #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
+
+
+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 {
@@ -58,6 +100,9 @@  is_tpm_present(void)
 int
 has_working_tpm(void)
 {
+    if (!CONFIG_TCGBIOS)
+        return 0;
+
     if (!tcpa_state.tpm_probed) {
         tcpa_state.tpm_probed = 1;
         tcpa_state.tpm_found = (is_tpm_present() != 0);
@@ -70,3 +115,352 @@  has_working_tpm(void)
 }
 
 
+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 &&
+            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;
+
+    rsdp = get_rsdp();
+
+    if (rsdp)
+        tcpa = find_tcpa_by_rsdp(rsdp);
+    else
+        tcpa_state.if_shutdown = 1;
+
+    if (!rsdp)
+        dprintf(DEBUG_tcg,
+                "TCGBIOS: RSDP was NOT found! -- Disabling interface.\n");
+    else if (!tcpa)
+        dprintf(DEBUG_tcg, "TCGBIOS: TCPA ACPI was NOT found!\n");
+
+    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)
+{
+    if (!CONFIG_TCGBIOS)
+        return;
+
+    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) {
+        dprintf(DEBUG_tcg, "TCGBIOS: size of requested buffers too big.");
+        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;
+    u32 returnCode;
+
+    if (!CONFIG_TCGBIOS)
+        return 0;
+
+    if (!has_working_tpm())
+        return 0;
+
+    dprintf(DEBUG_tcg, "TCGBIOS: Starting with TPM_Startup(ST_CLEAR)\n");
+    rc = build_and_send_cmd(TPM_ORD_Startup,
+                            Startup_ST_CLEAR, sizeof(Startup_ST_CLEAR),
+                            NULL, 10, &returnCode);
+
+    dprintf(DEBUG_tcg, "Return code from TPM_Startup = 0x%08x\n",
+            returnCode);
+
+    if (rc && returnCode)
+        goto err_exit;
+
+    rc = build_and_send_cmd(TPM_ORD_SelfTestFull, NULL, 0,
+                            NULL, 10, &returnCode);
+
+    dprintf(DEBUG_tcg, "Return code from TPM_SelfTestFull = 0x%08x\n",
+            returnCode);
+
+    if (rc || returnCode)
+        goto err_exit;
+
+    return 0;
+
+err_exit:
+    dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
+
+    tcpa_state.tpm_working = 0;
+    if (rc)
+        return rc;
+    return TCG_TCG_COMMAND_ERROR;
+}
+
+
+u32
+tcpa_leave_bios(void)
+{
+    u32 rc;
+    u32 returnCode;
+
+    if (!CONFIG_TCGBIOS)
+        return 0;
+
+    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:
+    dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
+
+    tcpa_state.tpm_working = 0;
+    if (rc)
+        return rc;
+    return TCG_TCG_COMMAND_ERROR;
+}
+
+
+u32
+tcpa_s3_resume(void)
+{
+    u32 rc;
+    u32 returnCode;
+
+    if (!CONFIG_TCGBIOS)
+        return 0;
+
+    if (!has_working_tpm())
+        return 0;
+
+    rc = build_and_send_cmd(TPM_ORD_Startup,
+                            Startup_ST_STATE,
+                            sizeof(Startup_ST_STATE),
+                            NULL, 10, &returnCode);
+
+    dprintf(DEBUG_tcg, "TCGBIOS: Resuming with TPM_Startup(ST_STATE)\n");
+    dprintf(DEBUG_tcg, "TCGBIOS: ReturnCode from TPM_Startup = 0x%08x\n",
+            returnCode);
+
+    if (rc || returnCode)
+        goto err_exit;
+
+    return 0;
+
+err_exit:
+    dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__);
+
+    tcpa_state.tpm_working = 0;
+    if (rc)
+        return rc;
+    return TCG_TCG_COMMAND_ERROR;
+}
+
+
+