Linux-Integrity Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH 0/8] tpm: Add driver for cr50
@ 2019-06-13 18:09 Stephen Boyd
  2019-06-13 18:09 ` [PATCH 1/8] tpm: block messages while suspended Stephen Boyd
                   ` (7 more replies)
  0 siblings, 8 replies; 24+ messages in thread
From: Stephen Boyd @ 2019-06-13 18:09 UTC (permalink / raw)
  To: Peter Huewe, Jarkko Sakkinen
  Cc: linux-kernel, Jason Gunthorpe, Arnd Bergmann, Greg Kroah-Hartman,
	linux-integrity, Andrey Pronin, devicetree, Duncan Laurie,
	Guenter Roeck

This patch series adds support for the the H1 secure microcontroller
running cr50 firmware found on various recent Chromebooks. This driver
is necessary to boot into a ChromeOS userspace environment. It
implements support for several functions, including TPM-like
functionality, and supports SPI and I2C interfaces.

The last time this was series sent looks to be [1]. I've looked over the
patches and review comments and tried to address any feedback that
Andrey didn't address (really minor things like newlines). The first
three patches add a couple pre-requisite core changes so that the
drivers can be merged. The last two patches add some sysfs attributes
and make symlinks so that ChromeOS userspace works.

[1] https://lkml.kernel.org/r/1469757314-116169-1-git-send-email-apronin@chromium.org

Andrey Pronin (7):
  tpm: block messages while suspended
  tpm_tis_core: add optional max xfer size check
  tpm_tis_spi: add max xfer size
  dt-bindings: tpm: document properties for cr50
  tpm: add driver for cr50 on SPI
  tpm: add sysfs attributes for tpm2
  tpm: add legacy sysfs attributes for tpm2

Duncan Laurie (1):
  tpm: Add driver for cr50 on I2C

 .../bindings/security/tpm/cr50_spi.txt        |  19 +
 drivers/char/tpm/Kconfig                      |  26 +
 drivers/char/tpm/Makefile                     |   3 +
 drivers/char/tpm/cr50.c                       |  39 +
 drivers/char/tpm/cr50.h                       |  30 +
 drivers/char/tpm/cr50_i2c.c                   | 704 ++++++++++++++++++
 drivers/char/tpm/cr50_spi.c                   | 450 +++++++++++
 drivers/char/tpm/tpm-chip.c                   |   4 +-
 drivers/char/tpm/tpm-interface.c              |  16 +-
 drivers/char/tpm/tpm-sysfs.c                  | 138 +++-
 drivers/char/tpm/tpm.h                        |  29 +-
 drivers/char/tpm/tpm_tis_core.c               |   9 +-
 drivers/char/tpm/tpm_tis_core.h               |   1 +
 drivers/char/tpm/tpm_tis_spi.c                |   1 +
 include/linux/tpm.h                           |   2 +
 15 files changed, 1455 insertions(+), 16 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/security/tpm/cr50_spi.txt
 create mode 100644 drivers/char/tpm/cr50.c
 create mode 100644 drivers/char/tpm/cr50.h
 create mode 100644 drivers/char/tpm/cr50_i2c.c
 create mode 100644 drivers/char/tpm/cr50_spi.c


base-commit: f2c7c76c5d0a443053e94adb9f0918fa2fb85c3a
-- 
Sent by a computer through tubes


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 1/8] tpm: block messages while suspended
  2019-06-13 18:09 [PATCH 0/8] tpm: Add driver for cr50 Stephen Boyd
@ 2019-06-13 18:09 ` Stephen Boyd
  2019-06-13 23:26   ` Jason Gunthorpe
  2019-06-14 15:27   ` Jarkko Sakkinen
  2019-06-13 18:09 ` [PATCH 2/8] tpm_tis_core: add optional max xfer size check Stephen Boyd
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 24+ messages in thread
From: Stephen Boyd @ 2019-06-13 18:09 UTC (permalink / raw)
  To: Peter Huewe, Jarkko Sakkinen
  Cc: Andrey Pronin, linux-kernel, Jason Gunthorpe, Arnd Bergmann,
	Greg Kroah-Hartman, linux-integrity, devicetree, Duncan Laurie,
	Guenter Roeck

From: Andrey Pronin <apronin@chromium.org>

Other drivers or userspace may initiate sending a message to the tpm
while the device itself and the controller of the bus it is on are
suspended. That may break the bus driver logic.
Block sending messages while the device is suspended.

Signed-off-by: Andrey Pronin <apronin@chromium.org>
Signed-off-by: Stephen Boyd <swboyd@chromium.org>
---

I don't think this was ever posted before.

 drivers/char/tpm/tpm-interface.c | 16 ++++++++++++++--
 include/linux/tpm.h              |  2 ++
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index ae1030c9b086..7232527652e8 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -86,6 +86,11 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz)
 		return -E2BIG;
 	}
 
+	if (test_bit(0, &chip->is_suspended)) {
+		dev_warn(&chip->dev, "blocking transmit while suspended\n");
+		return -EAGAIN;
+	}
+
 	rc = chip->ops->send(chip, buf, count);
 	if (rc < 0) {
 		if (rc != -EPIPE)
@@ -403,14 +408,19 @@ int tpm_pm_suspend(struct device *dev)
 		return 0;
 
 	if (!tpm_chip_start(chip)) {
-		if (chip->flags & TPM_CHIP_FLAG_TPM2)
+		if (chip->flags & TPM_CHIP_FLAG_TPM2) {
 			tpm2_shutdown(chip, TPM2_SU_STATE);
-		else
+			set_bit(0, &chip->is_suspended);
+		} else {
 			rc = tpm1_pm_suspend(chip, tpm_suspend_pcr);
+		}
 
 		tpm_chip_stop(chip);
 	}
 
+	if (!rc)
+		set_bit(0, &chip->is_suspended);
+
 	return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_pm_suspend);
@@ -426,6 +436,8 @@ int tpm_pm_resume(struct device *dev)
 	if (chip == NULL)
 		return -ENODEV;
 
+	clear_bit(0, &chip->is_suspended);
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(tpm_pm_resume);
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 1b5436b213a2..48df005228d0 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -132,6 +132,8 @@ struct tpm_chip {
 	int dev_num;		/* /dev/tpm# */
 	unsigned long is_open;	/* only one allowed */
 
+	unsigned long is_suspended;
+
 	char hwrng_name[64];
 	struct hwrng hwrng;
 
-- 
Sent by a computer through tubes


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 2/8] tpm_tis_core: add optional max xfer size check
  2019-06-13 18:09 [PATCH 0/8] tpm: Add driver for cr50 Stephen Boyd
  2019-06-13 18:09 ` [PATCH 1/8] tpm: block messages while suspended Stephen Boyd
@ 2019-06-13 18:09 ` Stephen Boyd
  2019-06-14 15:24   ` Jarkko Sakkinen
  2019-06-13 18:09 ` [PATCH 3/8] tpm_tis_spi: add max xfer size Stephen Boyd
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 24+ messages in thread
From: Stephen Boyd @ 2019-06-13 18:09 UTC (permalink / raw)
  To: Peter Huewe, Jarkko Sakkinen
  Cc: Andrey Pronin, linux-kernel, Jason Gunthorpe, Arnd Bergmann,
	Greg Kroah-Hartman, linux-integrity, devicetree, Duncan Laurie,
	Guenter Roeck, Dmitry Torokhov

From: Andrey Pronin <apronin@chromium.org>

If tpm reports a bigger burstcnt than allowed by the physical protocol,
set burstcnt to the max allowed value.

In practice, seen in case of xfer issues (e.g. in spi interface case,
lost header causing flow control issues and wrong values returned on read
from TPM_STS). Without catching, causes the physical layer to reject xfer.

Signed-off-by: Andrey Pronin <apronin@chromium.org>
Reviewed-by: Dmitry Torokhov <dtor@chromium.org>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
[swboyd@chromium.org: Drop extra parenthesis in if statement]
Signed-off-by: Stephen Boyd <swboyd@chromium.org>
---

This is a resend of https://lkml.kernel.org/r/1469677797-74304-2-git-send-email-apronin@chromium.org

 drivers/char/tpm/tpm_tis_core.c | 9 ++++++++-
 drivers/char/tpm/tpm_tis_core.h | 1 +
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c
index b9f64684c3fb..ecf703802333 100644
--- a/drivers/char/tpm/tpm_tis_core.c
+++ b/drivers/char/tpm/tpm_tis_core.c
@@ -272,8 +272,15 @@ static int get_burstcount(struct tpm_chip *chip)
 			return rc;
 
 		burstcnt = (value >> 8) & 0xFFFF;
-		if (burstcnt)
+		if (burstcnt) {
+			if (priv->phy_ops->max_xfer_size &&
+			    burstcnt > priv->phy_ops->max_xfer_size) {
+				dev_warn(&chip->dev,
+					 "Bad burstcnt read: %d\n", burstcnt);
+				burstcnt = priv->phy_ops->max_xfer_size;
+			}
 			return burstcnt;
+		}
 		usleep_range(TPM_TIMEOUT_USECS_MIN, TPM_TIMEOUT_USECS_MAX);
 	} while (time_before(jiffies, stop));
 	return -EBUSY;
diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h
index f48125f1e6e0..bb4979cf81ed 100644
--- a/drivers/char/tpm/tpm_tis_core.h
+++ b/drivers/char/tpm/tpm_tis_core.h
@@ -110,6 +110,7 @@ struct tpm_tis_phy_ops {
 	int (*read16)(struct tpm_tis_data *data, u32 addr, u16 *result);
 	int (*read32)(struct tpm_tis_data *data, u32 addr, u32 *result);
 	int (*write32)(struct tpm_tis_data *data, u32 addr, u32 src);
+	u16 max_xfer_size;
 };
 
 static inline int tpm_tis_read_bytes(struct tpm_tis_data *data, u32 addr,
-- 
Sent by a computer through tubes


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 3/8] tpm_tis_spi: add max xfer size
  2019-06-13 18:09 [PATCH 0/8] tpm: Add driver for cr50 Stephen Boyd
  2019-06-13 18:09 ` [PATCH 1/8] tpm: block messages while suspended Stephen Boyd
  2019-06-13 18:09 ` [PATCH 2/8] tpm_tis_core: add optional max xfer size check Stephen Boyd
@ 2019-06-13 18:09 ` Stephen Boyd
  2019-06-14 15:25   ` Jarkko Sakkinen
  2019-06-13 18:09 ` [PATCH 4/8] dt-bindings: tpm: document properties for cr50 Stephen Boyd
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 24+ messages in thread
From: Stephen Boyd @ 2019-06-13 18:09 UTC (permalink / raw)
  To: Peter Huewe, Jarkko Sakkinen
  Cc: Andrey Pronin, linux-kernel, Jason Gunthorpe, Arnd Bergmann,
	Greg Kroah-Hartman, linux-integrity, devicetree, Duncan Laurie,
	Guenter Roeck, Dmitry Torokhov

From: Andrey Pronin <apronin@chromium.org>

Reject burstcounts larger than 64 bytes reported by tpm.
SPI Hardware Protocol defined in section 6.4 of TCG PTP
Spec supports up to 64 bytes of data in a transaction.

Signed-off-by: Andrey Pronin <apronin@chromium.org>
Reviewed-by: Dmitry Torokhov <dtor@chromium.org>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Stephen Boyd <swboyd@chromium.org>
---

This is a resend of https://lkml.kernel.org/r/1469677797-74304-3-git-send-email-apronin@chromium.org

 drivers/char/tpm/tpm_tis_spi.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c
index 9914f6973463..0fdd3966a3b3 100644
--- a/drivers/char/tpm/tpm_tis_spi.c
+++ b/drivers/char/tpm/tpm_tis_spi.c
@@ -194,6 +194,7 @@ static const struct tpm_tis_phy_ops tpm_spi_phy_ops = {
 	.read16 = tpm_tis_spi_read16,
 	.read32 = tpm_tis_spi_read32,
 	.write32 = tpm_tis_spi_write32,
+	.max_xfer_size = MAX_SPI_FRAMESIZE,
 };
 
 static int tpm_tis_spi_probe(struct spi_device *dev)
-- 
Sent by a computer through tubes


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 4/8] dt-bindings: tpm: document properties for cr50
  2019-06-13 18:09 [PATCH 0/8] tpm: Add driver for cr50 Stephen Boyd
                   ` (2 preceding siblings ...)
  2019-06-13 18:09 ` [PATCH 3/8] tpm_tis_spi: add max xfer size Stephen Boyd
@ 2019-06-13 18:09 ` Stephen Boyd
  2019-07-09 14:41   ` Rob Herring
  2019-06-13 18:09 ` [PATCH 5/8] tpm: add driver for cr50 on SPI Stephen Boyd
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 24+ messages in thread
From: Stephen Boyd @ 2019-06-13 18:09 UTC (permalink / raw)
  To: Peter Huewe, Jarkko Sakkinen
  Cc: Andrey Pronin, linux-kernel, Jason Gunthorpe, Arnd Bergmann,
	Greg Kroah-Hartman, linux-integrity, devicetree, Duncan Laurie,
	Guenter Roeck, Rob Herring

From: Andrey Pronin <apronin@chromium.org>

Add TPM2.0 PTP FIFO compatible SPI interface for chips with Cr50
firmware.

Signed-off-by: Andrey Pronin <apronin@chromium.org>
Cc: Rob Herring <robh+dt@kernel.org>
Signed-off-by: Stephen Boyd <swboyd@chromium.org>
---

This is a resend of https://lkml.kernel.org/r/1469757314-116169-2-git-send-email-apronin@chromium.org
with status removed.

 .../bindings/security/tpm/cr50_spi.txt        | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/security/tpm/cr50_spi.txt

diff --git a/Documentation/devicetree/bindings/security/tpm/cr50_spi.txt b/Documentation/devicetree/bindings/security/tpm/cr50_spi.txt
new file mode 100644
index 000000000000..401f4ba281b7
--- /dev/null
+++ b/Documentation/devicetree/bindings/security/tpm/cr50_spi.txt
@@ -0,0 +1,19 @@
+* H1 Secure Microcontroller with Cr50 Firmware on SPI Bus.
+
+H1 Secure Microcontroller running Cr50 firmware provides several
+functions, including TPM-like functionality. It communicates over
+SPI using the FIFO protocol described in the PTP Spec, section 6.
+
+Required properties:
+- compatible: Should be "google,cr50".
+- spi-max-frequency: Maximum SPI frequency.
+
+Example:
+
+&spi0 {
+        cr50@0 {
+                compatible = "google,cr50";
+                reg = <0>;
+                spi-max-frequency = <800000>;
+        };
+};
-- 
Sent by a computer through tubes


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 5/8] tpm: add driver for cr50 on SPI
  2019-06-13 18:09 [PATCH 0/8] tpm: Add driver for cr50 Stephen Boyd
                   ` (3 preceding siblings ...)
  2019-06-13 18:09 ` [PATCH 4/8] dt-bindings: tpm: document properties for cr50 Stephen Boyd
@ 2019-06-13 18:09 ` Stephen Boyd
  2019-06-13 18:09 ` [PATCH 6/8] tpm: Add driver for cr50 on I2C Stephen Boyd
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 24+ messages in thread
From: Stephen Boyd @ 2019-06-13 18:09 UTC (permalink / raw)
  To: Peter Huewe, Jarkko Sakkinen
  Cc: Andrey Pronin, linux-kernel, Jason Gunthorpe, Arnd Bergmann,
	Greg Kroah-Hartman, linux-integrity, devicetree, Duncan Laurie,
	Guenter Roeck

From: Andrey Pronin <apronin@chromium.org>

Add TPM2.0 PTP FIFO compatible SPI interface for chips with Cr50
firmware. The firmware running on the currently supported H1
Secure Microcontroller requires a special driver to handle its
specifics:
 - need to ensure a certain delay between spi transactions, or else
   the chip may miss some part of the next transaction;
 - if there is no spi activity for some time, it may go to sleep,
   and needs to be waken up before sending further commands;
 - access to vendor-specific registers.

Signed-off-by: Andrey Pronin <apronin@chromium.org>
[swboyd@chromium.org: Replace boilerplate with SPDX tag]
Signed-off-by: Stephen Boyd <swboyd@chromium.org>
---

Previous revision is https://lkml.kernel.org/r/1469757314-116169-1-git-send-email-apronin@chromium.org

 drivers/char/tpm/Kconfig    |  16 ++
 drivers/char/tpm/Makefile   |   2 +
 drivers/char/tpm/cr50.c     |  39 ++++
 drivers/char/tpm/cr50.h     |  30 +++
 drivers/char/tpm/cr50_spi.c | 450 ++++++++++++++++++++++++++++++++++++
 5 files changed, 537 insertions(+)
 create mode 100644 drivers/char/tpm/cr50.c
 create mode 100644 drivers/char/tpm/cr50.h
 create mode 100644 drivers/char/tpm/cr50_spi.c

diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index 88a3c06fc153..b7028bfa6f87 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -114,6 +114,22 @@ config TCG_ATMEL
 	  will be accessible from within Linux.  To compile this driver 
 	  as a module, choose M here; the module will be called tpm_atmel.
 
+config TCG_CR50
+	bool
+	---help---
+	  Common routines shared by drivers for Cr50-based devices.
+
+config TCG_CR50_SPI
+	tristate "Cr50 SPI Interface"
+	depends on SPI
+	select TCG_TIS_CORE
+	select TCG_CR50
+	---help---
+	  If you have a H1 secure module running Cr50 firmware on SPI bus,
+	  say Yes and it will be accessible from within Linux. To compile
+	  this driver as a module, choose M here; the module will be called
+	  cr50_spi.
+
 config TCG_INFINEON
 	tristate "Infineon Technologies TPM Interface"
 	depends on PNP
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index a01c4cab902a..4e89538c73c8 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -27,6 +27,8 @@ obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
 obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o
 obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
 obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
+obj-$(CONFIG_TCG_CR50) += cr50.o
+obj-$(CONFIG_TCG_CR50_SPI) += cr50_spi.o
 obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
 obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
 obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/
diff --git a/drivers/char/tpm/cr50.c b/drivers/char/tpm/cr50.c
new file mode 100644
index 000000000000..d6082eccc6f6
--- /dev/null
+++ b/drivers/char/tpm/cr50.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2016 Google Inc.
+ */
+
+/*
+ * This file contains common code for devices with Cr50 firmware.
+ */
+
+#include <linux/suspend.h>
+#include "cr50.h"
+
+#ifdef CONFIG_PM_SLEEP
+int cr50_resume(struct device *dev)
+{
+	struct tpm_chip *chip = dev_get_drvdata(dev);
+
+	clear_bit(0, &chip->is_suspended);
+
+	if (pm_suspend_via_firmware())
+		return tpm_pm_resume(dev);
+	else
+		return 0;
+}
+EXPORT_SYMBOL(cr50_resume);
+
+int cr50_suspend(struct device *dev)
+{
+	struct tpm_chip *chip = dev_get_drvdata(dev);
+
+	if (pm_suspend_via_firmware()) {
+		return tpm_pm_suspend(dev);
+	} else {
+		set_bit(0, &chip->is_suspended);
+		return 0;
+	}
+}
+EXPORT_SYMBOL(cr50_suspend);
+#endif /* CONFIG_PM_SLEEP */
diff --git a/drivers/char/tpm/cr50.h b/drivers/char/tpm/cr50.h
new file mode 100644
index 000000000000..0ff5d7459f8c
--- /dev/null
+++ b/drivers/char/tpm/cr50.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * This file contains common code for devices with Cr50 firmware.
+ */
+
+#ifndef __CR50_H__
+#define __CR50_H__
+
+#include "tpm.h"
+
+#ifdef CONFIG_PM_SLEEP
+/* Handle suspend/resume. */
+int cr50_resume(struct device *dev);
+int cr50_suspend(struct device *dev);
+#endif /* CONFIG_PM_SLEEP */
+
+#endif /* __CR50_H__ */
diff --git a/drivers/char/tpm/cr50_spi.c b/drivers/char/tpm/cr50_spi.c
new file mode 100644
index 000000000000..3c1b472297ad
--- /dev/null
+++ b/drivers/char/tpm/cr50_spi.c
@@ -0,0 +1,450 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2016 Google, Inc
+ *
+ * This device driver implements a TCG PTP FIFO interface over SPI for chips
+ * with Cr50 firmware.
+ * It is based on tpm_tis_spi driver by Peter Huewe and Christophe Ricard.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/spi/spi.h>
+#include <linux/wait.h>
+#include "cr50.h"
+#include "tpm_tis_core.h"
+
+/*
+ * Cr50 timing constants:
+ * - can go to sleep not earlier than after CR50_SLEEP_DELAY_MSEC.
+ * - needs up to CR50_WAKE_START_DELAY_MSEC to wake after sleep.
+ * - requires waiting for "ready" IRQ, if supported; or waiting for at least
+ *   CR50_NOIRQ_ACCESS_DELAY_MSEC between transactions, if IRQ is not supported.
+ * - waits for up to CR50_FLOW_CONTROL_MSEC for flow control 'ready' indication.
+ */
+#define CR50_SLEEP_DELAY_MSEC			1000
+#define CR50_WAKE_START_DELAY_MSEC		1
+#define CR50_NOIRQ_ACCESS_DELAY_MSEC		2
+#define CR50_READY_IRQ_TIMEOUT_MSEC		TPM2_TIMEOUT_A
+#define CR50_FLOW_CONTROL_MSEC			TPM2_TIMEOUT_A
+#define MAX_IRQ_CONFIRMATION_ATTEMPTS		3
+
+#define MAX_SPI_FRAMESIZE			64
+
+#define TPM_CR50_FW_VER(l)			(0x0F90 | ((l) << 12))
+#define TPM_CR50_MAX_FW_VER_LEN			64
+
+static unsigned short rng_quality = 1022;
+module_param(rng_quality, ushort, 0644);
+MODULE_PARM_DESC(rng_quality,
+		 "Estimation of true entropy, in bits per 1024 bits.");
+
+struct cr50_spi_phy {
+	struct tpm_tis_data priv;
+	struct spi_device *spi_device;
+
+	struct mutex time_track_mutex;
+	unsigned long last_access_jiffies;
+	unsigned long wake_after_jiffies;
+
+	unsigned long access_delay_jiffies;
+	unsigned long sleep_delay_jiffies;
+	unsigned int wake_start_delay_msec;
+
+	struct completion tpm_ready;
+
+	unsigned int irq_confirmation_attempt;
+	bool irq_needs_confirmation;
+	bool irq_confirmed;
+
+	u8 tx_buf[MAX_SPI_FRAMESIZE] ____cacheline_aligned;
+	u8 rx_buf[MAX_SPI_FRAMESIZE] ____cacheline_aligned;
+};
+
+static struct cr50_spi_phy *to_cr50_spi_phy(struct tpm_tis_data *data)
+{
+	return container_of(data, struct cr50_spi_phy, priv);
+}
+
+/*
+ * The cr50 interrupt handler just signals waiting threads that the
+ * interrupt was asserted.  It does not do any processing triggered
+ * by interrupts but is instead used to avoid fixed delays.
+ */
+static irqreturn_t cr50_spi_irq_handler(int dummy, void *dev_id)
+{
+	struct cr50_spi_phy *phy = dev_id;
+
+	phy->irq_confirmed = true;
+	complete(&phy->tpm_ready);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Cr50 needs to have at least some delay between consecutive
+ * transactions. Make sure we wait.
+ */
+static void cr50_ensure_access_delay(struct cr50_spi_phy *phy)
+{
+	/*
+	 * Note: There is a small chance, if Cr50 is not accessed in a few days,
+	 * that time_in_range will not provide the correct result after the wrap
+	 * around for jiffies. In this case, we'll have an unneeded short delay,
+	 * which is fine.
+	 */
+	unsigned long allowed_access =
+		phy->last_access_jiffies + phy->access_delay_jiffies;
+	unsigned long time_now = jiffies;
+
+	if (time_in_range_open(time_now,
+			       phy->last_access_jiffies, allowed_access)) {
+		unsigned long remaining =
+			wait_for_completion_timeout(&phy->tpm_ready,
+						    allowed_access - time_now);
+		if (remaining == 0 && phy->irq_confirmed) {
+			dev_warn(&phy->spi_device->dev,
+				 "Timeout waiting for TPM ready IRQ\n");
+		}
+	}
+	if (phy->irq_needs_confirmation) {
+		if (phy->irq_confirmed) {
+			phy->irq_needs_confirmation = false;
+			phy->access_delay_jiffies =
+				msecs_to_jiffies(CR50_READY_IRQ_TIMEOUT_MSEC);
+			dev_info(&phy->spi_device->dev,
+				 "TPM ready IRQ confirmed on attempt %u\n",
+				 phy->irq_confirmation_attempt);
+		} else if (++phy->irq_confirmation_attempt >
+			   MAX_IRQ_CONFIRMATION_ATTEMPTS) {
+			phy->irq_needs_confirmation = false;
+			dev_warn(&phy->spi_device->dev,
+				 "IRQ not confirmed - will use delays\n");
+		}
+	}
+}
+
+/*
+ * Cr50 might go to sleep if there is no SPI activity for some time and
+ * miss the first few bits/bytes on the bus. In such case, wake it up
+ * by asserting CS and give it time to start up.
+ */
+static bool cr50_needs_waking(struct cr50_spi_phy *phy)
+{
+	/*
+	 * Note: There is a small chance, if Cr50 is not accessed in a few days,
+	 * that time_in_range will not provide the correct result after the wrap
+	 * around for jiffies. In this case, we'll probably timeout or read
+	 * incorrect value from TPM_STS and just retry the operation.
+	 */
+	return !time_in_range_open(jiffies,
+				   phy->last_access_jiffies,
+				   phy->wake_after_jiffies);
+}
+
+static void cr50_wake_if_needed(struct cr50_spi_phy *phy)
+{
+	if (cr50_needs_waking(phy)) {
+		/* Assert CS, wait 1 msec, deassert CS */
+		struct spi_transfer spi_cs_wake = { .delay_usecs = 1000 };
+
+		spi_sync_transfer(phy->spi_device, &spi_cs_wake, 1);
+		/* Wait for it to fully wake */
+		msleep(phy->wake_start_delay_msec);
+	}
+	/* Reset the time when we need to wake Cr50 again */
+	phy->wake_after_jiffies = jiffies + phy->sleep_delay_jiffies;
+}
+
+/*
+ * Flow control: clock the bus and wait for cr50 to set LSB before
+ * sending/receiving data. TCG PTP spec allows it to happen during
+ * the last byte of header, but cr50 never does that in practice,
+ * and earlier versions had a bug when it was set too early, so don't
+ * check for it during header transfer.
+ */
+static int cr50_spi_flow_control(struct cr50_spi_phy *phy)
+{
+	unsigned long timeout_jiffies =
+		jiffies + msecs_to_jiffies(CR50_FLOW_CONTROL_MSEC);
+	struct spi_message m;
+	int ret;
+	struct spi_transfer spi_xfer = {
+		.rx_buf = phy->rx_buf,
+		.len = 1,
+		.cs_change = 1,
+	};
+
+	do {
+		spi_message_init(&m);
+		spi_message_add_tail(&spi_xfer, &m);
+		ret = spi_sync_locked(phy->spi_device, &m);
+		if (ret < 0)
+			return ret;
+		if (time_after(jiffies, timeout_jiffies)) {
+			dev_warn(&phy->spi_device->dev,
+				 "Timeout during flow control\n");
+			return -EBUSY;
+		}
+	} while (!(phy->rx_buf[0] & 0x01));
+	return 0;
+}
+
+static int cr50_spi_xfer_bytes(struct tpm_tis_data *data, u32 addr,
+			       u16 len, const u8 *tx, u8 *rx)
+{
+	struct cr50_spi_phy *phy = to_cr50_spi_phy(data);
+	struct spi_message m;
+	struct spi_transfer spi_xfer = {
+		.tx_buf = phy->tx_buf,
+		.rx_buf = phy->rx_buf,
+		.len = 4,
+		.cs_change = 1,
+	};
+	int ret;
+
+	if (len > MAX_SPI_FRAMESIZE)
+		return -EINVAL;
+
+	/*
+	 * Do this outside of spi_bus_lock in case cr50 is not the
+	 * only device on that spi bus.
+	 */
+	mutex_lock(&phy->time_track_mutex);
+	cr50_ensure_access_delay(phy);
+	cr50_wake_if_needed(phy);
+
+	phy->tx_buf[0] = (tx ? 0x00 : 0x80) | (len - 1);
+	phy->tx_buf[1] = 0xD4;
+	phy->tx_buf[2] = (addr >> 8) & 0xFF;
+	phy->tx_buf[3] = addr & 0xFF;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&spi_xfer, &m);
+
+	spi_bus_lock(phy->spi_device->master);
+	ret = spi_sync_locked(phy->spi_device, &m);
+	if (ret < 0)
+		goto exit;
+
+	ret = cr50_spi_flow_control(phy);
+	if (ret < 0)
+		goto exit;
+
+	spi_xfer.cs_change = 0;
+	spi_xfer.len = len;
+	if (tx) {
+		memcpy(phy->tx_buf, tx, len);
+		spi_xfer.rx_buf = NULL;
+	} else {
+		spi_xfer.tx_buf = NULL;
+	}
+
+	spi_message_init(&m);
+	spi_message_add_tail(&spi_xfer, &m);
+	reinit_completion(&phy->tpm_ready);
+	ret = spi_sync_locked(phy->spi_device, &m);
+	if (rx)
+		memcpy(rx, phy->rx_buf, len);
+
+exit:
+	spi_bus_unlock(phy->spi_device->master);
+	phy->last_access_jiffies = jiffies;
+	mutex_unlock(&phy->time_track_mutex);
+
+	return ret;
+}
+
+static int cr50_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
+			       u16 len, u8 *result)
+{
+	return cr50_spi_xfer_bytes(data, addr, len, NULL, result);
+}
+
+static int cr50_spi_write_bytes(struct tpm_tis_data *data, u32 addr,
+				u16 len, const u8 *value)
+{
+	return cr50_spi_xfer_bytes(data, addr, len, value, NULL);
+}
+
+static int cr50_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
+{
+	int rc;
+	__le16 le_val;
+
+	rc = data->phy_ops->read_bytes(data, addr, sizeof(u16), (u8 *)&le_val);
+	if (!rc)
+		*result = le16_to_cpu(le_val);
+	return rc;
+}
+
+static int cr50_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
+{
+	int rc;
+	__le32 le_val;
+
+	rc = data->phy_ops->read_bytes(data, addr, sizeof(u32), (u8 *)&le_val);
+	if (!rc)
+		*result = le32_to_cpu(le_val);
+	return rc;
+}
+
+static int cr50_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value)
+{
+	__le32 le_val = cpu_to_le32(value);
+
+	return data->phy_ops->write_bytes(data, addr, sizeof(u32),
+					   (u8 *)&le_val);
+}
+
+static void cr50_get_fw_version(struct tpm_tis_data *data, char *fw_ver)
+{
+	int i, len = 0;
+	char fw_ver_block[4];
+
+	/*
+	 * Write anything to TPM_CR50_FW_VER to start from the beginning
+	 * of the version string
+	 */
+	tpm_tis_write8(data, TPM_CR50_FW_VER(data->locality), 0);
+
+	/* Read the string, 4 bytes at a time, until we get '\0' */
+	do {
+		tpm_tis_read_bytes(data, TPM_CR50_FW_VER(data->locality), 4,
+				   fw_ver_block);
+		for (i = 0; i < 4 && fw_ver_block[i]; ++len, ++i)
+			fw_ver[len] = fw_ver_block[i];
+	} while (i == 4 && len < TPM_CR50_MAX_FW_VER_LEN);
+	fw_ver[len] = 0;
+}
+
+static const struct tpm_tis_phy_ops cr50_spi_phy_ops = {
+	.read_bytes = cr50_spi_read_bytes,
+	.write_bytes = cr50_spi_write_bytes,
+	.read16 = cr50_spi_read16,
+	.read32 = cr50_spi_read32,
+	.write32 = cr50_spi_write32,
+	.max_xfer_size = MAX_SPI_FRAMESIZE,
+};
+
+static int cr50_spi_probe(struct spi_device *dev)
+{
+	char fw_ver[TPM_CR50_MAX_FW_VER_LEN + 1];
+	struct cr50_spi_phy *phy;
+	int rc;
+
+	phy = devm_kzalloc(&dev->dev, sizeof(*phy), GFP_KERNEL);
+	if (!phy)
+		return -ENOMEM;
+
+	phy->spi_device = dev;
+
+	phy->access_delay_jiffies =
+		msecs_to_jiffies(CR50_NOIRQ_ACCESS_DELAY_MSEC);
+	phy->sleep_delay_jiffies = msecs_to_jiffies(CR50_SLEEP_DELAY_MSEC);
+	phy->wake_start_delay_msec = CR50_WAKE_START_DELAY_MSEC;
+
+	mutex_init(&phy->time_track_mutex);
+	phy->wake_after_jiffies = jiffies;
+	phy->last_access_jiffies = jiffies;
+
+	init_completion(&phy->tpm_ready);
+	if (dev->irq > 0) {
+		rc = devm_request_irq(&dev->dev, dev->irq, cr50_spi_irq_handler,
+				      IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+				      "cr50_spi", phy);
+		if (rc < 0) {
+			if (rc == -EPROBE_DEFER)
+				return rc;
+			dev_warn(&dev->dev, "Requesting IRQ %d failed: %d\n",
+				 dev->irq, rc);
+			/*
+			 * This is not fatal, the driver will fall back to
+			 * delays automatically, since tpm_ready will never
+			 * be completed without a registered irq handler.
+			 * So, just fall through.
+			 */
+		} else {
+			/*
+			 * IRQ requested, let's verify that it is actually
+			 * triggered, before relying on it.
+			 */
+			phy->irq_needs_confirmation = true;
+		}
+	} else {
+		dev_warn(&dev->dev,
+			 "No IRQ - will use delays between transactions.\n");
+	}
+
+	phy->priv.rng_quality = rng_quality;
+
+	rc = tpm_tis_core_init(&dev->dev, &phy->priv, -1, &cr50_spi_phy_ops,
+			       NULL);
+	if (rc < 0)
+		return rc;
+
+	cr50_get_fw_version(&phy->priv, fw_ver);
+	dev_info(&dev->dev, "Cr50 firmware version: %s\n", fw_ver);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cr50_spi_resume(struct device *dev)
+{
+	struct tpm_chip *chip = dev_get_drvdata(dev);
+	struct tpm_tis_data *data = dev_get_drvdata(&chip->dev);
+	struct cr50_spi_phy *phy = to_cr50_spi_phy(data);
+
+	/*
+	 * Jiffies not increased during suspend, so we need to reset
+	 * the time to wake Cr50 after resume.
+	 */
+	phy->wake_after_jiffies = jiffies;
+
+	return cr50_resume(dev);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(cr50_spi_pm, cr50_suspend, cr50_spi_resume);
+
+static int cr50_spi_remove(struct spi_device *dev)
+{
+	struct tpm_chip *chip = spi_get_drvdata(dev);
+
+	tpm_chip_unregister(chip);
+	tpm_tis_remove(chip);
+	return 0;
+}
+
+static const struct spi_device_id cr50_spi_id[] = {
+	{ "cr50", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(spi, cr50_spi_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_cr50_spi_match[] = {
+	{ .compatible = "google,cr50", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, of_cr50_spi_match);
+#endif
+
+static struct spi_driver cr50_spi_driver = {
+	.driver = {
+		.name = "cr50_spi",
+		.pm = &cr50_spi_pm,
+		.of_match_table = of_match_ptr(of_cr50_spi_match),
+	},
+	.probe = cr50_spi_probe,
+	.remove = cr50_spi_remove,
+	.id_table = cr50_spi_id,
+};
+module_spi_driver(cr50_spi_driver);
+
+MODULE_DESCRIPTION("Cr50 TCG PTP FIFO SPI driver");
+MODULE_LICENSE("GPL v2");
-- 
Sent by a computer through tubes


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 6/8] tpm: Add driver for cr50 on I2C
  2019-06-13 18:09 [PATCH 0/8] tpm: Add driver for cr50 Stephen Boyd
                   ` (4 preceding siblings ...)
  2019-06-13 18:09 ` [PATCH 5/8] tpm: add driver for cr50 on SPI Stephen Boyd
@ 2019-06-13 18:09 ` Stephen Boyd
  2019-06-13 18:09 ` [PATCH 7/8] tpm: add sysfs attributes for tpm2 Stephen Boyd
  2019-06-13 18:09 ` [PATCH 8/8] tpm: add legacy " Stephen Boyd
  7 siblings, 0 replies; 24+ messages in thread
From: Stephen Boyd @ 2019-06-13 18:09 UTC (permalink / raw)
  To: Peter Huewe, Jarkko Sakkinen
  Cc: Duncan Laurie, linux-kernel, Jason Gunthorpe, Arnd Bergmann,
	Greg Kroah-Hartman, linux-integrity, Andrey Pronin, devicetree,
	Guenter Roeck

From: Duncan Laurie <dlaurie@chromium.org>

Add TPM 2.0 compatible I2C interface for chips with cr50 firmware.

The firmware running on the currently supported H1 MCU requires a
special driver to handle its specific protocol, and this makes it
unsuitable to use tpm_tis_core_* and instead it must implement the
underlying TPM protocol similar to the other I2C TPM drivers.

- All 4 byes of status register must be read/written at once.
- FIFO and burst count is limited to 63 and must be drained by AP.
- Provides an interrupt to indicate when read response data is ready
and when the TPM is finished processing write data.

This driver is based on the existing infineon I2C TPM driver, which
most closely matches the cr50 i2c protocol behavior.  The driver is
intentionally kept very similar in structure and style to the
corresponding drivers in coreboot and depthcharge.

Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
[swboyd@chromium.org: Depend on i2c even if it's a module, replace
boilier plate with SPDX tag, drop asm/byteorder.h include, simplify
return from probe]
Signed-off-by: Stephen Boyd <swboyd@chromium.org>
---
 drivers/char/tpm/Kconfig    |  10 +
 drivers/char/tpm/Makefile   |   1 +
 drivers/char/tpm/cr50_i2c.c | 704 ++++++++++++++++++++++++++++++++++++
 3 files changed, 715 insertions(+)
 create mode 100644 drivers/char/tpm/cr50_i2c.c

diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
index b7028bfa6f87..57a8c3540265 100644
--- a/drivers/char/tpm/Kconfig
+++ b/drivers/char/tpm/Kconfig
@@ -119,6 +119,16 @@ config TCG_CR50
 	---help---
 	  Common routines shared by drivers for Cr50-based devices.
 
+config TCG_CR50_I2C
+	tristate "Cr50 I2C Interface"
+	depends on I2C
+	select TCG_CR50
+	---help---
+	  If you have a H1 secure module running Cr50 firmware on I2C bus,
+	  say Yes and it will be accessible from within Linux. To compile
+	  this driver as a module, choose M here; the module will be called
+	  cr50_i2c.
+
 config TCG_CR50_SPI
 	tristate "Cr50 SPI Interface"
 	depends on SPI
diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
index 4e89538c73c8..3ac3448c21fa 100644
--- a/drivers/char/tpm/Makefile
+++ b/drivers/char/tpm/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
 obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
 obj-$(CONFIG_TCG_CR50) += cr50.o
 obj-$(CONFIG_TCG_CR50_SPI) += cr50_spi.o
+obj-$(CONFIG_TCG_CR50_I2C) += cr50_i2c.o
 obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
 obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
 obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/
diff --git a/drivers/char/tpm/cr50_i2c.c b/drivers/char/tpm/cr50_i2c.c
new file mode 100644
index 000000000000..9c5bec1631eb
--- /dev/null
+++ b/drivers/char/tpm/cr50_i2c.c
@@ -0,0 +1,704 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Based on Linux Kernel TPM driver by
+ * Peter Huewe <peter.huewe@infineon.com>
+ * Copyright (C) 2011 Infineon Technologies
+ */
+
+/*
+ * cr50 is a firmware for H1 secure modules that requires special
+ * handling for the I2C interface.
+ *
+ * - Use an interrupt for transaction status instead of hardcoded delays
+ * - Must use write+wait+read read protocol
+ * - All 4 bytes of status register must be read/written at once
+ * - Burst count max is 63 bytes, and burst count behaves
+ *   slightly differently than other I2C TPMs
+ * - When reading from FIFO the full burstcnt must be read
+ *   instead of just reading header and determining the remainder
+ */
+
+#include <linux/acpi.h>
+#include <linux/completion.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include "cr50.h"
+
+#define CR50_MAX_BUFSIZE	63
+#define CR50_TIMEOUT_SHORT_MS	2	/* Short timeout during transactions */
+#define CR50_TIMEOUT_NOIRQ_MS	20	/* Timeout for TPM ready without IRQ */
+#define CR50_I2C_DID_VID	0x00281ae0L
+#define CR50_I2C_MAX_RETRIES	3	/* Max retries due to I2C errors */
+#define CR50_I2C_RETRY_DELAY_LO	55	/* Min usecs between retries on I2C */
+#define CR50_I2C_RETRY_DELAY_HI	65	/* Max usecs between retries on I2C */
+
+static unsigned short rng_quality = 1022;
+
+module_param(rng_quality, ushort, 0644);
+MODULE_PARM_DESC(rng_quality,
+		 "Estimation of true entropy, in bits per 1024 bits.");
+
+struct priv_data {
+	int irq;
+	int locality;
+	struct completion tpm_ready;
+	u8 buf[CR50_MAX_BUFSIZE + sizeof(u8)];
+};
+
+/*
+ * The cr50 interrupt handler just signals waiting threads that the
+ * interrupt was asserted.  It does not do any processing triggered
+ * by interrupts but is instead used to avoid fixed delays.
+ */
+static irqreturn_t cr50_i2c_int_handler(int dummy, void *dev_id)
+{
+	struct tpm_chip *chip = dev_id;
+	struct priv_data *priv = dev_get_drvdata(&chip->dev);
+
+	complete(&priv->tpm_ready);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Wait for completion interrupt if available, otherwise use a fixed
+ * delay for the TPM to be ready.
+ *
+ * Returns negative number for error, positive number for success.
+ */
+static int cr50_i2c_wait_tpm_ready(struct tpm_chip *chip)
+{
+	struct priv_data *priv = dev_get_drvdata(&chip->dev);
+	long rc;
+
+	/* Use a safe fixed delay if interrupt is not supported */
+	if (priv->irq <= 0) {
+		msleep(CR50_TIMEOUT_NOIRQ_MS);
+		return 1;
+	}
+
+	/* Wait for interrupt to indicate TPM is ready to respond */
+	rc = wait_for_completion_timeout(&priv->tpm_ready,
+		msecs_to_jiffies(chip->timeout_a));
+
+	if (rc == 0)
+		dev_warn(&chip->dev, "Timeout waiting for TPM ready\n");
+
+	return rc;
+}
+
+static void cr50_i2c_enable_tpm_irq(struct tpm_chip *chip)
+{
+	struct priv_data *priv = dev_get_drvdata(&chip->dev);
+
+	if (priv->irq > 0) {
+		reinit_completion(&priv->tpm_ready);
+		enable_irq(priv->irq);
+	}
+}
+
+static void cr50_i2c_disable_tpm_irq(struct tpm_chip *chip)
+{
+	struct priv_data *priv = dev_get_drvdata(&chip->dev);
+
+	if (priv->irq > 0)
+		disable_irq(priv->irq);
+}
+
+/*
+ * cr50_i2c_transfer - transfer messages over i2c
+ *
+ * @adapter: i2c adapter
+ * @msgs: array of messages to transfer
+ * @num: number of messages in the array
+ *
+ * Call unlocked i2c transfer routine with the provided parameters and retry
+ * in case of bus errors. Returns the number of transferred messages.
+ */
+static int cr50_i2c_transfer(struct device *dev, struct i2c_adapter *adapter,
+			     struct i2c_msg *msgs, int num)
+{
+	int rc, try;
+
+	for (try = 0; try < CR50_I2C_MAX_RETRIES; try++) {
+		rc = __i2c_transfer(adapter, msgs, num);
+		if (rc > 0)
+			break;
+		if (try)
+			dev_warn(dev, "i2c transfer failed (attempt %d/%d): %d\n",
+				 try+1, CR50_I2C_MAX_RETRIES, rc);
+		usleep_range(CR50_I2C_RETRY_DELAY_LO, CR50_I2C_RETRY_DELAY_HI);
+	}
+
+	return rc;
+}
+
+/*
+ * cr50_i2c_read() - read from TPM register
+ *
+ * @chip: TPM chip information
+ * @addr: register address to read from
+ * @buffer: provided by caller
+ * @len: number of bytes to read
+ *
+ * 1) send register address byte 'addr' to the TPM
+ * 2) wait for TPM to indicate it is ready
+ * 3) read 'len' bytes of TPM response into the provided 'buffer'
+ *
+ * Returns negative number for error, 0 for success.
+ */
+static int cr50_i2c_read(struct tpm_chip *chip, u8 addr, u8 *buffer, size_t len)
+{
+	struct i2c_client *client = to_i2c_client(chip->dev.parent);
+	struct i2c_msg msg1 = {
+		.addr = client->addr,
+		.len = 1,
+		.buf = &addr
+	};
+	struct i2c_msg msg2 = {
+		.addr = client->addr,
+		.flags = I2C_M_RD,
+		.len = len,
+		.buf = buffer
+	};
+	int rc;
+
+	i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
+
+	/* Prepare for completion interrupt */
+	cr50_i2c_enable_tpm_irq(chip);
+
+	/* Send the register address byte to the TPM */
+	rc = cr50_i2c_transfer(&chip->dev, client->adapter, &msg1, 1);
+	if (rc <= 0)
+		goto out;
+
+	/* Wait for TPM to be ready with response data */
+	rc = cr50_i2c_wait_tpm_ready(chip);
+	if (rc < 0)
+		goto out;
+
+	/* Read response data from the TPM */
+	rc = cr50_i2c_transfer(&chip->dev, client->adapter, &msg2, 1);
+
+out:
+	cr50_i2c_disable_tpm_irq(chip);
+	i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
+
+	if (rc < 0)
+		return rc;
+	if (rc == 0)
+		return -EIO; /* No i2c segments transferred */
+
+	return 0;
+}
+
+/*
+ * cr50_i2c_write() - write to TPM register
+ *
+ * @chip: TPM chip information
+ * @addr: register address to write to
+ * @buffer: data to write
+ * @len: number of bytes to write
+ *
+ * 1) prepend the provided address to the provided data
+ * 2) send the address+data to the TPM
+ * 3) wait for TPM to indicate it is done writing
+ *
+ * Returns negative number for error, 0 for success.
+ */
+static int cr50_i2c_write(struct tpm_chip *chip, u8 addr, u8 *buffer,
+			  size_t len)
+{
+	struct priv_data *priv = dev_get_drvdata(&chip->dev);
+	struct i2c_client *client = to_i2c_client(chip->dev.parent);
+	struct i2c_msg msg1 = {
+		.addr = client->addr,
+		.len = len + 1,
+		.buf = priv->buf
+	};
+	int rc;
+
+	if (len > CR50_MAX_BUFSIZE)
+		return -EINVAL;
+
+	i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT);
+
+	/* Prepend the 'register address' to the buffer */
+	priv->buf[0] = addr;
+	memcpy(priv->buf + 1, buffer, len);
+
+	/* Prepare for completion interrupt */
+	cr50_i2c_enable_tpm_irq(chip);
+
+	/* Send write request buffer with address */
+	rc = cr50_i2c_transfer(&chip->dev, client->adapter, &msg1, 1);
+	if (rc <= 0)
+		goto out;
+
+	/* Wait for TPM to be ready, ignore timeout */
+	cr50_i2c_wait_tpm_ready(chip);
+
+out:
+	cr50_i2c_disable_tpm_irq(chip);
+	i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT);
+
+	if (rc < 0)
+		return rc;
+	if (rc == 0)
+		return -EIO; /* No i2c segments transferred */
+
+	return 0;
+}
+
+enum tis_access {
+	TPM_ACCESS_VALID = 0x80,
+	TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
+	TPM_ACCESS_REQUEST_PENDING = 0x04,
+	TPM_ACCESS_REQUEST_USE = 0x02,
+};
+
+enum tis_status {
+	TPM_STS_VALID = 0x80,
+	TPM_STS_COMMAND_READY = 0x40,
+	TPM_STS_GO = 0x20,
+	TPM_STS_DATA_AVAIL = 0x10,
+	TPM_STS_DATA_EXPECT = 0x08,
+};
+
+enum tis_defaults {
+	TIS_SHORT_TIMEOUT = 750,	/* ms */
+	TIS_LONG_TIMEOUT = 2000,	/* 2 sec */
+};
+
+#define	TPM_ACCESS(l)			(0x0000 | ((l) << 4))
+#define	TPM_STS(l)			(0x0001 | ((l) << 4))
+#define	TPM_DATA_FIFO(l)		(0x0005 | ((l) << 4))
+#define	TPM_DID_VID(l)			(0x0006 | ((l) << 4))
+
+static int check_locality(struct tpm_chip *chip, int loc)
+{
+	u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY;
+	u8 buf;
+	int rc;
+
+	rc = cr50_i2c_read(chip, TPM_ACCESS(loc), &buf, 1);
+	if (rc < 0)
+		return rc;
+
+	if ((buf & mask) == mask)
+		return loc;
+
+	return -EIO;
+}
+
+static void release_locality(struct tpm_chip *chip, int force)
+{
+	struct priv_data *priv = dev_get_drvdata(&chip->dev);
+	u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_REQUEST_PENDING;
+	u8 addr = TPM_ACCESS(priv->locality);
+	u8 buf;
+
+	if (cr50_i2c_read(chip, addr, &buf, 1) < 0)
+		return;
+
+	if (force || (buf & mask) == mask) {
+		buf = TPM_ACCESS_ACTIVE_LOCALITY;
+		cr50_i2c_write(chip, addr, &buf, 1);
+	}
+
+	priv->locality = 0;
+}
+
+static int request_locality(struct tpm_chip *chip, int loc)
+{
+	struct priv_data *priv = dev_get_drvdata(&chip->dev);
+	u8 buf = TPM_ACCESS_REQUEST_USE;
+	unsigned long stop;
+	int rc;
+
+	if (check_locality(chip, loc) == loc)
+		return loc;
+
+	rc = cr50_i2c_write(chip, TPM_ACCESS(loc), &buf, 1);
+	if (rc < 0)
+		return rc;
+
+	stop = jiffies + chip->timeout_a;
+	do {
+		if (check_locality(chip, loc) == loc) {
+			priv->locality = loc;
+			return loc;
+		}
+		msleep(CR50_TIMEOUT_SHORT_MS);
+	} while (time_before(jiffies, stop));
+
+	return -ETIMEDOUT;
+}
+
+/* cr50 requires all 4 bytes of status register to be read */
+static u8 cr50_i2c_tis_status(struct tpm_chip *chip)
+{
+	struct priv_data *priv = dev_get_drvdata(&chip->dev);
+	u8 buf[4];
+
+	if (cr50_i2c_read(chip, TPM_STS(priv->locality), buf, sizeof(buf)) < 0)
+		return 0;
+	return buf[0];
+}
+
+/* cr50 requires all 4 bytes of status register to be written */
+static void cr50_i2c_tis_ready(struct tpm_chip *chip)
+{
+	struct priv_data *priv = dev_get_drvdata(&chip->dev);
+	u8 buf[4] = { TPM_STS_COMMAND_READY };
+
+	cr50_i2c_write(chip, TPM_STS(priv->locality), buf, sizeof(buf));
+	msleep(CR50_TIMEOUT_SHORT_MS);
+}
+
+/*
+ * cr50 uses bytes 3:2 of status register for burst count and
+ * all 4 bytes must be read
+ */
+static int cr50_i2c_wait_burststs(struct tpm_chip *chip, u8 mask,
+				  size_t *burst, int *status)
+{
+	struct priv_data *priv = dev_get_drvdata(&chip->dev);
+	unsigned long stop;
+	u8 buf[4];
+
+	/* wait for burstcount */
+	stop = jiffies + chip->timeout_b;
+	do {
+		if (cr50_i2c_read(chip, TPM_STS(priv->locality),
+				  (u8 *)&buf, sizeof(buf)) < 0) {
+			msleep(CR50_TIMEOUT_SHORT_MS);
+			continue;
+		}
+
+		*status = *buf;
+		*burst = le16_to_cpup((__le16 *)(buf + 1));
+
+		if ((*status & mask) == mask &&
+		    *burst > 0 && *burst <= CR50_MAX_BUFSIZE)
+			return 0;
+
+		msleep(CR50_TIMEOUT_SHORT_MS);
+	} while (time_before(jiffies, stop));
+
+	dev_err(&chip->dev, "Timeout reading burst and status\n");
+	return -ETIMEDOUT;
+}
+
+static int cr50_i2c_tis_recv(struct tpm_chip *chip, u8 *buf, size_t buf_len)
+{
+	struct priv_data *priv = dev_get_drvdata(&chip->dev);
+	int status, rc;
+	size_t burstcnt, cur, len, expected;
+	u8 addr = TPM_DATA_FIFO(priv->locality);
+	u8 mask = TPM_STS_VALID | TPM_STS_DATA_AVAIL;
+
+	if (buf_len < TPM_HEADER_SIZE)
+		return -EINVAL;
+
+	rc = cr50_i2c_wait_burststs(chip, mask, &burstcnt, &status);
+	if (rc < 0)
+		goto out_err;
+
+	if (burstcnt > buf_len || burstcnt < TPM_HEADER_SIZE) {
+		dev_err(&chip->dev,
+			"Unexpected burstcnt: %zu (max=%zu, min=%d)\n",
+			burstcnt, buf_len, TPM_HEADER_SIZE);
+		rc = -EIO;
+		goto out_err;
+	}
+
+	/* Read first chunk of burstcnt bytes */
+	rc = cr50_i2c_read(chip, addr, buf, burstcnt);
+	if (rc < 0) {
+		dev_err(&chip->dev, "Read of first chunk failed\n");
+		goto out_err;
+	}
+
+	/* Determine expected data in the return buffer */
+	expected = be32_to_cpup((__be32 *)(buf + 2));
+	if (expected > buf_len) {
+		dev_err(&chip->dev, "Too much data in FIFO\n");
+		goto out_err;
+	}
+
+	/* Now read the rest of the data */
+	cur = burstcnt;
+	while (cur < expected) {
+		/* Read updated burst count and check status */
+		rc = cr50_i2c_wait_burststs(chip, mask, &burstcnt, &status);
+		if (rc < 0)
+			goto out_err;
+
+		len = min_t(size_t, burstcnt, expected - cur);
+		rc = cr50_i2c_read(chip, addr, buf + cur, len);
+		if (rc < 0) {
+			dev_err(&chip->dev, "Read failed\n");
+			goto out_err;
+		}
+
+		cur += len;
+	}
+
+	/* Ensure TPM is done reading data */
+	rc = cr50_i2c_wait_burststs(chip, TPM_STS_VALID, &burstcnt, &status);
+	if (rc < 0)
+		goto out_err;
+	if (status & TPM_STS_DATA_AVAIL) {
+		dev_err(&chip->dev, "Data still available\n");
+		rc = -EIO;
+		goto out_err;
+	}
+
+	release_locality(chip, 0);
+	return cur;
+
+out_err:
+	/* Abort current transaction if still pending */
+	if (cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY)
+		cr50_i2c_tis_ready(chip);
+
+	release_locality(chip, 0);
+	return rc;
+}
+
+static int cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+	struct priv_data *priv = dev_get_drvdata(&chip->dev);
+	int rc, status;
+	size_t burstcnt, limit, sent = 0;
+	u8 tpm_go[4] = { TPM_STS_GO };
+	unsigned long stop;
+
+	rc = request_locality(chip, 0);
+	if (rc < 0)
+		return rc;
+
+	/* Wait until TPM is ready for a command */
+	stop = jiffies + chip->timeout_b;
+	while (!(cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY)) {
+		if (time_after(jiffies, stop)) {
+			rc = -ETIMEDOUT;
+			goto out_err;
+		}
+
+		cr50_i2c_tis_ready(chip);
+	}
+
+	while (len > 0) {
+		u8 mask = TPM_STS_VALID;
+
+		/* Wait for data if this is not the first chunk */
+		if (sent > 0)
+			mask |= TPM_STS_DATA_EXPECT;
+
+		/* Read burst count and check status */
+		rc = cr50_i2c_wait_burststs(chip, mask, &burstcnt, &status);
+		if (rc < 0)
+			goto out_err;
+
+		/*
+		 * Use burstcnt - 1 to account for the address byte
+		 * that is inserted by cr50_i2c_write()
+		 */
+		limit = min_t(size_t, burstcnt - 1, len);
+		rc = cr50_i2c_write(chip, TPM_DATA_FIFO(priv->locality),
+				    &buf[sent], limit);
+		if (rc < 0) {
+			dev_err(&chip->dev, "Write failed\n");
+			goto out_err;
+		}
+
+		sent += limit;
+		len -= limit;
+	}
+
+	/* Ensure TPM is not expecting more data */
+	rc = cr50_i2c_wait_burststs(chip, TPM_STS_VALID, &burstcnt, &status);
+	if (rc < 0)
+		goto out_err;
+	if (status & TPM_STS_DATA_EXPECT) {
+		dev_err(&chip->dev, "Data still expected\n");
+		rc = -EIO;
+		goto out_err;
+	}
+
+	/* Start the TPM command */
+	rc = cr50_i2c_write(chip, TPM_STS(priv->locality), tpm_go,
+			    sizeof(tpm_go));
+	if (rc < 0) {
+		dev_err(&chip->dev, "Start command failed\n");
+		goto out_err;
+	}
+	return 0;
+
+out_err:
+	/* Abort current transaction if still pending */
+	if (cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY)
+		cr50_i2c_tis_ready(chip);
+
+	release_locality(chip, 0);
+	return rc;
+}
+
+static bool cr50_i2c_req_canceled(struct tpm_chip *chip, u8 status)
+{
+	return (status == TPM_STS_COMMAND_READY);
+}
+
+static const struct tpm_class_ops cr50_i2c = {
+	.flags = TPM_OPS_AUTO_STARTUP,
+	.status = &cr50_i2c_tis_status,
+	.recv = &cr50_i2c_tis_recv,
+	.send = &cr50_i2c_tis_send,
+	.cancel = &cr50_i2c_tis_ready,
+	.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+	.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+	.req_canceled = &cr50_i2c_req_canceled,
+};
+
+static int cr50_i2c_init(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct tpm_chip *chip;
+	struct priv_data *priv;
+	u8 buf[4];
+	u32 vendor;
+	int rc;
+
+	chip = tpmm_chip_alloc(dev, &cr50_i2c);
+	if (IS_ERR(chip))
+		return PTR_ERR(chip);
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	/* cr50 is a TPM 2.0 chip */
+	chip->flags |= TPM_CHIP_FLAG_TPM2;
+
+	/* Default timeouts */
+	chip->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+	chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+	chip->timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+	chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+
+	dev_set_drvdata(&chip->dev, priv);
+	init_completion(&priv->tpm_ready);
+
+	if (client->irq > 0) {
+		rc = devm_request_irq(dev, client->irq, cr50_i2c_int_handler,
+				      IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+				      dev->driver->name, chip);
+		if (rc < 0) {
+			dev_err(dev, "Failed to probe IRQ %d\n", client->irq);
+			return rc;
+		}
+
+		disable_irq(client->irq);
+		priv->irq = client->irq;
+	} else {
+		dev_warn(dev, "No IRQ, will use %ums delay for TPM ready\n",
+			 CR50_TIMEOUT_NOIRQ_MS);
+	}
+
+	rc = request_locality(chip, 0);
+	if (rc < 0) {
+		dev_err(dev, "Could not request locality\n");
+		return rc;
+	}
+
+	/* Read four bytes from DID_VID register */
+	rc = cr50_i2c_read(chip, TPM_DID_VID(0), buf, sizeof(buf));
+	if (rc < 0) {
+		dev_err(dev, "Could not read vendor id\n");
+		release_locality(chip, 1);
+		return rc;
+	}
+
+	vendor = le32_to_cpup((__le32 *)buf);
+	if (vendor != CR50_I2C_DID_VID) {
+		dev_err(dev, "Vendor ID did not match! ID was %08x\n", vendor);
+		release_locality(chip, 1);
+		return -ENODEV;
+	}
+
+	dev_info(dev, "cr50 TPM 2.0 (i2c 0x%02x irq %d id 0x%x)\n",
+		 client->addr, client->irq, vendor >> 16);
+
+	chip->hwrng.quality = rng_quality;
+
+	return tpm_chip_register(chip);
+}
+
+static const struct i2c_device_id cr50_i2c_table[] = {
+	{"cr50_i2c", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, cr50_i2c_table);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cr50_i2c_acpi_id[] = {
+	{ "GOOG0005", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(acpi, cr50_i2c_acpi_id);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_cr50_i2c_match[] = {
+	{ .compatible = "google,cr50", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, of_cr50_i2c_match);
+#endif
+
+static int cr50_i2c_probe(struct i2c_client *client,
+			  const struct i2c_device_id *id)
+{
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+		return -ENODEV;
+
+	return cr50_i2c_init(client);
+}
+
+static int cr50_i2c_remove(struct i2c_client *client)
+{
+	struct tpm_chip *chip = i2c_get_clientdata(client);
+
+	tpm_chip_unregister(chip);
+	release_locality(chip, 1);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(cr50_i2c_pm, cr50_suspend, cr50_resume);
+
+static struct i2c_driver cr50_i2c_driver = {
+	.id_table = cr50_i2c_table,
+	.probe = cr50_i2c_probe,
+	.remove = cr50_i2c_remove,
+	.driver = {
+		.name = "cr50_i2c",
+		.pm = &cr50_i2c_pm,
+		.acpi_match_table = ACPI_PTR(cr50_i2c_acpi_id),
+		.of_match_table = of_match_ptr(of_cr50_i2c_match),
+	},
+};
+
+module_i2c_driver(cr50_i2c_driver);
+
+MODULE_DESCRIPTION("cr50 TPM I2C Driver");
+MODULE_LICENSE("GPL");
-- 
Sent by a computer through tubes


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 7/8] tpm: add sysfs attributes for tpm2
  2019-06-13 18:09 [PATCH 0/8] tpm: Add driver for cr50 Stephen Boyd
                   ` (5 preceding siblings ...)
  2019-06-13 18:09 ` [PATCH 6/8] tpm: Add driver for cr50 on I2C Stephen Boyd
@ 2019-06-13 18:09 ` Stephen Boyd
  2019-06-13 23:30   ` Jason Gunthorpe
  2019-06-14 15:31   ` Jarkko Sakkinen
  2019-06-13 18:09 ` [PATCH 8/8] tpm: add legacy " Stephen Boyd
  7 siblings, 2 replies; 24+ messages in thread
From: Stephen Boyd @ 2019-06-13 18:09 UTC (permalink / raw)
  To: Peter Huewe, Jarkko Sakkinen
  Cc: Andrey Pronin, linux-kernel, Jason Gunthorpe, Arnd Bergmann,
	Greg Kroah-Hartman, linux-integrity, devicetree, Duncan Laurie,
	Guenter Roeck

From: Andrey Pronin <apronin@chromium.org>

Add sysfs attributes in TPM2.0 case for:
 - TPM_PT_PERMANENT flags
 - TPM_PT_STARTUP_CLEAR flags
 - lockout-related properties

Signed-off-by: Andrey Pronin <apronin@chromium.org>
Signed-off-by: Stephen Boyd <swboyd@chromium.org>
---

This was sent before as https://lkml.kernel.org/r/1469678766-117528-1-git-send-email-apronin@chromium.org
and Jarkko asked that the commit text be filled out more. I'll work with
Andrey to do so, but I'm including it here so we can see if there are
other comments.

 drivers/char/tpm/tpm-sysfs.c | 130 ++++++++++++++++++++++++++++++++---
 drivers/char/tpm/tpm.h       |  29 +++++++-
 2 files changed, 148 insertions(+), 11 deletions(-)

diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index 533a260d744e..e9f6f7960023 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -314,7 +314,7 @@ static ssize_t timeouts_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(timeouts);
 
-static struct attribute *tpm_dev_attrs[] = {
+static struct attribute *tpm1_dev_attrs[] = {
 	&dev_attr_pubek.attr,
 	&dev_attr_pcrs.attr,
 	&dev_attr_enabled.attr,
@@ -328,22 +328,132 @@ static struct attribute *tpm_dev_attrs[] = {
 	NULL,
 };
 
-static const struct attribute_group tpm_dev_group = {
-	.attrs = tpm_dev_attrs,
+static const struct attribute_group tpm1_dev_group = {
+	.attrs = tpm1_dev_attrs,
 };
 
-void tpm_sysfs_add_device(struct tpm_chip *chip)
+struct tpm2_prop_flag_dev_attribute {
+	struct device_attribute attr;
+	u32 property_id;
+	u32 flag_mask;
+};
+
+struct tpm2_prop_u32_dev_attribute {
+	struct device_attribute attr;
+	u32 property_id;
+};
+
+static ssize_t tpm2_prop_flag_show(struct device *dev,
+				   struct device_attribute *attr,
+				   char *buf)
 {
-	/* XXX: If you wish to remove this restriction, you must first update
-	 * tpm_sysfs to explicitly lock chip->ops.
-	 */
-	if (chip->flags & TPM_CHIP_FLAG_TPM2)
-		return;
+	struct tpm2_prop_flag_dev_attribute *pa =
+		container_of(attr, struct tpm2_prop_flag_dev_attribute, attr);
+	u32 flags;
+	ssize_t rc;
+
+	rc = tpm2_get_tpm_pt(to_tpm_chip(dev), pa->property_id, &flags,
+			     "reading property");
+	if (rc)
+		return 0;
+
+	return sprintf(buf, "%d\n", !!(flags & pa->flag_mask));
+}
+
+static ssize_t tpm2_prop_u32_show(struct device *dev,
+				  struct device_attribute *attr,
+				  char *buf)
+{
+	struct tpm2_prop_u32_dev_attribute *pa =
+		container_of(attr, struct tpm2_prop_u32_dev_attribute, attr);
+	u32 value;
+	ssize_t rc;
+
+	rc = tpm2_get_tpm_pt(to_tpm_chip(dev), pa->property_id, &value,
+			     "reading property");
+	if (rc)
+		return 0;
+
+	return sprintf(buf, "%u\n", value);
+}
 
+#define TPM2_PROP_FLAG_ATTR(_name, _property_id, _flag_mask)           \
+	struct tpm2_prop_flag_dev_attribute attr_tpm2_prop_##_name = { \
+		__ATTR(_name, S_IRUGO, tpm2_prop_flag_show, NULL),     \
+		_property_id, _flag_mask                               \
+	}
+
+#define TPM2_PROP_U32_ATTR(_name, _property_id)                        \
+	struct tpm2_prop_u32_dev_attribute attr_tpm2_prop_##_name = {  \
+		__ATTR(_name, S_IRUGO, tpm2_prop_u32_show, NULL),      \
+		_property_id                                           \
+	}
+
+TPM2_PROP_FLAG_ATTR(owner_auth_set,
+		    TPM2_PT_PERMANENT, TPM2_ATTR_OWNER_AUTH_SET);
+TPM2_PROP_FLAG_ATTR(endorsement_auth_set,
+		    TPM2_PT_PERMANENT, TPM2_ATTR_ENDORSEMENT_AUTH_SET);
+TPM2_PROP_FLAG_ATTR(lockout_auth_set,
+		    TPM2_PT_PERMANENT, TPM2_ATTR_LOCKOUT_AUTH_SET);
+TPM2_PROP_FLAG_ATTR(disable_clear,
+		    TPM2_PT_PERMANENT, TPM2_ATTR_DISABLE_CLEAR);
+TPM2_PROP_FLAG_ATTR(in_lockout,
+		    TPM2_PT_PERMANENT, TPM2_ATTR_IN_LOCKOUT);
+TPM2_PROP_FLAG_ATTR(tpm_generated_eps,
+		    TPM2_PT_PERMANENT, TPM2_ATTR_TPM_GENERATED_EPS);
+
+TPM2_PROP_FLAG_ATTR(ph_enable,
+		    TPM2_PT_STARTUP_CLEAR, TPM2_ATTR_PH_ENABLE);
+TPM2_PROP_FLAG_ATTR(sh_enable,
+		    TPM2_PT_STARTUP_CLEAR, TPM2_ATTR_SH_ENABLE);
+TPM2_PROP_FLAG_ATTR(eh_enable,
+		    TPM2_PT_STARTUP_CLEAR, TPM2_ATTR_EH_ENABLE);
+TPM2_PROP_FLAG_ATTR(ph_enable_nv,
+		    TPM2_PT_STARTUP_CLEAR, TPM2_ATTR_PH_ENABLE_NV);
+TPM2_PROP_FLAG_ATTR(orderly,
+		    TPM2_PT_STARTUP_CLEAR, TPM2_ATTR_ORDERLY);
+
+TPM2_PROP_U32_ATTR(lockout_counter, TPM2_PT_LOCKOUT_COUNTER);
+TPM2_PROP_U32_ATTR(max_auth_fail, TPM2_PT_MAX_AUTH_FAIL);
+TPM2_PROP_U32_ATTR(lockout_interval, TPM2_PT_LOCKOUT_INTERVAL);
+TPM2_PROP_U32_ATTR(lockout_recovery, TPM2_PT_LOCKOUT_RECOVERY);
+
+#define ATTR_FOR_TPM2_PROP(_name) (&attr_tpm2_prop_##_name.attr.attr)
+static struct attribute *tpm2_dev_attrs[] = {
+	ATTR_FOR_TPM2_PROP(owner_auth_set),
+	ATTR_FOR_TPM2_PROP(endorsement_auth_set),
+	ATTR_FOR_TPM2_PROP(lockout_auth_set),
+	ATTR_FOR_TPM2_PROP(disable_clear),
+	ATTR_FOR_TPM2_PROP(in_lockout),
+	ATTR_FOR_TPM2_PROP(tpm_generated_eps),
+	ATTR_FOR_TPM2_PROP(ph_enable),
+	ATTR_FOR_TPM2_PROP(sh_enable),
+	ATTR_FOR_TPM2_PROP(eh_enable),
+	ATTR_FOR_TPM2_PROP(ph_enable_nv),
+	ATTR_FOR_TPM2_PROP(orderly),
+	ATTR_FOR_TPM2_PROP(lockout_counter),
+	ATTR_FOR_TPM2_PROP(max_auth_fail),
+	ATTR_FOR_TPM2_PROP(lockout_interval),
+	ATTR_FOR_TPM2_PROP(lockout_recovery),
+	&dev_attr_durations.attr,
+	&dev_attr_timeouts.attr,
+	NULL,
+};
+
+static const struct attribute_group tpm2_dev_group = {
+	.attrs = tpm2_dev_attrs,
+};
+
+void tpm_sysfs_add_device(struct tpm_chip *chip)
+{
 	/* The sysfs routines rely on an implicit tpm_try_get_ops, device_del
 	 * is called before ops is null'd and the sysfs core synchronizes this
 	 * removal so that no callbacks are running or can run again
 	 */
+	/* FIXME: update tpm_sysfs to explicitly lock chip->ops for TPM 2.0
+	 */
 	WARN_ON(chip->groups_cnt != 0);
-	chip->groups[chip->groups_cnt++] = &tpm_dev_group;
+	chip->groups[chip->groups_cnt++] =
+		(chip->flags & TPM_CHIP_FLAG_TPM2) ?
+		&tpm2_dev_group : &tpm1_dev_group;
 }
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 2cce072f25b5..c58140f86839 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -144,7 +144,34 @@ enum tpm2_capabilities {
 };
 
 enum tpm2_properties {
-	TPM_PT_TOTAL_COMMANDS	= 0x0129,
+	TPM_PT_TOTAL_COMMANDS		= 0x0129,
+	TPM2_PT_NONE			= 0,
+	TPM2_PT_GROUP			= 0x100,
+	TPM2_PT_FIXED			= TPM2_PT_GROUP,
+	TPM2_PT_VAR			= TPM2_PT_GROUP * 2,
+	TPM2_PT_PERMANENT		= TPM2_PT_VAR + 0,
+	TPM2_PT_STARTUP_CLEAR		= TPM2_PT_VAR + 1,
+	TPM2_PT_LOCKOUT_COUNTER		= TPM2_PT_VAR + 14,
+	TPM2_PT_MAX_AUTH_FAIL		= TPM2_PT_VAR + 15,
+	TPM2_PT_LOCKOUT_INTERVAL	= TPM2_PT_VAR + 16,
+	TPM2_PT_LOCKOUT_RECOVERY	= TPM2_PT_VAR + 17,
+};
+
+enum tpm2_attr_permanent {
+	TPM2_ATTR_OWNER_AUTH_SET	= BIT(0),
+	TPM2_ATTR_ENDORSEMENT_AUTH_SET	= BIT(1),
+	TPM2_ATTR_LOCKOUT_AUTH_SET	= BIT(2),
+	TPM2_ATTR_DISABLE_CLEAR		= BIT(8),
+	TPM2_ATTR_IN_LOCKOUT		= BIT(9),
+	TPM2_ATTR_TPM_GENERATED_EPS	= BIT(10),
+};
+
+enum tpm2_attr_startup_clear {
+	TPM2_ATTR_PH_ENABLE		= BIT(0),
+	TPM2_ATTR_SH_ENABLE		= BIT(1),
+	TPM2_ATTR_EH_ENABLE		= BIT(2),
+	TPM2_ATTR_PH_ENABLE_NV		= BIT(3),
+	TPM2_ATTR_ORDERLY		= BIT(31),
 };
 
 enum tpm2_startup_types {
-- 
Sent by a computer through tubes


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH 8/8] tpm: add legacy sysfs attributes for tpm2
  2019-06-13 18:09 [PATCH 0/8] tpm: Add driver for cr50 Stephen Boyd
                   ` (6 preceding siblings ...)
  2019-06-13 18:09 ` [PATCH 7/8] tpm: add sysfs attributes for tpm2 Stephen Boyd
@ 2019-06-13 18:09 ` " Stephen Boyd
  2019-06-13 23:44   ` Jason Gunthorpe
  7 siblings, 1 reply; 24+ messages in thread
From: Stephen Boyd @ 2019-06-13 18:09 UTC (permalink / raw)
  To: Peter Huewe, Jarkko Sakkinen
  Cc: Andrey Pronin, linux-kernel, Jason Gunthorpe, Arnd Bergmann,
	Greg Kroah-Hartman, linux-integrity, devicetree, Duncan Laurie,
	Guenter Roeck

From: Andrey Pronin <apronin@chromium.org>

Userland scripts and tests rely on certain sysfs attributes
present in the familiar location:
/sys/class/tpm/tpm0/device/enabled
/sys/class/tpm/tpm0/device/owned

This change:
1) Adds two RO properties to sysfs for tpm in TPM2.0 case:
   - owned: set to 1 if storage hierarchy is enabled
     (TPMA_STARTUP_CLEAR.shEnable == 1)
   - enabled: set to 1 if an owner authorizaton is set
     (TPMA_PERMANENT.ownerAuthSet == 1)
2) Makes the sysfs attributes available at the legacy location:
   the attributes are now created under /sys/class/tpm/tpm0/,
   so symlinks are created in /sys/class/tpm/tpm0/device.

Signed-off-by: Andrey Pronin <apronin@chromium.org>
Signed-off-by: Stephen Boyd <swboyd@chromium.org>
---

This probably isn't necessary, but it's sent so if anyone wants to test
out ChromeOS userspace they can easily do so. I believe Jason rejected
this in https://lkml.kernel.org/r/20160715032145.GE9347@obsidianresearch.com/

 drivers/char/tpm/tpm-chip.c  | 4 ++--
 drivers/char/tpm/tpm-sysfs.c | 8 ++++++++
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
index 8804c9e916fd..43f99f4c4c8a 100644
--- a/drivers/char/tpm/tpm-chip.c
+++ b/drivers/char/tpm/tpm-chip.c
@@ -492,7 +492,7 @@ static void tpm_del_legacy_sysfs(struct tpm_chip *chip)
 {
 	struct attribute **i;
 
-	if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL))
+	if (chip->flags & TPM_CHIP_FLAG_VIRTUAL)
 		return;
 
 	sysfs_remove_link(&chip->dev.parent->kobj, "ppi");
@@ -510,7 +510,7 @@ static int tpm_add_legacy_sysfs(struct tpm_chip *chip)
 	struct attribute **i;
 	int rc;
 
-	if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL))
+	if (chip->flags & TPM_CHIP_FLAG_VIRTUAL)
 		return 0;
 
 	rc = __compat_only_sysfs_link_entry_to_kobj(
diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c
index e9f6f7960023..fe3683f82d48 100644
--- a/drivers/char/tpm/tpm-sysfs.c
+++ b/drivers/char/tpm/tpm-sysfs.c
@@ -413,6 +413,12 @@ TPM2_PROP_FLAG_ATTR(ph_enable_nv,
 TPM2_PROP_FLAG_ATTR(orderly,
 		    TPM2_PT_STARTUP_CLEAR, TPM2_ATTR_ORDERLY);
 
+/* Aliases for userland scripts in TPM2 case */
+TPM2_PROP_FLAG_ATTR(enabled,
+		    TPM2_PT_STARTUP_CLEAR, TPM2_ATTR_SH_ENABLE);
+TPM2_PROP_FLAG_ATTR(owned,
+		    TPM2_PT_PERMANENT, TPM2_ATTR_OWNER_AUTH_SET);
+
 TPM2_PROP_U32_ATTR(lockout_counter, TPM2_PT_LOCKOUT_COUNTER);
 TPM2_PROP_U32_ATTR(max_auth_fail, TPM2_PT_MAX_AUTH_FAIL);
 TPM2_PROP_U32_ATTR(lockout_interval, TPM2_PT_LOCKOUT_INTERVAL);
@@ -431,6 +437,8 @@ static struct attribute *tpm2_dev_attrs[] = {
 	ATTR_FOR_TPM2_PROP(eh_enable),
 	ATTR_FOR_TPM2_PROP(ph_enable_nv),
 	ATTR_FOR_TPM2_PROP(orderly),
+	ATTR_FOR_TPM2_PROP(enabled),
+	ATTR_FOR_TPM2_PROP(owned),
 	ATTR_FOR_TPM2_PROP(lockout_counter),
 	ATTR_FOR_TPM2_PROP(max_auth_fail),
 	ATTR_FOR_TPM2_PROP(lockout_interval),
-- 
Sent by a computer through tubes


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/8] tpm: block messages while suspended
  2019-06-13 18:09 ` [PATCH 1/8] tpm: block messages while suspended Stephen Boyd
@ 2019-06-13 23:26   ` Jason Gunthorpe
  2019-06-14 18:12     ` Stephen Boyd
  2019-06-14 15:27   ` Jarkko Sakkinen
  1 sibling, 1 reply; 24+ messages in thread
From: Jason Gunthorpe @ 2019-06-13 23:26 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Peter Huewe, Jarkko Sakkinen, Andrey Pronin, linux-kernel,
	Arnd Bergmann, Greg Kroah-Hartman, linux-integrity, devicetree,
	Duncan Laurie, Guenter Roeck

On Thu, Jun 13, 2019 at 11:09:24AM -0700, Stephen Boyd wrote:
> From: Andrey Pronin <apronin@chromium.org>
> 
> Other drivers or userspace may initiate sending a message to the tpm
> while the device itself and the controller of the bus it is on are
> suspended. That may break the bus driver logic.
> Block sending messages while the device is suspended.
> 
> Signed-off-by: Andrey Pronin <apronin@chromium.org>
> Signed-off-by: Stephen Boyd <swboyd@chromium.org>
> 
> I don't think this was ever posted before.

Use a real lock.

Jason

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 7/8] tpm: add sysfs attributes for tpm2
  2019-06-13 18:09 ` [PATCH 7/8] tpm: add sysfs attributes for tpm2 Stephen Boyd
@ 2019-06-13 23:30   ` Jason Gunthorpe
  2019-06-14 15:31   ` Jarkko Sakkinen
  1 sibling, 0 replies; 24+ messages in thread
From: Jason Gunthorpe @ 2019-06-13 23:30 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Peter Huewe, Jarkko Sakkinen, Andrey Pronin, linux-kernel,
	Arnd Bergmann, Greg Kroah-Hartman, linux-integrity, devicetree,
	Duncan Laurie, Guenter Roeck

On Thu, Jun 13, 2019 at 11:09:30AM -0700, Stephen Boyd wrote:

> +static ssize_t tpm2_prop_flag_show(struct device *dev,
> +				   struct device_attribute *attr,
> +				   char *buf)
>  {
> -	/* XXX: If you wish to remove this restriction, you must first update
> -	 * tpm_sysfs to explicitly lock chip->ops.
> -	 */
> -	if (chip->flags & TPM_CHIP_FLAG_TPM2)
> -		return;
> +	struct tpm2_prop_flag_dev_attribute *pa =
> +		container_of(attr, struct tpm2_prop_flag_dev_attribute, attr);
> +	u32 flags;
> +	ssize_t rc;
> +
> +	rc = tpm2_get_tpm_pt(to_tpm_chip(dev), pa->property_id, &flags,
> +			     "reading property");
> +	if (rc)
> +		return 0;
> +
> +	return sprintf(buf, "%d\n", !!(flags & pa->flag_mask));
> +}
> +
> +static ssize_t tpm2_prop_u32_show(struct device *dev,
> +				  struct device_attribute *attr,
> +				  char *buf)
> +{
> +	struct tpm2_prop_u32_dev_attribute *pa =
> +		container_of(attr, struct tpm2_prop_u32_dev_attribute, attr);
> +	u32 value;
> +	ssize_t rc;
> +
> +	rc = tpm2_get_tpm_pt(to_tpm_chip(dev), pa->property_id, &value,
> +			     "reading property");
> +	if (rc)
> +		return 0;
> +
> +	return sprintf(buf, "%u\n", value);
> +}
>  
> +#define TPM2_PROP_FLAG_ATTR(_name, _property_id, _flag_mask)           \
> +	struct tpm2_prop_flag_dev_attribute attr_tpm2_prop_##_name = { \
> +		__ATTR(_name, S_IRUGO, tpm2_prop_flag_show, NULL),     \
> +		_property_id, _flag_mask                               \
> +	}
> +
> +#define TPM2_PROP_U32_ATTR(_name, _property_id)                        \
> +	struct tpm2_prop_u32_dev_attribute attr_tpm2_prop_##_name = {  \
> +		__ATTR(_name, S_IRUGO, tpm2_prop_u32_show, NULL),      \
> +		_property_id                                           \
> +	}
> +
> +TPM2_PROP_FLAG_ATTR(owner_auth_set,
> +		    TPM2_PT_PERMANENT, TPM2_ATTR_OWNER_AUTH_SET);
> +TPM2_PROP_FLAG_ATTR(endorsement_auth_set,
> +		    TPM2_PT_PERMANENT, TPM2_ATTR_ENDORSEMENT_AUTH_SET);
> +TPM2_PROP_FLAG_ATTR(lockout_auth_set,
> +		    TPM2_PT_PERMANENT, TPM2_ATTR_LOCKOUT_AUTH_SET);
> +TPM2_PROP_FLAG_ATTR(disable_clear,
> +		    TPM2_PT_PERMANENT, TPM2_ATTR_DISABLE_CLEAR);
> +TPM2_PROP_FLAG_ATTR(in_lockout,
> +		    TPM2_PT_PERMANENT, TPM2_ATTR_IN_LOCKOUT);
> +TPM2_PROP_FLAG_ATTR(tpm_generated_eps,
> +		    TPM2_PT_PERMANENT, TPM2_ATTR_TPM_GENERATED_EPS);
> +
> +TPM2_PROP_FLAG_ATTR(ph_enable,
> +		    TPM2_PT_STARTUP_CLEAR, TPM2_ATTR_PH_ENABLE);
> +TPM2_PROP_FLAG_ATTR(sh_enable,
> +		    TPM2_PT_STARTUP_CLEAR, TPM2_ATTR_SH_ENABLE);
> +TPM2_PROP_FLAG_ATTR(eh_enable,
> +		    TPM2_PT_STARTUP_CLEAR, TPM2_ATTR_EH_ENABLE);
> +TPM2_PROP_FLAG_ATTR(ph_enable_nv,
> +		    TPM2_PT_STARTUP_CLEAR, TPM2_ATTR_PH_ENABLE_NV);
> +TPM2_PROP_FLAG_ATTR(orderly,
> +		    TPM2_PT_STARTUP_CLEAR, TPM2_ATTR_ORDERLY);
> +
> +TPM2_PROP_U32_ATTR(lockout_counter, TPM2_PT_LOCKOUT_COUNTER);
> +TPM2_PROP_U32_ATTR(max_auth_fail, TPM2_PT_MAX_AUTH_FAIL);
> +TPM2_PROP_U32_ATTR(lockout_interval, TPM2_PT_LOCKOUT_INTERVAL);
> +TPM2_PROP_U32_ATTR(lockout_recovery, TPM2_PT_LOCKOUT_RECOVERY);
> +
> +#define ATTR_FOR_TPM2_PROP(_name) (&attr_tpm2_prop_##_name.attr.attr)
> +static struct attribute *tpm2_dev_attrs[] = {
> +	ATTR_FOR_TPM2_PROP(owner_auth_set),
> +	ATTR_FOR_TPM2_PROP(endorsement_auth_set),
> +	ATTR_FOR_TPM2_PROP(lockout_auth_set),
> +	ATTR_FOR_TPM2_PROP(disable_clear),
> +	ATTR_FOR_TPM2_PROP(in_lockout),
> +	ATTR_FOR_TPM2_PROP(tpm_generated_eps),
> +	ATTR_FOR_TPM2_PROP(ph_enable),
> +	ATTR_FOR_TPM2_PROP(sh_enable),
> +	ATTR_FOR_TPM2_PROP(eh_enable),
> +	ATTR_FOR_TPM2_PROP(ph_enable_nv),
> +	ATTR_FOR_TPM2_PROP(orderly),
> +	ATTR_FOR_TPM2_PROP(lockout_counter),
> +	ATTR_FOR_TPM2_PROP(max_auth_fail),
> +	ATTR_FOR_TPM2_PROP(lockout_interval),
> +	ATTR_FOR_TPM2_PROP(lockout_recovery),
> +	&dev_attr_durations.attr,
> +	&dev_attr_timeouts.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group tpm2_dev_group = {
> +	.attrs = tpm2_dev_attrs,
> +};
> +
> +void tpm_sysfs_add_device(struct tpm_chip *chip)
> +{
>  	/* The sysfs routines rely on an implicit tpm_try_get_ops, device_del
>  	 * is called before ops is null'd and the sysfs core synchronizes this
>  	 * removal so that no callbacks are running or can run again
>  	 */
> +	/* FIXME: update tpm_sysfs to explicitly lock chip->ops for TPM 2.0
> +	 */

What does the fixme mean? You cold add proper get_ops locking for the
tpm2 callbacks, it is not so hard. 

I actually think it is needed...

Oh. Jarkko, this is why you can't set ops to null in the class
shutdown, sysfs needs to be fixed first. ops can only go to null for
TPM1 after device_del until someone fixes the locking.

Jason

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 8/8] tpm: add legacy sysfs attributes for tpm2
  2019-06-13 18:09 ` [PATCH 8/8] tpm: add legacy " Stephen Boyd
@ 2019-06-13 23:44   ` Jason Gunthorpe
  0 siblings, 0 replies; 24+ messages in thread
From: Jason Gunthorpe @ 2019-06-13 23:44 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Peter Huewe, Jarkko Sakkinen, Andrey Pronin, linux-kernel,
	Arnd Bergmann, Greg Kroah-Hartman, linux-integrity, devicetree,
	Duncan Laurie, Guenter Roeck

On Thu, Jun 13, 2019 at 11:09:31AM -0700, Stephen Boyd wrote:
> From: Andrey Pronin <apronin@chromium.org>
> 
> Userland scripts and tests rely on certain sysfs attributes
> present in the familiar location:
> /sys/class/tpm/tpm0/device/enabled
> /sys/class/tpm/tpm0/device/owned

no, we are expecting TPM2 userspace to use the new names and
locations. TPM2 is already not compatible with TPM1

Jason

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 2/8] tpm_tis_core: add optional max xfer size check
  2019-06-13 18:09 ` [PATCH 2/8] tpm_tis_core: add optional max xfer size check Stephen Boyd
@ 2019-06-14 15:24   ` Jarkko Sakkinen
  0 siblings, 0 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2019-06-14 15:24 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Peter Huewe, Andrey Pronin, linux-kernel, Jason Gunthorpe,
	Arnd Bergmann, Greg Kroah-Hartman, linux-integrity, devicetree,
	Duncan Laurie, Guenter Roeck, Dmitry Torokhov

On Thu, Jun 13, 2019 at 11:09:25AM -0700, Stephen Boyd wrote:
> From: Andrey Pronin <apronin@chromium.org>
> 
> If tpm reports a bigger burstcnt than allowed by the physical protocol,
> set burstcnt to the max allowed value.
> 
> In practice, seen in case of xfer issues (e.g. in spi interface case,
> lost header causing flow control issues and wrong values returned on read
> from TPM_STS). Without catching, causes the physical layer to reject xfer.
> 
> Signed-off-by: Andrey Pronin <apronin@chromium.org>
> Reviewed-by: Dmitry Torokhov <dtor@chromium.org>
> Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> [swboyd@chromium.org: Drop extra parenthesis in if statement]
> Signed-off-by: Stephen Boyd <swboyd@chromium.org>

Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

/Jarkko

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 3/8] tpm_tis_spi: add max xfer size
  2019-06-13 18:09 ` [PATCH 3/8] tpm_tis_spi: add max xfer size Stephen Boyd
@ 2019-06-14 15:25   ` Jarkko Sakkinen
  0 siblings, 0 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2019-06-14 15:25 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Peter Huewe, Andrey Pronin, linux-kernel, Jason Gunthorpe,
	Arnd Bergmann, Greg Kroah-Hartman, linux-integrity, devicetree,
	Duncan Laurie, Guenter Roeck, Dmitry Torokhov

On Thu, Jun 13, 2019 at 11:09:26AM -0700, Stephen Boyd wrote:
> From: Andrey Pronin <apronin@chromium.org>
> 
> Reject burstcounts larger than 64 bytes reported by tpm.
> SPI Hardware Protocol defined in section 6.4 of TCG PTP
> Spec supports up to 64 bytes of data in a transaction.
> 
> Signed-off-by: Andrey Pronin <apronin@chromium.org>
> Reviewed-by: Dmitry Torokhov <dtor@chromium.org>
> Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> Signed-off-by: Stephen Boyd <swboyd@chromium.org>

Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>

/Jarkko

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/8] tpm: block messages while suspended
  2019-06-13 18:09 ` [PATCH 1/8] tpm: block messages while suspended Stephen Boyd
  2019-06-13 23:26   ` Jason Gunthorpe
@ 2019-06-14 15:27   ` Jarkko Sakkinen
  2019-06-14 18:13     ` Stephen Boyd
  1 sibling, 1 reply; 24+ messages in thread
From: Jarkko Sakkinen @ 2019-06-14 15:27 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Peter Huewe, Andrey Pronin, linux-kernel, Jason Gunthorpe,
	Arnd Bergmann, Greg Kroah-Hartman, linux-integrity, devicetree,
	Duncan Laurie, Guenter Roeck

On Thu, Jun 13, 2019 at 11:09:24AM -0700, Stephen Boyd wrote:
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index 1b5436b213a2..48df005228d0 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -132,6 +132,8 @@ struct tpm_chip {
>  	int dev_num;		/* /dev/tpm# */
>  	unsigned long is_open;	/* only one allowed */
>  
> +	unsigned long is_suspended;
> +
>  	char hwrng_name[64];
>  	struct hwrng hwrng;

I think it would better idea to have a bitmask of some sort that
would have bits for 'open' and 'suspended'.

/Jarkko

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 7/8] tpm: add sysfs attributes for tpm2
  2019-06-13 18:09 ` [PATCH 7/8] tpm: add sysfs attributes for tpm2 Stephen Boyd
  2019-06-13 23:30   ` Jason Gunthorpe
@ 2019-06-14 15:31   ` Jarkko Sakkinen
  1 sibling, 0 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2019-06-14 15:31 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Peter Huewe, Andrey Pronin, linux-kernel, Jason Gunthorpe,
	Arnd Bergmann, Greg Kroah-Hartman, linux-integrity, devicetree,
	Duncan Laurie, Guenter Roeck

On Thu, Jun 13, 2019 at 11:09:30AM -0700, Stephen Boyd wrote:
> From: Andrey Pronin <apronin@chromium.org>
> 
> Add sysfs attributes in TPM2.0 case for:
>  - TPM_PT_PERMANENT flags
>  - TPM_PT_STARTUP_CLEAR flags
>  - lockout-related properties
> 
> Signed-off-by: Andrey Pronin <apronin@chromium.org>
> Signed-off-by: Stephen Boyd <swboyd@chromium.org>
> ---

You can use /dev/tpm0 for this from user space.

/Jarkko

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/8] tpm: block messages while suspended
  2019-06-13 23:26   ` Jason Gunthorpe
@ 2019-06-14 18:12     ` Stephen Boyd
  2019-06-17 22:51       ` Jason Gunthorpe
  0 siblings, 1 reply; 24+ messages in thread
From: Stephen Boyd @ 2019-06-14 18:12 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: Peter Huewe, Jarkko Sakkinen, Andrey Pronin, linux-kernel,
	Arnd Bergmann, Greg Kroah-Hartman, linux-integrity, devicetree,
	Duncan Laurie, Guenter Roeck

Quoting Jason Gunthorpe (2019-06-13 16:26:13)
> On Thu, Jun 13, 2019 at 11:09:24AM -0700, Stephen Boyd wrote:
> > From: Andrey Pronin <apronin@chromium.org>
> > 
> > Other drivers or userspace may initiate sending a message to the tpm
> > while the device itself and the controller of the bus it is on are
> > suspended. That may break the bus driver logic.
> > Block sending messages while the device is suspended.
> > 
> > Signed-off-by: Andrey Pronin <apronin@chromium.org>
> > Signed-off-by: Stephen Boyd <swboyd@chromium.org>
> > 
> > I don't think this was ever posted before.
> 
> Use a real lock.
> 

To make sure the bit is tested under a lock so that suspend/resume can't
update the bit in parallel?


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/8] tpm: block messages while suspended
  2019-06-14 15:27   ` Jarkko Sakkinen
@ 2019-06-14 18:13     ` Stephen Boyd
  2019-06-17 16:38       ` Jarkko Sakkinen
  0 siblings, 1 reply; 24+ messages in thread
From: Stephen Boyd @ 2019-06-14 18:13 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: Peter Huewe, Andrey Pronin, linux-kernel, Jason Gunthorpe,
	Arnd Bergmann, Greg Kroah-Hartman, linux-integrity, devicetree,
	Duncan Laurie, Guenter Roeck

Quoting Jarkko Sakkinen (2019-06-14 08:27:00)
> On Thu, Jun 13, 2019 at 11:09:24AM -0700, Stephen Boyd wrote:
> > diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> > index 1b5436b213a2..48df005228d0 100644
> > --- a/include/linux/tpm.h
> > +++ b/include/linux/tpm.h
> > @@ -132,6 +132,8 @@ struct tpm_chip {
> >       int dev_num;            /* /dev/tpm# */
> >       unsigned long is_open;  /* only one allowed */
> >  
> > +     unsigned long is_suspended;
> > +
> >       char hwrng_name[64];
> >       struct hwrng hwrng;
> 
> I think it would better idea to have a bitmask of some sort that
> would have bits for 'open' and 'suspended'.
> 

Sure. I can combine is_open and is_suspended into some sort of 'unsigned
long flags' member and then have #define TPM_IS_OPEN 0 and #define
TPM_IS_SUSPENDED 1 defines?


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/8] tpm: block messages while suspended
  2019-06-14 18:13     ` Stephen Boyd
@ 2019-06-17 16:38       ` Jarkko Sakkinen
  0 siblings, 0 replies; 24+ messages in thread
From: Jarkko Sakkinen @ 2019-06-17 16:38 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Peter Huewe, Andrey Pronin, linux-kernel, Jason Gunthorpe,
	Arnd Bergmann, Greg Kroah-Hartman, linux-integrity, devicetree,
	Duncan Laurie, Guenter Roeck

On Fri, Jun 14, 2019 at 11:13:13AM -0700, Stephen Boyd wrote:
> Quoting Jarkko Sakkinen (2019-06-14 08:27:00)
> > On Thu, Jun 13, 2019 at 11:09:24AM -0700, Stephen Boyd wrote:
> > > diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> > > index 1b5436b213a2..48df005228d0 100644
> > > --- a/include/linux/tpm.h
> > > +++ b/include/linux/tpm.h
> > > @@ -132,6 +132,8 @@ struct tpm_chip {
> > >       int dev_num;            /* /dev/tpm# */
> > >       unsigned long is_open;  /* only one allowed */
> > >  
> > > +     unsigned long is_suspended;
> > > +
> > >       char hwrng_name[64];
> > >       struct hwrng hwrng;
> > 
> > I think it would better idea to have a bitmask of some sort that
> > would have bits for 'open' and 'suspended'.
> > 
> 
> Sure. I can combine is_open and is_suspended into some sort of 'unsigned
> long flags' member and then have #define TPM_IS_OPEN 0 and #define
> TPM_IS_SUSPENDED 1 defines?

Sounds sustainable.

/Jarkko

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/8] tpm: block messages while suspended
  2019-06-14 18:12     ` Stephen Boyd
@ 2019-06-17 22:51       ` Jason Gunthorpe
  2019-06-21  1:03         ` Stephen Boyd
  0 siblings, 1 reply; 24+ messages in thread
From: Jason Gunthorpe @ 2019-06-17 22:51 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Peter Huewe, Jarkko Sakkinen, Andrey Pronin, linux-kernel,
	Arnd Bergmann, Greg Kroah-Hartman, linux-integrity, devicetree,
	Duncan Laurie, Guenter Roeck

On Fri, Jun 14, 2019 at 11:12:36AM -0700, Stephen Boyd wrote:
> Quoting Jason Gunthorpe (2019-06-13 16:26:13)
> > On Thu, Jun 13, 2019 at 11:09:24AM -0700, Stephen Boyd wrote:
> > > From: Andrey Pronin <apronin@chromium.org>
> > > 
> > > Other drivers or userspace may initiate sending a message to the tpm
> > > while the device itself and the controller of the bus it is on are
> > > suspended. That may break the bus driver logic.
> > > Block sending messages while the device is suspended.
> > > 
> > > Signed-off-by: Andrey Pronin <apronin@chromium.org>
> > > Signed-off-by: Stephen Boyd <swboyd@chromium.org>
> > > 
> > > I don't think this was ever posted before.
> > 
> > Use a real lock.
> > 
> 
> To make sure the bit is tested under a lock so that suspend/resume can't
> update the bit in parallel?

No, just use a real lock, don't make locks out of test bit/set bit

Jason

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/8] tpm: block messages while suspended
  2019-06-17 22:51       ` Jason Gunthorpe
@ 2019-06-21  1:03         ` Stephen Boyd
  2019-06-24 14:26           ` Herbert Xu
  0 siblings, 1 reply; 24+ messages in thread
From: Stephen Boyd @ 2019-06-21  1:03 UTC (permalink / raw)
  To: Jason Gunthorpe
  Cc: Peter Huewe, Jarkko Sakkinen, Andrey Pronin, linux-kernel,
	Arnd Bergmann, Greg Kroah-Hartman, linux-integrity, devicetree,
	Duncan Laurie, Guenter Roeck, Matt Mackall, Herbert Xu,
	linux-crypto

Quoting Jason Gunthorpe (2019-06-17 15:51:34)
> On Fri, Jun 14, 2019 at 11:12:36AM -0700, Stephen Boyd wrote:
> > Quoting Jason Gunthorpe (2019-06-13 16:26:13)
> > > On Thu, Jun 13, 2019 at 11:09:24AM -0700, Stephen Boyd wrote:
> > > > From: Andrey Pronin <apronin@chromium.org>
> > > > 
> > > > Other drivers or userspace may initiate sending a message to the tpm
> > > > while the device itself and the controller of the bus it is on are
> > > > suspended. That may break the bus driver logic.
> > > > Block sending messages while the device is suspended.
> > > > 
> > > > Signed-off-by: Andrey Pronin <apronin@chromium.org>
> > > > Signed-off-by: Stephen Boyd <swboyd@chromium.org>
> > > > 
> > > > I don't think this was ever posted before.
> > > 
> > > Use a real lock.
> > > 
> > 
> > To make sure the bit is tested under a lock so that suspend/resume can't
> > update the bit in parallel?
> 
> No, just use a real lock, don't make locks out of test bit/set bit
> 

Ok. I looked back on the history of this change in our kernel (seems it
wasn't attempted upstream for some time) and it looks like the problem
may have been that the khwrng kthread (i.e. hwrng_fill()) isn't frozen
across suspend/resume. This kthread runs concurrently with devices being
resumed, the cr50 hardware is still suspended, and then a tpm command is
sent and it hangs the I2C bus because the device hasn't been properly
resumed yet.

I suspect a better approach than trying to hold of all TPM commands
across suspend/resume would be to fix the caller here to not even try to
read the hwrng during this time. It's a general problem for other hwrngs
that have some suspend/resume hooks too. This kthread is going to be
running while suspend/resume is going on if the random entropy gets too
low, and that probably shouldn't be the case.

What do you think of the attached patch? I haven't tested it, but it
would make sure that the kthread is frozen so that the hardware can be
resumed before the kthread is thawed and tries to go touch the hardware.

----8<-----
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index 95be7228f327..3b88af3149a7 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -13,6 +13,7 @@
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/err.h>
+#include <linux/freezer.h>
 #include <linux/fs.h>
 #include <linux/hw_random.h>
 #include <linux/kernel.h>
@@ -421,7 +422,9 @@ static int hwrng_fillfn(void *unused)
 {
 	long rc;
 
-	while (!kthread_should_stop()) {
+	set_freezable();
+
+	while (!kthread_freezable_should_stop(NULL)) {
 		struct hwrng *rng;
 
 		rng = get_current_rng();

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/8] tpm: block messages while suspended
  2019-06-21  1:03         ` Stephen Boyd
@ 2019-06-24 14:26           ` Herbert Xu
  2019-07-16 18:28             ` Stephen Boyd
  0 siblings, 1 reply; 24+ messages in thread
From: Herbert Xu @ 2019-06-24 14:26 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Jason Gunthorpe, Peter Huewe, Jarkko Sakkinen, Andrey Pronin,
	linux-kernel, Arnd Bergmann, Greg Kroah-Hartman, linux-integrity,
	devicetree, Duncan Laurie, Guenter Roeck, Matt Mackall,
	linux-crypto

On Thu, Jun 20, 2019 at 06:03:17PM -0700, Stephen Boyd wrote:
>
> What do you think of the attached patch? I haven't tested it, but it
> would make sure that the kthread is frozen so that the hardware can be
> resumed before the kthread is thawed and tries to go touch the hardware.

Looks good to me.

Cheers,
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 4/8] dt-bindings: tpm: document properties for cr50
  2019-06-13 18:09 ` [PATCH 4/8] dt-bindings: tpm: document properties for cr50 Stephen Boyd
@ 2019-07-09 14:41   ` Rob Herring
  0 siblings, 0 replies; 24+ messages in thread
From: Rob Herring @ 2019-07-09 14:41 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: Peter Huewe, Jarkko Sakkinen, Andrey Pronin, linux-kernel,
	Jason Gunthorpe, Arnd Bergmann, Greg Kroah-Hartman,
	linux-integrity, devicetree, Duncan Laurie, Guenter Roeck

On Thu, Jun 13, 2019 at 11:09:27AM -0700, Stephen Boyd wrote:
> From: Andrey Pronin <apronin@chromium.org>
> 
> Add TPM2.0 PTP FIFO compatible SPI interface for chips with Cr50
> firmware.
> 
> Signed-off-by: Andrey Pronin <apronin@chromium.org>
> Cc: Rob Herring <robh+dt@kernel.org>
> Signed-off-by: Stephen Boyd <swboyd@chromium.org>
> ---
> 
> This is a resend of https://lkml.kernel.org/r/1469757314-116169-2-git-send-email-apronin@chromium.org
> with status removed.
> 
>  .../bindings/security/tpm/cr50_spi.txt        | 19 +++++++++++++++++++

google,cr50.txt instead.

With that,

Reviewed-by: Rob Herring <robh@kernel.org>

>  1 file changed, 19 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/security/tpm/cr50_spi.txt

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH 1/8] tpm: block messages while suspended
  2019-06-24 14:26           ` Herbert Xu
@ 2019-07-16 18:28             ` Stephen Boyd
  0 siblings, 0 replies; 24+ messages in thread
From: Stephen Boyd @ 2019-07-16 18:28 UTC (permalink / raw)
  To: Herbert Xu
  Cc: Jason Gunthorpe, Peter Huewe, Jarkko Sakkinen, Andrey Pronin,
	linux-kernel, Arnd Bergmann, Greg Kroah-Hartman, linux-integrity,
	devicetree, Duncan Laurie, Guenter Roeck, Matt Mackall,
	linux-crypto

Quoting Herbert Xu (2019-06-24 07:26:54)
> On Thu, Jun 20, 2019 at 06:03:17PM -0700, Stephen Boyd wrote:
> >
> > What do you think of the attached patch? I haven't tested it, but it
> > would make sure that the kthread is frozen so that the hardware can be
> > resumed before the kthread is thawed and tries to go touch the hardware.
> 
> Looks good to me.
> 

Thanks for checking. I haven't been able to reproduce the problem yet to
confirm this is actually fixing anything, even after tweaking the sysctl
values for khwrng to try and force the thread to run continuously.

From reading the bug report that caused this 'is_suspended' code to be
added to the driver I'm fairly convinced this is the right solution. To
give some more background, it looks like we use s2idle suspend (i.e.
all CPUs are idle across suspend) and then we have the kthread running
to ask the hardware for some more random numbers. The i2c transaction
fails when asking the hwrng for data, and we see these messages printed
on the resume path:

	i2c_designware i2c_designware.2: i2c_dw_handle_tx_abort: lost arbitration
	tpm tpm0: i2c transfer failed (attempt 3/3): -11
	tpm0: tpm_transmit: tpm_send: error -11
	hwrng: no data available

Userspace tasks are thawed after this failure so it looks like something
in the kernel kicks khwrng to grab more data before the i2c bus can be
resumed.


^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, back to index

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-13 18:09 [PATCH 0/8] tpm: Add driver for cr50 Stephen Boyd
2019-06-13 18:09 ` [PATCH 1/8] tpm: block messages while suspended Stephen Boyd
2019-06-13 23:26   ` Jason Gunthorpe
2019-06-14 18:12     ` Stephen Boyd
2019-06-17 22:51       ` Jason Gunthorpe
2019-06-21  1:03         ` Stephen Boyd
2019-06-24 14:26           ` Herbert Xu
2019-07-16 18:28             ` Stephen Boyd
2019-06-14 15:27   ` Jarkko Sakkinen
2019-06-14 18:13     ` Stephen Boyd
2019-06-17 16:38       ` Jarkko Sakkinen
2019-06-13 18:09 ` [PATCH 2/8] tpm_tis_core: add optional max xfer size check Stephen Boyd
2019-06-14 15:24   ` Jarkko Sakkinen
2019-06-13 18:09 ` [PATCH 3/8] tpm_tis_spi: add max xfer size Stephen Boyd
2019-06-14 15:25   ` Jarkko Sakkinen
2019-06-13 18:09 ` [PATCH 4/8] dt-bindings: tpm: document properties for cr50 Stephen Boyd
2019-07-09 14:41   ` Rob Herring
2019-06-13 18:09 ` [PATCH 5/8] tpm: add driver for cr50 on SPI Stephen Boyd
2019-06-13 18:09 ` [PATCH 6/8] tpm: Add driver for cr50 on I2C Stephen Boyd
2019-06-13 18:09 ` [PATCH 7/8] tpm: add sysfs attributes for tpm2 Stephen Boyd
2019-06-13 23:30   ` Jason Gunthorpe
2019-06-14 15:31   ` Jarkko Sakkinen
2019-06-13 18:09 ` [PATCH 8/8] tpm: add legacy " Stephen Boyd
2019-06-13 23:44   ` Jason Gunthorpe

Linux-Integrity Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-integrity/0 linux-integrity/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-integrity linux-integrity/ https://lore.kernel.org/linux-integrity \
		linux-integrity@vger.kernel.org linux-integrity@archiver.kernel.org
	public-inbox-index linux-integrity


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-integrity


AGPL code for this site: git clone https://public-inbox.org/ public-inbox