cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tpm_tis.c (9682B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2005, 2006 IBM Corporation
      4 * Copyright (C) 2014, 2015 Intel Corporation
      5 *
      6 * Authors:
      7 * Leendert van Doorn <leendert@watson.ibm.com>
      8 * Kylene Hall <kjhall@us.ibm.com>
      9 *
     10 * Maintained by: <tpmdd-devel@lists.sourceforge.net>
     11 *
     12 * Device driver for TCG/TCPA TPM (trusted platform module).
     13 * Specifications at www.trustedcomputinggroup.org
     14 *
     15 * This device driver implements the TPM interface as defined in
     16 * the TCG TPM Interface Spec version 1.2, revision 1.0.
     17 */
     18#include <linux/init.h>
     19#include <linux/module.h>
     20#include <linux/moduleparam.h>
     21#include <linux/pnp.h>
     22#include <linux/slab.h>
     23#include <linux/interrupt.h>
     24#include <linux/wait.h>
     25#include <linux/acpi.h>
     26#include <linux/freezer.h>
     27#include <linux/of.h>
     28#include <linux/of_device.h>
     29#include <linux/kernel.h>
     30#include <linux/dmi.h>
     31#include "tpm.h"
     32#include "tpm_tis_core.h"
     33
     34struct tpm_info {
     35	struct resource res;
     36	/* irq > 0 means: use irq $irq;
     37	 * irq = 0 means: autoprobe for an irq;
     38	 * irq = -1 means: no irq support
     39	 */
     40	int irq;
     41};
     42
     43struct tpm_tis_tcg_phy {
     44	struct tpm_tis_data priv;
     45	void __iomem *iobase;
     46};
     47
     48static inline struct tpm_tis_tcg_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *data)
     49{
     50	return container_of(data, struct tpm_tis_tcg_phy, priv);
     51}
     52
     53static int interrupts = -1;
     54module_param(interrupts, int, 0444);
     55MODULE_PARM_DESC(interrupts, "Enable interrupts");
     56
     57static bool itpm;
     58module_param(itpm, bool, 0444);
     59MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)");
     60
     61static bool force;
     62#ifdef CONFIG_X86
     63module_param(force, bool, 0444);
     64MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry");
     65#endif
     66
     67static int tpm_tis_disable_irq(const struct dmi_system_id *d)
     68{
     69	if (interrupts == -1) {
     70		pr_notice("tpm_tis: %s detected: disabling interrupts.\n", d->ident);
     71		interrupts = 0;
     72	}
     73
     74	return 0;
     75}
     76
     77static const struct dmi_system_id tpm_tis_dmi_table[] = {
     78	{
     79		.callback = tpm_tis_disable_irq,
     80		.ident = "ThinkPad T490s",
     81		.matches = {
     82			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
     83			DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T490s"),
     84		},
     85	},
     86	{}
     87};
     88
     89#if defined(CONFIG_PNP) && defined(CONFIG_ACPI)
     90static int has_hid(struct acpi_device *dev, const char *hid)
     91{
     92	struct acpi_hardware_id *id;
     93
     94	list_for_each_entry(id, &dev->pnp.ids, list)
     95		if (!strcmp(hid, id->id))
     96			return 1;
     97
     98	return 0;
     99}
    100
    101static inline int is_itpm(struct acpi_device *dev)
    102{
    103	if (!dev)
    104		return 0;
    105	return has_hid(dev, "INTC0102");
    106}
    107#else
    108static inline int is_itpm(struct acpi_device *dev)
    109{
    110	return 0;
    111}
    112#endif
    113
    114#if defined(CONFIG_ACPI)
    115#define DEVICE_IS_TPM2 1
    116
    117static const struct acpi_device_id tpm_acpi_tbl[] = {
    118	{"MSFT0101", DEVICE_IS_TPM2},
    119	{},
    120};
    121MODULE_DEVICE_TABLE(acpi, tpm_acpi_tbl);
    122
    123static int check_acpi_tpm2(struct device *dev)
    124{
    125	const struct acpi_device_id *aid = acpi_match_device(tpm_acpi_tbl, dev);
    126	struct acpi_table_tpm2 *tbl;
    127	acpi_status st;
    128
    129	if (!aid || aid->driver_data != DEVICE_IS_TPM2)
    130		return 0;
    131
    132	/* If the ACPI TPM2 signature is matched then a global ACPI_SIG_TPM2
    133	 * table is mandatory
    134	 */
    135	st =
    136	    acpi_get_table(ACPI_SIG_TPM2, 1, (struct acpi_table_header **)&tbl);
    137	if (ACPI_FAILURE(st) || tbl->header.length < sizeof(*tbl)) {
    138		dev_err(dev, FW_BUG "failed to get TPM2 ACPI table\n");
    139		return -EINVAL;
    140	}
    141
    142	/* The tpm2_crb driver handles this device */
    143	if (tbl->start_method != ACPI_TPM2_MEMORY_MAPPED)
    144		return -ENODEV;
    145
    146	return 0;
    147}
    148#else
    149static int check_acpi_tpm2(struct device *dev)
    150{
    151	return 0;
    152}
    153#endif
    154
    155static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
    156			      u8 *result, enum tpm_tis_io_mode io_mode)
    157{
    158	struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
    159	__le16 result_le16;
    160	__le32 result_le32;
    161
    162	switch (io_mode) {
    163	case TPM_TIS_PHYS_8:
    164		while (len--)
    165			*result++ = ioread8(phy->iobase + addr);
    166		break;
    167	case TPM_TIS_PHYS_16:
    168		result_le16 = cpu_to_le16(ioread16(phy->iobase + addr));
    169		memcpy(result, &result_le16, sizeof(u16));
    170		break;
    171	case TPM_TIS_PHYS_32:
    172		result_le32 = cpu_to_le32(ioread32(phy->iobase + addr));
    173		memcpy(result, &result_le32, sizeof(u32));
    174		break;
    175	}
    176
    177	return 0;
    178}
    179
    180static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
    181			       const u8 *value, enum tpm_tis_io_mode io_mode)
    182{
    183	struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
    184
    185	switch (io_mode) {
    186	case TPM_TIS_PHYS_8:
    187		while (len--)
    188			iowrite8(*value++, phy->iobase + addr);
    189		break;
    190	case TPM_TIS_PHYS_16:
    191		return -EINVAL;
    192	case TPM_TIS_PHYS_32:
    193		iowrite32(le32_to_cpu(*((__le32 *)value)), phy->iobase + addr);
    194		break;
    195	}
    196
    197	return 0;
    198}
    199
    200static const struct tpm_tis_phy_ops tpm_tcg = {
    201	.read_bytes = tpm_tcg_read_bytes,
    202	.write_bytes = tpm_tcg_write_bytes,
    203};
    204
    205static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info)
    206{
    207	struct tpm_tis_tcg_phy *phy;
    208	int irq = -1;
    209	int rc;
    210
    211	dmi_check_system(tpm_tis_dmi_table);
    212
    213	rc = check_acpi_tpm2(dev);
    214	if (rc)
    215		return rc;
    216
    217	phy = devm_kzalloc(dev, sizeof(struct tpm_tis_tcg_phy), GFP_KERNEL);
    218	if (phy == NULL)
    219		return -ENOMEM;
    220
    221	phy->iobase = devm_ioremap_resource(dev, &tpm_info->res);
    222	if (IS_ERR(phy->iobase))
    223		return PTR_ERR(phy->iobase);
    224
    225	if (interrupts)
    226		irq = tpm_info->irq;
    227
    228	if (itpm || is_itpm(ACPI_COMPANION(dev)))
    229		phy->priv.flags |= TPM_TIS_ITPM_WORKAROUND;
    230
    231	return tpm_tis_core_init(dev, &phy->priv, irq, &tpm_tcg,
    232				 ACPI_HANDLE(dev));
    233}
    234
    235static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
    236
    237static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
    238			    const struct pnp_device_id *pnp_id)
    239{
    240	struct tpm_info tpm_info = {};
    241	struct resource *res;
    242
    243	res = pnp_get_resource(pnp_dev, IORESOURCE_MEM, 0);
    244	if (!res)
    245		return -ENODEV;
    246	tpm_info.res = *res;
    247
    248	if (pnp_irq_valid(pnp_dev, 0))
    249		tpm_info.irq = pnp_irq(pnp_dev, 0);
    250	else
    251		tpm_info.irq = -1;
    252
    253	return tpm_tis_init(&pnp_dev->dev, &tpm_info);
    254}
    255
    256/*
    257 * There is a known bug caused by 93e1b7d42e1e ("[PATCH] tpm: add HID module
    258 * parameter"). This commit added IFX0102 device ID, which is also used by
    259 * tpm_infineon but ignored to add quirks to probe which driver ought to be
    260 * used.
    261 */
    262
    263static struct pnp_device_id tpm_pnp_tbl[] = {
    264	{"PNP0C31", 0},		/* TPM */
    265	{"ATM1200", 0},		/* Atmel */
    266	{"IFX0102", 0},		/* Infineon */
    267	{"BCM0101", 0},		/* Broadcom */
    268	{"BCM0102", 0},		/* Broadcom */
    269	{"NSC1200", 0},		/* National */
    270	{"ICO0102", 0},		/* Intel */
    271	/* Add new here */
    272	{"", 0},		/* User Specified */
    273	{"", 0}			/* Terminator */
    274};
    275MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl);
    276
    277static void tpm_tis_pnp_remove(struct pnp_dev *dev)
    278{
    279	struct tpm_chip *chip = pnp_get_drvdata(dev);
    280
    281	tpm_chip_unregister(chip);
    282	tpm_tis_remove(chip);
    283}
    284
    285static struct pnp_driver tis_pnp_driver = {
    286	.name = "tpm_tis",
    287	.id_table = tpm_pnp_tbl,
    288	.probe = tpm_tis_pnp_init,
    289	.remove = tpm_tis_pnp_remove,
    290	.driver	= {
    291		.pm = &tpm_tis_pm,
    292	},
    293};
    294
    295#define TIS_HID_USR_IDX (ARRAY_SIZE(tpm_pnp_tbl) - 2)
    296module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id,
    297		    sizeof(tpm_pnp_tbl[TIS_HID_USR_IDX].id), 0444);
    298MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe");
    299
    300static struct platform_device *force_pdev;
    301
    302static int tpm_tis_plat_probe(struct platform_device *pdev)
    303{
    304	struct tpm_info tpm_info = {};
    305	struct resource *res;
    306
    307	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    308	if (res == NULL) {
    309		dev_err(&pdev->dev, "no memory resource defined\n");
    310		return -ENODEV;
    311	}
    312	tpm_info.res = *res;
    313
    314	tpm_info.irq = platform_get_irq_optional(pdev, 0);
    315	if (tpm_info.irq <= 0) {
    316		if (pdev != force_pdev)
    317			tpm_info.irq = -1;
    318		else
    319			/* When forcing auto probe the IRQ */
    320			tpm_info.irq = 0;
    321	}
    322
    323	return tpm_tis_init(&pdev->dev, &tpm_info);
    324}
    325
    326static int tpm_tis_plat_remove(struct platform_device *pdev)
    327{
    328	struct tpm_chip *chip = dev_get_drvdata(&pdev->dev);
    329
    330	tpm_chip_unregister(chip);
    331	tpm_tis_remove(chip);
    332
    333	return 0;
    334}
    335
    336#ifdef CONFIG_OF
    337static const struct of_device_id tis_of_platform_match[] = {
    338	{.compatible = "tcg,tpm-tis-mmio"},
    339	{},
    340};
    341MODULE_DEVICE_TABLE(of, tis_of_platform_match);
    342#endif
    343
    344static struct platform_driver tis_drv = {
    345	.probe = tpm_tis_plat_probe,
    346	.remove = tpm_tis_plat_remove,
    347	.driver = {
    348		.name		= "tpm_tis",
    349		.pm		= &tpm_tis_pm,
    350		.of_match_table = of_match_ptr(tis_of_platform_match),
    351		.acpi_match_table = ACPI_PTR(tpm_acpi_tbl),
    352	},
    353};
    354
    355static int tpm_tis_force_device(void)
    356{
    357	struct platform_device *pdev;
    358	static const struct resource x86_resources[] = {
    359		DEFINE_RES_MEM(0xFED40000, TIS_MEM_LEN)
    360	};
    361
    362	if (!force)
    363		return 0;
    364
    365	/* The driver core will match the name tpm_tis of the device to
    366	 * the tpm_tis platform driver and complete the setup via
    367	 * tpm_tis_plat_probe
    368	 */
    369	pdev = platform_device_register_simple("tpm_tis", -1, x86_resources,
    370					       ARRAY_SIZE(x86_resources));
    371	if (IS_ERR(pdev))
    372		return PTR_ERR(pdev);
    373	force_pdev = pdev;
    374
    375	return 0;
    376}
    377
    378static int __init init_tis(void)
    379{
    380	int rc;
    381
    382	rc = tpm_tis_force_device();
    383	if (rc)
    384		goto err_force;
    385
    386	rc = platform_driver_register(&tis_drv);
    387	if (rc)
    388		goto err_platform;
    389
    390
    391	if (IS_ENABLED(CONFIG_PNP)) {
    392		rc = pnp_register_driver(&tis_pnp_driver);
    393		if (rc)
    394			goto err_pnp;
    395	}
    396
    397	return 0;
    398
    399err_pnp:
    400	platform_driver_unregister(&tis_drv);
    401err_platform:
    402	if (force_pdev)
    403		platform_device_unregister(force_pdev);
    404err_force:
    405	return rc;
    406}
    407
    408static void __exit cleanup_tis(void)
    409{
    410	pnp_unregister_driver(&tis_pnp_driver);
    411	platform_driver_unregister(&tis_drv);
    412
    413	if (force_pdev)
    414		platform_device_unregister(force_pdev);
    415}
    416
    417module_init(init_tis);
    418module_exit(cleanup_tis);
    419MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)");
    420MODULE_DESCRIPTION("TPM Driver");
    421MODULE_VERSION("2.0");
    422MODULE_LICENSE("GPL");