Commit f2f393c3 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull tpm fixes from Jarkko Sakkinen.

Mostly interrupt storm fixes, with some other minor changes.

* tag 'tpmdd-v6.5-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd:
  tpm,tpm_tis: Disable interrupts after 1000 unhandled IRQs
  tpm/tpm_tis: Disable interrupts for Lenovo L590 devices
  tpm: Do not remap from ACPI resources again for Pluton TPM
  tpm/tpm_tis: Disable interrupts for Framework Laptop Intel 13th gen
  tpm/tpm_tis: Disable interrupts for Framework Laptop Intel 12th gen
  security: keys: Modify mismatched function name
  tpm: return false from tpm_amd_is_rng_defective on non-x86 platforms
  keys: Fix linking a duplicate key to a keyring's assoc_array
  tpm: tis_i2c: Limit write bursts to I2C_SMBUS_BLOCK_MAX (32) bytes
  tpm: tis_i2c: Limit read bursts to I2C_SMBUS_BLOCK_MAX (32) bytes
  tpm_tis_spi: Release chip select when flow control fails
  tpm: tpm_tis: Disable interrupts *only* for AEON UPX-i11
  tpm: tpm_vtpm_proxy: fix a race condition in /dev/vtpmx creation
parents fdf0eaf1 481c2d14
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -518,6 +518,7 @@ static int tpm_add_legacy_sysfs(struct tpm_chip *chip)
 * 6.x.y.z series: 6.0.18.6 +
 * 3.x.y.z series: 3.57.y.5 +
 */
#ifdef CONFIG_X86
static bool tpm_amd_is_rng_defective(struct tpm_chip *chip)
{
	u32 val1, val2;
@@ -566,6 +567,12 @@ static bool tpm_amd_is_rng_defective(struct tpm_chip *chip)

	return true;
}
#else
static inline bool tpm_amd_is_rng_defective(struct tpm_chip *chip)
{
	return false;
}
#endif /* CONFIG_X86 */

static int tpm_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait)
{
+11 −8
Original line number Diff line number Diff line
@@ -563,6 +563,11 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
	u32 rsp_size;
	int ret;

	/*
	 * Pluton sometimes does not define ACPI memory regions.
	 * Mapping is then done in crb_map_pluton
	 */
	if (priv->sm != ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) {
		INIT_LIST_HEAD(&acpi_resource_list);
		ret = acpi_dev_get_resources(device, &acpi_resource_list,
					     crb_check_resource, iores_array);
@@ -570,8 +575,6 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
			return ret;
		acpi_dev_free_resource_list(&acpi_resource_list);

	/* Pluton doesn't appear to define ACPI memory regions */
	if (priv->sm != ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) {
		if (resource_type(iores_array) != IORESOURCE_MEM) {
			dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n");
			return -EINVAL;
+25 −0
Original line number Diff line number Diff line
@@ -114,6 +114,22 @@ static int tpm_tis_disable_irq(const struct dmi_system_id *d)
}

static const struct dmi_system_id tpm_tis_dmi_table[] = {
	{
		.callback = tpm_tis_disable_irq,
		.ident = "Framework Laptop (12th Gen Intel Core)",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Framework"),
			DMI_MATCH(DMI_PRODUCT_NAME, "Laptop (12th Gen Intel Core)"),
		},
	},
	{
		.callback = tpm_tis_disable_irq,
		.ident = "Framework Laptop (13th Gen Intel Core)",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Framework"),
			DMI_MATCH(DMI_PRODUCT_NAME, "Laptop (13th Gen Intel Core)"),
		},
	},
	{
		.callback = tpm_tis_disable_irq,
		.ident = "ThinkPad T490s",
@@ -138,11 +154,20 @@ static const struct dmi_system_id tpm_tis_dmi_table[] = {
			DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L490"),
		},
	},
	{
		.callback = tpm_tis_disable_irq,
		.ident = "ThinkPad L590",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
			DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L590"),
		},
	},
	{
		.callback = tpm_tis_disable_irq,
		.ident = "UPX-TGL",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "AAEON"),
			DMI_MATCH(DMI_PRODUCT_VERSION, "UPX-TGL"),
		},
	},
	{}
+88 −15
Original line number Diff line number Diff line
@@ -24,9 +24,12 @@
#include <linux/wait.h>
#include <linux/acpi.h>
#include <linux/freezer.h>
#include <linux/dmi.h>
#include "tpm.h"
#include "tpm_tis_core.h"

#define TPM_TIS_MAX_UNHANDLED_IRQS	1000

static void tpm_tis_clkrun_enable(struct tpm_chip *chip, bool value);

static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask,
@@ -468,25 +471,29 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len)
	return rc;
}

static void disable_interrupts(struct tpm_chip *chip)
static void __tpm_tis_disable_interrupts(struct tpm_chip *chip)
{
	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
	u32 int_mask = 0;

	tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &int_mask);
	int_mask &= ~TPM_GLOBAL_INT_ENABLE;
	tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), int_mask);

	chip->flags &= ~TPM_CHIP_FLAG_IRQ;
}

static void tpm_tis_disable_interrupts(struct tpm_chip *chip)
{
	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
	u32 intmask;
	int rc;

	if (priv->irq == 0)
		return;

	rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask);
	if (rc < 0)
		intmask = 0;

	intmask &= ~TPM_GLOBAL_INT_ENABLE;
	rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask);
	__tpm_tis_disable_interrupts(chip);

	devm_free_irq(chip->dev.parent, priv->irq, chip);
	priv->irq = 0;
	chip->flags &= ~TPM_CHIP_FLAG_IRQ;
}

/*
@@ -552,7 +559,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
	if (!test_bit(TPM_TIS_IRQ_TESTED, &priv->flags))
		tpm_msleep(1);
	if (!test_bit(TPM_TIS_IRQ_TESTED, &priv->flags))
		disable_interrupts(chip);
		tpm_tis_disable_interrupts(chip);
	set_bit(TPM_TIS_IRQ_TESTED, &priv->flags);
	return rc;
}
@@ -752,6 +759,57 @@ static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status)
	return status == TPM_STS_COMMAND_READY;
}

static irqreturn_t tpm_tis_revert_interrupts(struct tpm_chip *chip)
{
	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
	const char *product;
	const char *vendor;

	dev_warn(&chip->dev, FW_BUG
		 "TPM interrupt storm detected, polling instead\n");

	vendor = dmi_get_system_info(DMI_SYS_VENDOR);
	product = dmi_get_system_info(DMI_PRODUCT_VERSION);

	if (vendor && product) {
		dev_info(&chip->dev,
			"Consider adding the following entry to tpm_tis_dmi_table:\n");
		dev_info(&chip->dev, "\tDMI_SYS_VENDOR: %s\n", vendor);
		dev_info(&chip->dev, "\tDMI_PRODUCT_VERSION: %s\n", product);
	}

	if (tpm_tis_request_locality(chip, 0) != 0)
		return IRQ_NONE;

	__tpm_tis_disable_interrupts(chip);
	tpm_tis_relinquish_locality(chip, 0);

	schedule_work(&priv->free_irq_work);

	return IRQ_HANDLED;
}

static irqreturn_t tpm_tis_update_unhandled_irqs(struct tpm_chip *chip)
{
	struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
	irqreturn_t irqret = IRQ_HANDLED;

	if (!(chip->flags & TPM_CHIP_FLAG_IRQ))
		return IRQ_HANDLED;

	if (time_after(jiffies, priv->last_unhandled_irq + HZ/10))
		priv->unhandled_irqs = 1;
	else
		priv->unhandled_irqs++;

	priv->last_unhandled_irq = jiffies;

	if (priv->unhandled_irqs > TPM_TIS_MAX_UNHANDLED_IRQS)
		irqret = tpm_tis_revert_interrupts(chip);

	return irqret;
}

static irqreturn_t tis_int_handler(int dummy, void *dev_id)
{
	struct tpm_chip *chip = dev_id;
@@ -761,10 +819,10 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)

	rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt);
	if (rc < 0)
		return IRQ_NONE;
		goto err;

	if (interrupt == 0)
		return IRQ_NONE;
		goto err;

	set_bit(TPM_TIS_IRQ_TESTED, &priv->flags);
	if (interrupt & TPM_INTF_DATA_AVAIL_INT)
@@ -780,10 +838,13 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)
	rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), interrupt);
	tpm_tis_relinquish_locality(chip, 0);
	if (rc < 0)
		return IRQ_NONE;
		goto err;

	tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt);
	return IRQ_HANDLED;

err:
	return tpm_tis_update_unhandled_irqs(chip);
}

static void tpm_tis_gen_interrupt(struct tpm_chip *chip)
@@ -804,6 +865,15 @@ static void tpm_tis_gen_interrupt(struct tpm_chip *chip)
		chip->flags &= ~TPM_CHIP_FLAG_IRQ;
}

static void tpm_tis_free_irq_func(struct work_struct *work)
{
	struct tpm_tis_data *priv = container_of(work, typeof(*priv), free_irq_work);
	struct tpm_chip *chip = priv->chip;

	devm_free_irq(chip->dev.parent, priv->irq, chip);
	priv->irq = 0;
}

/* Register the IRQ and issue a command that will cause an interrupt. If an
 * irq is seen then leave the chip setup for IRQ operation, otherwise reverse
 * everything and leave in polling mode. Returns 0 on success.
@@ -816,6 +886,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask,
	int rc;
	u32 int_status;

	INIT_WORK(&priv->free_irq_work, tpm_tis_free_irq_func);

	rc = devm_request_threaded_irq(chip->dev.parent, irq, NULL,
				       tis_int_handler, IRQF_ONESHOT | flags,
@@ -918,6 +989,7 @@ void tpm_tis_remove(struct tpm_chip *chip)
		interrupt = 0;

	tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt);
	flush_work(&priv->free_irq_work);

	tpm_tis_clkrun_enable(chip, false);

@@ -1021,6 +1093,7 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
	chip->timeout_b = msecs_to_jiffies(TIS_TIMEOUT_B_MAX);
	chip->timeout_c = msecs_to_jiffies(TIS_TIMEOUT_C_MAX);
	chip->timeout_d = msecs_to_jiffies(TIS_TIMEOUT_D_MAX);
	priv->chip = chip;
	priv->timeout_min = TPM_TIMEOUT_USECS_MIN;
	priv->timeout_max = TPM_TIMEOUT_USECS_MAX;
	priv->phy_ops = phy_ops;
@@ -1179,7 +1252,7 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
			rc = tpm_tis_request_locality(chip, 0);
			if (rc < 0)
				goto out_err;
			disable_interrupts(chip);
			tpm_tis_disable_interrupts(chip);
			tpm_tis_relinquish_locality(chip, 0);
		}
	}
+4 −0
Original line number Diff line number Diff line
@@ -91,11 +91,15 @@ enum tpm_tis_flags {
};

struct tpm_tis_data {
	struct tpm_chip *chip;
	u16 manufacturer_id;
	struct mutex locality_count_mutex;
	unsigned int locality_count;
	int locality;
	int irq;
	struct work_struct free_irq_work;
	unsigned long last_unhandled_irq;
	unsigned int unhandled_irqs;
	unsigned int int_mask;
	unsigned long flags;
	void __iomem *ilb_base_addr;
Loading