Received: by 2002:a25:6193:0:0:0:0:0 with SMTP id v141csp3408866ybb; Tue, 31 Mar 2020 04:48:25 -0700 (PDT) X-Google-Smtp-Source: ADFU+vtq+gBusmPxh9PWTnAV6BuqQPnAMXxK3kr81Vr3Clxtjv5LUyfwV3ye8OCmNwQ+9eL27xEk X-Received: by 2002:a05:6830:148d:: with SMTP id s13mr12325576otq.342.1585655305471; Tue, 31 Mar 2020 04:48:25 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1585655305; cv=none; d=google.com; s=arc-20160816; b=pzjy5bs7BimD2+3cO+8akbpchbXAKRX7OVsafFKnHI516O/VMVOPjillKnLZwShv6x XPq0ORn5m5TTjQwUXwF5whQPLbVAwXUsl8oM04BfRd7t8ervX3kqseowExrX4GQzC7uH IEZbeFkbiiiVudzg6XASyJI8P6RGOt9B+TxWfPNwlSldr8CnNtqjI+CkjRAgtqwUPB9O MSfD8jl6BIDNlfpdbZ563oXb7c/4J3G5IzWtCCoZhAFv/vqGHFMTr2NL4RMLAh5AMGpR Ytw+m8trYK40v0oZtGzsoojt/a14ZkHm1moDfQJz/2yybCJGHACj/csRjwL3WADN8adN hYHA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=OXpvMnPNUnHPvd5XTzygC8g/Bdav/hRwDIDc9qh7UEo=; b=hqKYpPAE2+rqp8/M+xlZJYVJUiucTmmQe//TOX/6YWzjGzX6xRcJBA0pVceeabOd1j 0kIesjH+288ILaoCPBfqC58OIPQe8kRLidcC9wJCNdnKvqkXd6SXKfaC+J0zO1oXCydQ uJtfaQsv5KLwDhEvFKHGV/8ghbTKxglpEhXx20f4faK0kbbxdhluT5yl/xpihhF1T48N uPfqJkFbL/9VypVgmaiiFha2bzzMVvL5Mw1FZQOHmgchG9s0HDL4uCxLd9p59NaE4Z/+ hxW+fe6uFDSjbNAyb92yTt+TTbXHjDRtu3VpNvcl/tx7qpNxZRNFz7QWxYHcNuTC/cMr 3p5w== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s5si6188775oie.153.2020.03.31.04.48.12; Tue, 31 Mar 2020 04:48:25 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730761AbgCaLql convert rfc822-to-8bit (ORCPT + 99 others); Tue, 31 Mar 2020 07:46:41 -0400 Received: from maillog.nuvoton.com ([202.39.227.15]:40894 "EHLO maillog.nuvoton.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730490AbgCaLqX (ORCPT ); Tue, 31 Mar 2020 07:46:23 -0400 Received: from NTHCCAS02.nuvoton.com (nthccas02.nuvoton.com [10.1.8.29]) by maillog.nuvoton.com (Postfix) with ESMTP id 5A04B1C80EFC; Tue, 31 Mar 2020 19:35:23 +0800 (CST) Received: from NTILML02.nuvoton.com (10.190.1.46) by NTHCCAS02.nuvoton.com (10.1.8.29) with Microsoft SMTP Server (TLS) id 15.0.1130.7; Tue, 31 Mar 2020 19:35:22 +0800 Received: from NTILML02.nuvoton.com (10.190.1.47) by NTILML02.nuvoton.com (10.190.1.47) with Microsoft SMTP Server (TLS) id 15.0.1130.7; Tue, 31 Mar 2020 14:35:20 +0300 Received: from taln70.nuvoton.co.il (10.191.1.70) by NTILML02.nuvoton.com (10.190.1.47) with Microsoft SMTP Server id 15.0.1130.7 via Frontend Transport; Tue, 31 Mar 2020 14:35:20 +0300 Received: from taln60.nuvoton.co.il (taln60 [10.191.1.180]) by taln70.nuvoton.co.il (Postfix) with ESMTP id 64F30250; Tue, 31 Mar 2020 14:35:20 +0300 (IDT) Received: by taln60.nuvoton.co.il (Postfix, from userid 10140) id 97CA8639B4; Tue, 31 Mar 2020 14:34:32 +0300 (IDT) From: To: , , , , , , , , , CC: , , , , , , , , , , , Amir Mizinski Subject: [PATCH v4 7/7] tpm: tpm_tis: add tpm_tis_i2c driver Date: Tue, 31 Mar 2020 14:32:07 +0300 Message-ID: <20200331113207.107080-8-amirmizi6@gmail.com> X-Mailer: git-send-email 2.22.0 In-Reply-To: <20200331113207.107080-1-amirmizi6@gmail.com> References: <20200331113207.107080-1-amirmizi6@gmail.com> MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8BIT Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Amir Mizinski Implements the functionality needed to communicate with an I2C TPM according to the TCG TPM I2C Interface Specification. Signed-off-by: Amir Mizinski --- drivers/char/tpm/Kconfig | 12 ++ drivers/char/tpm/Makefile | 1 + drivers/char/tpm/tpm_tis_i2c.c | 292 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 drivers/char/tpm/tpm_tis_i2c.c diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index aacdeed..b482bbf 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -74,6 +74,18 @@ config TCG_TIS_SPI_CR50 If you have a H1 secure module running Cr50 firmware on SPI bus, say Yes and it will be accessible from within Linux. +config TCG_TIS_I2C + tristate "TPM I2C Interface Specification" + depends on I2C + depends on CRC_CCITT + select TCG_TIS_CORE + ---help--- + If you have a TPM security chip which is connected to a regular + I2C master (i.e. most embedded platforms) that is compliant with the + TCG TPM I2C Interface Specification 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 tpm_tis_i2c. + config TCG_TIS_I2C_ATMEL tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)" depends on I2C diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 9567e51..97999cf 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi.o tpm_tis_spi-y := tpm_tis_spi_main.o tpm_tis_spi-$(CONFIG_TCG_TIS_SPI_CR50) += tpm_tis_spi_cr50.o +obj-$(CONFIG_TCG_TIS_I2C) += tpm_tis_i2c.o obj-$(CONFIG_TCG_TIS_I2C_ATMEL) += tpm_i2c_atmel.o obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o diff --git a/drivers/char/tpm/tpm_tis_i2c.c b/drivers/char/tpm/tpm_tis_i2c.c new file mode 100644 index 0000000..c10ec7e --- /dev/null +++ b/drivers/char/tpm/tpm_tis_i2c.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2014-2019 Nuvoton Technology corporation + * + * TPM TIS I2C + * + * TPM TIS I2C Device Driver Interface for devices that implement the TPM I2C + * Interface defined by TCG PC Client Platform TPM Profile (PTP) Specification + * Revision 01.03 v22 at www.trustedcomputinggroup.org + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "tpm.h" +#include "tpm_tis_core.h" + +#define TPM_LOC_SEL 0x04 +#define TPM_I2C_INTERFACE_CAPABILITY 0x30 +#define TPM_I2C_DEVICE_ADDRESS 0x38 +#define TPM_DATA_CSUM_ENABLE 0x40 +#define TPM_DATA_CSUM 0x44 +#define TPM_I2C_DID_VID 0x48 +#define TPM_I2C_RID 0x4C + +//#define I2C_IS_TPM2 1 + +struct tpm_tis_i2c_phy { + struct tpm_tis_data priv; + struct i2c_client *i2c_client; + bool data_csum; + u8 *iobuf; +}; + +static inline struct tpm_tis_i2c_phy *to_tpm_tis_i2c_phy(struct tpm_tis_data *data) +{ + return container_of(data, struct tpm_tis_i2c_phy, priv); +} + +static u8 address_to_register(u32 addr) +{ + addr &= 0xFFF; + + switch (addr) { + // adapt register addresses that have changed compared to + // older TIS versions + case TPM_ACCESS(0): + return 0x04; + case TPM_LOC_SEL: + return 0x00; + case TPM_DID_VID(0): + return 0x48; + case TPM_RID(0): + return 0x4C; + default: + return addr; + } +} + +static int tpm_tis_i2c_read_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, u8 *result) +{ + struct tpm_tis_i2c_phy *phy = to_tpm_tis_i2c_phy(data); + int ret = 0; + int i = 0; + u8 reg = address_to_register(addr); + struct i2c_msg msgs[] = { + { + .addr = phy->i2c_client->addr, + .len = sizeof(reg), + .buf = ®, + }, + { + .addr = phy->i2c_client->addr, + .len = len, + .buf = result, + .flags = I2C_M_RD, + }, + }; + + do { + ret = i2c_transfer(phy->i2c_client->adapter, msgs, + ARRAY_SIZE(msgs)); + usleep_range(250, 300); // wait default GUARD_TIME of 250µs + + } while (ret < 0 && i++ < TPM_RETRY); + + if (ret < 0) + return ret; + + + return 0; +} + +static int tpm_tis_i2c_write_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, const u8 *value) +{ + struct tpm_tis_i2c_phy *phy = to_tpm_tis_i2c_phy(data); + int ret = 0; + int i = 0; + + if (phy->iobuf) { + if (len > TPM_BUFSIZE - 1) + return -EIO; + + phy->iobuf[0] = address_to_register(addr); + memcpy(phy->iobuf + 1, value, len); + + { + struct i2c_msg msgs[] = { + { + .addr = phy->i2c_client->addr, + .len = len + 1, + .buf = phy->iobuf, + }, + }; + + do { + ret = i2c_transfer(phy->i2c_client->adapter, + msgs, ARRAY_SIZE(msgs)); + // wait default GUARD_TIME of 250µs + usleep_range(250, 300); + } while (ret < 0 && i++ < TPM_RETRY); + } + } else { + u8 reg = address_to_register(addr); + + struct i2c_msg msgs[] = { + { + .addr = phy->i2c_client->addr, + .len = sizeof(reg), + .buf = ®, + }, + { + .addr = phy->i2c_client->addr, + .len = len, + .buf = (u8 *)value, + .flags = I2C_M_NOSTART, + }, + }; + do { + ret = i2c_transfer(phy->i2c_client->adapter, msgs, + ARRAY_SIZE(msgs)); + // wait default GUARD_TIME of 250µs + usleep_range(250, 300); + } while (ret < 0 && i++ < TPM_RETRY); + } + + if (ret < 0) + return ret; + + + return 0; +} + +static bool tpm_tis_i2c_check_data(struct tpm_tis_data *data, + const u8 *buf, size_t len) +{ + struct tpm_tis_i2c_phy *phy = to_tpm_tis_i2c_phy(data); + u16 crc, crc_tpm; + int rc; + + if (phy->data_csum) { + crc = crc_ccitt(0x0000, buf, len); + rc = tpm_tis_read16(data, TPM_DATA_CSUM, &crc_tpm); + if (rc < 0) + return false; + + crc_tpm = be16_to_cpu(crc_tpm); + return crc == crc_tpm; + } + + return true; +} + +static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume); + +static int csum_state_store(struct tpm_tis_data *data, u8 new_state) +{ + struct tpm_tis_i2c_phy *phy = to_tpm_tis_i2c_phy(data); + u8 cur_state; + int rc; + + rc = tpm_tis_i2c_write_bytes(&phy->priv, TPM_DATA_CSUM_ENABLE, + 1, &new_state); + if (rc < 0) + return rc; + + rc = tpm_tis_i2c_read_bytes(&phy->priv, TPM_DATA_CSUM_ENABLE, + 1, &cur_state); + if (rc < 0) + return rc; + + if (new_state == cur_state) + phy->data_csum = (bool)new_state; + + return rc; +} + +static const struct tpm_tis_phy_ops tpm_i2c_phy_ops = { + .read_bytes = tpm_tis_i2c_read_bytes, + .write_bytes = tpm_tis_i2c_write_bytes, + .check_data = tpm_tis_i2c_check_data, +}; + +static int tpm_tis_i2c_probe(struct i2c_client *dev, + const struct i2c_device_id *id) +{ + struct tpm_tis_i2c_phy *phy; + int rc; + int CRC_Checksum = 0; + const u8 loc_init = 0; + struct device_node *np; + + phy = devm_kzalloc(&dev->dev, sizeof(struct tpm_tis_i2c_phy), + GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->i2c_client = dev; + + if (!i2c_check_functionality(dev->adapter, I2C_FUNC_NOSTART)) { + phy->iobuf = devm_kmalloc(&dev->dev, TPM_BUFSIZE, GFP_KERNEL); + if (!phy->iobuf) + return -ENOMEM; + } + + // select locality 0 (the driver will access only via locality 0) + rc = tpm_tis_i2c_write_bytes(&phy->priv, TPM_LOC_SEL, 1, &loc_init); + if (rc < 0) + return rc; + + // set CRC checksum calculation enable + np = dev->dev.of_node; + if (of_property_read_bool(np, "crc-checksum")) + CRC_Checksum = 1; + + rc = csum_state_store(&phy->priv, CRC_Checksum); + if (rc < 0) + return rc; + + return tpm_tis_core_init(&dev->dev, &phy->priv, -1, &tpm_i2c_phy_ops, + NULL); +} + +static const struct i2c_device_id tpm_tis_i2c_id[] = { + {"tpm_tis_i2c", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_id); + +static const struct of_device_id of_tis_i2c_match[] = { + { .compatible = "tcg,tpm-tis-i2c", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_tis_i2c_match); + +static const struct acpi_device_id acpi_tis_i2c_match[] = { + {"SMO0768", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, acpi_tis_i2c_match); + +static struct i2c_driver tpm_tis_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tpm_tis_i2c", + .pm = &tpm_tis_pm, + .of_match_table = of_match_ptr(of_tis_i2c_match), + .acpi_match_table = ACPI_PTR(acpi_tis_i2c_match), + }, + .probe = tpm_tis_i2c_probe, + .id_table = tpm_tis_i2c_id, +}; + +module_i2c_driver(tpm_tis_i2c_driver); + +MODULE_DESCRIPTION("TPM Driver for native I2C access"); +MODULE_LICENSE("GPL"); -- 2.7.4 =========================================================================================== The privileged confidential information contained in this email is intended for use only by the addressees as indicated by the original sender of this email. If you are not the addressee indicated in this email or are not responsible for delivery of the email to such a person, please kindly reply to the sender indicating this fact and delete all copies of it from your computer and network server immediately. Your cooperation is highly appreciated. It is advised that any unauthorized use of confidential information of Nuvoton is strictly prohibited; and any information in this email irrelevant to the official business of Nuvoton shall be deemed as neither given nor endorsed by Nuvoton.