diff mbox series

tpm: Detach page allocation from tpm_buf

Message ID 20190926172324.3405-1-jarkko.sakkinen@linux.intel.com (mailing list archive)
State New, archived
Headers show
Series tpm: Detach page allocation from tpm_buf | expand

Commit Message

Jarkko Sakkinen Sept. 26, 2019, 5:23 p.m. UTC
As has been seen recently, binding the buffer allocation and tpm_buf
together is sometimes far from optimal. The buffer might come from the
caller namely when tpm_send() is used by another subsystem. In addition we
can stability in call sites w/o rollback (e.g. power events)>

Take allocation out of the tpm_buf framework and make it purely a wrapper
for the data buffer.

Link: https://patchwork.kernel.org/patch/11146585/
Cc: Mimi Zohar <zohar@linux.ibm.com>
Cc: Jerry Snitselaar <jsnitsel@redhat.com>
Cc: James Bottomley <James.Bottomley@HansenPartnership.com>
Cc: Sumit Garg <sumit.garg@linaro.org>
Cc: Stefan Berger <stefanb@linux.vnet.ibm.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
---
v2:
* In tpm2_get_random(), TPM2_CC_GET_RANDOM was accidently switch to
  TPM2_CC_PCR_EXTEND. Now it has been switched back.
 drivers/char/tpm/tpm-sysfs.c      |  19 ++-
 drivers/char/tpm/tpm.h            |  40 ++---
 drivers/char/tpm/tpm1-cmd.c       | 114 +++++++++----
 drivers/char/tpm/tpm2-cmd.c       | 265 +++++++++++++++++++-----------
 drivers/char/tpm/tpm2-space.c     |  64 +++++---
 drivers/char/tpm/tpm_vtpm_proxy.c |  24 +--
 6 files changed, 333 insertions(+), 193 deletions(-)

Comments

Jarkko Sakkinen Sept. 27, 2019, 3:42 p.m. UTC | #1
On Thu, Sep 26, 2019 at 08:23:24PM +0300, Jarkko Sakkinen wrote:
> As has been seen recently, binding the buffer allocation and tpm_buf
> together is sometimes far from optimal. The buffer might come from the
> caller namely when tpm_send() is used by another subsystem. In addition we
> can stability in call sites w/o rollback (e.g. power events)>
> 
> Take allocation out of the tpm_buf framework and make it purely a wrapper
> for the data buffer.
> 
> Link: https://patchwork.kernel.org/patch/11146585/
> Cc: Mimi Zohar <zohar@linux.ibm.com>
> Cc: Jerry Snitselaar <jsnitsel@redhat.com>
> Cc: James Bottomley <James.Bottomley@HansenPartnership.com>
> Cc: Sumit Garg <sumit.garg@linaro.org>
> Cc: Stefan Berger <stefanb@linux.vnet.ibm.com>
> Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> ---
> v2:
> * In tpm2_get_random(), TPM2_CC_GET_RANDOM was accidently switch to
>   TPM2_CC_PCR_EXTEND. Now it has been switched back.
Forgot --subject-prefix="PATCH v2".

/Jarkko
Jerry Snitselaar Sept. 28, 2019, 7:58 a.m. UTC | #2
On Thu Sep 26 19, Jarkko Sakkinen wrote:
>As has been seen recently, binding the buffer allocation and tpm_buf
>together is sometimes far from optimal. The buffer might come from the
>caller namely when tpm_send() is used by another subsystem. In addition we
>can stability in call sites w/o rollback (e.g. power events)>
>
>Take allocation out of the tpm_buf framework and make it purely a wrapper
>for the data buffer.
>
>Link: https://patchwork.kernel.org/patch/11146585/
>Cc: Mimi Zohar <zohar@linux.ibm.com>
>Cc: Jerry Snitselaar <jsnitsel@redhat.com>
>Cc: James Bottomley <James.Bottomley@HansenPartnership.com>
>Cc: Sumit Garg <sumit.garg@linaro.org>
>Cc: Stefan Berger <stefanb@linux.vnet.ibm.com>
>Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
>---
>v2:
>* In tpm2_get_random(), TPM2_CC_GET_RANDOM was accidently switch to
>  TPM2_CC_PCR_EXTEND. Now it has been switched back.
> drivers/char/tpm/tpm-sysfs.c      |  19 ++-
> drivers/char/tpm/tpm.h            |  40 ++---
> drivers/char/tpm/tpm1-cmd.c       | 114 +++++++++----
> drivers/char/tpm/tpm2-cmd.c       | 265 +++++++++++++++++++-----------
> drivers/char/tpm/tpm2-space.c     |  64 +++++---
> drivers/char/tpm/tpm_vtpm_proxy.c |  24 +--
> 6 files changed, 333 insertions(+), 193 deletions(-)
>
>diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
>index edfa89160010..eeb90c9225b9 100644
>--- a/drivers/char/tpm/tpm-sysfs.c
>+++ b/drivers/char/tpm/tpm-sysfs.c
>@@ -32,21 +32,26 @@ struct tpm_readpubek_out {
> static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
> 			  char *buf)
> {
>-	struct tpm_buf tpm_buf;
>-	struct tpm_readpubek_out *out;
>-	int i;
>-	char *str = buf;
> 	struct tpm_chip *chip = to_tpm_chip(dev);
>+	struct tpm_readpubek_out *out;
>+	struct page *data_page;
>+	struct tpm_buf tpm_buf;
> 	char anti_replay[20];
>+	char *str = buf;
>+	int i;
>
> 	memset(&anti_replay, 0, sizeof(anti_replay));
>
> 	if (tpm_try_get_ops(chip))
> 		return 0;
>
>-	if (tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK))
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
> 		goto out_ops;
>
>+	tpm_buf_reset(&tpm_buf, kmap(data_page), PAGE_SIZE, TPM_TAG_RQU_COMMAND,
>+		      TPM_ORD_READPUBEK);
>+
> 	tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay));
>
> 	if (tpm_transmit_cmd(chip, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE,
>@@ -83,7 +88,9 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
> 	}
>
> out_buf:
>-	tpm_buf_destroy(&tpm_buf);
>+	kunmap(data_page);
>+	__free_page(data_page);
>+
> out_ops:
> 	tpm_put_ops(chip);
> 	return str - buf;
>diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
>index a7fea3e0ca86..45316e5d2d36 100644
>--- a/drivers/char/tpm/tpm.h
>+++ b/drivers/char/tpm/tpm.h
>@@ -284,36 +284,30 @@ enum tpm_buf_flags {
> };
>
> struct tpm_buf {
>-	struct page *data_page;
>-	unsigned int flags;
> 	u8 *data;
>+	unsigned int size;
>+	unsigned int flags;
> };
>
>-static inline void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
>+static inline void tpm_buf_reset(struct tpm_buf *buf, u8 *data,
>+	unsigned int size, u16 tag, u32 ordinal)
> {
>-	struct tpm_header *head = (struct tpm_header *)buf->data;
>+	struct tpm_header *head = (struct tpm_header *)data;
>
>-	head->tag = cpu_to_be16(tag);
>-	head->length = cpu_to_be32(sizeof(*head));
>-	head->ordinal = cpu_to_be32(ordinal);
>-}
>-
>-static inline int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
>-{
>-	buf->data_page = alloc_page(GFP_HIGHUSER);
>-	if (!buf->data_page)
>-		return -ENOMEM;
>+	/* sanity check */
>+	if (size < TPM_HEADER_SIZE) {
>+		WARN(1, "tpm_buf: overflow\n");
>+		buf->flags |= TPM_BUF_OVERFLOW;
>+		return;
>+	}
>
>+	buf->data = data;
>+	buf->size = size;
> 	buf->flags = 0;
>-	buf->data = kmap(buf->data_page);
>-	tpm_buf_reset(buf, tag, ordinal);
>-	return 0;
>-}
>
>-static inline void tpm_buf_destroy(struct tpm_buf *buf)
>-{
>-	kunmap(buf->data_page);
>-	__free_page(buf->data_page);
>+	head->tag = cpu_to_be16(tag);
>+	head->length = cpu_to_be32(sizeof(*head));
>+	head->ordinal = cpu_to_be32(ordinal);
> }
>
> static inline u32 tpm_buf_length(struct tpm_buf *buf)
>@@ -341,7 +335,7 @@ static inline void tpm_buf_append(struct tpm_buf *buf,
> 	if (buf->flags & TPM_BUF_OVERFLOW)
> 		return;
>
>-	if ((len + new_len) > PAGE_SIZE) {
>+	if ((len + new_len) > buf->size) {
> 		WARN(1, "tpm_buf: overflow\n");
> 		buf->flags |= TPM_BUF_OVERFLOW;
> 		return;
>diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
>index 149e953ca369..2753699454ab 100644
>--- a/drivers/char/tpm/tpm1-cmd.c
>+++ b/drivers/char/tpm/tpm1-cmd.c
>@@ -323,19 +323,25 @@ unsigned long tpm1_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
>  */
> static int tpm1_startup(struct tpm_chip *chip)
> {
>+	struct page *data_page;
> 	struct tpm_buf buf;
> 	int rc;
>
> 	dev_info(&chip->dev, "starting up the TPM manually\n");
>
>-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_STARTUP);
>-	if (rc < 0)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM_TAG_RQU_COMMAND,
>+		      TPM_ORD_STARTUP);
>
> 	tpm_buf_append_u16(&buf, TPM_ST_CLEAR);
>
> 	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
>-	tpm_buf_destroy(&buf);
>+
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
>
>@@ -448,18 +454,24 @@ int tpm1_get_timeouts(struct tpm_chip *chip)
> int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
> 		    const char *log_msg)
> {
>+	struct page *data_page;
> 	struct tpm_buf buf;
> 	int rc;
>
>-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM_TAG_RQU_COMMAND,
>+		      TPM_ORD_PCR_EXTEND);
>
> 	tpm_buf_append_u32(&buf, pcr_idx);
> 	tpm_buf_append(&buf, hash, TPM_DIGEST_SIZE);
>
> 	rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, log_msg);
>-	tpm_buf_destroy(&buf);
>+
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
>
>@@ -467,12 +479,16 @@ int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
> ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
> 		    const char *desc, size_t min_cap_length)
> {
>+	struct page *data_page;
> 	struct tpm_buf buf;
> 	int rc;
>
>-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_CAP);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM_TAG_RQU_COMMAND,
>+		      TPM_ORD_GET_CAP);
>
> 	if (subcap_id == TPM_CAP_VERSION_1_1 ||
> 	    subcap_id == TPM_CAP_VERSION_1_2) {
>@@ -491,7 +507,9 @@ ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
> 	rc = tpm_transmit_cmd(chip, &buf, min_cap_length, desc);
> 	if (!rc)
> 		*cap = *(cap_t *)&buf.data[TPM_HEADER_SIZE + 4];
>-	tpm_buf_destroy(&buf);
>+
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
> EXPORT_SYMBOL_GPL(tpm1_getcap);
>@@ -514,19 +532,26 @@ struct tpm1_get_random_out {
>  */
> int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> {
>-	struct tpm1_get_random_out *out;
> 	u32 num_bytes =  min_t(u32, max, TPM_MAX_RNG_DATA);
>+	struct tpm1_get_random_out *out;
>+	struct page *data_page;
> 	struct tpm_buf buf;
>-	u32 total = 0;
> 	int retries = 5;
>+	void *data_ptr;
>+	u32 total = 0;
> 	u32 recd;
> 	int rc;
>
>-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	data_ptr = kmap(data_page);
>
> 	do {
>+		tpm_buf_reset(&buf, data_ptr, PAGE_SIZE, TPM_TAG_RQU_COMMAND,
>+			      TPM_ORD_GET_RANDOM);
>+
> 		tpm_buf_append_u32(&buf, num_bytes);
>
> 		rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
>@@ -555,25 +580,29 @@ int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> 		dest += recd;
> 		total += recd;
> 		num_bytes -= recd;
>-
>-		tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
> 	} while (retries-- && total < max);
>
> 	rc = total ? (int)total : -EIO;
>+
> out:
>-	tpm_buf_destroy(&buf);
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
>
> #define TPM_ORD_PCRREAD 21
> int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
> {
>+	struct page *data_page;
> 	struct tpm_buf buf;
> 	int rc;
>
>-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCRREAD);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM_TAG_RQU_COMMAND,
>+		      TPM_ORD_PCRREAD);
>
> 	tpm_buf_append_u32(&buf, pcr_idx);
>
>@@ -590,7 +619,8 @@ int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
> 	memcpy(res_buf, &buf.data[TPM_HEADER_SIZE], TPM_DIGEST_SIZE);
>
> out:
>-	tpm_buf_destroy(&buf);
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
>
>@@ -604,15 +634,21 @@ int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
>  */
> static int tpm1_continue_selftest(struct tpm_chip *chip)
> {
>+	struct page *data_page;
> 	struct tpm_buf buf;
> 	int rc;
>
>-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM_TAG_RQU_COMMAND,
>+		      TPM_ORD_CONTINUE_SELFTEST);
>
> 	rc = tpm_transmit_cmd(chip, &buf, 0, "continue selftest");
>-	tpm_buf_destroy(&buf);
>+
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
>
>@@ -722,21 +758,28 @@ int tpm1_auto_startup(struct tpm_chip *chip)
> int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
> {
> 	u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 };
>+	struct page *data_page;
> 	struct tpm_buf buf;
> 	unsigned int try;
>+	void *data_ptr;
> 	int rc;
>
>-
> 	/* for buggy tpm, flush pcrs with extend to selected dummy */
> 	if (tpm_suspend_pcr)
> 		rc = tpm1_pcr_extend(chip, tpm_suspend_pcr, dummy_hash,
> 				     "extending dummy pcr before suspend");
>
>-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	data_ptr = kmap(data_page);
>+
> 	/* now do the actual savestate */
> 	for (try = 0; try < TPM_RETRY; try++) {
>+		tpm_buf_reset(&buf, data_ptr, PAGE_SIZE,
>+			      TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
>+
> 		rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
> 		/*
> 		 * If the TPM indicates that it is too busy to respond to
>@@ -750,9 +793,8 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
> 		 */
> 		if (rc != TPM_WARN_RETRY)
> 			break;
>-		tpm_msleep(TPM_TIMEOUT_RETRY);
>
>-		tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
>+		tpm_msleep(TPM_TIMEOUT_RETRY);
> 	}
>
> 	if (rc)
>@@ -762,8 +804,8 @@ int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
> 		dev_warn(&chip->dev, "TPM savestate took %dms\n",
> 			 try * TPM_TIMEOUT_RETRY);
>
>-	tpm_buf_destroy(&buf);
>-
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
>
>diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
>index ba9acae83bff..50674710aed0 100644
>--- a/drivers/char/tpm/tpm2-cmd.c
>+++ b/drivers/char/tpm/tpm2-cmd.c
>@@ -175,13 +175,14 @@ struct tpm2_pcr_read_out {
> int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> 		  struct tpm_digest *digest, u16 *digest_size_ptr)
> {
>-	int i;
>-	int rc;
>-	struct tpm_buf buf;
>-	struct tpm2_pcr_read_out *out;
> 	u8 pcr_select[TPM2_PCR_SELECT_MIN] = {0};
>-	u16 digest_size;
>+	struct tpm2_pcr_read_out *out;
> 	u16 expected_digest_size = 0;
>+	struct page *data_page;
>+	struct tpm_buf buf;
>+	u16 digest_size;
>+	int rc;
>+	int i;
>
> 	if (pcr_idx >= TPM2_PLATFORM_PCR)
> 		return -EINVAL;
>@@ -197,9 +198,12 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> 		expected_digest_size = chip->allocated_banks[i].digest_size;
> 	}
>
>-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
>+		      TPM2_CC_PCR_READ);
>
> 	pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
>
>@@ -225,8 +229,10 @@ int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
> 		*digest_size_ptr = digest_size;
>
> 	memcpy(digest->digest, out->digest, digest_size);
>+
> out:
>-	tpm_buf_destroy(&buf);
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
>
>@@ -249,14 +255,18 @@ struct tpm2_null_auth_area {
> int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
> 		    struct tpm_digest *digests)
> {
>-	struct tpm_buf buf;
> 	struct tpm2_null_auth_area auth_area;
>+	struct page *data_page;
>+	struct tpm_buf buf;
> 	int rc;
> 	int i;
>
>-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_SESSIONS,
>+		      TPM2_CC_PCR_EXTEND);
>
> 	tpm_buf_append_u32(&buf, pcr_idx);
>
>@@ -278,8 +288,8 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
>
> 	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
>
>-	tpm_buf_destroy(&buf);
>-
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
>
>@@ -302,23 +312,29 @@ struct tpm2_get_random_out {
> int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> {
> 	struct tpm2_get_random_out *out;
>+	struct page *data_page;
>+	u8 *dest_ptr = dest;
>+	u32 num_bytes = max;
> 	struct tpm_buf buf;
>+	void *data_ptr;
>+	int retries = 5;
>+	int total = 0;
> 	u32 recd;
>-	u32 num_bytes = max;
> 	int err;
>-	int total = 0;
>-	int retries = 5;
>-	u8 *dest_ptr = dest;
>
> 	if (!num_bytes || max > TPM_MAX_RNG_DATA)
> 		return -EINVAL;
>
>-	err = tpm_buf_init(&buf, 0, 0);
>-	if (err)
>-		return err;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	data_ptr = kmap(data_page);
>
> 	do {
>-		tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_RANDOM);
>+		tpm_buf_reset(&buf, data_ptr, PAGE_SIZE,
>+			      TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_EXTEND);

TPM2_CC_GET_RANDOM

>+
> 		tpm_buf_append_u16(&buf, num_bytes);
> 		err = tpm_transmit_cmd(chip, &buf,
> 				       offsetof(struct tpm2_get_random_out,
>@@ -347,10 +363,13 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
> 		num_bytes -= recd;
> 	} while (retries-- && total < max);
>
>-	tpm_buf_destroy(&buf);
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return total ? total : -EIO;
>+
> out:
>-	tpm_buf_destroy(&buf);
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return err;
> }

Would this work here?

      	err = total ? total : -EIO;
out:
	kunmap(data_page);
	__free_page(data_page);
	return err;


>
>@@ -361,20 +380,24 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
>  */
> void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
> {
>+	struct page *data_page;
> 	struct tpm_buf buf;
>-	int rc;
>
>-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
>-	if (rc) {
>-		dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
>-			 handle);
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page) {
>+		WARN(1, "tpm: out of memory");
> 		return;
> 	}
>
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
>+		      TPM2_CC_FLUSH_CONTEXT);
>+
> 	tpm_buf_append_u32(&buf, handle);
>
> 	tpm_transmit_cmd(chip, &buf, 0, "flushing context");
>-	tpm_buf_destroy(&buf);
>+
>+	kunmap(data_page);
>+	__free_page(data_page);
> }
>
> /**
>@@ -420,6 +443,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> 		      struct trusted_key_payload *payload,
> 		      struct trusted_key_options *options)
> {
>+	struct page *data_page;
> 	unsigned int blob_len;
> 	struct tpm_buf buf;
> 	u32 hash;
>@@ -436,9 +460,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> 	if (i == ARRAY_SIZE(tpm2_hash_map))
> 		return -EINVAL;
>
>-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_SESSIONS,
>+		      TPM2_CC_CREATE);
>
> 	tpm_buf_append_u32(&buf, options->keyhandle);
> 	tpm2_buf_append_auth(&buf, TPM2_RS_PW,
>@@ -505,7 +532,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
> 	payload->blob_len = blob_len;
>
> out:
>-	tpm_buf_destroy(&buf);
>+	kunmap(data_page);
>+	__free_page(data_page);
>
> 	if (rc > 0) {
> 		if (tpm2_rc_value(rc) == TPM2_RC_HASH)
>@@ -535,10 +563,11 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> 			 struct trusted_key_options *options,
> 			 u32 *blob_handle)
> {
>-	struct tpm_buf buf;
> 	unsigned int private_len;
> 	unsigned int public_len;
>+	struct page *data_page;
> 	unsigned int blob_len;
>+	struct tpm_buf buf;
> 	int rc;
>
> 	private_len = be16_to_cpup((__be16 *) &payload->blob[0]);
>@@ -550,9 +579,12 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> 	if (blob_len > payload->blob_len)
> 		return -E2BIG;
>
>-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_SESSIONS,
>+		      TPM2_CC_LOAD);
>
> 	tpm_buf_append_u32(&buf, options->keyhandle);
> 	tpm2_buf_append_auth(&buf, TPM2_RS_PW,
>@@ -574,7 +606,8 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
> 			(__be32 *) &buf.data[TPM_HEADER_SIZE]);
>
> out:
>-	tpm_buf_destroy(&buf);
>+	kunmap(data_page);
>+	__free_page(data_page);
>
> 	if (rc > 0)
> 		rc = -EPERM;
>@@ -599,14 +632,18 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> 			   struct trusted_key_options *options,
> 			   u32 blob_handle)
> {
>+	struct page *data_page;
> 	struct tpm_buf buf;
> 	u16 data_len;
> 	u8 *data;
> 	int rc;
>
>-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_SESSIONS,
>+		      TPM2_CC_UNSEAL);
>
> 	tpm_buf_append_u32(&buf, blob_handle);
> 	tpm2_buf_append_auth(&buf,
>@@ -641,7 +678,8 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
> 	}
>
> out:
>-	tpm_buf_destroy(&buf);
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
>
>@@ -693,12 +731,17 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,  u32 *value,
> 			const char *desc)
> {
> 	struct tpm2_get_cap_out *out;
>+	struct page *data_page;
> 	struct tpm_buf buf;
> 	int rc;
>
>-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
>+		      TPM2_CC_GET_CAPABILITY);
>+
> 	tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
> 	tpm_buf_append_u32(&buf, property_id);
> 	tpm_buf_append_u32(&buf, 1);
>@@ -708,7 +751,9 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,  u32 *value,
> 			&buf.data[TPM_HEADER_SIZE];
> 		*value = be32_to_cpu(out->value);
> 	}
>-	tpm_buf_destroy(&buf);
>+
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
> EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
>@@ -725,15 +770,23 @@ EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
>  */
> void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
> {
>+	struct page *data_page;
> 	struct tpm_buf buf;
>-	int rc;
>
>-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN);
>-	if (rc)
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page) {
>+		WARN(1, "tpm: out of memory");
> 		return;
>+	}
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
>+		      TPM2_CC_SHUTDOWN);
>+
> 	tpm_buf_append_u16(&buf, shutdown_type);
> 	tpm_transmit_cmd(chip, &buf, 0, "stopping the TPM");
>-	tpm_buf_destroy(&buf);
>+
>+	kunmap(data_page);
>+	__free_page(data_page);
> }
>
> /**
>@@ -751,26 +804,36 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
>  */
> static int tpm2_do_selftest(struct tpm_chip *chip)
> {
>+	struct page *data_page;
> 	struct tpm_buf buf;
>+	void *data_ptr;
> 	int full;
> 	int rc;
>
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	data_ptr = kmap(data_page);
>+
> 	for (full = 0; full < 2; full++) {
>-		rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
>-		if (rc)
>-			return rc;
>+		tpm_buf_reset(&buf, data_ptr, PAGE_SIZE, TPM2_ST_NO_SESSIONS,
>+			      TPM2_CC_SELF_TEST);
>
> 		tpm_buf_append_u8(&buf, full);
>+
> 		rc = tpm_transmit_cmd(chip, &buf, 0,
> 				      "attempting the self test");
>-		tpm_buf_destroy(&buf);
>
> 		if (rc == TPM2_RC_TESTING)
> 			rc = TPM2_RC_SUCCESS;
>+
> 		if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS)
>-			return rc;
>+			break;
> 	}
>
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
>
>@@ -788,16 +851,22 @@ static int tpm2_do_selftest(struct tpm_chip *chip)
>  */
> int tpm2_probe(struct tpm_chip *chip)
> {
>+	struct page *data_page;
> 	struct tpm_header *out;
> 	struct tpm_buf buf;
> 	int rc;
>
>-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
>+		      TPM2_CC_GET_CAPABILITY);
>+
> 	tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
> 	tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS);
> 	tpm_buf_append_u32(&buf, 1);
>+
> 	rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
> 	/* We ignore TPM return codes on purpose. */
> 	if (rc >=  0) {
>@@ -805,7 +874,9 @@ int tpm2_probe(struct tpm_chip *chip)
> 		if (be16_to_cpu(out->tag) == TPM2_ST_NO_SESSIONS)
> 			chip->flags |= TPM_CHIP_FLAG_TPM2;
> 	}
>-	tpm_buf_destroy(&buf);
>+
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return 0;
> }
> EXPORT_SYMBOL_GPL(tpm2_probe);
>@@ -843,21 +914,25 @@ struct tpm2_pcr_selection {
> ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
> {
> 	struct tpm2_pcr_selection pcr_selection;
>-	struct tpm_buf buf;
>-	void *marker;
>-	void *end;
>-	void *pcr_select_offset;
> 	u32 sizeof_pcr_selection;
>-	u32 nr_possible_banks;
>+	void *pcr_select_offset;
> 	u32 nr_alloc_banks = 0;
>+	struct page *data_page;
>+	u32 nr_possible_banks;
>+	struct tpm_buf buf;
> 	u16 hash_alg;
>+	void *marker;
> 	u32 rsp_len;
>-	int rc;
> 	int i = 0;
>+	void *end;
>+	int rc;
>
>-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
>+		      TPM2_CC_GET_CAPABILITY);
>
> 	tpm_buf_append_u32(&buf, TPM2_CAP_PCRS);
> 	tpm_buf_append_u32(&buf, 0);
>@@ -913,14 +988,16 @@ ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
> 	}
>
> 	chip->nr_allocated_banks = nr_alloc_banks;
>-out:
>-	tpm_buf_destroy(&buf);
>
>+out:
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
>
> static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
> {
>+	struct page *data_page;
> 	struct tpm_buf buf;
> 	u32 nr_commands;
> 	__be32 *attrs;
>@@ -930,35 +1007,32 @@ static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
>
> 	rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL);
> 	if (rc)
>-		goto out;
>+		return rc;
>
>-	if (nr_commands > 0xFFFFF) {
>-		rc = -EFAULT;
>-		goto out;
>-	}
>+	if (nr_commands > 0xFFFFF)
>+		return -EFAULT;
>
> 	chip->cc_attrs_tbl = devm_kcalloc(&chip->dev, 4, nr_commands,
> 					  GFP_KERNEL);
>
>-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
>-	if (rc)
>-		goto out;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
>+		      TPM2_CC_GET_CAPABILITY);
>
> 	tpm_buf_append_u32(&buf, TPM2_CAP_COMMANDS);
> 	tpm_buf_append_u32(&buf, TPM2_CC_FIRST);
> 	tpm_buf_append_u32(&buf, nr_commands);
>
> 	rc = tpm_transmit_cmd(chip, &buf, 9 + 4 * nr_commands, NULL);
>-	if (rc) {
>-		tpm_buf_destroy(&buf);
>+	if (rc)
> 		goto out;
>-	}
>
> 	if (nr_commands !=
>-	    be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5])) {
>-		tpm_buf_destroy(&buf);
>+	    be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5]))
> 		goto out;
>-	}
>
> 	chip->nr_commands = nr_commands;
>
>@@ -974,11 +1048,13 @@ static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
> 		}
> 	}
>
>-	tpm_buf_destroy(&buf);
>-
> out:
>+	kunmap(data_page);
>+	__free_page(data_page);
>+
> 	if (rc > 0)
> 		rc = -ENODEV;
>+
> 	return rc;
> }
>
>@@ -995,19 +1071,24 @@ static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
>
> static int tpm2_startup(struct tpm_chip *chip)
> {
>+	struct page *data_page;
> 	struct tpm_buf buf;
> 	int rc;
>
> 	dev_info(&chip->dev, "starting up the TPM manually\n");
>
>-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
>-	if (rc < 0)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
>+		      TPM2_CC_STARTUP);
>
> 	tpm_buf_append_u16(&buf, TPM2_SU_CLEAR);
> 	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
>-	tpm_buf_destroy(&buf);
>
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return rc;
> }
>
>diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c
>index 982d341d8837..1d3e47392f23 100644
>--- a/drivers/char/tpm/tpm2-space.c
>+++ b/drivers/char/tpm/tpm2-space.c
>@@ -68,14 +68,18 @@ void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
> static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
> 			     unsigned int *offset, u32 *handle)
> {
>-	struct tpm_buf tbuf;
> 	struct tpm2_context *ctx;
>+	struct page *data_page;
> 	unsigned int body_size;
>+	struct tpm_buf tbuf;
> 	int rc;
>
>-	rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>+
>+	tpm_buf_reset(&tbuf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
>+		      TPM2_CC_CONTEXT_LOAD);
>
> 	ctx = (struct tpm2_context *)&buf[*offset];
> 	body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
>@@ -85,8 +89,8 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
> 	if (rc < 0) {
> 		dev_warn(&chip->dev, "%s: failed with a system error %d\n",
> 			 __func__, rc);
>-		tpm_buf_destroy(&tbuf);
>-		return -EFAULT;
>+		rc = -EFAULT;
>+		goto out;
> 	} else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||
> 		   rc == TPM2_RC_REFERENCE_H0) {
> 		/*
>@@ -100,62 +104,70 @@ static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
> 		 * flushed outside the space
> 		 */
> 		*handle = 0;
>-		tpm_buf_destroy(&tbuf);
>-		return -ENOENT;
>+		rc = -ENOENT;
>+		goto out;
> 	} else if (rc > 0) {
> 		dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
> 			 __func__, rc);
>-		tpm_buf_destroy(&tbuf);
>-		return -EFAULT;
>+		rc = -EFAULT;
>+		goto out;
> 	}
>
> 	*handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
> 	*offset += body_size;
>
>-	tpm_buf_destroy(&tbuf);
>-	return 0;
>+out:
>+	kunmap(data_page);
>+	__free_page(data_page);
>+	return rc;
> }
>
> static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
> 			     unsigned int buf_size, unsigned int *offset)
> {
>-	struct tpm_buf tbuf;
> 	unsigned int body_size;
>+	struct page *data_page;
>+	struct tpm_buf tbuf;
> 	int rc;
>
>-	rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
>-	if (rc)
>-		return rc;
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>
>+	tpm_buf_reset(&tbuf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
>+		      TPM2_CC_CONTEXT_SAVE);
> 	tpm_buf_append_u32(&tbuf, handle);
>
> 	rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL);
> 	if (rc < 0) {
> 		dev_warn(&chip->dev, "%s: failed with a system error %d\n",
> 			 __func__, rc);
>-		tpm_buf_destroy(&tbuf);
>-		return -EFAULT;
>+		rc = -EFAULT;
>+		goto out;
> 	} else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
>-		tpm_buf_destroy(&tbuf);
>-		return -ENOENT;
>+		rc = -ENOENT;
>+		goto out;
> 	} else if (rc) {
> 		dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
> 			 __func__, rc);
>-		tpm_buf_destroy(&tbuf);
>-		return -EFAULT;
>+		rc = -EFAULT;
>+		goto out;
> 	}
>
> 	body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
> 	if ((*offset + body_size) > buf_size) {
> 		dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
>-		tpm_buf_destroy(&tbuf);
>-		return -ENOMEM;
>+		rc = -ENOMEM;
>+		goto out;
> 	}
>
> 	memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
> 	*offset += body_size;
>-	tpm_buf_destroy(&tbuf);
>-	return 0;
>+
>+out:
>+	kunmap(data_page);
>+	__free_page(data_page);
>+	return rc;
> }
>
> void tpm2_flush_space(struct tpm_chip *chip)
>diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
>index 2f6e087ec496..e6e89be0e149 100644
>--- a/drivers/char/tpm/tpm_vtpm_proxy.c
>+++ b/drivers/char/tpm/tpm_vtpm_proxy.c
>@@ -394,19 +394,23 @@ static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip  *chip, u8 status)
>
> static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
> {
>+	struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
>+	const struct tpm_header *header;
>+	struct page *data_page;
> 	struct tpm_buf buf;
> 	int rc;
>-	const struct tpm_header *header;
>-	struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
>+
>+	data_page = alloc_page(GFP_HIGHUSER);
>+	if (!data_page)
>+		return -ENOMEM;
>
> 	if (chip->flags & TPM_CHIP_FLAG_TPM2)
>-		rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS,
>-				  TPM2_CC_SET_LOCALITY);
>+		tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE,
>+			      TPM2_ST_SESSIONS, TPM2_CC_SET_LOCALITY);
> 	else
>-		rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND,
>-				  TPM_ORD_SET_LOCALITY);
>-	if (rc)
>-		return rc;
>+		tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE,
>+			      TPM_TAG_RQU_COMMAND, TPM_ORD_SET_LOCALITY);
>+
> 	tpm_buf_append_u8(&buf, locality);
>
> 	proxy_dev->state |= STATE_DRIVER_COMMAND;
>@@ -426,8 +430,8 @@ static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
> 		locality = -1;
>
> out:
>-	tpm_buf_destroy(&buf);
>-
>+	kunmap(data_page);
>+	__free_page(data_page);
> 	return locality;
> }
>
>-- 
>2.20.1
>
Jarkko Sakkinen Oct. 1, 2019, 8:51 p.m. UTC | #3
On Sat, Sep 28, 2019 at 12:58:13AM -0700, Jerry Snitselaar wrote:
> TPM2_CC_GET_RANDOM

Ugh, I somehow sent v1 2nd time also in terms of contents (was fixed in
v2).

I think it would be a great idea to add call to tpm_get_random() to the
chip startup as an additional test.

> Would this work here?
> 
>      	err = total ? total : -EIO;
> out:
> 	kunmap(data_page);
> 	__free_page(data_page);
> 	return err;

I'd guess but I want to keep this commit as dumbed down and mechanical
as possible given the amount of changes.

/Jarkko
diff mbox series

Patch

diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index edfa89160010..eeb90c9225b9 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -32,21 +32,26 @@  struct tpm_readpubek_out {
 static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
 			  char *buf)
 {
-	struct tpm_buf tpm_buf;
-	struct tpm_readpubek_out *out;
-	int i;
-	char *str = buf;
 	struct tpm_chip *chip = to_tpm_chip(dev);
+	struct tpm_readpubek_out *out;
+	struct page *data_page;
+	struct tpm_buf tpm_buf;
 	char anti_replay[20];
+	char *str = buf;
+	int i;
 
 	memset(&anti_replay, 0, sizeof(anti_replay));
 
 	if (tpm_try_get_ops(chip))
 		return 0;
 
-	if (tpm_buf_init(&tpm_buf, TPM_TAG_RQU_COMMAND, TPM_ORD_READPUBEK))
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
 		goto out_ops;
 
+	tpm_buf_reset(&tpm_buf, kmap(data_page), PAGE_SIZE, TPM_TAG_RQU_COMMAND,
+		      TPM_ORD_READPUBEK);
+
 	tpm_buf_append(&tpm_buf, anti_replay, sizeof(anti_replay));
 
 	if (tpm_transmit_cmd(chip, &tpm_buf, READ_PUBEK_RESULT_MIN_BODY_SIZE,
@@ -83,7 +88,9 @@  static ssize_t pubek_show(struct device *dev, struct device_attribute *attr,
 	}
 
 out_buf:
-	tpm_buf_destroy(&tpm_buf);
+	kunmap(data_page);
+	__free_page(data_page);
+
 out_ops:
 	tpm_put_ops(chip);
 	return str - buf;
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index a7fea3e0ca86..45316e5d2d36 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -284,36 +284,30 @@  enum tpm_buf_flags {
 };
 
 struct tpm_buf {
-	struct page *data_page;
-	unsigned int flags;
 	u8 *data;
+	unsigned int size;
+	unsigned int flags;
 };
 
-static inline void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal)
+static inline void tpm_buf_reset(struct tpm_buf *buf, u8 *data,
+	unsigned int size, u16 tag, u32 ordinal)
 {
-	struct tpm_header *head = (struct tpm_header *)buf->data;
+	struct tpm_header *head = (struct tpm_header *)data;
 
-	head->tag = cpu_to_be16(tag);
-	head->length = cpu_to_be32(sizeof(*head));
-	head->ordinal = cpu_to_be32(ordinal);
-}
-
-static inline int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal)
-{
-	buf->data_page = alloc_page(GFP_HIGHUSER);
-	if (!buf->data_page)
-		return -ENOMEM;
+	/* sanity check */
+	if (size < TPM_HEADER_SIZE) {
+		WARN(1, "tpm_buf: overflow\n");
+		buf->flags |= TPM_BUF_OVERFLOW;
+		return;
+	}
 
+	buf->data = data;
+	buf->size = size;
 	buf->flags = 0;
-	buf->data = kmap(buf->data_page);
-	tpm_buf_reset(buf, tag, ordinal);
-	return 0;
-}
 
-static inline void tpm_buf_destroy(struct tpm_buf *buf)
-{
-	kunmap(buf->data_page);
-	__free_page(buf->data_page);
+	head->tag = cpu_to_be16(tag);
+	head->length = cpu_to_be32(sizeof(*head));
+	head->ordinal = cpu_to_be32(ordinal);
 }
 
 static inline u32 tpm_buf_length(struct tpm_buf *buf)
@@ -341,7 +335,7 @@  static inline void tpm_buf_append(struct tpm_buf *buf,
 	if (buf->flags & TPM_BUF_OVERFLOW)
 		return;
 
-	if ((len + new_len) > PAGE_SIZE) {
+	if ((len + new_len) > buf->size) {
 		WARN(1, "tpm_buf: overflow\n");
 		buf->flags |= TPM_BUF_OVERFLOW;
 		return;
diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c
index 149e953ca369..2753699454ab 100644
--- a/drivers/char/tpm/tpm1-cmd.c
+++ b/drivers/char/tpm/tpm1-cmd.c
@@ -323,19 +323,25 @@  unsigned long tpm1_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
  */
 static int tpm1_startup(struct tpm_chip *chip)
 {
+	struct page *data_page;
 	struct tpm_buf buf;
 	int rc;
 
 	dev_info(&chip->dev, "starting up the TPM manually\n");
 
-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_STARTUP);
-	if (rc < 0)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM_TAG_RQU_COMMAND,
+		      TPM_ORD_STARTUP);
 
 	tpm_buf_append_u16(&buf, TPM_ST_CLEAR);
 
 	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
-	tpm_buf_destroy(&buf);
+
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 
@@ -448,18 +454,24 @@  int tpm1_get_timeouts(struct tpm_chip *chip)
 int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
 		    const char *log_msg)
 {
+	struct page *data_page;
 	struct tpm_buf buf;
 	int rc;
 
-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM_TAG_RQU_COMMAND,
+		      TPM_ORD_PCR_EXTEND);
 
 	tpm_buf_append_u32(&buf, pcr_idx);
 	tpm_buf_append(&buf, hash, TPM_DIGEST_SIZE);
 
 	rc = tpm_transmit_cmd(chip, &buf, TPM_DIGEST_SIZE, log_msg);
-	tpm_buf_destroy(&buf);
+
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 
@@ -467,12 +479,16 @@  int tpm1_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, const u8 *hash,
 ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
 		    const char *desc, size_t min_cap_length)
 {
+	struct page *data_page;
 	struct tpm_buf buf;
 	int rc;
 
-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_CAP);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM_TAG_RQU_COMMAND,
+		      TPM_ORD_GET_CAP);
 
 	if (subcap_id == TPM_CAP_VERSION_1_1 ||
 	    subcap_id == TPM_CAP_VERSION_1_2) {
@@ -491,7 +507,9 @@  ssize_t tpm1_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
 	rc = tpm_transmit_cmd(chip, &buf, min_cap_length, desc);
 	if (!rc)
 		*cap = *(cap_t *)&buf.data[TPM_HEADER_SIZE + 4];
-	tpm_buf_destroy(&buf);
+
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 EXPORT_SYMBOL_GPL(tpm1_getcap);
@@ -514,19 +532,26 @@  struct tpm1_get_random_out {
  */
 int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {
-	struct tpm1_get_random_out *out;
 	u32 num_bytes =  min_t(u32, max, TPM_MAX_RNG_DATA);
+	struct tpm1_get_random_out *out;
+	struct page *data_page;
 	struct tpm_buf buf;
-	u32 total = 0;
 	int retries = 5;
+	void *data_ptr;
+	u32 total = 0;
 	u32 recd;
 	int rc;
 
-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	data_ptr = kmap(data_page);
 
 	do {
+		tpm_buf_reset(&buf, data_ptr, PAGE_SIZE, TPM_TAG_RQU_COMMAND,
+			      TPM_ORD_GET_RANDOM);
+
 		tpm_buf_append_u32(&buf, num_bytes);
 
 		rc = tpm_transmit_cmd(chip, &buf, sizeof(out->rng_data_len),
@@ -555,25 +580,29 @@  int tpm1_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 		dest += recd;
 		total += recd;
 		num_bytes -= recd;
-
-		tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_GET_RANDOM);
 	} while (retries-- && total < max);
 
 	rc = total ? (int)total : -EIO;
+
 out:
-	tpm_buf_destroy(&buf);
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 
 #define TPM_ORD_PCRREAD 21
 int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
 {
+	struct page *data_page;
 	struct tpm_buf buf;
 	int rc;
 
-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCRREAD);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM_TAG_RQU_COMMAND,
+		      TPM_ORD_PCRREAD);
 
 	tpm_buf_append_u32(&buf, pcr_idx);
 
@@ -590,7 +619,8 @@  int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
 	memcpy(res_buf, &buf.data[TPM_HEADER_SIZE], TPM_DIGEST_SIZE);
 
 out:
-	tpm_buf_destroy(&buf);
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 
@@ -604,15 +634,21 @@  int tpm1_pcr_read(struct tpm_chip *chip, u32 pcr_idx, u8 *res_buf)
  */
 static int tpm1_continue_selftest(struct tpm_chip *chip)
 {
+	struct page *data_page;
 	struct tpm_buf buf;
 	int rc;
 
-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_CONTINUE_SELFTEST);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM_TAG_RQU_COMMAND,
+		      TPM_ORD_CONTINUE_SELFTEST);
 
 	rc = tpm_transmit_cmd(chip, &buf, 0, "continue selftest");
-	tpm_buf_destroy(&buf);
+
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 
@@ -722,21 +758,28 @@  int tpm1_auto_startup(struct tpm_chip *chip)
 int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
 {
 	u8 dummy_hash[TPM_DIGEST_SIZE] = { 0 };
+	struct page *data_page;
 	struct tpm_buf buf;
 	unsigned int try;
+	void *data_ptr;
 	int rc;
 
-
 	/* for buggy tpm, flush pcrs with extend to selected dummy */
 	if (tpm_suspend_pcr)
 		rc = tpm1_pcr_extend(chip, tpm_suspend_pcr, dummy_hash,
 				     "extending dummy pcr before suspend");
 
-	rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	data_ptr = kmap(data_page);
+
 	/* now do the actual savestate */
 	for (try = 0; try < TPM_RETRY; try++) {
+		tpm_buf_reset(&buf, data_ptr, PAGE_SIZE,
+			      TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
+
 		rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
 		/*
 		 * If the TPM indicates that it is too busy to respond to
@@ -750,9 +793,8 @@  int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
 		 */
 		if (rc != TPM_WARN_RETRY)
 			break;
-		tpm_msleep(TPM_TIMEOUT_RETRY);
 
-		tpm_buf_reset(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_SAVESTATE);
+		tpm_msleep(TPM_TIMEOUT_RETRY);
 	}
 
 	if (rc)
@@ -762,8 +804,8 @@  int tpm1_pm_suspend(struct tpm_chip *chip, u32 tpm_suspend_pcr)
 		dev_warn(&chip->dev, "TPM savestate took %dms\n",
 			 try * TPM_TIMEOUT_RETRY);
 
-	tpm_buf_destroy(&buf);
-
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index ba9acae83bff..50674710aed0 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -175,13 +175,14 @@  struct tpm2_pcr_read_out {
 int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
 		  struct tpm_digest *digest, u16 *digest_size_ptr)
 {
-	int i;
-	int rc;
-	struct tpm_buf buf;
-	struct tpm2_pcr_read_out *out;
 	u8 pcr_select[TPM2_PCR_SELECT_MIN] = {0};
-	u16 digest_size;
+	struct tpm2_pcr_read_out *out;
 	u16 expected_digest_size = 0;
+	struct page *data_page;
+	struct tpm_buf buf;
+	u16 digest_size;
+	int rc;
+	int i;
 
 	if (pcr_idx >= TPM2_PLATFORM_PCR)
 		return -EINVAL;
@@ -197,9 +198,12 @@  int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
 		expected_digest_size = chip->allocated_banks[i].digest_size;
 	}
 
-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_READ);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
+		      TPM2_CC_PCR_READ);
 
 	pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7);
 
@@ -225,8 +229,10 @@  int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx,
 		*digest_size_ptr = digest_size;
 
 	memcpy(digest->digest, out->digest, digest_size);
+
 out:
-	tpm_buf_destroy(&buf);
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 
@@ -249,14 +255,18 @@  struct tpm2_null_auth_area {
 int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
 		    struct tpm_digest *digests)
 {
-	struct tpm_buf buf;
 	struct tpm2_null_auth_area auth_area;
+	struct page *data_page;
+	struct tpm_buf buf;
 	int rc;
 	int i;
 
-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_SESSIONS,
+		      TPM2_CC_PCR_EXTEND);
 
 	tpm_buf_append_u32(&buf, pcr_idx);
 
@@ -278,8 +288,8 @@  int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx,
 
 	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting extend a PCR value");
 
-	tpm_buf_destroy(&buf);
-
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 
@@ -302,23 +312,29 @@  struct tpm2_get_random_out {
 int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 {
 	struct tpm2_get_random_out *out;
+	struct page *data_page;
+	u8 *dest_ptr = dest;
+	u32 num_bytes = max;
 	struct tpm_buf buf;
+	void *data_ptr;
+	int retries = 5;
+	int total = 0;
 	u32 recd;
-	u32 num_bytes = max;
 	int err;
-	int total = 0;
-	int retries = 5;
-	u8 *dest_ptr = dest;
 
 	if (!num_bytes || max > TPM_MAX_RNG_DATA)
 		return -EINVAL;
 
-	err = tpm_buf_init(&buf, 0, 0);
-	if (err)
-		return err;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	data_ptr = kmap(data_page);
 
 	do {
-		tpm_buf_reset(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_RANDOM);
+		tpm_buf_reset(&buf, data_ptr, PAGE_SIZE,
+			      TPM2_ST_NO_SESSIONS, TPM2_CC_PCR_EXTEND);
+
 		tpm_buf_append_u16(&buf, num_bytes);
 		err = tpm_transmit_cmd(chip, &buf,
 				       offsetof(struct tpm2_get_random_out,
@@ -347,10 +363,13 @@  int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
 		num_bytes -= recd;
 	} while (retries-- && total < max);
 
-	tpm_buf_destroy(&buf);
+	kunmap(data_page);
+	__free_page(data_page);
 	return total ? total : -EIO;
+
 out:
-	tpm_buf_destroy(&buf);
+	kunmap(data_page);
+	__free_page(data_page);
 	return err;
 }
 
@@ -361,20 +380,24 @@  int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max)
  */
 void tpm2_flush_context(struct tpm_chip *chip, u32 handle)
 {
+	struct page *data_page;
 	struct tpm_buf buf;
-	int rc;
 
-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT);
-	if (rc) {
-		dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n",
-			 handle);
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page) {
+		WARN(1, "tpm: out of memory");
 		return;
 	}
 
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
+		      TPM2_CC_FLUSH_CONTEXT);
+
 	tpm_buf_append_u32(&buf, handle);
 
 	tpm_transmit_cmd(chip, &buf, 0, "flushing context");
-	tpm_buf_destroy(&buf);
+
+	kunmap(data_page);
+	__free_page(data_page);
 }
 
 /**
@@ -420,6 +443,7 @@  int tpm2_seal_trusted(struct tpm_chip *chip,
 		      struct trusted_key_payload *payload,
 		      struct trusted_key_options *options)
 {
+	struct page *data_page;
 	unsigned int blob_len;
 	struct tpm_buf buf;
 	u32 hash;
@@ -436,9 +460,12 @@  int tpm2_seal_trusted(struct tpm_chip *chip,
 	if (i == ARRAY_SIZE(tpm2_hash_map))
 		return -EINVAL;
 
-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_SESSIONS,
+		      TPM2_CC_CREATE);
 
 	tpm_buf_append_u32(&buf, options->keyhandle);
 	tpm2_buf_append_auth(&buf, TPM2_RS_PW,
@@ -505,7 +532,8 @@  int tpm2_seal_trusted(struct tpm_chip *chip,
 	payload->blob_len = blob_len;
 
 out:
-	tpm_buf_destroy(&buf);
+	kunmap(data_page);
+	__free_page(data_page);
 
 	if (rc > 0) {
 		if (tpm2_rc_value(rc) == TPM2_RC_HASH)
@@ -535,10 +563,11 @@  static int tpm2_load_cmd(struct tpm_chip *chip,
 			 struct trusted_key_options *options,
 			 u32 *blob_handle)
 {
-	struct tpm_buf buf;
 	unsigned int private_len;
 	unsigned int public_len;
+	struct page *data_page;
 	unsigned int blob_len;
+	struct tpm_buf buf;
 	int rc;
 
 	private_len = be16_to_cpup((__be16 *) &payload->blob[0]);
@@ -550,9 +579,12 @@  static int tpm2_load_cmd(struct tpm_chip *chip,
 	if (blob_len > payload->blob_len)
 		return -E2BIG;
 
-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_SESSIONS,
+		      TPM2_CC_LOAD);
 
 	tpm_buf_append_u32(&buf, options->keyhandle);
 	tpm2_buf_append_auth(&buf, TPM2_RS_PW,
@@ -574,7 +606,8 @@  static int tpm2_load_cmd(struct tpm_chip *chip,
 			(__be32 *) &buf.data[TPM_HEADER_SIZE]);
 
 out:
-	tpm_buf_destroy(&buf);
+	kunmap(data_page);
+	__free_page(data_page);
 
 	if (rc > 0)
 		rc = -EPERM;
@@ -599,14 +632,18 @@  static int tpm2_unseal_cmd(struct tpm_chip *chip,
 			   struct trusted_key_options *options,
 			   u32 blob_handle)
 {
+	struct page *data_page;
 	struct tpm_buf buf;
 	u16 data_len;
 	u8 *data;
 	int rc;
 
-	rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_SESSIONS,
+		      TPM2_CC_UNSEAL);
 
 	tpm_buf_append_u32(&buf, blob_handle);
 	tpm2_buf_append_auth(&buf,
@@ -641,7 +678,8 @@  static int tpm2_unseal_cmd(struct tpm_chip *chip,
 	}
 
 out:
-	tpm_buf_destroy(&buf);
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 
@@ -693,12 +731,17 @@  ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,  u32 *value,
 			const char *desc)
 {
 	struct tpm2_get_cap_out *out;
+	struct page *data_page;
 	struct tpm_buf buf;
 	int rc;
 
-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
+		      TPM2_CC_GET_CAPABILITY);
+
 	tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
 	tpm_buf_append_u32(&buf, property_id);
 	tpm_buf_append_u32(&buf, 1);
@@ -708,7 +751,9 @@  ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,  u32 *value,
 			&buf.data[TPM_HEADER_SIZE];
 		*value = be32_to_cpu(out->value);
 	}
-	tpm_buf_destroy(&buf);
+
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
@@ -725,15 +770,23 @@  EXPORT_SYMBOL_GPL(tpm2_get_tpm_pt);
  */
 void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
 {
+	struct page *data_page;
 	struct tpm_buf buf;
-	int rc;
 
-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SHUTDOWN);
-	if (rc)
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page) {
+		WARN(1, "tpm: out of memory");
 		return;
+	}
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
+		      TPM2_CC_SHUTDOWN);
+
 	tpm_buf_append_u16(&buf, shutdown_type);
 	tpm_transmit_cmd(chip, &buf, 0, "stopping the TPM");
-	tpm_buf_destroy(&buf);
+
+	kunmap(data_page);
+	__free_page(data_page);
 }
 
 /**
@@ -751,26 +804,36 @@  void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type)
  */
 static int tpm2_do_selftest(struct tpm_chip *chip)
 {
+	struct page *data_page;
 	struct tpm_buf buf;
+	void *data_ptr;
 	int full;
 	int rc;
 
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	data_ptr = kmap(data_page);
+
 	for (full = 0; full < 2; full++) {
-		rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
-		if (rc)
-			return rc;
+		tpm_buf_reset(&buf, data_ptr, PAGE_SIZE, TPM2_ST_NO_SESSIONS,
+			      TPM2_CC_SELF_TEST);
 
 		tpm_buf_append_u8(&buf, full);
+
 		rc = tpm_transmit_cmd(chip, &buf, 0,
 				      "attempting the self test");
-		tpm_buf_destroy(&buf);
 
 		if (rc == TPM2_RC_TESTING)
 			rc = TPM2_RC_SUCCESS;
+
 		if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS)
-			return rc;
+			break;
 	}
 
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 
@@ -788,16 +851,22 @@  static int tpm2_do_selftest(struct tpm_chip *chip)
  */
 int tpm2_probe(struct tpm_chip *chip)
 {
+	struct page *data_page;
 	struct tpm_header *out;
 	struct tpm_buf buf;
 	int rc;
 
-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
+		      TPM2_CC_GET_CAPABILITY);
+
 	tpm_buf_append_u32(&buf, TPM2_CAP_TPM_PROPERTIES);
 	tpm_buf_append_u32(&buf, TPM_PT_TOTAL_COMMANDS);
 	tpm_buf_append_u32(&buf, 1);
+
 	rc = tpm_transmit_cmd(chip, &buf, 0, NULL);
 	/* We ignore TPM return codes on purpose. */
 	if (rc >=  0) {
@@ -805,7 +874,9 @@  int tpm2_probe(struct tpm_chip *chip)
 		if (be16_to_cpu(out->tag) == TPM2_ST_NO_SESSIONS)
 			chip->flags |= TPM_CHIP_FLAG_TPM2;
 	}
-	tpm_buf_destroy(&buf);
+
+	kunmap(data_page);
+	__free_page(data_page);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(tpm2_probe);
@@ -843,21 +914,25 @@  struct tpm2_pcr_selection {
 ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
 {
 	struct tpm2_pcr_selection pcr_selection;
-	struct tpm_buf buf;
-	void *marker;
-	void *end;
-	void *pcr_select_offset;
 	u32 sizeof_pcr_selection;
-	u32 nr_possible_banks;
+	void *pcr_select_offset;
 	u32 nr_alloc_banks = 0;
+	struct page *data_page;
+	u32 nr_possible_banks;
+	struct tpm_buf buf;
 	u16 hash_alg;
+	void *marker;
 	u32 rsp_len;
-	int rc;
 	int i = 0;
+	void *end;
+	int rc;
 
-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
+		      TPM2_CC_GET_CAPABILITY);
 
 	tpm_buf_append_u32(&buf, TPM2_CAP_PCRS);
 	tpm_buf_append_u32(&buf, 0);
@@ -913,14 +988,16 @@  ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip)
 	}
 
 	chip->nr_allocated_banks = nr_alloc_banks;
-out:
-	tpm_buf_destroy(&buf);
 
+out:
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 
 static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
 {
+	struct page *data_page;
 	struct tpm_buf buf;
 	u32 nr_commands;
 	__be32 *attrs;
@@ -930,35 +1007,32 @@  static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
 
 	rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL);
 	if (rc)
-		goto out;
+		return rc;
 
-	if (nr_commands > 0xFFFFF) {
-		rc = -EFAULT;
-		goto out;
-	}
+	if (nr_commands > 0xFFFFF)
+		return -EFAULT;
 
 	chip->cc_attrs_tbl = devm_kcalloc(&chip->dev, 4, nr_commands,
 					  GFP_KERNEL);
 
-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY);
-	if (rc)
-		goto out;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
+		      TPM2_CC_GET_CAPABILITY);
 
 	tpm_buf_append_u32(&buf, TPM2_CAP_COMMANDS);
 	tpm_buf_append_u32(&buf, TPM2_CC_FIRST);
 	tpm_buf_append_u32(&buf, nr_commands);
 
 	rc = tpm_transmit_cmd(chip, &buf, 9 + 4 * nr_commands, NULL);
-	if (rc) {
-		tpm_buf_destroy(&buf);
+	if (rc)
 		goto out;
-	}
 
 	if (nr_commands !=
-	    be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5])) {
-		tpm_buf_destroy(&buf);
+	    be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5]))
 		goto out;
-	}
 
 	chip->nr_commands = nr_commands;
 
@@ -974,11 +1048,13 @@  static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
 		}
 	}
 
-	tpm_buf_destroy(&buf);
-
 out:
+	kunmap(data_page);
+	__free_page(data_page);
+
 	if (rc > 0)
 		rc = -ENODEV;
+
 	return rc;
 }
 
@@ -995,19 +1071,24 @@  static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip)
 
 static int tpm2_startup(struct tpm_chip *chip)
 {
+	struct page *data_page;
 	struct tpm_buf buf;
 	int rc;
 
 	dev_info(&chip->dev, "starting up the TPM manually\n");
 
-	rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_STARTUP);
-	if (rc < 0)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
+		      TPM2_CC_STARTUP);
 
 	tpm_buf_append_u16(&buf, TPM2_SU_CLEAR);
 	rc = tpm_transmit_cmd(chip, &buf, 0, "attempting to start the TPM");
-	tpm_buf_destroy(&buf);
 
+	kunmap(data_page);
+	__free_page(data_page);
 	return rc;
 }
 
diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c
index 982d341d8837..1d3e47392f23 100644
--- a/drivers/char/tpm/tpm2-space.c
+++ b/drivers/char/tpm/tpm2-space.c
@@ -68,14 +68,18 @@  void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space)
 static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
 			     unsigned int *offset, u32 *handle)
 {
-	struct tpm_buf tbuf;
 	struct tpm2_context *ctx;
+	struct page *data_page;
 	unsigned int body_size;
+	struct tpm_buf tbuf;
 	int rc;
 
-	rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
+
+	tpm_buf_reset(&tbuf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
+		      TPM2_CC_CONTEXT_LOAD);
 
 	ctx = (struct tpm2_context *)&buf[*offset];
 	body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size);
@@ -85,8 +89,8 @@  static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
 	if (rc < 0) {
 		dev_warn(&chip->dev, "%s: failed with a system error %d\n",
 			 __func__, rc);
-		tpm_buf_destroy(&tbuf);
-		return -EFAULT;
+		rc = -EFAULT;
+		goto out;
 	} else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE ||
 		   rc == TPM2_RC_REFERENCE_H0) {
 		/*
@@ -100,62 +104,70 @@  static int tpm2_load_context(struct tpm_chip *chip, u8 *buf,
 		 * flushed outside the space
 		 */
 		*handle = 0;
-		tpm_buf_destroy(&tbuf);
-		return -ENOENT;
+		rc = -ENOENT;
+		goto out;
 	} else if (rc > 0) {
 		dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
 			 __func__, rc);
-		tpm_buf_destroy(&tbuf);
-		return -EFAULT;
+		rc = -EFAULT;
+		goto out;
 	}
 
 	*handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]);
 	*offset += body_size;
 
-	tpm_buf_destroy(&tbuf);
-	return 0;
+out:
+	kunmap(data_page);
+	__free_page(data_page);
+	return rc;
 }
 
 static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf,
 			     unsigned int buf_size, unsigned int *offset)
 {
-	struct tpm_buf tbuf;
 	unsigned int body_size;
+	struct page *data_page;
+	struct tpm_buf tbuf;
 	int rc;
 
-	rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE);
-	if (rc)
-		return rc;
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
 
+	tpm_buf_reset(&tbuf, kmap(data_page), PAGE_SIZE, TPM2_ST_NO_SESSIONS,
+		      TPM2_CC_CONTEXT_SAVE);
 	tpm_buf_append_u32(&tbuf, handle);
 
 	rc = tpm_transmit_cmd(chip, &tbuf, 0, NULL);
 	if (rc < 0) {
 		dev_warn(&chip->dev, "%s: failed with a system error %d\n",
 			 __func__, rc);
-		tpm_buf_destroy(&tbuf);
-		return -EFAULT;
+		rc = -EFAULT;
+		goto out;
 	} else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) {
-		tpm_buf_destroy(&tbuf);
-		return -ENOENT;
+		rc = -ENOENT;
+		goto out;
 	} else if (rc) {
 		dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n",
 			 __func__, rc);
-		tpm_buf_destroy(&tbuf);
-		return -EFAULT;
+		rc = -EFAULT;
+		goto out;
 	}
 
 	body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE;
 	if ((*offset + body_size) > buf_size) {
 		dev_warn(&chip->dev, "%s: out of backing storage\n", __func__);
-		tpm_buf_destroy(&tbuf);
-		return -ENOMEM;
+		rc = -ENOMEM;
+		goto out;
 	}
 
 	memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size);
 	*offset += body_size;
-	tpm_buf_destroy(&tbuf);
-	return 0;
+
+out:
+	kunmap(data_page);
+	__free_page(data_page);
+	return rc;
 }
 
 void tpm2_flush_space(struct tpm_chip *chip)
diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
index 2f6e087ec496..e6e89be0e149 100644
--- a/drivers/char/tpm/tpm_vtpm_proxy.c
+++ b/drivers/char/tpm/tpm_vtpm_proxy.c
@@ -394,19 +394,23 @@  static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip  *chip, u8 status)
 
 static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
 {
+	struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
+	const struct tpm_header *header;
+	struct page *data_page;
 	struct tpm_buf buf;
 	int rc;
-	const struct tpm_header *header;
-	struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev);
+
+	data_page = alloc_page(GFP_HIGHUSER);
+	if (!data_page)
+		return -ENOMEM;
 
 	if (chip->flags & TPM_CHIP_FLAG_TPM2)
-		rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS,
-				  TPM2_CC_SET_LOCALITY);
+		tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE,
+			      TPM2_ST_SESSIONS, TPM2_CC_SET_LOCALITY);
 	else
-		rc = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND,
-				  TPM_ORD_SET_LOCALITY);
-	if (rc)
-		return rc;
+		tpm_buf_reset(&buf, kmap(data_page), PAGE_SIZE,
+			      TPM_TAG_RQU_COMMAND, TPM_ORD_SET_LOCALITY);
+
 	tpm_buf_append_u8(&buf, locality);
 
 	proxy_dev->state |= STATE_DRIVER_COMMAND;
@@ -426,8 +430,8 @@  static int vtpm_proxy_request_locality(struct tpm_chip *chip, int locality)
 		locality = -1;
 
 out:
-	tpm_buf_destroy(&buf);
-
+	kunmap(data_page);
+	__free_page(data_page);
 	return locality;
 }