swtpm: Track last command processed by the TPM

Track the last command processed by the TPM so we can determine whether
we may need to send a TPM2_Shutdown() before reset of the TPM 2.

Introduce a variable lastCommand to help track the last command that
was sent to the TPM 2.

In relation to deciding whether a TPM2_Shutdown() needs to be sent, the
tracking of the last-sent command is merely an optimization since for
example a VM with EDK2 will send a TPM2_Shutdown() followed by a
TPM2_GetRandom() upon suspend-to-ram, thus indicating that the last
command was TPM2_GetRandom(). However, under most circumstances it helps
to avoid sending an additional TPM2_Shutdown() if the OS TPM driver sent
one already.

When the suspended VM resume swtpm gets a CMD_INIT that requires swtpm
to decide whether a TPM2_Shutdown() needs to be sent and per the last-sent
command it will then send a TPM2_Shutdown(SU_STATE) as in the abrupt
termination case.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
diff --git a/src/swtpm/cuse_tpm.c b/src/swtpm/cuse_tpm.c
index e73b413..dcbe018 100644
--- a/src/swtpm/cuse_tpm.c
+++ b/src/swtpm/cuse_tpm.c
@@ -110,6 +110,9 @@
 /* the fuse_session that we will signal an exit to to exit the prg. */
 static struct fuse_session *ptm_fuse_session;
 
+/* last command sent to the TPM */
+static uint32_t g_lastCommand = TPM_ORDINAL_NONE;
+
 #if GLIB_MAJOR_VERSION >= 2
 # if GLIB_MINOR_VERSION >= 32
 
@@ -518,6 +521,8 @@
                                tpmversion,
                                command, max_command_length);
     if (command_length > 0) {
+        g_lastCommand = tpmlib_get_cmd_ordinal(command, command_length);
+
         rc = TPMLIB_Process(&ptm_response, &ptm_res_len, &ptm_res_tot,
                            (unsigned char *)command, command_length);
         ptm_read_offset = 0;
@@ -871,6 +876,8 @@
 static void ptm_write_cmd(fuse_req_t req, const char *buf, size_t size,
                           TPMLIB_TPMVersion l_tpmversion)
 {
+    uint32_t lastCommand;
+
     ptm_req_len = size;
     ptm_res_len = 0;
 
@@ -896,6 +903,10 @@
             goto skip_process;
         }
 
+        lastCommand = tpmlib_get_cmd_ordinal((unsigned char *)buf, ptm_req_len);
+        if (lastCommand != TPM_ORDINAL_NONE)
+            g_lastCommand = lastCommand;
+
         if (tpmlib_is_request_cancelable(l_tpmversion,
                                          (const unsigned char*)buf,
                                          ptm_req_len)) {
diff --git a/src/swtpm/mainloop.c b/src/swtpm/mainloop.c
index 44d67b7..5280f90 100644
--- a/src/swtpm/mainloop.c
+++ b/src/swtpm/mainloop.c
@@ -102,6 +102,7 @@
     struct iovec        iov[3];
     uint32_t            ack = htobe32(0);
     struct tpm2_resp_prefix respprefix;
+    uint32_t            lastCommand;
 
     /* poolfd[] indexes */
     enum {
@@ -141,9 +142,11 @@
                                   mlp->startupType,
                                   mlp->tpmversion,
                                   command, max_command_length);
-        if (command_length > 0)
+        if (command_length > 0) {
+            mlp->lastCommand = tpmlib_get_cmd_ordinal(command, command_length);
             rc = TPMLIB_Process(&rbuffer, &rlength, &rTotal,
                                 command, command_length);
+        }
 
         if (rc || command_length == 0) {
             mainloop_terminate = true;
@@ -274,6 +277,14 @@
             }
 
             if (rc == 0) {
+                lastCommand =
+                    tpmlib_get_cmd_ordinal(&command[cmd_offset],
+                                           command_length - cmd_offset);
+                if (lastCommand != TPM_ORDINAL_NONE)
+                    mlp->lastCommand = lastCommand;
+            }
+
+            if (rc == 0) {
                 rlength = 0;                                /* clear the response buffer */
                 rc = tpmlib_process(&rbuffer,
                                     &rlength,
diff --git a/src/swtpm/mainloop.h b/src/swtpm/mainloop.h
index c006d14..8feed1e 100644
--- a/src/swtpm/mainloop.h
+++ b/src/swtpm/mainloop.h
@@ -60,6 +60,7 @@
     uint32_t locality_flags;
     TPMLIB_TPMVersion tpmversion;
     uint16_t startupType; /* use TPM 1.2 types */
+    uint32_t lastCommand; /* last Command sent to TPM */
 };
 
 int mainLoop(struct mainLoopParams *mlp,
diff --git a/src/swtpm/swtpm.c b/src/swtpm/swtpm.c
index b8bf8d9..c2c255b 100644
--- a/src/swtpm/swtpm.c
+++ b/src/swtpm/swtpm.c
@@ -215,6 +215,7 @@
         .locality_flags = 0,
         .tpmversion = TPMLIB_TPM_VERSION_1_2,
         .startupType = _TPM_ST_NONE,
+        .lastCommand = TPM_ORDINAL_NONE,
     };
     struct server *server = NULL;
     unsigned long val;
diff --git a/src/swtpm/swtpm_chardev.c b/src/swtpm/swtpm_chardev.c
index 6c9ef13..a1d6c8d 100644
--- a/src/swtpm/swtpm_chardev.c
+++ b/src/swtpm/swtpm_chardev.c
@@ -274,6 +274,7 @@
         .locality_flags = 0,
         .tpmversion = TPMLIB_TPM_VERSION_1_2,
         .startupType = _TPM_ST_NONE,
+        .lastCommand = TPM_ORDINAL_NONE,
     };
     unsigned long val;
     char *end_ptr;
diff --git a/src/swtpm/tpmlib.c b/src/swtpm/tpmlib.c
index 4771995..b29a3d0 100644
--- a/src/swtpm/tpmlib.c
+++ b/src/swtpm/tpmlib.c
@@ -155,6 +155,17 @@
     return result;
 }
 
+uint32_t tpmlib_get_cmd_ordinal(const unsigned char *request, size_t req_len)
+{
+    struct tpm_req_header *hdr;
+
+    if (req_len < sizeof(struct tpm_req_header))
+        return TPM_ORDINAL_NONE;
+
+    hdr = (struct tpm_req_header *)request;
+    return be32toh(hdr->ordinal);
+}
+
 bool tpmlib_is_request_cancelable(TPMLIB_TPMVersion tpmversion,
                                   const unsigned char *request, size_t req_len)
 {
diff --git a/src/swtpm/tpmlib.h b/src/swtpm/tpmlib.h
index 16ab717..2f018cf 100644
--- a/src/swtpm/tpmlib.h
+++ b/src/swtpm/tpmlib.h
@@ -49,6 +49,7 @@
 TPM_RESULT tpmlib_choose_tpm_version(TPMLIB_TPMVersion tpmversion);
 TPM_RESULT tpmlib_start(uint32_t flags, TPMLIB_TPMVersion tpmversion);
 int tpmlib_get_tpm_property(enum TPMLIB_TPMProperty prop);
+uint32_t tpmlib_get_cmd_ordinal(const unsigned char *request, size_t req_len);
 bool tpmlib_is_request_cancelable(TPMLIB_TPMVersion tpmversion,
                                   const unsigned char *request, size_t req_len);
 void tpmlib_write_fatal_error_response(unsigned char **rbuffer,
@@ -142,4 +143,7 @@
 #define TPM2_SU_CLEAR                  0x0000
 #define TPM2_SU_STATE                  0x0001
 
+/* common */
+#define TPM_ORDINAL_NONE               0x00000000
+
 #endif /* _SWTPM_TPMLIB_H_ */