diff options
-rw-r--r-- | drivers/char/tpm/tpm_crb.c | 27 |
1 files changed, 25 insertions, 2 deletions
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 86f355b..421dfa9 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -295,6 +295,27 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv, return priv->iobase + (new_res.start - io_res->start); } +/* + * Work around broken BIOSs that return inconsistent values from the ACPI + * region vs the registers. Trust the ACPI region. Such broken systems + * probably cannot send large TPM commands since the buffer will be truncated. + */ +static u64 crb_fixup_cmd_size(struct device *dev, struct resource *io_res, + u64 start, u64 size) +{ + if (io_res->start > start || io_res->end < start) + return size; + + if (start + size - 1 <= io_res->end) + return size; + + dev_err(dev, + FW_BUG "ACPI region does not cover the entire command/response buffer. %pr vs %llx %llx\n", + io_res, start, size); + + return io_res->end - start + 1; +} + static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, struct acpi_table_tpm2 *buf) { @@ -340,7 +361,8 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, pa_high = ioread32(&priv->cca->cmd_pa_high); pa_low = ioread32(&priv->cca->cmd_pa_low); cmd_pa = ((u64)pa_high << 32) | pa_low; - cmd_size = ioread32(&priv->cca->cmd_size); + cmd_size = crb_fixup_cmd_size(dev, &io_res, cmd_pa, + ioread32(&priv->cca->cmd_size)); dev_dbg(dev, "cmd_hi = %X cmd_low = %X cmd_size %X\n", pa_high, pa_low, cmd_size); @@ -353,7 +375,8 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, memcpy_fromio(&rsp_pa, &priv->cca->rsp_pa, 8); rsp_pa = le64_to_cpu(rsp_pa); - rsp_size = ioread32(&priv->cca->rsp_size); + rsp_size = crb_fixup_cmd_size(dev, &io_res, rsp_pa, + ioread32(&priv->cca->rsp_size)); if (cmd_pa != rsp_pa) { priv->rsp = crb_map_res(dev, priv, &io_res, rsp_pa, rsp_size); |