diff options
Diffstat (limited to 'drivers/char/tpm/tpm_crb.c')
| -rw-r--r-- | drivers/char/tpm/tpm_crb.c | 608 |
1 files changed, 428 insertions, 180 deletions
diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index a4ac63a21d8a..6c25305c256e 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2014 Intel Corporation * @@ -8,11 +9,6 @@ * * This device driver implements the TPM interface as defined in * the TCG CRB 2.0 TPM specification. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. */ #include <linux/acpi.h> @@ -23,9 +19,11 @@ #ifdef CONFIG_ARM64 #include <linux/arm-smccc.h> #endif +#include "tpm_crb_ffa.h" #include "tpm.h" #define ACPI_SIG_TPM2 "TPM2" +#define TPM_CRB_MAX_RESOURCES 3 static const guid_t crb_acpi_start_guid = GUID_INIT(0x6BBF6CAB, 0x5463, 0x4714, @@ -92,21 +90,19 @@ enum crb_status { CRB_DRV_STS_COMPLETE = BIT(0), }; -enum crb_flags { - CRB_FL_ACPI_START = BIT(0), - CRB_FL_CRB_START = BIT(1), - CRB_FL_CRB_SMC_START = BIT(2), -}; - struct crb_priv { - unsigned int flags; - void __iomem *iobase; + u32 sm; + const char *hid; struct crb_regs_head __iomem *regs_h; struct crb_regs_tail __iomem *regs_t; u8 __iomem *cmd; u8 __iomem *rsp; u32 cmd_size; u32 smc_func_id; + u32 __iomem *pluton_start_addr; + u32 __iomem *pluton_reply_addr; + u8 ffa_flags; + u8 ffa_attributes; }; struct tpm2_crb_smc { @@ -117,74 +113,157 @@ struct tpm2_crb_smc { u32 smc_func_id; }; +/* CRB over FFA start method parameters in TCG2 ACPI table */ +struct tpm2_crb_ffa { + u8 flags; + u8 attributes; + u16 partition_id; + u8 reserved[8]; +}; + +struct tpm2_crb_pluton { + u64 start_addr; + u64 reply_addr; +}; + +/* + * Returns true if the start method supports idle. + */ +static inline bool tpm_crb_has_idle(u32 start_method) +{ + return !(start_method == ACPI_TPM2_START_METHOD || + start_method == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD || + start_method == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC); +} + +static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, + unsigned long timeout) +{ + ktime_t start; + ktime_t stop; + + start = ktime_get(); + stop = ktime_add(start, ms_to_ktime(timeout)); + + do { + if ((ioread32(reg) & mask) == value) + return true; + + usleep_range(50, 100); + } while (ktime_before(ktime_get(), stop)); + + return ((ioread32(reg) & mask) == value); +} + +static int crb_try_pluton_doorbell(struct crb_priv *priv, bool wait_for_complete) +{ + if (priv->sm != ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) + return 0; + + if (!crb_wait_for_reg_32(priv->pluton_reply_addr, ~0, 1, TPM2_TIMEOUT_C)) + return -ETIME; + + iowrite32(1, priv->pluton_start_addr); + if (wait_for_complete == false) + return 0; + + if (!crb_wait_for_reg_32(priv->pluton_start_addr, + 0xffffffff, 0, 200)) + return -ETIME; + + return 0; +} + /** - * crb_go_idle - request tpm crb device to go the idle state + * __crb_go_idle - request tpm crb device to go the idle state * * @dev: crb device * @priv: crb private data + * @loc: locality * * Write CRB_CTRL_REQ_GO_IDLE to TPM_CRB_CTRL_REQ * The device should respond within TIMEOUT_C by clearing the bit. * Anyhow, we do not wait here as a consequent CMD_READY request * will be handled correctly even if idle was not completed. * - * The function does nothing for devices with ACPI-start method. + * The function does nothing for devices with ACPI-start method + * or SMC-start method. * * Return: 0 always */ -static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv) +static int __crb_go_idle(struct device *dev, struct crb_priv *priv, int loc) { - if ((priv->flags & CRB_FL_ACPI_START) || - (priv->flags & CRB_FL_CRB_SMC_START)) + int rc; + + if (!tpm_crb_has_idle(priv->sm)) return 0; iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req); - /* we don't really care when this settles */ - return 0; -} + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, loc); + if (rc) + return rc; + } -static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, - unsigned long timeout) -{ - ktime_t start; - ktime_t stop; + rc = crb_try_pluton_doorbell(priv, true); + if (rc) + return rc; - start = ktime_get(); - stop = ktime_add(start, ms_to_ktime(timeout)); + if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req, + CRB_CTRL_REQ_GO_IDLE/* mask */, + 0, /* value */ + TPM2_TIMEOUT_C)) { + dev_warn(dev, "goIdle timed out\n"); + return -ETIME; + } - do { - if ((ioread32(reg) & mask) == value) - return true; + return 0; +} - usleep_range(50, 100); - } while (ktime_before(ktime_get(), stop)); +static int crb_go_idle(struct tpm_chip *chip) +{ + struct device *dev = &chip->dev; + struct crb_priv *priv = dev_get_drvdata(dev); - return false; + return __crb_go_idle(dev, priv, chip->locality); } /** - * crb_cmd_ready - request tpm crb device to enter ready state + * __crb_cmd_ready - request tpm crb device to enter ready state * * @dev: crb device * @priv: crb private data + * @loc: locality * * Write CRB_CTRL_REQ_CMD_READY to TPM_CRB_CTRL_REQ * and poll till the device acknowledge it by clearing the bit. * The device should respond within TIMEOUT_C. * * The function does nothing for devices with ACPI-start method + * or SMC-start method. * * Return: 0 on success -ETIME on timeout; */ -static int __maybe_unused crb_cmd_ready(struct device *dev, - struct crb_priv *priv) +static int __crb_cmd_ready(struct device *dev, struct crb_priv *priv, int loc) { - if ((priv->flags & CRB_FL_ACPI_START) || - (priv->flags & CRB_FL_CRB_SMC_START)) + int rc; + + if (!tpm_crb_has_idle(priv->sm)) return 0; iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->regs_t->ctrl_req); + + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, loc); + if (rc) + return rc; + } + + rc = crb_try_pluton_doorbell(priv, true); + if (rc) + return rc; + if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req, CRB_CTRL_REQ_CMD_READY /* mask */, 0, /* value */ @@ -196,33 +275,79 @@ static int __maybe_unused crb_cmd_ready(struct device *dev, return 0; } -static int crb_request_locality(struct tpm_chip *chip, int loc) +static int crb_cmd_ready(struct tpm_chip *chip) { - struct crb_priv *priv = dev_get_drvdata(&chip->dev); - u32 value = CRB_LOC_STATE_LOC_ASSIGNED | - CRB_LOC_STATE_TPM_REG_VALID_STS; + struct device *dev = &chip->dev; + struct crb_priv *priv = dev_get_drvdata(dev); + + return __crb_cmd_ready(dev, priv, chip->locality); +} + +static int __crb_request_locality(struct device *dev, + struct crb_priv *priv, int loc) +{ + u32 value = CRB_LOC_STATE_LOC_ASSIGNED | CRB_LOC_STATE_TPM_REG_VALID_STS; + int rc; if (!priv->regs_h) return 0; iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl); + + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_LOCALITY_REQUEST, loc); + if (rc) + return rc; + } + if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value, TPM2_TIMEOUT_C)) { - dev_warn(&chip->dev, "TPM_LOC_STATE_x.requestAccess timed out\n"); + dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n"); return -ETIME; } return 0; } -static void crb_relinquish_locality(struct tpm_chip *chip, int loc) +static int crb_request_locality(struct tpm_chip *chip, int loc) { struct crb_priv *priv = dev_get_drvdata(&chip->dev); + return __crb_request_locality(&chip->dev, priv, loc); +} + +static int __crb_relinquish_locality(struct device *dev, + struct crb_priv *priv, int loc) +{ + u32 mask = CRB_LOC_STATE_LOC_ASSIGNED | CRB_LOC_STATE_TPM_REG_VALID_STS; + u32 value = CRB_LOC_STATE_TPM_REG_VALID_STS; + int rc; + if (!priv->regs_h) - return; + return 0; iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl); + + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_LOCALITY_REQUEST, loc); + if (rc) + return rc; + } + + if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, mask, value, + TPM2_TIMEOUT_C)) { + dev_warn(dev, "TPM_LOC_STATE_x.Relinquish timed out\n"); + return -ETIME; + } + + return 0; +} + +static int crb_relinquish_locality(struct tpm_chip *chip, int loc) +{ + struct crb_priv *priv = dev_get_drvdata(&chip->dev); + + return __crb_relinquish_locality(&chip->dev, priv, loc); } static u8 crb_status(struct tpm_chip *chip) @@ -242,19 +367,29 @@ static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count) struct crb_priv *priv = dev_get_drvdata(&chip->dev); unsigned int expected; - /* sanity check */ - if (count < 6) + /* A sanity check that the upper layer wants to get at least the header + * as that is the minimum size for any TPM response. + */ + if (count < TPM_HEADER_SIZE) return -EIO; + /* If this bit is set, according to the spec, the TPM is in + * unrecoverable condition. + */ if (ioread32(&priv->regs_t->ctrl_sts) & CRB_CTRL_STS_ERROR) return -EIO; - memcpy_fromio(buf, priv->rsp, 6); - expected = be32_to_cpup((__be32 *) &buf[2]); - if (expected > count || expected < 6) + /* Read the first 8 bytes in order to get the length of the response. + * We read exactly a quad word in order to make sure that the remaining + * reads will be aligned. + */ + memcpy_fromio(buf, priv->rsp, 8); + + expected = be32_to_cpup((__be32 *)&buf[2]); + if (expected > count || expected < TPM_HEADER_SIZE) return -EIO; - memcpy_fromio(&buf[6], &priv->rsp[6], expected - 6); + memcpy_fromio(&buf[8], &priv->rsp[8], expected - 8); return expected; } @@ -279,7 +414,7 @@ static int crb_do_acpi_start(struct tpm_chip *chip) #ifdef CONFIG_ARM64 /* * This is a TPM Command Response Buffer start method that invokes a - * Secure Monitor Call to requrest the firmware to execute or cancel + * Secure Monitor Call to request the firmware to execute or cancel * a TPM 2.0 command. */ static int tpm_crb_smc_start(struct device *dev, unsigned long func_id) @@ -304,7 +439,7 @@ static int tpm_crb_smc_start(struct device *dev, unsigned long func_id) } #endif -static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) +static int crb_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz, size_t len) { struct crb_priv *priv = dev_get_drvdata(&chip->dev); int rc = 0; @@ -320,33 +455,61 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) return -E2BIG; } + /* Seems to be necessary for every command */ + if (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) + __crb_cmd_ready(&chip->dev, priv, chip->locality); + memcpy_toio(priv->cmd, buf, len); /* Make sure that cmd is populated before issuing start. */ wmb(); - if (priv->flags & CRB_FL_CRB_START) + /* The reason for the extra quirk is that the PTT in 4th Gen Core CPUs + * report only ACPI start but in practice seems to require both + * CRB start, hence invoking CRB start method if hid == MSFT0101. + */ + if (priv->sm == ACPI_TPM2_COMMAND_BUFFER || + priv->sm == ACPI_TPM2_MEMORY_MAPPED || + !strcmp(priv->hid, "MSFT0101")) iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start); - if (priv->flags & CRB_FL_ACPI_START) + if (priv->sm == ACPI_TPM2_START_METHOD || + priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) rc = crb_do_acpi_start(chip); - if (priv->flags & CRB_FL_CRB_SMC_START) { + if (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC) { iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start); rc = tpm_crb_smc_start(&chip->dev, priv->smc_func_id); } - return rc; + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start); + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, chip->locality); + } + + if (rc) + return rc; + + return crb_try_pluton_doorbell(priv, false); } static void crb_cancel(struct tpm_chip *chip) { struct crb_priv *priv = dev_get_drvdata(&chip->dev); + int rc; iowrite32(CRB_CANCEL_INVOKE, &priv->regs_t->ctrl_cancel); - if ((priv->flags & CRB_FL_ACPI_START) && crb_do_acpi_start(chip)) + if ((priv->sm == ACPI_TPM2_START_METHOD || + priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) && + crb_do_acpi_start(chip)) dev_err(&chip->dev, "ACPI Start failed\n"); + + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, chip->locality); + if (rc) + dev_err(&chip->dev, "FF-A Start failed\n"); + } } static bool crb_req_canceled(struct tpm_chip *chip, u8 status) @@ -364,6 +527,8 @@ static const struct tpm_class_ops tpm_crb = { .send = crb_send, .cancel = crb_cancel, .req_canceled = crb_req_canceled, + .go_idle = crb_go_idle, + .cmd_ready = crb_cmd_ready, .request_locality = crb_request_locality, .relinquish_locality = crb_relinquish_locality, .req_complete_mask = CRB_DRV_STS_COMPLETE, @@ -372,21 +537,27 @@ static const struct tpm_class_ops tpm_crb = { 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 *res = &(win.res); + int i; if (acpi_dev_resource_memory(ares, res) || acpi_dev_resource_address_space(ares, &win)) { - *io_res = *res; - io_res->name = NULL; + for (i = 0; i < TPM_CRB_MAX_RESOURCES + 1; ++i) { + if (resource_type(iores_array + i) != IORESOURCE_MEM) { + iores_array[i] = *res; + iores_array[i].name = NULL; + break; + } + } } return 1; } -static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv, - struct resource *io_res, u64 start, u32 size) +static void __iomem *crb_map_res(struct device *dev, struct resource *iores, + void __iomem **iobase_ptr, u64 start, u32 size) { struct resource new_res = { .start = start, @@ -396,12 +567,18 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv, /* Detect a 64 bit address on a 32 bit system */ if (start != new_res.start) - return (void __iomem *) ERR_PTR(-EINVAL); + return IOMEM_ERR_PTR(-EINVAL); - if (!resource_contains(io_res, &new_res)) + if (!iores) 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); } /* @@ -428,79 +605,139 @@ 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, struct acpi_table_tpm2 *buf) { - struct list_head resources; - struct resource io_res; + struct list_head acpi_resource_list; + 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 resource *iores; + void __iomem **iobase_ptr; + int i; u32 pa_high, pa_low; u64 cmd_pa; u32 cmd_size; + __le64 __rsp_pa; u64 rsp_pa; u32 rsp_size; int ret; - INIT_LIST_HEAD(&resources); - ret = acpi_dev_get_resources(device, &resources, crb_check_resource, - &io_res); - if (ret < 0) - return ret; - acpi_dev_free_resource_list(&resources); + /* + * Pluton sometimes does not define ACPI memory regions. + * Mapping is then done in crb_map_pluton + */ + if (priv->sm != ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) { + INIT_LIST_HEAD(&acpi_resource_list); + ret = acpi_dev_get_resources(device, &acpi_resource_list, + crb_check_resource, iores_array); + if (ret < 0) + return ret; + acpi_dev_free_resource_list(&acpi_resource_list); + + if (resource_type(iores_array) != IORESOURCE_MEM) { + dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n"); + 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; + } + } - if (resource_type(&io_res) != IORESOURCE_MEM) { - dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n"); - return -EINVAL; + 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); - if (IS_ERR(priv->iobase)) - return PTR_ERR(priv->iobase); + priv->regs_t = crb_map_res(dev, iores, iobase_ptr, buf->control_address, + sizeof(struct crb_regs_tail)); + + 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 control area, as one nice sane region except for some older * stuff that puts the control area outside the ACPI IO region. */ - if (!(priv->flags & CRB_FL_ACPI_START)) { - if (buf->control_address == io_res.start + + if (priv->sm == ACPI_TPM2_COMMAND_BUFFER || + priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA || + priv->sm == ACPI_TPM2_MEMORY_MAPPED) { + if (iores && + buf->control_address == iores->start + sizeof(*priv->regs_h)) - priv->regs_h = priv->iobase; + priv->regs_h = *iobase_ptr; else dev_warn(dev, FW_BUG "Bad ACPI memory layout"); } - priv->regs_t = crb_map_res(dev, priv, &io_res, buf->control_address, - sizeof(struct crb_regs_tail)); - if (IS_ERR(priv->regs_t)) - return PTR_ERR(priv->regs_t); + ret = __crb_request_locality(dev, priv, 0); + if (ret) + return ret; /* * PTT HW bug w/a: wake up the device to access * possibly not retained registers. */ - ret = crb_cmd_ready(dev, priv); + ret = __crb_cmd_ready(dev, priv, 0); if (ret) - return ret; + goto out_relinquish_locality; pa_high = ioread32(&priv->regs_t->ctrl_cmd_pa_high); pa_low = ioread32(&priv->regs_t->ctrl_cmd_pa_low); cmd_pa = ((u64)pa_high << 32) | pa_low; - cmd_size = crb_fixup_cmd_size(dev, &io_res, cmd_pa, - ioread32(&priv->regs_t->ctrl_cmd_size)); + 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", 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)) { ret = PTR_ERR(priv->cmd); goto out; } - memcpy_fromio(&rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8); - rsp_pa = le64_to_cpu(rsp_pa); - rsp_size = crb_fixup_cmd_size(dev, &io_res, rsp_pa, - ioread32(&priv->regs_t->ctrl_rsp_size)); + memcpy_fromio(&__rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8); + rsp_pa = le64_to_cpu(__rsp_pa); + 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) { - 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); goto out; } @@ -520,11 +757,31 @@ out: if (!ret) priv->cmd_size = cmd_size; - crb_go_idle(dev, priv); + __crb_go_idle(dev, priv, 0); + +out_relinquish_locality: + + __crb_relinquish_locality(dev, priv, 0); return ret; } +static int crb_map_pluton(struct device *dev, struct crb_priv *priv, + struct acpi_table_tpm2 *buf, struct tpm2_crb_pluton *crb_pluton) +{ + priv->pluton_start_addr = crb_map_res(dev, NULL, NULL, + crb_pluton->start_addr, 4); + if (IS_ERR(priv->pluton_start_addr)) + return PTR_ERR(priv->pluton_start_addr); + + priv->pluton_reply_addr = crb_map_res(dev, NULL, NULL, + crb_pluton->reply_addr, 4); + if (IS_ERR(priv->pluton_reply_addr)) + return PTR_ERR(priv->pluton_reply_addr); + + return 0; +} + static int crb_acpi_add(struct acpi_device *device) { struct acpi_table_tpm2 *buf; @@ -532,6 +789,8 @@ static int crb_acpi_add(struct acpi_device *device) struct tpm_chip *chip; struct device *dev = &device->dev; struct tpm2_crb_smc *crb_smc; + struct tpm2_crb_ffa *crb_ffa; + struct tpm2_crb_pluton *crb_pluton; acpi_status status; u32 sm; int rc; @@ -545,24 +804,16 @@ static int crb_acpi_add(struct acpi_device *device) /* Should the FIFO driver handle this? */ sm = buf->start_method; - if (sm == ACPI_TPM2_MEMORY_MAPPED) - return -ENODEV; + if (sm == ACPI_TPM2_MEMORY_MAPPED) { + rc = -ENODEV; + goto out; + } priv = devm_kzalloc(dev, sizeof(struct crb_priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - /* The reason for the extra quirk is that the PTT in 4th Gen Core CPUs - * report only ACPI start but in practice seems to require both - * ACPI start and CRB start. - */ - if (sm == ACPI_TPM2_COMMAND_BUFFER || sm == ACPI_TPM2_MEMORY_MAPPED || - !strcmp(acpi_device_hid(device), "MSFT0101")) - priv->flags |= CRB_FL_CRB_START; - - if (sm == ACPI_TPM2_START_METHOD || - sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) - priv->flags |= CRB_FL_ACPI_START; + if (!priv) { + rc = -ENOMEM; + goto out; + } if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC) { if (buf->header.length < (sizeof(*buf) + sizeof(*crb_smc))) { @@ -570,102 +821,99 @@ static int crb_acpi_add(struct acpi_device *device) FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n", buf->header.length, ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC); - return -EINVAL; + rc = -EINVAL; + goto out; } crb_smc = ACPI_ADD_PTR(struct tpm2_crb_smc, buf, sizeof(*buf)); priv->smc_func_id = crb_smc->smc_func_id; - priv->flags |= CRB_FL_CRB_SMC_START; } + if (sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + if (buf->header.length < (sizeof(*buf) + sizeof(*crb_ffa))) { + dev_err(dev, + FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n", + buf->header.length, + ACPI_TPM2_CRB_WITH_ARM_FFA); + rc = -EINVAL; + goto out; + } + crb_ffa = ACPI_ADD_PTR(struct tpm2_crb_ffa, buf, sizeof(*buf)); + priv->ffa_flags = crb_ffa->flags; + priv->ffa_attributes = crb_ffa->attributes; + rc = tpm_crb_ffa_init(); + if (rc) { + /* If FF-A driver is not available yet, request probe retry */ + if (rc == -ENOENT) + rc = -EPROBE_DEFER; + goto out; + } + } + + if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) { + if (buf->header.length < (sizeof(*buf) + sizeof(*crb_pluton))) { + dev_err(dev, + FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n", + buf->header.length, + ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON); + rc = -EINVAL; + goto out; + } + crb_pluton = ACPI_ADD_PTR(struct tpm2_crb_pluton, buf, sizeof(*buf)); + rc = crb_map_pluton(dev, priv, buf, crb_pluton); + if (rc) + goto out; + } + + priv->sm = sm; + priv->hid = acpi_device_hid(device); + rc = crb_map_io(device, priv, buf); if (rc) - return rc; + goto out; chip = tpmm_chip_alloc(dev, &tpm_crb); - if (IS_ERR(chip)) - return PTR_ERR(chip); + if (IS_ERR(chip)) { + rc = PTR_ERR(chip); + goto out; + } dev_set_drvdata(&chip->dev, priv); chip->acpi_dev_handle = device->handle; chip->flags = TPM_CHIP_FLAG_TPM2; - rc = crb_cmd_ready(dev, priv); + rc = tpm_chip_bootstrap(chip); if (rc) - return rc; - - pm_runtime_get_noresume(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + goto out; - rc = tpm_chip_register(chip); - if (rc) { - crb_go_idle(dev, priv); - pm_runtime_put_noidle(dev); - pm_runtime_disable(dev); - return rc; +#ifdef CONFIG_X86 + /* A quirk for https://www.amd.com/en/support/kb/faq/pa-410 */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && + priv->sm != ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) { + dev_info(dev, "Disabling hwrng\n"); + chip->flags |= TPM_CHIP_FLAG_HWRNG_DISABLED; } +#endif /* CONFIG_X86 */ - pm_runtime_put(dev); + rc = tpm_chip_register(chip); - return 0; +out: + acpi_put_table((struct acpi_table_header *)buf); + return rc; } -static int crb_acpi_remove(struct acpi_device *device) +static void crb_acpi_remove(struct acpi_device *device) { struct device *dev = &device->dev; struct tpm_chip *chip = dev_get_drvdata(dev); tpm_chip_unregister(chip); - - pm_runtime_disable(dev); - - return 0; -} - -static int __maybe_unused crb_pm_runtime_suspend(struct device *dev) -{ - struct tpm_chip *chip = dev_get_drvdata(dev); - struct crb_priv *priv = dev_get_drvdata(&chip->dev); - - return crb_go_idle(dev, priv); -} - -static int __maybe_unused crb_pm_runtime_resume(struct device *dev) -{ - struct tpm_chip *chip = dev_get_drvdata(dev); - struct crb_priv *priv = dev_get_drvdata(&chip->dev); - - return crb_cmd_ready(dev, priv); -} - -static int __maybe_unused crb_pm_suspend(struct device *dev) -{ - int ret; - - ret = tpm_pm_suspend(dev); - if (ret) - return ret; - - return crb_pm_runtime_suspend(dev); -} - -static int __maybe_unused crb_pm_resume(struct device *dev) -{ - int ret; - - ret = crb_pm_runtime_resume(dev); - if (ret) - return ret; - - return tpm_pm_resume(dev); } static const struct dev_pm_ops crb_pm = { - SET_SYSTEM_SLEEP_PM_OPS(crb_pm_suspend, crb_pm_resume) - SET_RUNTIME_PM_OPS(crb_pm_runtime_suspend, crb_pm_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(tpm_pm_suspend, tpm_pm_resume) }; -static struct acpi_device_id crb_device_ids[] = { +static const struct acpi_device_id crb_device_ids[] = { {"MSFT0101", 0}, {"", 0}, }; |
