Commit 3ef19382 authored by Ivan Lazeev's avatar Ivan Lazeev Committed by Jarkko Sakkinen

tpm_crb: fix fTPM on AMD Zen+ CPUs

Bug link: https://bugzilla.kernel.org/show_bug.cgi?id=195657

cmd/rsp buffers are expected to be in the same ACPI region.
For Zen+ CPUs BIOS's might report two different regions, some of
them also report region sizes inconsistent with values from TPM
registers.

Memory configuration on ASRock x470 ITX:

db0a0000-dc59efff : Reserved
        dc57e000-dc57efff : MSFT0101:00
        dc582000-dc582fff : MSFT0101:00

Work around the issue by storing ACPI regions declared for the
device in a fixed array and adding an array for pointers to
corresponding possibly allocated resources in crb_map_io function.
This data was previously held for a single resource
in struct crb_priv (iobase field) and local variable io_res in
crb_map_io function. ACPI resources array is used to find index of
corresponding region for each buffer and make the buffer size
consistent with region's length. Array of pointers to allocated
resources is used to map the region at most once.
Signed-off-by: default avatarIvan Lazeev <ivan.lazeev@gmail.com>
Tested-by: default avatarJerry Snitselaar <jsnitsel@redhat.com>
Tested-by: default avatarJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Reviewed-by: default avatarJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: default avatarJarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
parent 2e19e101
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "tpm.h" #include "tpm.h"
#define ACPI_SIG_TPM2 "TPM2" #define ACPI_SIG_TPM2 "TPM2"
#define TPM_CRB_MAX_RESOURCES 3
static const guid_t crb_acpi_start_guid = static const guid_t crb_acpi_start_guid =
GUID_INIT(0x6BBF6CAB, 0x5463, 0x4714, GUID_INIT(0x6BBF6CAB, 0x5463, 0x4714,
...@@ -91,7 +92,6 @@ enum crb_status { ...@@ -91,7 +92,6 @@ enum crb_status {
struct crb_priv { struct crb_priv {
u32 sm; u32 sm;
const char *hid; const char *hid;
void __iomem *iobase;
struct crb_regs_head __iomem *regs_h; struct crb_regs_head __iomem *regs_h;
struct crb_regs_tail __iomem *regs_t; struct crb_regs_tail __iomem *regs_t;
u8 __iomem *cmd; u8 __iomem *cmd;
...@@ -434,21 +434,27 @@ static const struct tpm_class_ops tpm_crb = { ...@@ -434,21 +434,27 @@ static const struct tpm_class_ops tpm_crb = {
static int crb_check_resource(struct acpi_resource *ares, void *data) static int crb_check_resource(struct acpi_resource *ares, void *data)
{ {
struct resource *io_res = data; struct resource *iores_array = data;
struct resource_win win; struct resource_win win;
struct resource *res = &(win.res); struct resource *res = &(win.res);
int i;
if (acpi_dev_resource_memory(ares, res) || if (acpi_dev_resource_memory(ares, res) ||
acpi_dev_resource_address_space(ares, &win)) { acpi_dev_resource_address_space(ares, &win)) {
*io_res = *res; for (i = 0; i < TPM_CRB_MAX_RESOURCES + 1; ++i) {
io_res->name = NULL; if (resource_type(iores_array + i) != IORESOURCE_MEM) {
iores_array[i] = *res;
iores_array[i].name = NULL;
break;
}
}
} }
return 1; return 1;
} }
static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv, static void __iomem *crb_map_res(struct device *dev, struct resource *iores,
struct resource *io_res, u64 start, u32 size) void __iomem **iobase_ptr, u64 start, u32 size)
{ {
struct resource new_res = { struct resource new_res = {
.start = start, .start = start,
...@@ -460,10 +466,16 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv, ...@@ -460,10 +466,16 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv,
if (start != new_res.start) if (start != new_res.start)
return (void __iomem *) ERR_PTR(-EINVAL); return (void __iomem *) ERR_PTR(-EINVAL);
if (!resource_contains(io_res, &new_res)) if (!iores)
return devm_ioremap_resource(dev, &new_res); return devm_ioremap_resource(dev, &new_res);
return priv->iobase + (new_res.start - io_res->start); if (!*iobase_ptr) {
*iobase_ptr = devm_ioremap_resource(dev, iores);
if (IS_ERR(*iobase_ptr))
return *iobase_ptr;
}
return *iobase_ptr + (new_res.start - iores->start);
} }
/* /*
...@@ -490,9 +502,13 @@ static u64 crb_fixup_cmd_size(struct device *dev, struct resource *io_res, ...@@ -490,9 +502,13 @@ static u64 crb_fixup_cmd_size(struct device *dev, struct resource *io_res,
static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
struct acpi_table_tpm2 *buf) struct acpi_table_tpm2 *buf)
{ {
struct list_head resources; struct list_head acpi_resource_list;
struct resource io_res; struct resource iores_array[TPM_CRB_MAX_RESOURCES + 1] = { {0} };
void __iomem *iobase_array[TPM_CRB_MAX_RESOURCES] = {NULL};
struct device *dev = &device->dev; struct device *dev = &device->dev;
struct resource *iores;
void __iomem **iobase_ptr;
int i;
u32 pa_high, pa_low; u32 pa_high, pa_low;
u64 cmd_pa; u64 cmd_pa;
u32 cmd_size; u32 cmd_size;
...@@ -501,21 +517,41 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, ...@@ -501,21 +517,41 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
u32 rsp_size; u32 rsp_size;
int ret; int ret;
INIT_LIST_HEAD(&resources); INIT_LIST_HEAD(&acpi_resource_list);
ret = acpi_dev_get_resources(device, &resources, crb_check_resource, ret = acpi_dev_get_resources(device, &acpi_resource_list,
&io_res); crb_check_resource, iores_array);
if (ret < 0) if (ret < 0)
return ret; return ret;
acpi_dev_free_resource_list(&resources); acpi_dev_free_resource_list(&acpi_resource_list);
if (resource_type(&io_res) != IORESOURCE_MEM) { if (resource_type(iores_array) != IORESOURCE_MEM) {
dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n"); dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n");
return -EINVAL; return -EINVAL;
} else if (resource_type(iores_array + TPM_CRB_MAX_RESOURCES) ==
IORESOURCE_MEM) {
dev_warn(dev, "TPM2 ACPI table defines too many memory resources\n");
memset(iores_array + TPM_CRB_MAX_RESOURCES,
0, sizeof(*iores_array));
iores_array[TPM_CRB_MAX_RESOURCES].flags = 0;
}
iores = NULL;
iobase_ptr = NULL;
for (i = 0; resource_type(iores_array + i) == IORESOURCE_MEM; ++i) {
if (buf->control_address >= iores_array[i].start &&
buf->control_address + sizeof(struct crb_regs_tail) - 1 <=
iores_array[i].end) {
iores = iores_array + i;
iobase_ptr = iobase_array + i;
break;
}
} }
priv->iobase = devm_ioremap_resource(dev, &io_res); priv->regs_t = crb_map_res(dev, iores, iobase_ptr, buf->control_address,
if (IS_ERR(priv->iobase)) sizeof(struct crb_regs_tail));
return PTR_ERR(priv->iobase);
if (IS_ERR(priv->regs_t))
return PTR_ERR(priv->regs_t);
/* The ACPI IO region starts at the head area and continues to include /* The ACPI IO region starts at the head area and continues to include
* the control area, as one nice sane region except for some older * the control area, as one nice sane region except for some older
...@@ -523,9 +559,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, ...@@ -523,9 +559,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
*/ */
if ((priv->sm == ACPI_TPM2_COMMAND_BUFFER) || if ((priv->sm == ACPI_TPM2_COMMAND_BUFFER) ||
(priv->sm == ACPI_TPM2_MEMORY_MAPPED)) { (priv->sm == ACPI_TPM2_MEMORY_MAPPED)) {
if (buf->control_address == io_res.start + if (iores &&
buf->control_address == iores->start +
sizeof(*priv->regs_h)) sizeof(*priv->regs_h))
priv->regs_h = priv->iobase; priv->regs_h = *iobase_ptr;
else else
dev_warn(dev, FW_BUG "Bad ACPI memory layout"); dev_warn(dev, FW_BUG "Bad ACPI memory layout");
} }
...@@ -534,13 +571,6 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, ...@@ -534,13 +571,6 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
if (ret) if (ret)
return ret; return ret;
priv->regs_t = crb_map_res(dev, priv, &io_res, buf->control_address,
sizeof(struct crb_regs_tail));
if (IS_ERR(priv->regs_t)) {
ret = PTR_ERR(priv->regs_t);
goto out_relinquish_locality;
}
/* /*
* PTT HW bug w/a: wake up the device to access * PTT HW bug w/a: wake up the device to access
* possibly not retained registers. * possibly not retained registers.
...@@ -552,13 +582,26 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, ...@@ -552,13 +582,26 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
pa_high = ioread32(&priv->regs_t->ctrl_cmd_pa_high); pa_high = ioread32(&priv->regs_t->ctrl_cmd_pa_high);
pa_low = ioread32(&priv->regs_t->ctrl_cmd_pa_low); pa_low = ioread32(&priv->regs_t->ctrl_cmd_pa_low);
cmd_pa = ((u64)pa_high << 32) | pa_low; cmd_pa = ((u64)pa_high << 32) | pa_low;
cmd_size = crb_fixup_cmd_size(dev, &io_res, cmd_pa, cmd_size = ioread32(&priv->regs_t->ctrl_cmd_size);
ioread32(&priv->regs_t->ctrl_cmd_size));
iores = NULL;
iobase_ptr = NULL;
for (i = 0; iores_array[i].end; ++i) {
if (cmd_pa >= iores_array[i].start &&
cmd_pa <= iores_array[i].end) {
iores = iores_array + i;
iobase_ptr = iobase_array + i;
break;
}
}
if (iores)
cmd_size = crb_fixup_cmd_size(dev, iores, cmd_pa, cmd_size);
dev_dbg(dev, "cmd_hi = %X cmd_low = %X cmd_size %X\n", dev_dbg(dev, "cmd_hi = %X cmd_low = %X cmd_size %X\n",
pa_high, pa_low, cmd_size); pa_high, pa_low, cmd_size);
priv->cmd = crb_map_res(dev, priv, &io_res, cmd_pa, cmd_size); priv->cmd = crb_map_res(dev, iores, iobase_ptr, cmd_pa, cmd_size);
if (IS_ERR(priv->cmd)) { if (IS_ERR(priv->cmd)) {
ret = PTR_ERR(priv->cmd); ret = PTR_ERR(priv->cmd);
goto out; goto out;
...@@ -566,11 +609,25 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, ...@@ -566,11 +609,25 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
memcpy_fromio(&__rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8); memcpy_fromio(&__rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8);
rsp_pa = le64_to_cpu(__rsp_pa); rsp_pa = le64_to_cpu(__rsp_pa);
rsp_size = crb_fixup_cmd_size(dev, &io_res, rsp_pa, rsp_size = ioread32(&priv->regs_t->ctrl_rsp_size);
ioread32(&priv->regs_t->ctrl_rsp_size));
iores = NULL;
iobase_ptr = NULL;
for (i = 0; resource_type(iores_array + i) == IORESOURCE_MEM; ++i) {
if (rsp_pa >= iores_array[i].start &&
rsp_pa <= iores_array[i].end) {
iores = iores_array + i;
iobase_ptr = iobase_array + i;
break;
}
}
if (iores)
rsp_size = crb_fixup_cmd_size(dev, iores, rsp_pa, rsp_size);
if (cmd_pa != rsp_pa) { if (cmd_pa != rsp_pa) {
priv->rsp = crb_map_res(dev, priv, &io_res, rsp_pa, rsp_size); priv->rsp = crb_map_res(dev, iores, iobase_ptr,
rsp_pa, rsp_size);
ret = PTR_ERR_OR_ZERO(priv->rsp); ret = PTR_ERR_OR_ZERO(priv->rsp);
goto out; goto out;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment