Commit eba5cf3d authored by George Wilson's avatar George Wilson Committed by Jarkko Sakkinen

tpm: ibmvtpm: retry on H_CLOSED in tpm_ibmvtpm_send()

tpm_ibmvtpm_send() can fail during PowerVM Live Partition Mobility resume
with an H_CLOSED return from ibmvtpm_send_crq().  The PAPR says, 'The
"partner partition suspended" transport event disables the associated CRQ
such that any H_SEND_CRQ hcall() to the associated CRQ returns H_Closed
until the CRQ has been explicitly enabled using the H_ENABLE_CRQ hcall.'
This patch adds a check in tpm_ibmvtpm_send() for an H_CLOSED return from
ibmvtpm_send_crq() and in that case calls tpm_ibmvtpm_resume() and
retries the ibmvtpm_send_crq() once.

Cc: stable@vger.kernel.org # 3.7.x
Fixes: 132f7629 ("drivers/char/tpm: Add new device driver to support IBM vTPM")
Reported-by: default avatarLinh Pham <phaml@us.ibm.com>
Reviewed-by: default avatarStefan Berger <stefanb@linux.ibm.com>
Signed-off-by: default avatarGeorge Wilson <gcwilson@linux.ibm.com>
Tested-by: default avatarLinh Pham <phaml@us.ibm.com>
Reviewed-by: default avatarJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: default avatarJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
parent 684c6bd8
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* Copyright (C) 2012 IBM Corporation * Copyright (C) 2012-2020 IBM Corporation
* *
* Author: Ashley Lai <ashleydlai@gmail.com> * Author: Ashley Lai <ashleydlai@gmail.com>
* *
...@@ -134,6 +134,64 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) ...@@ -134,6 +134,64 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
return len; return len;
} }
/**
* ibmvtpm_crq_send_init - Send a CRQ initialize message
* @ibmvtpm: vtpm device struct
*
* Return:
* 0 on success.
* Non-zero on failure.
*/
static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm)
{
int rc;
rc = ibmvtpm_send_crq_word(ibmvtpm->vdev, INIT_CRQ_CMD);
if (rc != H_SUCCESS)
dev_err(ibmvtpm->dev,
"%s failed rc=%d\n", __func__, rc);
return rc;
}
/**
* tpm_ibmvtpm_resume - Resume from suspend
*
* @dev: device struct
*
* Return: Always 0.
*/
static int tpm_ibmvtpm_resume(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
int rc = 0;
do {
if (rc)
msleep(100);
rc = plpar_hcall_norets(H_ENABLE_CRQ,
ibmvtpm->vdev->unit_address);
} while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
if (rc) {
dev_err(dev, "Error enabling ibmvtpm rc=%d\n", rc);
return rc;
}
rc = vio_enable_interrupts(ibmvtpm->vdev);
if (rc) {
dev_err(dev, "Error vio_enable_interrupts rc=%d\n", rc);
return rc;
}
rc = ibmvtpm_crq_send_init(ibmvtpm);
if (rc)
dev_err(dev, "Error send_init rc=%d\n", rc);
return rc;
}
/** /**
* tpm_ibmvtpm_send() - Send a TPM command * tpm_ibmvtpm_send() - Send a TPM command
* @chip: tpm chip struct * @chip: tpm chip struct
...@@ -147,6 +205,7 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) ...@@ -147,6 +205,7 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
{ {
struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
bool retry = true;
int rc, sig; int rc, sig;
if (!ibmvtpm->rtce_buf) { if (!ibmvtpm->rtce_buf) {
...@@ -180,18 +239,27 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) ...@@ -180,18 +239,27 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
*/ */
ibmvtpm->tpm_processing_cmd = true; ibmvtpm->tpm_processing_cmd = true;
again:
rc = ibmvtpm_send_crq(ibmvtpm->vdev, rc = ibmvtpm_send_crq(ibmvtpm->vdev,
IBMVTPM_VALID_CMD, VTPM_TPM_COMMAND, IBMVTPM_VALID_CMD, VTPM_TPM_COMMAND,
count, ibmvtpm->rtce_dma_handle); count, ibmvtpm->rtce_dma_handle);
if (rc != H_SUCCESS) { if (rc != H_SUCCESS) {
/*
* H_CLOSED can be returned after LPM resume. Call
* tpm_ibmvtpm_resume() to re-enable the CRQ then retry
* ibmvtpm_send_crq() once before failing.
*/
if (rc == H_CLOSED && retry) {
tpm_ibmvtpm_resume(ibmvtpm->dev);
retry = false;
goto again;
}
dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc); dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc);
rc = 0;
ibmvtpm->tpm_processing_cmd = false; ibmvtpm->tpm_processing_cmd = false;
} else }
rc = 0;
spin_unlock(&ibmvtpm->rtce_lock); spin_unlock(&ibmvtpm->rtce_lock);
return rc; return 0;
} }
static void tpm_ibmvtpm_cancel(struct tpm_chip *chip) static void tpm_ibmvtpm_cancel(struct tpm_chip *chip)
...@@ -269,26 +337,6 @@ static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm) ...@@ -269,26 +337,6 @@ static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm)
return rc; return rc;
} }
/**
* ibmvtpm_crq_send_init - Send a CRQ initialize message
* @ibmvtpm: vtpm device struct
*
* Return:
* 0 on success.
* Non-zero on failure.
*/
static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm)
{
int rc;
rc = ibmvtpm_send_crq_word(ibmvtpm->vdev, INIT_CRQ_CMD);
if (rc != H_SUCCESS)
dev_err(ibmvtpm->dev,
"ibmvtpm_crq_send_init failed rc=%d\n", rc);
return rc;
}
/** /**
* tpm_ibmvtpm_remove - ibm vtpm remove entry point * tpm_ibmvtpm_remove - ibm vtpm remove entry point
* @vdev: vio device struct * @vdev: vio device struct
...@@ -401,44 +449,6 @@ static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm) ...@@ -401,44 +449,6 @@ static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm)
ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE); ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE);
} }
/**
* tpm_ibmvtpm_resume - Resume from suspend
*
* @dev: device struct
*
* Return: Always 0.
*/
static int tpm_ibmvtpm_resume(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
int rc = 0;
do {
if (rc)
msleep(100);
rc = plpar_hcall_norets(H_ENABLE_CRQ,
ibmvtpm->vdev->unit_address);
} while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
if (rc) {
dev_err(dev, "Error enabling ibmvtpm rc=%d\n", rc);
return rc;
}
rc = vio_enable_interrupts(ibmvtpm->vdev);
if (rc) {
dev_err(dev, "Error vio_enable_interrupts rc=%d\n", rc);
return rc;
}
rc = ibmvtpm_crq_send_init(ibmvtpm);
if (rc)
dev_err(dev, "Error send_init rc=%d\n", rc);
return rc;
}
static bool tpm_ibmvtpm_req_canceled(struct tpm_chip *chip, u8 status) static bool tpm_ibmvtpm_req_canceled(struct tpm_chip *chip, u8 status)
{ {
return (status == 0); return (status == 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment