tpm: Implement tpm20_menu

In the TPM 2 menu we currently only allow to run the TPM2_Clear operation.
For this we follow the TCG Physical Presence Interface Specification
to be found here:

http://www.trustedcomputinggroup.org/resources/tcg_physical_presence_interface_specification

Table 3 shows the 'Clear' operation and the sequence of commands to send.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
diff --git a/src/std/tcg.h b/src/std/tcg.h
index d45c7f6..dd860e6 100644
--- a/src/std/tcg.h
+++ b/src/std/tcg.h
@@ -382,6 +382,8 @@
 #define TPM2_ST_SESSIONS            0x8002
 
 /* TPM 2 commands */
+#define TPM2_CC_Clear               0x126
+#define TPM2_CC_ClearControl        0x127
 #define TPM2_CC_HierarchyChangeAuth 0x129
 #define TPM2_CC_SelfTest            0x143
 #define TPM2_CC_Startup             0x144
@@ -443,4 +445,19 @@
     struct tpm2_digest_value digest;
 } PACKED;
 
+struct tpm2_req_clearcontrol {
+    struct tpm_req_header hdr;
+    u32 authhandle;
+    u32 authblocksize;
+    struct tpm2_authblock authblock;
+    u8 disable;
+} PACKED;
+
+struct tpm2_req_clear {
+    struct tpm_req_header hdr;
+    u32 authhandle;
+    u32 authblocksize;
+    struct tpm2_authblock authblock;
+} PACKED;
+
 #endif // tcg.h
diff --git a/src/tcgbios.c b/src/tcgbios.c
index 435e2eb..cd2b228 100644
--- a/src/tcgbios.c
+++ b/src/tcgbios.c
@@ -1441,6 +1441,87 @@
 }
 
 static int
+tpm20_clearcontrol(u8 disable, int verbose)
+{
+    struct tpm2_req_clearcontrol trc = {
+        .hdr.tag     = cpu_to_be16(TPM2_ST_SESSIONS),
+        .hdr.totlen  = cpu_to_be32(sizeof(trc)),
+        .hdr.ordinal = cpu_to_be32(TPM2_CC_ClearControl),
+        .authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+        .authblocksize = cpu_to_be32(sizeof(trc.authblock)),
+        .authblock = {
+            .handle = cpu_to_be32(TPM2_RS_PW),
+            .noncesize = cpu_to_be16(0),
+            .contsession = TPM2_YES,
+            .pwdsize = cpu_to_be16(0),
+        },
+        .disable = disable,
+    };
+    struct tpm_rsp_header rsp;
+    u32 resp_length = sizeof(rsp);
+    int ret = tpmhw_transmit(0, &trc.hdr, &rsp, &resp_length,
+                             TPM_DURATION_TYPE_SHORT);
+    if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+        ret = -1;
+
+    dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_ClearControl = 0x%08x\n",
+            ret);
+
+    return ret;
+}
+
+static int
+tpm20_clear(void)
+{
+    struct tpm2_req_clear trq = {
+        .hdr.tag     = cpu_to_be16(TPM2_ST_SESSIONS),
+        .hdr.totlen  = cpu_to_be32(sizeof(trq)),
+        .hdr.ordinal = cpu_to_be32(TPM2_CC_Clear),
+        .authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+        .authblocksize = cpu_to_be32(sizeof(trq.authblock)),
+        .authblock = {
+            .handle = cpu_to_be32(TPM2_RS_PW),
+            .noncesize = cpu_to_be16(0),
+            .contsession = TPM2_YES,
+            .pwdsize = cpu_to_be16(0),
+        },
+    };
+    struct tpm_rsp_header rsp;
+    u32 resp_length = sizeof(rsp);
+    int ret = tpmhw_transmit(0, &trq.hdr, &rsp, &resp_length,
+                             TPM_DURATION_TYPE_MEDIUM);
+    if (ret || resp_length != sizeof(rsp) || rsp.errcode)
+        ret = -1;
+
+    dprintf(DEBUG_tcg, "TCGBIOS: Return value from sending TPM2_CC_Clear = 0x%08x\n",
+            ret);
+
+    return ret;
+}
+
+static int
+tpm20_process_cfg(tpm_ppi_code msgCode, int verbose)
+{
+    int ret = 0;
+
+    switch (msgCode) {
+        case TPM_PPI_OP_NOOP: /* no-op */
+            break;
+
+        case TPM_PPI_OP_CLEAR:
+            ret = tpm20_clearcontrol(0, verbose);
+            if (!ret)
+                 ret = tpm20_clear();
+            break;
+    }
+
+    if (ret)
+        printf("Op %d: An error occurred: 0x%x\n", msgCode, ret);
+
+    return ret;
+}
+
+static int
 tpm12_get_tpm_state(void)
 {
     int state = 0;
@@ -1614,6 +1695,40 @@
     }
 }
 
+static void
+tpm20_menu(void)
+{
+    int scan_code;
+    tpm_ppi_code msgCode;
+
+    for (;;) {
+        printf("1. Clear TPM\n");
+
+        printf("\nIf no change is desired or if this menu was reached by "
+               "mistake, press ESC to\n"
+               "reboot the machine.\n");
+
+        msgCode = TPM_PPI_OP_NOOP;
+
+        while ((scan_code = get_keystroke(1000)) == ~0)
+            ;
+
+        switch (scan_code) {
+        case 1:
+            // ESC
+            reset();
+            break;
+        case 2:
+            msgCode = TPM_PPI_OP_CLEAR;
+            break;
+        default:
+            continue;
+        }
+
+        tpm20_process_cfg(msgCode, 0);
+    }
+}
+
 void
 tpm_menu(void)
 {
@@ -1629,7 +1744,7 @@
         tpm12_menu();
         break;
     case TPM_VERSION_2:
-        // FIXME: missing code
+        tpm20_menu();
         break;
     }
 }