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

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
Loading
Loading
Loading
Loading
+73 −63
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2012 IBM Corporation
 * Copyright (C) 2012-2020 IBM Corporation
 *
 * Author: Ashley Lai <ashleydlai@gmail.com>
 *
@@ -134,6 +134,64 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
	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
 * @chip:	tpm chip struct
@@ -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)
{
	struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev);
	bool retry = true;
	int rc, sig;

	if (!ibmvtpm->rtce_buf) {
@@ -180,18 +239,27 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
	 */
	ibmvtpm->tpm_processing_cmd = true;

again:
	rc = ibmvtpm_send_crq(ibmvtpm->vdev,
			IBMVTPM_VALID_CMD, VTPM_TPM_COMMAND,
			count, ibmvtpm->rtce_dma_handle);
	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);
		rc = 0;
		ibmvtpm->tpm_processing_cmd = false;
	} else
		rc = 0;
	}

	spin_unlock(&ibmvtpm->rtce_lock);
	return rc;
	return 0;
}

static void tpm_ibmvtpm_cancel(struct tpm_chip *chip)
@@ -269,26 +337,6 @@ static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm)
	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
 * @vdev:	vio device struct
@@ -401,44 +449,6 @@ static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm)
				  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)
{
	return (status == 0);