diff mbox series

tpm: Detach page allocation from tpm_buf

Message ID 20190925134842.19305-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. 25, 2019, 1:48 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>
---
 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

James Bottomley Sept. 25, 2019, 2:03 p.m. UTC | #1
On Wed, 2019-09-25 at 16:48 +0300, Jarkko Sakkinen wrote:
[...]
> +	data_page = alloc_page(GFP_HIGHUSER);
> +	if (!data_page)
> +		return -ENOMEM;
> +
> +	data_ptr = kmap(data_page);

I don't think this is such a good idea.  On 64 bit it's no different
from GFP_KERNEL and on 32 bit where we do have highmem, kmap space is
at a premium, so doing a highmem allocation + kmap is more wasteful of
resources than simply doing GFP_KERNEL.  In general, you should only do
GFP_HIGHMEM if the page is going to be mostly used by userspace, which
really isn't the case here.

James
Jarkko Sakkinen Sept. 26, 2019, 12:46 p.m. UTC | #2
On Wed, Sep 25, 2019 at 04:48:41PM +0300, Jarkko Sakkinen wrote:
> -		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);

Oops.

/Jarkko
Jarkko Sakkinen Sept. 26, 2019, 1:12 p.m. UTC | #3
On Thu, Sep 26, 2019 at 03:46:35PM +0300, Jarkko Sakkinen wrote:
> On Wed, Sep 25, 2019 at 04:48:41PM +0300, Jarkko Sakkinen wrote:
> > -		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);
> 
> Oops.

Maybe we could use random as the probe for TPM version since we anyway
send a TPM command as a probe for TPM version:

1. Try TPM2 get random.
2. If fail, try TPM1 get random.
3. Output random number to klog.

Something like 8 bytes would be sufficient. This would make sure that
no new change breaks tpm_get_random() and also this would give some
feedback that TPM is at least somewhat working.

/Jarkko
Jarkko Sakkinen Sept. 27, 2019, 1:06 p.m. UTC | #4
On Wed, Sep 25, 2019 at 10:03:46AM -0400, James Bottomley wrote:
> On Wed, 2019-09-25 at 16:48 +0300, Jarkko Sakkinen wrote:
> [...]
> > +	data_page = alloc_page(GFP_HIGHUSER);
> > +	if (!data_page)
> > +		return -ENOMEM;
> > +
> > +	data_ptr = kmap(data_page);
> 
> I don't think this is such a good idea.  On 64 bit it's no different
> from GFP_KERNEL and on 32 bit where we do have highmem, kmap space is
> at a premium, so doing a highmem allocation + kmap is more wasteful of
> resources than simply doing GFP_KERNEL.  In general, you should only do
> GFP_HIGHMEM if the page is going to be mostly used by userspace, which
> really isn't the case here.

Changing that in this commit would be wrong even if you are right.
After this commit has been applied it is somewhat easier to make
best choices for allocation in each call site (probably most will
end up using stack).

/Jarkko
Mimi Zohar Oct. 2, 2019, 12:40 p.m. UTC | #5
On Thu, 2019-09-26 at 16:12 +0300, Jarkko Sakkinen wrote:
> On Thu, Sep 26, 2019 at 03:46:35PM +0300, Jarkko Sakkinen wrote:
> > On Wed, Sep 25, 2019 at 04:48:41PM +0300, Jarkko Sakkinen wrote:
> > > -		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);
> > 
> > Oops.
> 
> Maybe we could use random as the probe for TPM version since we anyway
> send a TPM command as a probe for TPM version:
> 
> 1. Try TPM2 get random.
> 2. If fail, try TPM1 get random.
> 3. Output random number to klog.
> 
> Something like 8 bytes would be sufficient. This would make sure that
> no new change breaks tpm_get_random() and also this would give some
> feedback that TPM is at least somewhat working.

That involves sending 2 TPM commands.  At what point does this occur?
 On registration?  Whenever getting a random number?  Is the result
cached in chip->flags?

Will this delay the TPM initialization, causing IMA to go into "TPM
bypass mode"?

Mimi
Mimi Zohar Oct. 2, 2019, 12:41 p.m. UTC | #6
On Fri, 2019-09-27 at 16:06 +0300, Jarkko Sakkinen wrote:
> On Wed, Sep 25, 2019 at 10:03:46AM -0400, James Bottomley wrote:
> > On Wed, 2019-09-25 at 16:48 +0300, Jarkko Sakkinen wrote:
> > [...]
> > > +	data_page = alloc_page(GFP_HIGHUSER);
> > > +	if (!data_page)
> > > +		return -ENOMEM;
> > > +
> > > +	data_ptr = kmap(data_page);
> > 
> > I don't think this is such a good idea.  On 64 bit it's no different
> > from GFP_KERNEL and on 32 bit where we do have highmem, kmap space is
> > at a premium, so doing a highmem allocation + kmap is more wasteful of
> > resources than simply doing GFP_KERNEL.  In general, you should only do
> > GFP_HIGHMEM if the page is going to be mostly used by userspace, which
> > really isn't the case here.
> 
> Changing that in this commit would be wrong even if you are right.
> After this commit has been applied it is somewhat easier to make
> best choices for allocation in each call site (probably most will
> end up using stack).

Agreed, but it could be a separate patch, prior to this one.  Why
duplicate the problem all over only to change it later?

Mimi
Jarkko Sakkinen Oct. 3, 2019, 11:32 a.m. UTC | #7
On Wed, Oct 02, 2019 at 08:40:24AM -0400, Mimi Zohar wrote:
> On Thu, 2019-09-26 at 16:12 +0300, Jarkko Sakkinen wrote:
> > On Thu, Sep 26, 2019 at 03:46:35PM +0300, Jarkko Sakkinen wrote:
> > > On Wed, Sep 25, 2019 at 04:48:41PM +0300, Jarkko Sakkinen wrote:
> > > > -		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);
> > > 
> > > Oops.
> > 
> > Maybe we could use random as the probe for TPM version since we anyway
> > send a TPM command as a probe for TPM version:
> > 
> > 1. Try TPM2 get random.
> > 2. If fail, try TPM1 get random.
> > 3. Output random number to klog.
> > 
> > Something like 8 bytes would be sufficient. This would make sure that
> > no new change breaks tpm_get_random() and also this would give some
> > feedback that TPM is at least somewhat working.
> 
> That involves sending 2 TPM commands.  At what point does this occur?
>  On registration?  Whenever getting a random number?  Is the result
> cached in chip->flags?

On registeration. It is just printed to klog.

> Will this delay the TPM initialization, causing IMA to go into "TPM
> bypass mode"?

Of course it will delay the init.

As I've stated before the real fix for the bypass issue would be
to make TPM as part of the core but this has not received much
appeal. I think I've sent patch for this once.

/Jarkko
Jarkko Sakkinen Oct. 3, 2019, 11:33 a.m. UTC | #8
On Thu, Oct 03, 2019 at 02:32:11PM +0300, Jarkko Sakkinen wrote:
> On Wed, Oct 02, 2019 at 08:40:24AM -0400, Mimi Zohar wrote:
> > On Thu, 2019-09-26 at 16:12 +0300, Jarkko Sakkinen wrote:
> > > On Thu, Sep 26, 2019 at 03:46:35PM +0300, Jarkko Sakkinen wrote:
> > > > On Wed, Sep 25, 2019 at 04:48:41PM +0300, Jarkko Sakkinen wrote:
> > > > > -		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);
> > > > 
> > > > Oops.
> > > 
> > > Maybe we could use random as the probe for TPM version since we anyway
> > > send a TPM command as a probe for TPM version:
> > > 
> > > 1. Try TPM2 get random.
> > > 2. If fail, try TPM1 get random.
> > > 3. Output random number to klog.
> > > 
> > > Something like 8 bytes would be sufficient. This would make sure that
> > > no new change breaks tpm_get_random() and also this would give some
> > > feedback that TPM is at least somewhat working.
> > 
> > That involves sending 2 TPM commands.  At what point does this occur?
> >  On registration?  Whenever getting a random number?  Is the result
> > cached in chip->flags?
> 
> On registeration. It is just printed to klog.
> 
> > Will this delay the TPM initialization, causing IMA to go into "TPM
> > bypass mode"?
> 
> Of course it will delay the init.
> 
> As I've stated before the real fix for the bypass issue would be
> to make TPM as part of the core but this has not received much
> appeal. I think I've sent patch for this once.

It has been like that people reject a fix to a race condition and
then I get complains on adding minor latency to the init because
of the existing race. It is ridicilous, really.

/Jarkko
Jarkko Sakkinen Oct. 3, 2019, 11:35 a.m. UTC | #9
On Wed, Oct 02, 2019 at 08:41:45AM -0400, Mimi Zohar wrote:
> On Fri, 2019-09-27 at 16:06 +0300, Jarkko Sakkinen wrote:
> > On Wed, Sep 25, 2019 at 10:03:46AM -0400, James Bottomley wrote:
> > > On Wed, 2019-09-25 at 16:48 +0300, Jarkko Sakkinen wrote:
> > > [...]
> > > > +	data_page = alloc_page(GFP_HIGHUSER);
> > > > +	if (!data_page)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	data_ptr = kmap(data_page);
> > > 
> > > I don't think this is such a good idea.  On 64 bit it's no different
> > > from GFP_KERNEL and on 32 bit where we do have highmem, kmap space is
> > > at a premium, so doing a highmem allocation + kmap is more wasteful of
> > > resources than simply doing GFP_KERNEL.  In general, you should only do
> > > GFP_HIGHMEM if the page is going to be mostly used by userspace, which
> > > really isn't the case here.
> > 
> > Changing that in this commit would be wrong even if you are right.
> > After this commit has been applied it is somewhat easier to make
> > best choices for allocation in each call site (probably most will
> > end up using stack).
> 
> Agreed, but it could be a separate patch, prior to this one.  Why
> duplicate the problem all over only to change it later?

What problem exactly it is duplicating? The existing allocation
scheme here works correctly.

/Jarkko
Mimi Zohar Oct. 3, 2019, 12:39 p.m. UTC | #10
On Thu, 2019-10-03 at 14:32 +0300, Jarkko Sakkinen wrote:
> On Wed, Oct 02, 2019 at 08:40:24AM -0400, Mimi Zohar wrote:
> > On Thu, 2019-09-26 at 16:12 +0300, Jarkko Sakkinen wrote:
> > > On Thu, Sep 26, 2019 at 03:46:35PM +0300, Jarkko Sakkinen wrote:
> > > > On Wed, Sep 25, 2019 at 04:48:41PM +0300, Jarkko Sakkinen wrote:
> > > > > -		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);
> > > > 
> > > > Oops.
> > > 
> > > Maybe we could use random as the probe for TPM version since we anyway
> > > send a TPM command as a probe for TPM version:
> > > 
> > > 1. Try TPM2 get random.
> > > 2. If fail, try TPM1 get random.
> > > 3. Output random number to klog.
> > > 
> > > Something like 8 bytes would be sufficient. This would make sure that
> > > no new change breaks tpm_get_random() and also this would give some
> > > feedback that TPM is at least somewhat working.
> > 
> > That involves sending 2 TPM commands.  At what point does this occur?
> >  On registration?  Whenever getting a random number?  Is the result
> > cached in chip->flags?
> 
> On registeration. It is just printed to klog.

What sets "TPM_CHIP_FLAG_TPM2" in chip->flags?  And when?

> 
> > Will this delay the TPM initialization, causing IMA to go into "TPM
> > bypass mode"?
> 
> Of course it will delay the init.

Delaying the init will most likely cause regressions on systems with
TPM 1.2 systems.

Instead of sending the TPM 2.0 command and on failure sending the TPM
1.2 version of the command, could chip->flags be tested?  And if not
chip->flags, then provide the TPM version as part of registration.

> 
> As I've stated before the real fix for the bypass issue would be
> to make TPM as part of the core but this has not received much
> appeal. I think I've sent patch for this once.

I must have missed this discussion.

Mimi
Mimi Zohar Oct. 3, 2019, 12:50 p.m. UTC | #11
On Thu, 2019-10-03 at 14:35 +0300, Jarkko Sakkinen wrote:
> On Wed, Oct 02, 2019 at 08:41:45AM -0400, Mimi Zohar wrote:
> > On Fri, 2019-09-27 at 16:06 +0300, Jarkko Sakkinen wrote:
> > > On Wed, Sep 25, 2019 at 10:03:46AM -0400, James Bottomley wrote:
> > > > On Wed, 2019-09-25 at 16:48 +0300, Jarkko Sakkinen wrote:
> > > > [...]
> > > > > +	data_page = alloc_page(GFP_HIGHUSER);
> > > > > +	if (!data_page)
> > > > > +		return -ENOMEM;
> > > > > +
> > > > > +	data_ptr = kmap(data_page);
> > > > 
> > > > I don't think this is such a good idea.  On 64 bit it's no different
> > > > from GFP_KERNEL and on 32 bit where we do have highmem, kmap space is
> > > > at a premium, so doing a highmem allocation + kmap is more wasteful of
> > > > resources than simply doing GFP_KERNEL.  In general, you should only do
> > > > GFP_HIGHMEM if the page is going to be mostly used by userspace, which
> > > > really isn't the case here.
> > > 
> > > Changing that in this commit would be wrong even if you are right.
> > > After this commit has been applied it is somewhat easier to make
> > > best choices for allocation in each call site (probably most will
> > > end up using stack).
> > 
> > Agreed, but it could be a separate patch, prior to this one.  Why
> > duplicate the problem all over only to change it later?
> 
> What problem exactly it is duplicating? The existing allocation
> scheme here works correctly.

In the current code "alloc_page(GFP_HIGHUSER)" exists in a single
function.  With this patch, "alloc_page(GFP_HIGHUSER)" is duplicated
24 times.  If it is incorrect and we shouldn't be using GFP_HIGHUSER,
as James said, then why duplicate it 24 times?  Fix it as a separate
patch first, that could be backported if needed, and then make the
change.

Mimi
Mimi Zohar Oct. 3, 2019, 3:24 p.m. UTC | #12
On Thu, 2019-10-03 at 14:33 +0300, Jarkko Sakkinen wrote:

> > > Will this delay the TPM initialization, causing IMA to go into "TPM
> > > bypass mode"?
> > 
> > Of course it will delay the init.
> > 
> > As I've stated before the real fix for the bypass issue would be
> > to make TPM as part of the core but this has not received much
> > appeal. I think I've sent patch for this once.

IMA initialization is way later than the TPM.  IMA is on the
late_initcall(), while the TPM is on the subsys_initcall().  I'm not
sure moving the TPM to core would make a difference.  There must be a
way of deferring IMA until after the TPM has been initialized.  Any
suggestions would be much appreciated.

(The TPM on the Pi still has a dependency on clock.) 

> It has been like that people reject a fix to a race condition and
> then I get complains on adding minor latency to the init because
> of the existing race. It is ridicilous, really.

I agree, but adding any latency will cause a regression.

Mimi
Jarkko Sakkinen Oct. 3, 2019, 6:26 p.m. UTC | #13
On Thu, Oct 03, 2019 at 08:50:54AM -0400, Mimi Zohar wrote:
> On Thu, 2019-10-03 at 14:35 +0300, Jarkko Sakkinen wrote:
> > On Wed, Oct 02, 2019 at 08:41:45AM -0400, Mimi Zohar wrote:
> > > On Fri, 2019-09-27 at 16:06 +0300, Jarkko Sakkinen wrote:
> > > > On Wed, Sep 25, 2019 at 10:03:46AM -0400, James Bottomley wrote:
> > > > > On Wed, 2019-09-25 at 16:48 +0300, Jarkko Sakkinen wrote:
> > > > > [...]
> > > > > > +	data_page = alloc_page(GFP_HIGHUSER);
> > > > > > +	if (!data_page)
> > > > > > +		return -ENOMEM;
> > > > > > +
> > > > > > +	data_ptr = kmap(data_page);
> > > > > 
> > > > > I don't think this is such a good idea.  On 64 bit it's no different
> > > > > from GFP_KERNEL and on 32 bit where we do have highmem, kmap space is
> > > > > at a premium, so doing a highmem allocation + kmap is more wasteful of
> > > > > resources than simply doing GFP_KERNEL.  In general, you should only do
> > > > > GFP_HIGHMEM if the page is going to be mostly used by userspace, which
> > > > > really isn't the case here.
> > > > 
> > > > Changing that in this commit would be wrong even if you are right.
> > > > After this commit has been applied it is somewhat easier to make
> > > > best choices for allocation in each call site (probably most will
> > > > end up using stack).
> > > 
> > > Agreed, but it could be a separate patch, prior to this one.  Why
> > > duplicate the problem all over only to change it later?
> > 
> > What problem exactly it is duplicating? The existing allocation
> > scheme here works correctly.
> 
> In the current code "alloc_page(GFP_HIGHUSER)" exists in a single
> function.  With this patch, "alloc_page(GFP_HIGHUSER)" is duplicated
> 24 times.  If it is incorrect and we shouldn't be using GFP_HIGHUSER,
> as James said, then why duplicate it 24 times?  Fix it as a separate
> patch first, that could be backported if needed, and then make the
> change.

Sorry I mixed this with Jerry's proposal :-) I can do that of course.

/Jarkko
Jarkko Sakkinen Oct. 3, 2019, 6:31 p.m. UTC | #14
On Thu, Oct 03, 2019 at 08:39:10AM -0400, Mimi Zohar wrote:
> On Thu, 2019-10-03 at 14:32 +0300, Jarkko Sakkinen wrote:
> > On Wed, Oct 02, 2019 at 08:40:24AM -0400, Mimi Zohar wrote:
> > > On Thu, 2019-09-26 at 16:12 +0300, Jarkko Sakkinen wrote:
> > > > On Thu, Sep 26, 2019 at 03:46:35PM +0300, Jarkko Sakkinen wrote:
> > > > > On Wed, Sep 25, 2019 at 04:48:41PM +0300, Jarkko Sakkinen wrote:
> > > > > > -		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);
> > > > > 
> > > > > Oops.
> > > > 
> > > > Maybe we could use random as the probe for TPM version since we anyway
> > > > send a TPM command as a probe for TPM version:
> > > > 
> > > > 1. Try TPM2 get random.
> > > > 2. If fail, try TPM1 get random.
> > > > 3. Output random number to klog.
> > > > 
> > > > Something like 8 bytes would be sufficient. This would make sure that
> > > > no new change breaks tpm_get_random() and also this would give some
> > > > feedback that TPM is at least somewhat working.
> > > 
> > > That involves sending 2 TPM commands.  At what point does this occur?
> > >  On registration?  Whenever getting a random number?  Is the result
> > > cached in chip->flags?
> > 
> > On registeration. It is just printed to klog.
> 
> What sets "TPM_CHIP_FLAG_TPM2" in chip->flags?  And when?
> 
> > 
> > > Will this delay the TPM initialization, causing IMA to go into "TPM
> > > bypass mode"?
> > 
> > Of course it will delay the init.
> 
> Delaying the init will most likely cause regressions on systems with
> TPM 1.2 systems.
> 
> Instead of sending the TPM 2.0 command and on failure sending the TPM
> 1.2 version of the command, could chip->flags be tested?  And if not
> chip->flags, then provide the TPM version as part of registration.

No rush pushing this forward. I got your point.

> > As I've stated before the real fix for the bypass issue would be
> > to make TPM as part of the core but this has not received much
> > appeal. I think I've sent patch for this once.
> 
> I must have missed this discussion.

Yeah, I think that'd be a great idea. We need a better control on
TPM core as multiple subsystem's depend on it in API level. Something
to reconsider in future.

/Jarkko
Jarkko Sakkinen Oct. 3, 2019, 6:33 p.m. UTC | #15
On Thu, Oct 03, 2019 at 11:24:21AM -0400, Mimi Zohar wrote:
> On Thu, 2019-10-03 at 14:33 +0300, Jarkko Sakkinen wrote:
> 
> > > > Will this delay the TPM initialization, causing IMA to go into "TPM
> > > > bypass mode"?
> > > 
> > > Of course it will delay the init.
> > > 
> > > As I've stated before the real fix for the bypass issue would be
> > > to make TPM as part of the core but this has not received much
> > > appeal. I think I've sent patch for this once.
> 
> IMA initialization is way later than the TPM.  IMA is on the
> late_initcall(), while the TPM is on the subsys_initcall().  I'm not
> sure moving the TPM to core would make a difference.  There must be a
> way of deferring IMA until after the TPM has been initialized.  Any
> suggestions would be much appreciated.
> 
> (The TPM on the Pi still has a dependency on clock.) 

Right. I seriously need to study IMA code in near future with time.

> > It has been like that people reject a fix to a race condition and
> > then I get complains on adding minor latency to the init because
> > of the existing race. It is ridicilous, really.
> 
> I agree, but adding any latency will cause a regression.

OK, I get the picture here now. I have to some day look at the IMA
code and see if I could draft something that would improve the
situation. Thanks for explaining all this!

/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;
 }