소스 검색

tpm/tpm_crb: Enable TPM CRB interface for ARM64

This enables TPM Command Response Buffer interface driver for
ARM64 and implements an ARM specific TPM CRB start method that
invokes a Secure Monitor Call (SMC) to request the TrustZone
Firmware to execute or cancel a TPM 2.0 command.

In ARM, TrustZone security extensions enable a secure software
environment with Secure Monitor mode.  A Secure Monitor Call
(SMC) is used to enter the Secure Monitor mode and perform a
Secure Monitor service to communicate with TrustZone firmware
which has control over the TPM hardware.

Signed-off-by: Jiandi An <anjiandi@codeaurora.org>
Tested-by: Shanker Donthineni <shankerd@codeaurora.org>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Tested-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> (on x86/PTT)
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Jiandi An 8 년 전
부모
커밋
08eff49d63
2개의 변경된 파일66개의 추가작업 그리고 3개의 파일을 삭제
  1. 1 1
      drivers/char/tpm/Kconfig
  2. 65 2
      drivers/char/tpm/tpm_crb.c

+ 1 - 1
drivers/char/tpm/Kconfig

@@ -136,7 +136,7 @@ config TCG_XEN
 
 
 config TCG_CRB
 config TCG_CRB
 	tristate "TPM 2.0 CRB Interface"
 	tristate "TPM 2.0 CRB Interface"
-	depends on X86 && ACPI
+	depends on ACPI
 	---help---
 	---help---
 	  If you have a TPM security chip that is compliant with the
 	  If you have a TPM security chip that is compliant with the
 	  TCG CRB 2.0 TPM specification say Yes and it will be accessible
 	  TCG CRB 2.0 TPM specification say Yes and it will be accessible

+ 65 - 2
drivers/char/tpm/tpm_crb.c

@@ -20,6 +20,9 @@
 #include <linux/rculist.h>
 #include <linux/rculist.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm_runtime.h>
+#ifdef CONFIG_ARM64
+#include <linux/arm-smccc.h>
+#endif
 #include "tpm.h"
 #include "tpm.h"
 
 
 #define ACPI_SIG_TPM2 "TPM2"
 #define ACPI_SIG_TPM2 "TPM2"
@@ -93,6 +96,7 @@ enum crb_status {
 enum crb_flags {
 enum crb_flags {
 	CRB_FL_ACPI_START	= BIT(0),
 	CRB_FL_ACPI_START	= BIT(0),
 	CRB_FL_CRB_START	= BIT(1),
 	CRB_FL_CRB_START	= BIT(1),
+	CRB_FL_CRB_SMC_START	= BIT(2),
 };
 };
 
 
 struct crb_priv {
 struct crb_priv {
@@ -103,6 +107,15 @@ struct crb_priv {
 	u8 __iomem *cmd;
 	u8 __iomem *cmd;
 	u8 __iomem *rsp;
 	u8 __iomem *rsp;
 	u32 cmd_size;
 	u32 cmd_size;
+	u32 smc_func_id;
+};
+
+struct tpm2_crb_smc {
+	u32 interrupt;
+	u8 interrupt_flags;
+	u8 op_flags;
+	u16 reserved2;
+	u32 smc_func_id;
 };
 };
 
 
 /**
 /**
@@ -122,7 +135,8 @@ struct crb_priv {
  */
  */
 static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv)
 static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv)
 {
 {
-	if (priv->flags & CRB_FL_ACPI_START)
+	if ((priv->flags & CRB_FL_ACPI_START) ||
+	    (priv->flags & CRB_FL_CRB_SMC_START))
 		return 0;
 		return 0;
 
 
 	iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req);
 	iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req);
@@ -167,7 +181,8 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
 static int __maybe_unused crb_cmd_ready(struct device *dev,
 static int __maybe_unused crb_cmd_ready(struct device *dev,
 					struct crb_priv *priv)
 					struct crb_priv *priv)
 {
 {
-	if (priv->flags & CRB_FL_ACPI_START)
+	if ((priv->flags & CRB_FL_ACPI_START) ||
+	    (priv->flags & CRB_FL_CRB_SMC_START))
 		return 0;
 		return 0;
 
 
 	iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->regs_t->ctrl_req);
 	iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->regs_t->ctrl_req);
@@ -262,6 +277,34 @@ static int crb_do_acpi_start(struct tpm_chip *chip)
 	return rc;
 	return rc;
 }
 }
 
 
+#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
+ * a TPM 2.0 command.
+ */
+static int tpm_crb_smc_start(struct device *dev, unsigned long func_id)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(func_id, 0, 0, 0, 0, 0, 0, 0, &res);
+	if (res.a0 != 0) {
+		dev_err(dev,
+			FW_BUG "tpm_crb_smc_start() returns res.a0 = 0x%lx\n",
+			res.a0);
+		return -EIO;
+	}
+
+	return 0;
+}
+#else
+static int tpm_crb_smc_start(struct device *dev, unsigned long func_id)
+{
+	dev_err(dev, FW_BUG "tpm_crb: incorrect start method\n");
+	return -EINVAL;
+}
+#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 len)
 {
 {
 	struct crb_priv *priv = dev_get_drvdata(&chip->dev);
 	struct crb_priv *priv = dev_get_drvdata(&chip->dev);
@@ -289,6 +332,11 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len)
 	if (priv->flags & CRB_FL_ACPI_START)
 	if (priv->flags & CRB_FL_ACPI_START)
 		rc = crb_do_acpi_start(chip);
 		rc = crb_do_acpi_start(chip);
 
 
+	if (priv->flags & CRB_FL_CRB_SMC_START) {
+		iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start);
+		rc = tpm_crb_smc_start(&chip->dev, priv->smc_func_id);
+	}
+
 	return rc;
 	return rc;
 }
 }
 
 
@@ -483,6 +531,7 @@ static int crb_acpi_add(struct acpi_device *device)
 	struct crb_priv *priv;
 	struct crb_priv *priv;
 	struct tpm_chip *chip;
 	struct tpm_chip *chip;
 	struct device *dev = &device->dev;
 	struct device *dev = &device->dev;
+	struct tpm2_crb_smc *crb_smc;
 	acpi_status status;
 	acpi_status status;
 	u32 sm;
 	u32 sm;
 	int rc;
 	int rc;
@@ -515,6 +564,20 @@ static int crb_acpi_add(struct acpi_device *device)
 	    sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD)
 	    sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD)
 		priv->flags |= CRB_FL_ACPI_START;
 		priv->flags |= CRB_FL_ACPI_START;
 
 
+	if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_SMC) {
+		if (buf->header.length < (sizeof(*buf) + sizeof(*crb_smc))) {
+			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_SMC);
+			return -EINVAL;
+		}
+		crb_smc = ACPI_ADD_PTR(struct tpm2_crb_smc, buf,
+				       ACPI_TPM2_START_METHOD_PARAMETER_OFFSET);
+		priv->smc_func_id = crb_smc->smc_func_id;
+		priv->flags |= CRB_FL_CRB_SMC_START;
+	}
+
 	rc = crb_map_io(device, priv, buf);
 	rc = crb_map_io(device, priv, buf);
 	if (rc)
 	if (rc)
 		return rc;
 		return rc;