tpm_ibmvtpm: properly handle interrupted packet receptions
authorStefan Berger <stefanb@linux.vnet.ibm.com>
Wed, 9 Dec 2015 13:52:01 +0000 (08:52 -0500)
committerJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Sun, 20 Dec 2015 13:27:12 +0000 (15:27 +0200)
When the TPM response reception is interrupted in the wait_event_interruptable
call, the TPM is still busy processing the command and will only deliver the
response later. So we have to wait for an outstanding response before sending
a new request to avoid trying to put a 2nd request into the CRQ. Also reset
the res_len before sending a command so we will end up in that
wait_event_interruptable() waiting for the response rather than reading the
command packet as a response.

The easiest way to trigger the problem is to run the following

cd /sys/device/vio/71000004

while :; cat pcrs >/dev/null; done

And press Ctrl-C. This will then display an error

tpm_ibmvtpm 71000004: tpm_transmit: tpm_recv: error -4

followed by several other errors once interaction with the TPM resumes.

tpm_ibmvtpm 71000004: A TPM error (101) occurred attempting to determine the number of PCRS.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
Tested-by: Hon Ching(Vicky) Lo <honclo@linux.vnet.ibm.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Reviewed-by: Ashley Lai <ashley@ashleylai.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Acked-by: Peter Huewe <peterhuewe@gmx.de>
drivers/char/tpm/tpm_ibmvtpm.c
drivers/char/tpm/tpm_ibmvtpm.h

index 3e6a226..b0a9a9e 100644 (file)
@@ -90,7 +90,7 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
                return 0;
        }
 
-       sig = wait_event_interruptible(ibmvtpm->wq, ibmvtpm->res_len != 0);
+       sig = wait_event_interruptible(ibmvtpm->wq, !ibmvtpm->tpm_processing_cmd);
        if (sig)
                return -EINTR;
 
@@ -125,7 +125,7 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
        struct ibmvtpm_dev *ibmvtpm;
        struct ibmvtpm_crq crq;
        __be64 *word = (__be64 *)&crq;
-       int rc;
+       int rc, sig;
 
        ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip);
 
@@ -141,18 +141,35 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
                return -EIO;
        }
 
+       if (ibmvtpm->tpm_processing_cmd) {
+               dev_info(ibmvtpm->dev,
+                        "Need to wait for TPM to finish\n");
+               /* wait for previous command to finish */
+               sig = wait_event_interruptible(ibmvtpm->wq, !ibmvtpm->tpm_processing_cmd);
+               if (sig)
+                       return -EINTR;
+       }
+
        spin_lock(&ibmvtpm->rtce_lock);
+       ibmvtpm->res_len = 0;
        memcpy((void *)ibmvtpm->rtce_buf, (void *)buf, count);
        crq.valid = (u8)IBMVTPM_VALID_CMD;
        crq.msg = (u8)VTPM_TPM_COMMAND;
        crq.len = cpu_to_be16(count);
        crq.data = cpu_to_be32(ibmvtpm->rtce_dma_handle);
 
+       /*
+        * set the processing flag before the Hcall, since we may get the
+        * result (interrupt) before even being able to check rc.
+        */
+       ibmvtpm->tpm_processing_cmd = true;
+
        rc = ibmvtpm_send_crq(ibmvtpm->vdev, be64_to_cpu(word[0]),
                              be64_to_cpu(word[1]));
        if (rc != H_SUCCESS) {
                dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc);
                rc = 0;
+               ibmvtpm->tpm_processing_cmd = false;
        } else
                rc = count;
 
@@ -515,6 +532,7 @@ static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq,
                case VTPM_TPM_COMMAND_RES:
                        /* len of the data in rtce buffer */
                        ibmvtpm->res_len = be16_to_cpu(crq->len);
+                       ibmvtpm->tpm_processing_cmd = false;
                        wake_up_interruptible(&ibmvtpm->wq);
                        return;
                default:
index 6af9289..91dfe76 100644 (file)
@@ -45,6 +45,7 @@ struct ibmvtpm_dev {
        wait_queue_head_t wq;
        u16 res_len;
        u32 vtpm_version;
+       bool tpm_processing_cmd;
 };
 
 #define CRQ_RES_BUF_SIZE       PAGE_SIZE