diff mbox series

[v2,2/2] tpm: tpm_crb: enhance resource mapping mechanism for supporting AMD's fTPM

Message ID 20190909090906.28700-3-kkamagui@gmail.com (mailing list archive)
State New, archived
Headers show
Series Enhance support for the AMD's fTPM | expand

Commit Message

Seunghun Han Sept. 9, 2019, 9:09 a.m. UTC
I got an AMD system which had a Ryzen Threadripper 1950X and MSI
mainboard, and I had a problem with AMD's fTPM. My machine showed an error
message below, and the fTPM didn't work because of it.

[  5.732084] tpm_crb MSFT0101:00: can't request region for resource
             [mem 0x79b4f000-0x79b4ffff]
[  5.732089] tpm_crb: probe of MSFT0101:00 failed with error -16

When I saw the iomem, I found two fTPM regions were in the ACPI NVS area. 
The regions are below.

79a39000-79b6afff : ACPI Non-volatile Storage
  79b4b000-79b4bfff : MSFT0101:00
  79b4f000-79b4ffff : MSFT0101:00

After analyzing this issue, I found that crb_map_io() function called
devm_ioremap_resource() and it failed. The ACPI NVS didn't allow the TPM
CRB driver to assign a resource in it because a busy bit was set to
the ACPI NVS area.

To support AMD's fTPM, I added a function to check intersects between
the TPM region and ACPI NVS before it mapped the region. If some
intersects are detected, the function just calls devm_ioremap() for
a workaround. If there is no intersect, it calls devm_ioremap_resource().

Signed-off-by: Seunghun Han <kkamagui@gmail.com>
---
Changes in v2: fix a warning of kbuild test robot. The link is below.
               https://lkml.org/lkml/2019/8/31/217

 drivers/char/tpm/tpm_crb.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

Comments

Jarkko Sakkinen Sept. 10, 2019, 2:42 p.m. UTC | #1
On Mon, Sep 09, 2019 at 06:09:06PM +0900, Seunghun Han wrote:
> I got an AMD system which had a Ryzen Threadripper 1950X and MSI
> mainboard, and I had a problem with AMD's fTPM. My machine showed an error
> message below, and the fTPM didn't work because of it.
> 
> [  5.732084] tpm_crb MSFT0101:00: can't request region for resource
>              [mem 0x79b4f000-0x79b4ffff]
> [  5.732089] tpm_crb: probe of MSFT0101:00 failed with error -16
> 
> When I saw the iomem, I found two fTPM regions were in the ACPI NVS area. 
> The regions are below.
> 
> 79a39000-79b6afff : ACPI Non-volatile Storage
>   79b4b000-79b4bfff : MSFT0101:00
>   79b4f000-79b4ffff : MSFT0101:00
> 
> After analyzing this issue, I found that crb_map_io() function called
> devm_ioremap_resource() and it failed. The ACPI NVS didn't allow the TPM
> CRB driver to assign a resource in it because a busy bit was set to
> the ACPI NVS area.
> 
> To support AMD's fTPM, I added a function to check intersects between
> the TPM region and ACPI NVS before it mapped the region. If some
> intersects are detected, the function just calls devm_ioremap() for
> a workaround. If there is no intersect, it calls devm_ioremap_resource().
> 
> Signed-off-by: Seunghun Han <kkamagui@gmail.com>

This problem is still valid and not addressed by Vanya's patch (and
should not be as it is a disjoint issue).  However, calling
devm_ioremap() is somewhat racy as the NVS driver is not aware of that.

My take is that this should be fixed in the code that assigns regions to
the NVS driver e.g. it could look up the regions assigned to the
MSFT0101 and ignore those regions. In the end linux-acpi maintainers
have the say on this but this would be the angle that I'd take to
implement such patch probably.

/Jarkko
Jarkko Sakkinen Sept. 10, 2019, 3:06 p.m. UTC | #2
On Tue, Sep 10, 2019 at 03:42:15PM +0100, Jarkko Sakkinen wrote:
> On Mon, Sep 09, 2019 at 06:09:06PM +0900, Seunghun Han wrote:
> > I got an AMD system which had a Ryzen Threadripper 1950X and MSI
> > mainboard, and I had a problem with AMD's fTPM. My machine showed an error
> > message below, and the fTPM didn't work because of it.
> > 
> > [  5.732084] tpm_crb MSFT0101:00: can't request region for resource
> >              [mem 0x79b4f000-0x79b4ffff]
> > [  5.732089] tpm_crb: probe of MSFT0101:00 failed with error -16
> > 
> > When I saw the iomem, I found two fTPM regions were in the ACPI NVS area. 
> > The regions are below.
> > 
> > 79a39000-79b6afff : ACPI Non-volatile Storage
> >   79b4b000-79b4bfff : MSFT0101:00
> >   79b4f000-79b4ffff : MSFT0101:00
> > 
> > After analyzing this issue, I found that crb_map_io() function called
> > devm_ioremap_resource() and it failed. The ACPI NVS didn't allow the TPM
> > CRB driver to assign a resource in it because a busy bit was set to
> > the ACPI NVS area.
> > 
> > To support AMD's fTPM, I added a function to check intersects between
> > the TPM region and ACPI NVS before it mapped the region. If some
> > intersects are detected, the function just calls devm_ioremap() for
> > a workaround. If there is no intersect, it calls devm_ioremap_resource().
> > 
> > Signed-off-by: Seunghun Han <kkamagui@gmail.com>
> 
> This problem is still valid and not addressed by Vanya's patch (and
> should not be as it is a disjoint issue).  However, calling
> devm_ioremap() is somewhat racy as the NVS driver is not aware of that.
> 
> My take is that this should be fixed in the code that assigns regions to
> the NVS driver e.g. it could look up the regions assigned to the
> MSFT0101 and ignore those regions. In the end linux-acpi maintainers
> have the say on this but this would be the angle that I'd take to
> implement such patch probably.

Matthew pointed out that having a hook in NVS driver is better solution
because it is nil functionality if the TPM driver is loaded. We need
functions to:

1. Request a region from the NVS driver (when tpm_crb loads)
2. Release a region back to the NVS Driver (when tpm_crb unloads).

My proposal would unnecessarily duplicate code and also leave a
side-effect when TPM is not used in the first place.

I see this as the overally best solution. If you can come up with a
patch for the NVS side and changes to CRB drivers to utilize the new
hooks, then combined with Vanya's changes we have a sustainable solution
for AMD fTPM.

/Jarkko
Seunghun Han Sept. 10, 2019, 3:28 p.m. UTC | #3
>
> On Tue, Sep 10, 2019 at 03:42:15PM +0100, Jarkko Sakkinen wrote:
> > On Mon, Sep 09, 2019 at 06:09:06PM +0900, Seunghun Han wrote:
> > > I got an AMD system which had a Ryzen Threadripper 1950X and MSI
> > > mainboard, and I had a problem with AMD's fTPM. My machine showed an error
> > > message below, and the fTPM didn't work because of it.
> > >
> > > [  5.732084] tpm_crb MSFT0101:00: can't request region for resource
> > >              [mem 0x79b4f000-0x79b4ffff]
> > > [  5.732089] tpm_crb: probe of MSFT0101:00 failed with error -16
> > >
> > > When I saw the iomem, I found two fTPM regions were in the ACPI NVS area.
> > > The regions are below.
> > >
> > > 79a39000-79b6afff : ACPI Non-volatile Storage
> > >   79b4b000-79b4bfff : MSFT0101:00
> > >   79b4f000-79b4ffff : MSFT0101:00
> > >
> > > After analyzing this issue, I found that crb_map_io() function called
> > > devm_ioremap_resource() and it failed. The ACPI NVS didn't allow the TPM
> > > CRB driver to assign a resource in it because a busy bit was set to
> > > the ACPI NVS area.
> > >
> > > To support AMD's fTPM, I added a function to check intersects between
> > > the TPM region and ACPI NVS before it mapped the region. If some
> > > intersects are detected, the function just calls devm_ioremap() for
> > > a workaround. If there is no intersect, it calls devm_ioremap_resource().
> > >
> > > Signed-off-by: Seunghun Han <kkamagui@gmail.com>
> >
> > This problem is still valid and not addressed by Vanya's patch (and
> > should not be as it is a disjoint issue).  However, calling
> > devm_ioremap() is somewhat racy as the NVS driver is not aware of that.
> >
> > My take is that this should be fixed in the code that assigns regions to
> > the NVS driver e.g. it could look up the regions assigned to the
> > MSFT0101 and ignore those regions. In the end linux-acpi maintainers
> > have the say on this but this would be the angle that I'd take to
> > implement such patch probably.
>
> Matthew pointed out that having a hook in NVS driver is better solution
> because it is nil functionality if the TPM driver is loaded. We need
> functions to:
>
> 1. Request a region from the NVS driver (when tpm_crb loads)
> 2. Release a region back to the NVS Driver (when tpm_crb unloads).
>
> My proposal would unnecessarily duplicate code and also leave a
> side-effect when TPM is not used in the first place.
>
> I see this as the overally best solution. If you can come up with a
> patch for the NVS side and changes to CRB drivers to utilize the new
> hooks, then combined with Vanya's changes we have a sustainable solution
> for AMD fTPM.

It's a great solution. I will update this patch on your advice and
send it to you soon.

By the way, I have a question about your advice.
If we handle the NVS region with NVS driver, calling devm_ioremap()
function is fine like crb_ioremap_resource() function in this patch?

>
> /Jarkko
Jarkko Sakkinen Sept. 13, 2019, 1:12 p.m. UTC | #4
On Wed, Sep 11, 2019 at 12:28:18AM +0900, Seunghun Han wrote:
> > Matthew pointed out that having a hook in NVS driver is better solution
> > because it is nil functionality if the TPM driver is loaded. We need
> > functions to:
> >
> > 1. Request a region from the NVS driver (when tpm_crb loads)
> > 2. Release a region back to the NVS Driver (when tpm_crb unloads).
> >
> > My proposal would unnecessarily duplicate code and also leave a
> > side-effect when TPM is not used in the first place.
> >
> > I see this as the overally best solution. If you can come up with a
> > patch for the NVS side and changes to CRB drivers to utilize the new
> > hooks, then combined with Vanya's changes we have a sustainable solution
> > for AMD fTPM.
> 
> It's a great solution. I will update this patch on your advice and
> send it to you soon.
> 
> By the way, I have a question about your advice.
> If we handle the NVS region with NVS driver, calling devm_ioremap()
> function is fine like crb_ioremap_resource() function in this patch?

No, you should reclaim the resource that conflicts and return it back
when tpm_crb is unregistered (e.g. rmmod tpm_crb).

I would try something like enumerating iomem resources with
walk_iomem_res_desc(). I would advice to peek at arch/x86/kernel/crash.c
for an example how to use this for NVS regions
(IORES_DESC_ACPI_NV_STORAGE).

E.g. you could use a callback for it along the lines of:

static int crb_find_iomem_res_cb(struct resource *res, void *io_res_ptr)
{
	struct resource *io_res = io_res_ptr;

	if (res->start == io_res->start && res->end == io_res->end) {
		/*
		 * Backup all resource data so that it can be inserted
		 * later on with the flags it had etc.
		 */
		*io_res = *res;
		return 1;
	}

	return 0;
}

Then you could __release_region() to unallocate the source. When tpm_crb
is removed you can then allocate and insert a resource with data
matching it had.


/Jarkko
Seunghun Han Sept. 16, 2019, 8:18 a.m. UTC | #5
> > > Matthew pointed out that having a hook in NVS driver is better solution
> > > because it is nil functionality if the TPM driver is loaded. We need
> > > functions to:
> > >
> > > 1. Request a region from the NVS driver (when tpm_crb loads)
> > > 2. Release a region back to the NVS Driver (when tpm_crb unloads).
> > >
> > > My proposal would unnecessarily duplicate code and also leave a
> > > side-effect when TPM is not used in the first place.
> > >
> > > I see this as the overally best solution. If you can come up with a
> > > patch for the NVS side and changes to CRB drivers to utilize the new
> > > hooks, then combined with Vanya's changes we have a sustainable solution
> > > for AMD fTPM.
> >
> > It's a great solution. I will update this patch on your advice and
> > send it to you soon.
> >
> > By the way, I have a question about your advice.
> > If we handle the NVS region with NVS driver, calling devm_ioremap()
> > function is fine like crb_ioremap_resource() function in this patch?
>
> No, you should reclaim the resource that conflicts and return it back
> when tpm_crb is unregistered (e.g. rmmod tpm_crb).
>
> I would try something like enumerating iomem resources with
> walk_iomem_res_desc(). I would advice to peek at arch/x86/kernel/crash.c
> for an example how to use this for NVS regions
>
> Then you could __release_region() to unallocate the source. When tpm_crb
> is removed you can then allocate and insert a resource with data
> matching it had.

Thank you for your sincere advice, and I have some questions about it.
As you know, the core reason of this ACPI NVS problem is that a busy
bit is set to the ACPI NVS area. So, devm_ioremap_resource() function
fails because of it.

If we want to call devm_ioremap_resource() for this case, we maybe
need to rearrange the existing memory layout from the child
relationship to the sibling relationship below. We also need to get
back when tpm_crb unloads.
[ ACPI NVS (parent) [ TPM CMD buffer (child of NVS) ] [ TPM RSP buffer
(child of NVS) ] ]   <--->   [ ACPI NVS head ] [ CMD buffer ] [ NVS
middle ] [ RSP buffer ] [ NVS tails ]

Our concern is a race condition between NVS driver and TPM CRB driver.
In my view, we could solve this problem if we only make and call the
functions you said, requesting and releasing a region from NVS driver.
NVS driver doesn't rely on iomem layout, and it relies on internal
nvs_list data.

Therefore, I added some details to your guide. How about this sequence?
1) When tpm_crb driver loads, the driver checks if command/response
buffers are in ACPI NVS area. If so, it requests (or removes) the
buffer regions from NVS driver's nvs_list (with
suspend_nvs_unregister() function I will add to the nvs.c driver).

2) If command/response buffers are in ACPI NVS area, tpm_crb driver
calls devm_ioremap() instead of devm_ioremap_resource() like this
patch.

3) When tpm_crb driver unloads, the driver releases (or adds) the
buffer regions to NVS driver's nvs_list (with existing
suspend_nvs_register() function in the nvs.c driver).

I think the sequence could solve the problem we concerned.
What do you think about the sequence?

Seunghun
Seunghun Han Sept. 16, 2019, 8:42 a.m. UTC | #6
Sorry for my mistake.
I misunderstood some functions in nvs.c. So I have fixed it and sent
my email again. My email is below.

> > > Matthew pointed out that having a hook in NVS driver is better solution
> > > because it is nil functionality if the TPM driver is loaded. We need
> > > functions to:
> > >
> > > 1. Request a region from the NVS driver (when tpm_crb loads)
> > > 2. Release a region back to the NVS Driver (when tpm_crb unloads).
> > >
> > > My proposal would unnecessarily duplicate code and also leave a
> > > side-effect when TPM is not used in the first place.
> > >
> > > I see this as the overally best solution. If you can come up with a
> > > patch for the NVS side and changes to CRB drivers to utilize the new
> > > hooks, then combined with Vanya's changes we have a sustainable solution
> > > for AMD fTPM.
> >
> > It's a great solution. I will update this patch on your advice and
> > send it to you soon.
> >
> > By the way, I have a question about your advice.
> > If we handle the NVS region with NVS driver, calling devm_ioremap()
> > function is fine like crb_ioremap_resource() function in this patch?
>
> No, you should reclaim the resource that conflicts and return it back
> when tpm_crb is unregistered (e.g. rmmod tpm_crb).
>
> I would try something like enumerating iomem resources with
> walk_iomem_res_desc(). I would advice to peek at arch/x86/kernel/crash.c
> for an example how to use this for NVS regions
>
> Then you could __release_region() to unallocate the source. When tpm_crb
> is removed you can then allocate and insert a resource with data
> matching it had.

Thank you for your sincere advice, and I have some questions about it.
As you know, the core reason of this ACPI NVS problem is that a busy
bit is set to the ACPI NVS area. So, devm_ioremap_resource() function
fails because of it.

If we want to call devm_ioremap_resource() for this case, we maybe
need to rearrange the existing memory layout from the child
relationship to the sibling relationship below. We also need to get
back when tpm_crb unloads.
[ ACPI NVS (parent) [ TPM CMD buffer (child of NVS) ] [ TPM RSP buffer
(child of NVS) ] ]   <--->   [ ACPI NVS head ] [ CMD buffer ] [ NVS
middle ] [ RSP buffer ] [ NVS tails ]

Our concern is a race condition between NVS driver and TPM CRB driver.
In my view, we could solve this problem if we only make and call the
functions you said, requesting and releasing a region from NVS driver.
NVS driver doesn't rely on iomem layout, and it relies on internal
nvs_region_list data.

Therefore, I added some details to your guide. How about this sequence?
1) When tpm_crb driver loads, the driver checks if command/response
buffers are in ACPI NVS area. If so, it requests (or removes) the
buffer regions from NVS driver's nvs_region_list (with
acpi_nvs_unregister() function I will add to the nvs.c driver).

2) If command/response buffers are in ACPI NVS area, tpm_crb driver
calls devm_ioremap() instead of devm_ioremap_resource() like this
patch.

3) When tpm_crb driver unloads, the driver releases (or adds) the
buffer regions to NVS driver's nvs_region_list (with existing
acpi_nvs_register() function in the nvs.c driver).

I think the sequence could solve the problem we concerned.
What do you think about the sequence?

Seunghun
diff mbox series

Patch

diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c
index 14f486c23af2..6b98a3a995b7 100644
--- a/drivers/char/tpm/tpm_crb.c
+++ b/drivers/char/tpm/tpm_crb.c
@@ -450,6 +450,27 @@  static int crb_check_resource(struct acpi_resource *ares, void *data)
 	return 1;
 }
 
+static void __iomem *crb_ioremap_resource(struct device *dev,
+					  const struct resource *res)
+{
+	int rc;
+	resource_size_t size = resource_size(res);
+
+	/* Broken BIOS assigns command and response buffers in ACPI NVS region.
+	 * Check intersections between a resource and ACPI NVS for W/A.
+	 */
+	rc = region_intersects(res->start, size, IORESOURCE_MEM |
+			       IORESOURCE_BUSY, IORES_DESC_ACPI_NV_STORAGE);
+	if (rc != REGION_DISJOINT) {
+		dev_err(dev,
+			FW_BUG "Resource overlaps with a ACPI NVS. %pr\n",
+			res);
+		return devm_ioremap(dev, res->start, size);
+	}
+
+	return devm_ioremap_resource(dev, res);
+}
+
 static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv,
 				 struct resource *io_res, u64 start, u32 size)
 {
@@ -464,7 +485,7 @@  static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv,
 		return (void __iomem *) ERR_PTR(-EINVAL);
 
 	if (!resource_contains(io_res, &new_res))
-		return devm_ioremap_resource(dev, &new_res);
+		return crb_ioremap_resource(dev, &new_res);
 
 	return priv->iobase + (new_res.start - io_res->start);
 }
@@ -536,7 +557,7 @@  static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
 		goto out_early;
 	}
 
-	priv->iobase = devm_ioremap_resource(dev, &io_res);
+	priv->iobase = crb_ioremap_resource(dev, &io_res);
 	if (IS_ERR(priv->iobase)) {
 		ret = PTR_ERR(priv->iobase);
 		goto out_early;