From 3d9ae54af1d02a7c0edc55c77d7df2b921e58a87 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Thu, 1 Oct 2020 11:09:21 -0700 Subject: tpm_tis: Fix check_locality for correct locality acquisition The TPM TIS specification says the TPM signals the acquisition of locality when the TMP_ACCESS_REQUEST_USE bit goes to one *and* the TPM_ACCESS_REQUEST_USE bit goes to zero. Currently we only check the former not the latter, so check both. Adding the check on TPM_ACCESS_REQUEST_USE should fix the case where the locality is re-requested before the TPM has released it. In this case the locality may get released briefly before it is reacquired, which causes all sorts of problems. However, with the added check, TPM_ACCESS_REQUEST_USE should remain 1 until the second request for the locality is granted. Cc: stable@ger.kernel.org Fixes: 27084efee0c3 ("[PATCH] tpm: driver for next generation TPM chips") Signed-off-by: James Bottomley Reviewed-by: Jerry Snitselaar Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 92c51c6cfd1b..f3ecde8df47d 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -125,7 +125,8 @@ static bool check_locality(struct tpm_chip *chip, int l) if (rc < 0) return false; - if ((access & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == + if ((access & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID + | TPM_ACCESS_REQUEST_USE)) == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) { priv->locality = l; return true; -- cgit v1.2.3 From e42acf104d6e0bd7ccd2f09103d5be5e6d3c637c Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Thu, 1 Oct 2020 11:09:22 -0700 Subject: tpm_tis: Clean up locality release The current release locality code seems to be based on the misunderstanding that the TPM interrupts when a locality is released: it doesn't, only when the locality is acquired. Furthermore, there seems to be no point in waiting for the locality to be released. All it does is penalize the last TPM user. However, if there's no next TPM user, this is a pointless wait and if there is a next TPM user, they'll pay the penalty waiting for the new locality (or possibly not if it's the same as the old locality). Fix the code by making release_locality as simple write to release with no waiting for completion. Cc: stable@ger.kernel.org Fixes: 33bafe90824b ("tpm_tis: verify locality released before returning from release_locality") Signed-off-by: James Bottomley Reviewed-by: Jerry Snitselaar Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm_tis_core.c | 47 +---------------------------------------- 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index f3ecde8df47d..431919d5f48a 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -135,58 +135,13 @@ static bool check_locality(struct tpm_chip *chip, int l) return false; } -static bool locality_inactive(struct tpm_chip *chip, int l) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - int rc; - u8 access; - - rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access); - if (rc < 0) - return false; - - if ((access & (TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY)) - == TPM_ACCESS_VALID) - return true; - - return false; -} - static int release_locality(struct tpm_chip *chip, int l) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - unsigned long stop, timeout; - long rc; tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY); - stop = jiffies + chip->timeout_a; - - if (chip->flags & TPM_CHIP_FLAG_IRQ) { -again: - timeout = stop - jiffies; - if ((long)timeout <= 0) - return -1; - - rc = wait_event_interruptible_timeout(priv->int_queue, - (locality_inactive(chip, l)), - timeout); - - if (rc > 0) - return 0; - - if (rc == -ERESTARTSYS && freezing(current)) { - clear_thread_flag(TIF_SIGPENDING); - goto again; - } - } else { - do { - if (locality_inactive(chip, l)) - return 0; - tpm_msleep(TPM_TIMEOUT); - } while (time_before(jiffies, stop)); - } - return -1; + return 0; } static int request_locality(struct tpm_chip *chip, int l) -- cgit v1.2.3 From d87719c14464825aee86d5f193c4e09285cca0b3 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 20 Nov 2020 12:40:14 -0600 Subject: tpm: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a warning by explicitly adding a break statement instead of letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/eventlog/tpm1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c index 2c96977ad080..8aa9057601d6 100644 --- a/drivers/char/tpm/eventlog/tpm1.c +++ b/drivers/char/tpm/eventlog/tpm1.c @@ -210,6 +210,7 @@ static int get_event_name(char *dest, struct tcpa_event *event, default: break; } + break; default: break; } -- cgit v1.2.3 From 3a253caaad11cf4ac371dd6549a9ec6e2f2152fa Mon Sep 17 00:00:00 2001 From: Duncan Laurie Date: Mon, 7 Dec 2020 16:20:16 +0200 Subject: char: tpm: add i2c driver for cr50 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 bytes 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. Signed-off-by: Duncan Laurie Signed-off-by: Stephen Boyd Signed-off-by: Fabien Lahoudere Signed-off-by: Adrian Ratiu Tested-by: Adrian Ratiu Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/Kconfig | 10 + drivers/char/tpm/Makefile | 2 + drivers/char/tpm/tpm_tis_i2c_cr50.c | 790 ++++++++++++++++++++++++++++++++++++ 3 files changed, 802 insertions(+) create mode 100644 drivers/char/tpm/tpm_tis_i2c_cr50.c diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index a18c314da211..4308f9ca7a43 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -86,6 +86,16 @@ config TCG_TIS_SYNQUACER To compile this driver as a module, choose M here; the module will be called tpm_tis_synquacer. +config TCG_TIS_I2C_CR50 + tristate "TPM Interface Specification 2.0 Interface (I2C - CR50)" + depends on I2C + select TCG_CR50 + help + This is a driver for the Google cr50 I2C TPM interface which is a + custom microcontroller and requires a custom i2c protocol interface + to handle the limitations of the hardware. To compile this driver + as a module, choose M here; the module will be called tcg_tis_i2c_cr50. + 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 84db4fb3a9c9..66d39ea6bd10 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -27,6 +27,8 @@ 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_CR50) += tpm_tis_i2c_cr50.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_cr50.c b/drivers/char/tpm/tpm_tis_i2c_cr50.c new file mode 100644 index 000000000000..ec9a65e7887d --- /dev/null +++ b/drivers/char/tpm/tpm_tis_i2c_cr50.c @@ -0,0 +1,790 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020 Google Inc. + * + * Based on Infineon TPM driver by Peter Huewe. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include "tpm_tis_core.h" + +#define TPM_CR50_MAX_BUFSIZE 64 +#define TPM_CR50_TIMEOUT_SHORT_MS 2 /* Short timeout during transactions */ +#define TPM_CR50_TIMEOUT_NOIRQ_MS 20 /* Timeout for TPM ready without IRQ */ +#define TPM_CR50_I2C_DID_VID 0x00281ae0L /* Device and vendor ID reg value */ +#define TPM_CR50_I2C_MAX_RETRIES 3 /* Max retries due to I2C errors */ +#define TPM_CR50_I2C_RETRY_DELAY_LO 55 /* Min usecs between retries on I2C */ +#define TPM_CR50_I2C_RETRY_DELAY_HI 65 /* Max usecs between retries on I2C */ + +#define TPM_I2C_ACCESS(l) (0x0000 | ((l) << 4)) +#define TPM_I2C_STS(l) (0x0001 | ((l) << 4)) +#define TPM_I2C_DATA_FIFO(l) (0x0005 | ((l) << 4)) +#define TPM_I2C_DID_VID(l) (0x0006 | ((l) << 4)) + +/** + * struct tpm_i2c_cr50_priv_data - Driver private data. + * @irq: Irq number used for this chip. + * If irq <= 0, then a fixed timeout is used instead of waiting for irq. + * @tpm_ready: Struct used by irq handler to signal R/W readiness. + * @buf: Buffer used for i2c writes, with i2c address prepended to content. + * + * Private driver struct used by kernel threads and interrupt context. + */ +struct tpm_i2c_cr50_priv_data { + int irq; + struct completion tpm_ready; + u8 buf[TPM_CR50_MAX_BUFSIZE]; +}; + +/** + * tpm_cr50_i2c_int_handler() - cr50 interrupt handler. + * @dummy: Unused parameter. + * @tpm_info: TPM chip information. + * + * The cr50 interrupt handler signals waiting threads that the + * interrupt has been asserted. It does not do any interrupt triggered + * processing but is instead used to avoid fixed delays. + * + * Return: + * IRQ_HANDLED signifies irq was handled by this device. + */ +static irqreturn_t tpm_cr50_i2c_int_handler(int dummy, void *tpm_info) +{ + struct tpm_chip *chip = tpm_info; + struct tpm_i2c_cr50_priv_data *priv = dev_get_drvdata(&chip->dev); + + complete(&priv->tpm_ready); + + return IRQ_HANDLED; +} + +/** + * tpm_cr50_i2c_wait_tpm_ready() - Wait for tpm to signal ready. + * @chip: A TPM chip. + * + * Wait for completion interrupt if available, otherwise use a fixed + * delay for the TPM to be ready. + * + * Return: + * - 0: Success. + * - -errno: A POSIX error code. + */ +static int tpm_cr50_i2c_wait_tpm_ready(struct tpm_chip *chip) +{ + struct tpm_i2c_cr50_priv_data *priv = dev_get_drvdata(&chip->dev); + + /* Use a safe fixed delay if interrupt is not supported */ + if (priv->irq <= 0) { + msleep(TPM_CR50_TIMEOUT_NOIRQ_MS); + return 0; + } + + /* Wait for interrupt to indicate TPM is ready to respond */ + if (!wait_for_completion_timeout(&priv->tpm_ready, + msecs_to_jiffies(chip->timeout_a))) { + dev_warn(&chip->dev, "Timeout waiting for TPM ready\n"); + return -ETIMEDOUT; + } + + return 0; +} + +/** + * tpm_cr50_i2c_enable_tpm_irq() - Enable TPM irq. + * @chip: A TPM chip. + */ +static void tpm_cr50_i2c_enable_tpm_irq(struct tpm_chip *chip) +{ + struct tpm_i2c_cr50_priv_data *priv = dev_get_drvdata(&chip->dev); + + if (priv->irq > 0) { + reinit_completion(&priv->tpm_ready); + enable_irq(priv->irq); + } +} + +/** + * tpm_cr50_i2c_disable_tpm_irq() - Disable TPM irq. + * @chip: A TPM chip. + */ +static void tpm_cr50_i2c_disable_tpm_irq(struct tpm_chip *chip) +{ + struct tpm_i2c_cr50_priv_data *priv = dev_get_drvdata(&chip->dev); + + if (priv->irq > 0) + disable_irq(priv->irq); +} + +/** + * tpm_cr50_i2c_transfer_message() - Transfer a message over i2c. + * @dev: Device information. + * @adapter: I2C adapter. + * @msg: Message to transfer. + * + * Call unlocked i2c transfer routine with the provided parameters and + * retry in case of bus errors. + * + * Return: + * - 0: Success. + * - -errno: A POSIX error code. + */ +static int tpm_cr50_i2c_transfer_message(struct device *dev, + struct i2c_adapter *adapter, + struct i2c_msg *msg) +{ + unsigned int try; + int rc; + + for (try = 0; try < TPM_CR50_I2C_MAX_RETRIES; try++) { + rc = __i2c_transfer(adapter, msg, 1); + if (rc == 1) + return 0; /* Successfully transferred the message */ + if (try) + dev_warn(dev, "i2c transfer failed (attempt %d/%d): %d\n", + try + 1, TPM_CR50_I2C_MAX_RETRIES, rc); + usleep_range(TPM_CR50_I2C_RETRY_DELAY_LO, TPM_CR50_I2C_RETRY_DELAY_HI); + } + + /* No i2c message transferred */ + return -EIO; +} + +/** + * tpm_cr50_i2c_read() - Read from TPM register. + * @chip: A TPM chip. + * @addr: Register address to read from. + * @buffer: Read destination, provided by caller. + * @len: Number of bytes to read. + * + * Sends the register address byte to the TPM, then waits until TPM + * is ready via interrupt signal or timeout expiration, then 'len' + * bytes are read from TPM response into the provided 'buffer'. + * + * Return: + * - 0: Success. + * - -errno: A POSIX error code. + */ +static int tpm_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 msg_reg_addr = { + .addr = client->addr, + .len = 1, + .buf = &addr + }; + struct i2c_msg msg_response = { + .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 */ + tpm_cr50_i2c_enable_tpm_irq(chip); + + /* Send the register address byte to the TPM */ + rc = tpm_cr50_i2c_transfer_message(&chip->dev, client->adapter, &msg_reg_addr); + if (rc < 0) + goto out; + + /* Wait for TPM to be ready with response data */ + rc = tpm_cr50_i2c_wait_tpm_ready(chip); + if (rc < 0) + goto out; + + /* Read response data from the TPM */ + rc = tpm_cr50_i2c_transfer_message(&chip->dev, client->adapter, &msg_response); + +out: + tpm_cr50_i2c_disable_tpm_irq(chip); + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + + if (rc < 0) + return rc; + + return 0; +} + +/** + * tpm_cr50_i2c_write()- Write to TPM register. + * @chip: A TPM chip. + * @addr: Register address to write to. + * @buffer: Data to write. + * @len: Number of bytes to write. + * + * The provided address is prepended to the data in 'buffer', the + * cobined address+data is sent to the TPM, then wait for TPM to + * indicate it is done writing. + * + * Return: + * - 0: Success. + * - -errno: A POSIX error code. + */ +static int tpm_cr50_i2c_write(struct tpm_chip *chip, u8 addr, u8 *buffer, + size_t len) +{ + struct tpm_i2c_cr50_priv_data *priv = dev_get_drvdata(&chip->dev); + struct i2c_client *client = to_i2c_client(chip->dev.parent); + struct i2c_msg msg = { + .addr = client->addr, + .len = len + 1, + .buf = priv->buf + }; + int rc; + + if (len > TPM_CR50_MAX_BUFSIZE - 1) + return -EINVAL; + + /* Prepend the 'register address' to the buffer */ + priv->buf[0] = addr; + memcpy(priv->buf + 1, buffer, len); + + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + + /* Prepare for completion interrupt */ + tpm_cr50_i2c_enable_tpm_irq(chip); + + /* Send write request buffer with address */ + rc = tpm_cr50_i2c_transfer_message(&chip->dev, client->adapter, &msg); + if (rc < 0) + goto out; + + /* Wait for TPM to be ready, ignore timeout */ + tpm_cr50_i2c_wait_tpm_ready(chip); + +out: + tpm_cr50_i2c_disable_tpm_irq(chip); + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + + if (rc < 0) + return rc; + + return 0; +} + +/** + * tpm_cr50_check_locality() - Verify TPM locality 0 is active. + * @chip: A TPM chip. + * + * Return: + * - 0: Success. + * - -errno: A POSIX error code. + */ +static int tpm_cr50_check_locality(struct tpm_chip *chip) +{ + u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_ACTIVE_LOCALITY; + u8 buf; + int rc; + + rc = tpm_cr50_i2c_read(chip, TPM_I2C_ACCESS(0), &buf, sizeof(buf)); + if (rc < 0) + return rc; + + if ((buf & mask) == mask) + return 0; + + return -EIO; +} + +/** + * tpm_cr50_release_locality() - Release TPM locality. + * @chip: A TPM chip. + * @force: Flag to force release if set. + */ +static void tpm_cr50_release_locality(struct tpm_chip *chip, bool force) +{ + u8 mask = TPM_ACCESS_VALID | TPM_ACCESS_REQUEST_PENDING; + u8 addr = TPM_I2C_ACCESS(0); + u8 buf; + + if (tpm_cr50_i2c_read(chip, addr, &buf, sizeof(buf)) < 0) + return; + + if (force || (buf & mask) == mask) { + buf = TPM_ACCESS_ACTIVE_LOCALITY; + tpm_cr50_i2c_write(chip, addr, &buf, sizeof(buf)); + } +} + +/** + * tpm_cr50_request_locality() - Request TPM locality 0. + * @chip: A TPM chip. + * + * Return: + * - 0: Success. + * - -errno: A POSIX error code. + */ +static int tpm_cr50_request_locality(struct tpm_chip *chip) +{ + u8 buf = TPM_ACCESS_REQUEST_USE; + unsigned long stop; + int rc; + + if (!tpm_cr50_check_locality(chip)) + return 0; + + rc = tpm_cr50_i2c_write(chip, TPM_I2C_ACCESS(0), &buf, sizeof(buf)); + if (rc < 0) + return rc; + + stop = jiffies + chip->timeout_a; + do { + if (!tpm_cr50_check_locality(chip)) + return 0; + + msleep(TPM_CR50_TIMEOUT_SHORT_MS); + } while (time_before(jiffies, stop)); + + return -ETIMEDOUT; +} + +/** + * tpm_cr50_i2c_tis_status() - Read cr50 tis status. + * @chip: A TPM chip. + * + * cr50 requires all 4 bytes of status register to be read. + * + * Return: + * TPM status byte. + */ +static u8 tpm_cr50_i2c_tis_status(struct tpm_chip *chip) +{ + u8 buf[4]; + + if (tpm_cr50_i2c_read(chip, TPM_I2C_STS(0), buf, sizeof(buf)) < 0) + return 0; + + return buf[0]; +} + +/** + * tpm_cr50_i2c_tis_set_ready() - Set status register to ready. + * @chip: A TPM chip. + * + * cr50 requires all 4 bytes of status register to be written. + */ +static void tpm_cr50_i2c_tis_set_ready(struct tpm_chip *chip) +{ + u8 buf[4] = { TPM_STS_COMMAND_READY }; + + tpm_cr50_i2c_write(chip, TPM_I2C_STS(0), buf, sizeof(buf)); + msleep(TPM_CR50_TIMEOUT_SHORT_MS); +} + +/** + * tpm_cr50_i2c_get_burst_and_status() - Get burst count and status. + * @chip: A TPM chip. + * @mask: Status mask. + * @burst: Return value for burst. + * @status: Return value for status. + * + * cr50 uses bytes 3:2 of status register for burst count and + * all 4 bytes must be read. + * + * Return: + * - 0: Success. + * - -errno: A POSIX error code. + */ +static int tpm_cr50_i2c_get_burst_and_status(struct tpm_chip *chip, u8 mask, + size_t *burst, u32 *status) +{ + unsigned long stop; + u8 buf[4]; + + *status = 0; + + /* wait for burstcount */ + stop = jiffies + chip->timeout_b; + + do { + if (tpm_cr50_i2c_read(chip, TPM_I2C_STS(0), buf, sizeof(buf)) < 0) { + msleep(TPM_CR50_TIMEOUT_SHORT_MS); + continue; + } + + *status = *buf; + *burst = le16_to_cpup((__le16 *)(buf + 1)); + + if ((*status & mask) == mask && + *burst > 0 && *burst <= TPM_CR50_MAX_BUFSIZE - 1) + return 0; + + msleep(TPM_CR50_TIMEOUT_SHORT_MS); + } while (time_before(jiffies, stop)); + + dev_err(&chip->dev, "Timeout reading burst and status\n"); + return -ETIMEDOUT; +} + +/** + * tpm_cr50_i2c_tis_recv() - TPM reception callback. + * @chip: A TPM chip. + * @buf: Reception buffer. + * @buf_len: Buffer length to read. + * + * Return: + * - >= 0: Number of read bytes. + * - -errno: A POSIX error code. + */ +static int tpm_cr50_i2c_tis_recv(struct tpm_chip *chip, u8 *buf, size_t buf_len) +{ + + u8 mask = TPM_STS_VALID | TPM_STS_DATA_AVAIL; + size_t burstcnt, cur, len, expected; + u8 addr = TPM_I2C_DATA_FIFO(0); + u32 status; + int rc; + + if (buf_len < TPM_HEADER_SIZE) + return -EINVAL; + + rc = tpm_cr50_i2c_get_burst_and_status(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 = tpm_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, "Buffer too small to receive i2c data\n"); + goto out_err; + } + + /* Now read the rest of the data */ + cur = burstcnt; + while (cur < expected) { + /* Read updated burst count and check status */ + rc = tpm_cr50_i2c_get_burst_and_status(chip, mask, &burstcnt, &status); + if (rc < 0) + goto out_err; + + len = min_t(size_t, burstcnt, expected - cur); + rc = tpm_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 = tpm_cr50_i2c_get_burst_and_status(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; + } + + tpm_cr50_release_locality(chip, false); + return cur; + +out_err: + /* Abort current transaction if still pending */ + if (tpm_cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY) + tpm_cr50_i2c_tis_set_ready(chip); + + tpm_cr50_release_locality(chip, false); + return rc; +} + +/** + * tpm_cr50_i2c_tis_send() - TPM transmission callback. + * @chip: A TPM chip. + * @buf: Buffer to send. + * @len: Buffer length. + * + * Return: + * - 0: Success. + * - -errno: A POSIX error code. + */ +static int tpm_cr50_i2c_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) +{ + size_t burstcnt, limit, sent = 0; + u8 tpm_go[4] = { TPM_STS_GO }; + unsigned long stop; + u32 status; + int rc; + + rc = tpm_cr50_request_locality(chip); + if (rc < 0) + return rc; + + /* Wait until TPM is ready for a command */ + stop = jiffies + chip->timeout_b; + while (!(tpm_cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY)) { + if (time_after(jiffies, stop)) { + rc = -ETIMEDOUT; + goto out_err; + } + + tpm_cr50_i2c_tis_set_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 = tpm_cr50_i2c_get_burst_and_status(chip, mask, &burstcnt, &status); + if (rc < 0) + goto out_err; + + /* + * Use burstcnt - 1 to account for the address byte + * that is inserted by tpm_cr50_i2c_write() + */ + limit = min_t(size_t, burstcnt - 1, len); + rc = tpm_cr50_i2c_write(chip, TPM_I2C_DATA_FIFO(0), &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 = tpm_cr50_i2c_get_burst_and_status(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 = tpm_cr50_i2c_write(chip, TPM_I2C_STS(0), 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 (tpm_cr50_i2c_tis_status(chip) & TPM_STS_COMMAND_READY) + tpm_cr50_i2c_tis_set_ready(chip); + + tpm_cr50_release_locality(chip, false); + return rc; +} + +/** + * tpm_cr50_i2c_req_canceled() - Callback to notify a request cancel. + * @chip: A TPM chip. + * @status: Status given by the cancel callback. + * + * Return: + * True if command is ready, False otherwise. + */ +static bool tpm_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 = &tpm_cr50_i2c_tis_status, + .recv = &tpm_cr50_i2c_tis_recv, + .send = &tpm_cr50_i2c_tis_send, + .cancel = &tpm_cr50_i2c_tis_set_ready, + .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_canceled = &tpm_cr50_i2c_req_canceled, +}; + +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 + +/** + * tpm_cr50_i2c_probe() - Driver probe function. + * @client: I2C client information. + * @id: I2C device id. + * + * Return: + * - 0: Success. + * - -errno: A POSIX error code. + */ +static int tpm_cr50_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tpm_i2c_cr50_priv_data *priv; + struct device *dev = &client->dev; + struct tpm_chip *chip; + u32 vendor; + u8 buf[4]; + int rc; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + 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; + chip->flags |= TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED; + + /* 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, tpm_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", + TPM_CR50_TIMEOUT_NOIRQ_MS); + } + + rc = tpm_cr50_request_locality(chip); + if (rc < 0) { + dev_err(dev, "Could not request locality\n"); + return rc; + } + + /* Read four bytes from DID_VID register */ + rc = tpm_cr50_i2c_read(chip, TPM_I2C_DID_VID(0), buf, sizeof(buf)); + if (rc < 0) { + dev_err(dev, "Could not read vendor id\n"); + tpm_cr50_release_locality(chip, true); + return rc; + } + + vendor = le32_to_cpup((__le32 *)buf); + if (vendor != TPM_CR50_I2C_DID_VID) { + dev_err(dev, "Vendor ID did not match! ID was %08x\n", vendor); + tpm_cr50_release_locality(chip, true); + return -ENODEV; + } + + dev_info(dev, "cr50 TPM 2.0 (i2c 0x%02x irq %d id 0x%x)\n", + client->addr, client->irq, vendor >> 16); + + return tpm_chip_register(chip); +} + +/** + * tpm_cr50_i2c_remove() - Driver remove function. + * @client: I2C client information. + * + * Return: + * - 0: Success. + * - -errno: A POSIX error code. + */ +static int tpm_cr50_i2c_remove(struct i2c_client *client) +{ + struct tpm_chip *chip = i2c_get_clientdata(client); + struct device *dev = &client->dev; + + if (!chip) { + dev_err(dev, "Could not get client data at remove\n"); + return -ENODEV; + } + + tpm_chip_unregister(chip); + tpm_cr50_release_locality(chip, true); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(cr50_i2c_pm, tpm_pm_suspend, tpm_pm_resume); + +static struct i2c_driver cr50_i2c_driver = { + .id_table = cr50_i2c_table, + .probe = tpm_cr50_i2c_probe, + .remove = tpm_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"); -- cgit v1.2.3 From 724eaba40ef623194196323c05baa6a0b4bd0210 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 14 Dec 2020 23:07:21 +0100 Subject: tpm: Remove tpm_dev_wq_lock Never used since it was added. Fixes: 9e1b74a63f776 ("tpm: add support for nonblocking operation") Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Thomas Gleixner Cc: linux-integrity@vger.kernel.org Cc: Jason Gunthorpe Cc: Philip Tricca Cc: Tadeusz Struk Cc: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm-dev-common.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c index 1784530b8387..c08cbb306636 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -20,7 +20,6 @@ #include "tpm-dev.h" static struct workqueue_struct *tpm_dev_wq; -static DEFINE_MUTEX(tpm_dev_wq_lock); static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space, u8 *buf, size_t bufsiz) -- cgit v1.2.3 From 40d32b59e37346fe89d27f83279ad81cd7dcc4a5 Mon Sep 17 00:00:00 2001 From: Andrew Zaborowski Date: Mon, 4 Jan 2021 17:40:48 +0100 Subject: keys: Update comment for restrict_link_by_key_or_keyring_chain Add the bit of information that makes restrict_link_by_key_or_keyring_chain different from restrict_link_by_key_or_keyring to the inline docs comment. Signed-off-by: Andrew Zaborowski Acked-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- crypto/asymmetric_keys/restrict.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index 77ebebada29c..84cefe3b3585 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -244,9 +244,10 @@ int restrict_link_by_key_or_keyring(struct key *dest_keyring, * @payload: The payload of the new key. * @trusted: A key or ring of keys that can be used to vouch for the new cert. * - * Check the new certificate only against the key or keys passed in the data - * parameter. If one of those is the signing key and validates the new - * certificate, then mark the new certificate as being ok to link. + * Check the new certificate against the key or keys passed in the data + * parameter and against the keys already linked to the destination keyring. If + * one of those is the signing key and validates the new certificate, then mark + * the new certificate as being ok to link. * * Returns 0 if the new certificate was accepted, -ENOKEY if we * couldn't find a matching parent certificate in the trusted list, -- cgit v1.2.3 From aab73d9524026caa14aab17fa9b750a6539fd49f Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 13 Jan 2021 15:26:33 -0800 Subject: tpm: add sysfs exports for all banks of PCR registers Create sysfs per hash groups with 24 PCR files in them one group, named pcr-, for each agile hash of the TPM. The files are plugged in to a PCR read function which is TPM version agnostic, so this works also for TPM 1.2 but the hash is only sha1 in that case. Note: the macros used to create the hashes emit spurious checkpatch warnings. Do not try to "fix" them as checkpatch recommends, otherwise they'll break. Signed-off-by: James Bottomley Reviewed-by: Jerry Snitselaar Tested-by: Thiago Jung Bauermann Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm-sysfs.c | 179 +++++++++++++++++++++++++++++++++++++++++++ include/linux/tpm.h | 9 ++- 2 files changed, 187 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index e2ff0b273a0f..63f03cfb8e6a 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -337,11 +337,190 @@ static const struct attribute_group tpm2_dev_group = { .attrs = tpm2_dev_attrs, }; +struct tpm_pcr_attr { + int alg_id; + int pcr; + struct device_attribute attr; +}; + +#define to_tpm_pcr_attr(a) container_of(a, struct tpm_pcr_attr, attr) + +static ssize_t pcr_value_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tpm_pcr_attr *ha = to_tpm_pcr_attr(attr); + struct tpm_chip *chip = to_tpm_chip(dev); + struct tpm_digest digest; + int i; + int digest_size = 0; + int rc; + char *str = buf; + + for (i = 0; i < chip->nr_allocated_banks; i++) + if (ha->alg_id == chip->allocated_banks[i].alg_id) + digest_size = chip->allocated_banks[i].digest_size; + /* should never happen */ + if (!digest_size) + return -EINVAL; + + digest.alg_id = ha->alg_id; + rc = tpm_pcr_read(chip, ha->pcr, &digest); + if (rc) + return rc; + for (i = 0; i < digest_size; i++) + str += sprintf(str, "%02X", digest.digest[i]); + str += sprintf(str, "\n"); + + return str - buf; +} + +/* + * The following set of defines represents all the magic to build + * the per hash attribute groups for displaying each bank of PCRs. + * The only slight problem with this approach is that every PCR is + * hard coded to be present, so you don't know if an PCR is missing + * until a cat of the file returns -EINVAL + * + * Also note you must ignore checkpatch warnings in this macro + * code. This is deep macro magic that checkpatch.pl doesn't + * understand. + */ + +/* Note, this must match TPM2_PLATFORM_PCR which is fixed at 24. */ +#define _TPM_HELPER(_alg, _hash, F) \ + F(_alg, _hash, 0) \ + F(_alg, _hash, 1) \ + F(_alg, _hash, 2) \ + F(_alg, _hash, 3) \ + F(_alg, _hash, 4) \ + F(_alg, _hash, 5) \ + F(_alg, _hash, 6) \ + F(_alg, _hash, 7) \ + F(_alg, _hash, 8) \ + F(_alg, _hash, 9) \ + F(_alg, _hash, 10) \ + F(_alg, _hash, 11) \ + F(_alg, _hash, 12) \ + F(_alg, _hash, 13) \ + F(_alg, _hash, 14) \ + F(_alg, _hash, 15) \ + F(_alg, _hash, 16) \ + F(_alg, _hash, 17) \ + F(_alg, _hash, 18) \ + F(_alg, _hash, 19) \ + F(_alg, _hash, 20) \ + F(_alg, _hash, 21) \ + F(_alg, _hash, 22) \ + F(_alg, _hash, 23) + +/* ignore checkpatch warning about trailing ; in macro. */ +#define PCR_ATTR(_alg, _hash, _pcr) \ + static struct tpm_pcr_attr dev_attr_pcr_##_hash##_##_pcr = { \ + .alg_id = _alg, \ + .pcr = _pcr, \ + .attr = { \ + .attr = { \ + .name = __stringify(_pcr), \ + .mode = 0444 \ + }, \ + .show = pcr_value_show \ + } \ + }; + +#define PCR_ATTRS(_alg, _hash) \ + _TPM_HELPER(_alg, _hash, PCR_ATTR) + +/* ignore checkpatch warning about trailing , in macro. */ +#define PCR_ATTR_VAL(_alg, _hash, _pcr) \ + &dev_attr_pcr_##_hash##_##_pcr.attr.attr, + +#define PCR_ATTR_GROUP_ARRAY(_alg, _hash) \ + static struct attribute *pcr_group_attrs_##_hash[] = { \ + _TPM_HELPER(_alg, _hash, PCR_ATTR_VAL) \ + NULL \ + } + +#define PCR_ATTR_GROUP(_alg, _hash) \ + static struct attribute_group pcr_group_##_hash = { \ + .name = "pcr-" __stringify(_hash), \ + .attrs = pcr_group_attrs_##_hash \ + } + +#define PCR_ATTR_BUILD(_alg, _hash) \ + PCR_ATTRS(_alg, _hash) \ + PCR_ATTR_GROUP_ARRAY(_alg, _hash); \ + PCR_ATTR_GROUP(_alg, _hash) +/* + * End of macro structure to build an attribute group containing 24 + * PCR value files for each supported hash algorithm + */ + +/* + * The next set of macros implements the cleverness for each hash to + * build a static attribute group called pcr_group_ which can be + * added to chip->groups[]. + * + * The first argument is the TPM algorithm id and the second is the + * hash used as both the suffix and the group name. Note: the group + * name is a directory in the top level tpm class with the name + * pcr-, so it must not clash with any other names already + * in the sysfs directory. + */ +PCR_ATTR_BUILD(TPM_ALG_SHA1, sha1); +PCR_ATTR_BUILD(TPM_ALG_SHA256, sha256); +PCR_ATTR_BUILD(TPM_ALG_SHA384, sha384); +PCR_ATTR_BUILD(TPM_ALG_SHA512, sha512); +PCR_ATTR_BUILD(TPM_ALG_SM3_256, sm3); + + void tpm_sysfs_add_device(struct tpm_chip *chip) { + int i; + WARN_ON(chip->groups_cnt != 0); + if (chip->flags & TPM_CHIP_FLAG_TPM2) chip->groups[chip->groups_cnt++] = &tpm2_dev_group; else chip->groups[chip->groups_cnt++] = &tpm1_dev_group; + + /* add one group for each bank hash */ + for (i = 0; i < chip->nr_allocated_banks; i++) { + switch (chip->allocated_banks[i].alg_id) { + case TPM_ALG_SHA1: + chip->groups[chip->groups_cnt++] = &pcr_group_sha1; + break; + case TPM_ALG_SHA256: + chip->groups[chip->groups_cnt++] = &pcr_group_sha256; + break; + case TPM_ALG_SHA384: + chip->groups[chip->groups_cnt++] = &pcr_group_sha384; + break; + case TPM_ALG_SHA512: + chip->groups[chip->groups_cnt++] = &pcr_group_sha512; + break; + case TPM_ALG_SM3_256: + chip->groups[chip->groups_cnt++] = &pcr_group_sm3; + break; + default: + /* + * If triggers, send a patch to add both a + * PCR_ATTR_BUILD() macro above for the + * missing algorithm as well as an additional + * case in this switch statement. + */ + dev_err(&chip->dev, + "TPM with unsupported bank algorithm 0x%04x", + chip->allocated_banks[i].alg_id); + break; + } + } + + /* + * This will only trigger if someone has added an additional + * hash to the tpm_algorithms enum without incrementing + * TPM_MAX_HASHES. + */ + WARN_ON(chip->groups_cnt > TPM_MAX_HASHES + 1); } diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 8f4ff39f51e7..ae2482510f8c 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -31,6 +31,7 @@ struct tpm_chip; struct trusted_key_payload; struct trusted_key_options; +/* if you add a new hash to this, increment TPM_MAX_HASHES below */ enum tpm_algorithms { TPM_ALG_ERROR = 0x0000, TPM_ALG_SHA1 = 0x0004, @@ -42,6 +43,12 @@ enum tpm_algorithms { TPM_ALG_SM3_256 = 0x0012, }; +/* + * maximum number of hashing algorithms a TPM can have. This is + * basically a count of every hash in tpm_algorithms above + */ +#define TPM_MAX_HASHES 5 + struct tpm_digest { u16 alg_id; u8 digest[TPM_MAX_DIGEST_SIZE]; @@ -146,7 +153,7 @@ struct tpm_chip { struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES]; - const struct attribute_group *groups[3]; + const struct attribute_group *groups[3 + TPM_MAX_HASHES]; unsigned int groups_cnt; u32 nr_allocated_banks; -- cgit v1.2.3 From 52d0848e1d60e8884eca6a949300f8e5be094ddf Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Wed, 13 Jan 2021 15:26:34 -0800 Subject: ABI: add sysfs description for tpm exports of PCR registers Adds the ABI entries for the new /sys/class/tpm/tpm/pcr-/ files which are added to export the PCR hash values on a one value per file basis. Signed-off-by: James Bottomley Acked-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- Documentation/ABI/stable/sysfs-class-tpm | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/ABI/stable/sysfs-class-tpm b/Documentation/ABI/stable/sysfs-class-tpm index 91ca63ec7581..d897ecb9615f 100644 --- a/Documentation/ABI/stable/sysfs-class-tpm +++ b/Documentation/ABI/stable/sysfs-class-tpm @@ -194,3 +194,17 @@ Description: The "tpm_version_major" property shows the TCG spec major version Example output:: 2 + +What: /sys/class/tpm/tpmX/pcr-H/N +Date: March 2021 +KernelVersion: 5.12 +Contact: linux-integrity@vger.kernel.org +Description: produces output in compact hex representation for PCR + number N from hash bank H. N is the numeric value of + the PCR number and H is the crypto string + representation of the hash + + Example output:: + + cat /sys/class/tpm/tpm0/pcr-sha256/7 + 2ED93F199692DC6788EFA6A1FE74514AB9760B2A6CEEAEF6C808C13E4ABB0D42 -- cgit v1.2.3 From 90cba8d20f8b09d62a25f9864cb8e67722d76c3a Mon Sep 17 00:00:00 2001 From: Rikard Falkeborn Date: Thu, 4 Feb 2021 22:54:27 +0100 Subject: tpm/ppi: Constify static struct attribute_group The only usage of ppi_attr_grp is to put its address in an array of pointers to const struct attribute_group. Make it const to allow the compiler to put it in read-only memory. Signed-off-by: Rikard Falkeborn Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm-chip.c | 2 ++ drivers/char/tpm/tpm_ppi.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index ddaeceb7e109..19e23fcc6bc8 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -278,6 +278,8 @@ static void tpm_devs_release(struct device *dev) { struct tpm_chip *chip = container_of(dev, struct tpm_chip, devs); + dump_stack(); + /* release the master device reference */ put_device(&chip->dev); } diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index b2dab941cb7f..40018a73b3cb 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -358,7 +358,7 @@ static struct attribute *ppi_attrs[] = { &dev_attr_tcg_operations.attr, &dev_attr_vs_operations.attr, NULL, }; -static struct attribute_group ppi_attr_grp = { +static const struct attribute_group ppi_attr_grp = { .name = "ppi", .attrs = ppi_attrs }; -- cgit v1.2.3 From 5df16caada3fba3b21cb09b85cdedf99507f4ec1 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Fri, 29 Jan 2021 01:56:19 +0200 Subject: KEYS: trusted: Fix incorrect handling of tpm_get_random() When tpm_get_random() was introduced, it defined the following API for the return value: 1. A positive value tells how many bytes of random data was generated. 2. A negative value on error. However, in the call sites the API was used incorrectly, i.e. as it would only return negative values and otherwise zero. Returning he positive read counts to the user space does not make any possible sense. Fix this by returning -EIO when tpm_get_random() returns a positive value. Fixes: 41ab999c80f1 ("tpm: Move tpm_get_random api into the TPM device driver") Cc: stable@vger.kernel.org Cc: Mimi Zohar Cc: "James E.J. Bottomley" Cc: David Howells Cc: Kent Yoder Signed-off-by: Jarkko Sakkinen Reviewed-by: Mimi Zohar --- security/keys/trusted-keys/trusted_tpm1.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c index 74d82093cbaa..204826b734ac 100644 --- a/security/keys/trusted-keys/trusted_tpm1.c +++ b/security/keys/trusted-keys/trusted_tpm1.c @@ -403,9 +403,12 @@ static int osap(struct tpm_buf *tb, struct osapsess *s, int ret; ret = tpm_get_random(chip, ononce, TPM_NONCE_SIZE); - if (ret != TPM_NONCE_SIZE) + if (ret < 0) return ret; + if (ret != TPM_NONCE_SIZE) + return -EIO; + tpm_buf_reset(tb, TPM_TAG_RQU_COMMAND, TPM_ORD_OSAP); tpm_buf_append_u16(tb, type); tpm_buf_append_u32(tb, handle); @@ -496,8 +499,12 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, goto out; ret = tpm_get_random(chip, td->nonceodd, TPM_NONCE_SIZE); + if (ret < 0) + return ret; + if (ret != TPM_NONCE_SIZE) - goto out; + return -EIO; + ordinal = htonl(TPM_ORD_SEAL); datsize = htonl(datalen); pcrsize = htonl(pcrinfosize); @@ -601,9 +608,12 @@ static int tpm_unseal(struct tpm_buf *tb, ordinal = htonl(TPM_ORD_UNSEAL); ret = tpm_get_random(chip, nonceodd, TPM_NONCE_SIZE); + if (ret < 0) + return ret; + if (ret != TPM_NONCE_SIZE) { pr_info("trusted_key: tpm_get_random failed (%d)\n", ret); - return ret; + return -EIO; } ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE, enonce1, nonceodd, cont, sizeof(uint32_t), @@ -1013,8 +1023,12 @@ static int trusted_instantiate(struct key *key, case Opt_new: key_len = payload->key_len; ret = tpm_get_random(chip, payload->key, key_len); + if (ret < 0) + goto out; + if (ret != key_len) { pr_info("trusted_key: key_create failed (%d)\n", ret); + ret = -EIO; goto out; } if (tpm2) -- cgit v1.2.3 From 8da7520c80468c48f981f0b81fc1be6599e3b0ad Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Fri, 29 Jan 2021 01:56:20 +0200 Subject: KEYS: trusted: Fix migratable=1 failing Consider the following transcript: $ keyctl add trusted kmk "new 32 blobauth=helloworld keyhandle=80000000 migratable=1" @u add_key: Invalid argument The documentation has the following description: migratable= 0|1 indicating permission to reseal to new PCR values, default 1 (resealing allowed) The consequence is that "migratable=1" should succeed. Fix this by allowing this condition to pass instead of return -EINVAL. [*] Documentation/security/keys/trusted-encrypted.rst Cc: stable@vger.kernel.org Cc: "James E.J. Bottomley" Cc: Mimi Zohar Cc: David Howells Fixes: d00a1c72f7f4 ("keys: add new trusted key-type") Signed-off-by: Jarkko Sakkinen --- security/keys/trusted-keys/trusted_tpm1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c index 204826b734ac..493eb91ed017 100644 --- a/security/keys/trusted-keys/trusted_tpm1.c +++ b/security/keys/trusted-keys/trusted_tpm1.c @@ -801,7 +801,7 @@ static int getoptions(char *c, struct trusted_key_payload *pay, case Opt_migratable: if (*args[0].from == '0') pay->migratable = 0; - else + else if (*args[0].from != '1') return -EINVAL; break; case Opt_pcrlock: -- cgit v1.2.3 From 8c657a0590de585b1115847c17b34a58025f2f4b Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Fri, 29 Jan 2021 01:56:21 +0200 Subject: KEYS: trusted: Reserve TPM for seal and unseal operations When TPM 2.0 trusted keys code was moved to the trusted keys subsystem, the operations were unwrapped from tpm_try_get_ops() and tpm_put_ops(), which are used to take temporarily the ownership of the TPM chip. The ownership is only taken inside tpm_send(), but this is not sufficient, as in the key load TPM2_CC_LOAD, TPM2_CC_UNSEAL and TPM2_FLUSH_CONTEXT need to be done as a one single atom. Take the TPM chip ownership before sending anything with tpm_try_get_ops() and tpm_put_ops(), and use tpm_transmit_cmd() to send TPM commands instead of tpm_send(), reverting back to the old behaviour. Fixes: 2e19e10131a0 ("KEYS: trusted: Move TPM2 trusted keys code") Reported-by: "James E.J. Bottomley" Cc: stable@vger.kernel.org Cc: David Howells Cc: Mimi Zohar Cc: Sumit Garg Acked-by Sumit Garg Tested-by: Mimi Zohar Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm.h | 4 ---- include/linux/tpm.h | 5 ++++- security/keys/trusted-keys/trusted_tpm2.c | 22 ++++++++++++++++++---- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 947d1db0a5cc..283f78211c3a 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -164,8 +164,6 @@ extern const struct file_operations tpmrm_fops; extern struct idr dev_nums_idr; ssize_t tpm_transmit(struct tpm_chip *chip, u8 *buf, size_t bufsiz); -ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf, - size_t min_rsp_body_length, const char *desc); int tpm_get_timeouts(struct tpm_chip *); int tpm_auto_startup(struct tpm_chip *chip); @@ -194,8 +192,6 @@ static inline void tpm_msleep(unsigned int delay_msec) int tpm_chip_start(struct tpm_chip *chip); void tpm_chip_stop(struct tpm_chip *chip); struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip); -__must_check int tpm_try_get_ops(struct tpm_chip *chip); -void tpm_put_ops(struct tpm_chip *chip); struct tpm_chip *tpm_chip_alloc(struct device *dev, const struct tpm_class_ops *ops); diff --git a/include/linux/tpm.h b/include/linux/tpm.h index ae2482510f8c..543aa3b1dedc 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -404,6 +404,10 @@ static inline u32 tpm2_rc_value(u32 rc) #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE) extern int tpm_is_tpm2(struct tpm_chip *chip); +extern __must_check int tpm_try_get_ops(struct tpm_chip *chip); +extern void tpm_put_ops(struct tpm_chip *chip); +extern ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_buf *buf, + size_t min_rsp_body_length, const char *desc); extern int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digest); extern int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, @@ -417,7 +421,6 @@ static inline int tpm_is_tpm2(struct tpm_chip *chip) { return -ENODEV; } - static inline int tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, struct tpm_digest *digest) { diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c index 08ec7f48f01d..e2a0ed5d02f0 100644 --- a/security/keys/trusted-keys/trusted_tpm2.c +++ b/security/keys/trusted-keys/trusted_tpm2.c @@ -83,6 +83,12 @@ int tpm2_seal_trusted(struct tpm_chip *chip, if (rc) return rc; + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); + if (rc) { + tpm_put_ops(chip); + return rc; + } + tpm_buf_append_u32(&buf, options->keyhandle); tpm2_buf_append_auth(&buf, TPM2_RS_PW, NULL /* nonce */, 0, @@ -130,7 +136,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, goto out; } - rc = tpm_send(chip, buf.data, tpm_buf_length(&buf)); + rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data"); if (rc) goto out; @@ -157,6 +163,7 @@ out: rc = -EPERM; } + tpm_put_ops(chip); return rc; } @@ -211,7 +218,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip, goto out; } - rc = tpm_send(chip, buf.data, tpm_buf_length(&buf)); + rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob"); if (!rc) *blob_handle = be32_to_cpup( (__be32 *) &buf.data[TPM_HEADER_SIZE]); @@ -260,7 +267,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, options->blobauth /* hmac */, TPM_DIGEST_SIZE); - rc = tpm_send(chip, buf.data, tpm_buf_length(&buf)); + rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing"); if (rc > 0) rc = -EPERM; @@ -304,12 +311,19 @@ int tpm2_unseal_trusted(struct tpm_chip *chip, u32 blob_handle; int rc; - rc = tpm2_load_cmd(chip, payload, options, &blob_handle); + rc = tpm_try_get_ops(chip); if (rc) return rc; + rc = tpm2_load_cmd(chip, payload, options, &blob_handle); + if (rc) + goto out; + rc = tpm2_unseal_cmd(chip, payload, options, blob_handle); tpm2_flush_context(chip, blob_handle); +out: + tpm_put_ops(chip); + return rc; } -- cgit v1.2.3