diff options
Diffstat (limited to 'drivers/char')
32 files changed, 1168 insertions, 208 deletions
diff --git a/drivers/char/efirtc.c b/drivers/char/efirtc.c index e39e7402e623..dc62568b7dde 100644 --- a/drivers/char/efirtc.c +++ b/drivers/char/efirtc.c @@ -30,7 +30,6 @@ #include <linux/types.h> #include <linux/errno.h> #include <linux/miscdevice.h> -#include <linux/module.h> #include <linux/init.h> #include <linux/rtc.h> #include <linux/proc_fs.h> @@ -395,14 +394,8 @@ efi_rtc_init(void) } return 0; } +device_initcall(efi_rtc_init); -static void __exit -efi_rtc_exit(void) -{ - /* not yet used */ -} - -module_init(efi_rtc_init); -module_exit(efi_rtc_exit); - +/* MODULE_LICENSE("GPL"); +*/ diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 5c0baa9ffc64..240b6cf1d97c 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -12,7 +12,6 @@ */ #include <linux/interrupt.h> -#include <linux/module.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/miscdevice.h> @@ -1043,24 +1042,16 @@ static int hpet_acpi_add(struct acpi_device *device) return hpet_alloc(&data); } -static int hpet_acpi_remove(struct acpi_device *device) -{ - /* XXX need to unregister clocksource, dealloc mem, etc */ - return -EINVAL; -} - static const struct acpi_device_id hpet_device_ids[] = { {"PNP0103", 0}, {"", 0}, }; -MODULE_DEVICE_TABLE(acpi, hpet_device_ids); static struct acpi_driver hpet_acpi_driver = { .name = "hpet", .ids = hpet_device_ids, .ops = { .add = hpet_acpi_add, - .remove = hpet_acpi_remove, }, }; @@ -1086,19 +1077,9 @@ static int __init hpet_init(void) return 0; } +device_initcall(hpet_init); -static void __exit hpet_exit(void) -{ - acpi_bus_unregister_driver(&hpet_acpi_driver); - - if (sysctl_header) - unregister_sysctl_table(sysctl_header); - misc_deregister(&hpet_misc); - - return; -} - -module_init(hpet_init); -module_exit(hpet_exit); +/* MODULE_AUTHOR("Bob Picco <Robert.Picco@hp.com>"); MODULE_LICENSE("GPL"); +*/ diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index f48cf11c655e..dbf22719462f 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -10,7 +10,7 @@ menuconfig HW_RANDOM To compile this driver as a module, choose M here: the module will be called rng-core. This provides a device - that's usually called /dev/hw_random, and which exposes one + that's usually called /dev/hwrng, and which exposes one of possibly several hardware random number generators. These hardware random number generators do not feed directly @@ -346,6 +346,16 @@ config HW_RANDOM_MSM If unsure, say Y. +config HW_RANDOM_ST + tristate "ST Microelectronics HW Random Number Generator support" + depends on HW_RANDOM && ARCH_STI + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on STi series of SoCs. + + To compile this driver as a module, choose M here: the + module will be called st-rng. + config HW_RANDOM_XGENE tristate "APM X-Gene True Random Number Generator (TRNG) support" depends on HW_RANDOM && ARCH_XGENE @@ -359,6 +369,18 @@ config HW_RANDOM_XGENE If unsure, say Y. +config HW_RANDOM_STM32 + tristate "STMicroelectronics STM32 random number generator" + depends on HW_RANDOM && (ARCH_STM32 || COMPILE_TEST) + help + This driver provides kernel-side support for the Random Number + Generator hardware found on STM32 microcontrollers. + + To compile this driver as a module, choose M here: the + module will be called stm32-rng. + + If unsure, say N. + endif # HW_RANDOM config UML_RANDOM diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 055bb01510ad..5ad397635128 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -30,4 +30,6 @@ obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o +obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o +obj-$(CONFIG_HW_RANDOM_STM32) += stm32-rng.o diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 5643b65cee20..6f497aa1b276 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -323,7 +323,7 @@ static ssize_t hwrng_attr_current_store(struct device *dev, return -ERESTARTSYS; err = -ENODEV; list_for_each_entry(rng, &rng_list, list) { - if (strcmp(rng->name, buf) == 0) { + if (sysfs_streq(rng->name, buf)) { err = 0; if (rng != current_rng) err = set_current_rng(rng); diff --git a/drivers/char/hw_random/exynos-rng.c b/drivers/char/hw_random/exynos-rng.c index dc4701fd814f..30cf4623184f 100644 --- a/drivers/char/hw_random/exynos-rng.c +++ b/drivers/char/hw_random/exynos-rng.c @@ -53,15 +53,11 @@ static void exynos_rng_writel(struct exynos_rng *rng, u32 val, u32 offset) __raw_writel(val, rng->mem + offset); } -static int exynos_init(struct hwrng *rng) +static int exynos_rng_configure(struct exynos_rng *exynos_rng) { - struct exynos_rng *exynos_rng = container_of(rng, - struct exynos_rng, rng); int i; int ret = 0; - pm_runtime_get_sync(exynos_rng->dev); - for (i = 0 ; i < 5 ; i++) exynos_rng_writel(exynos_rng, jiffies, EXYNOS_PRNG_SEED_OFFSET + 4*i); @@ -70,6 +66,17 @@ static int exynos_init(struct hwrng *rng) & SEED_SETTING_DONE)) ret = -EIO; + return ret; +} + +static int exynos_init(struct hwrng *rng) +{ + struct exynos_rng *exynos_rng = container_of(rng, + struct exynos_rng, rng); + int ret = 0; + + pm_runtime_get_sync(exynos_rng->dev); + ret = exynos_rng_configure(exynos_rng); pm_runtime_put_noidle(exynos_rng->dev); return ret; @@ -81,21 +88,24 @@ static int exynos_read(struct hwrng *rng, void *buf, struct exynos_rng *exynos_rng = container_of(rng, struct exynos_rng, rng); u32 *data = buf; + int retry = 100; pm_runtime_get_sync(exynos_rng->dev); exynos_rng_writel(exynos_rng, PRNG_START, 0); while (!(exynos_rng_readl(exynos_rng, - EXYNOS_PRNG_STATUS_OFFSET) & PRNG_DONE)) + EXYNOS_PRNG_STATUS_OFFSET) & PRNG_DONE) && --retry) cpu_relax(); + if (!retry) + return -ETIMEDOUT; exynos_rng_writel(exynos_rng, PRNG_DONE, EXYNOS_PRNG_STATUS_OFFSET); *data = exynos_rng_readl(exynos_rng, EXYNOS_PRNG_OUT1_OFFSET); pm_runtime_mark_last_busy(exynos_rng->dev); - pm_runtime_autosuspend(exynos_rng->dev); + pm_runtime_put_sync_autosuspend(exynos_rng->dev); return 4; } @@ -152,15 +162,45 @@ static int exynos_rng_runtime_resume(struct device *dev) return clk_prepare_enable(exynos_rng->clk); } + +static int exynos_rng_suspend(struct device *dev) +{ + return pm_runtime_force_suspend(dev); +} + +static int exynos_rng_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct exynos_rng *exynos_rng = platform_get_drvdata(pdev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + return exynos_rng_configure(exynos_rng); +} #endif -static UNIVERSAL_DEV_PM_OPS(exynos_rng_pm_ops, exynos_rng_runtime_suspend, - exynos_rng_runtime_resume, NULL); +static const struct dev_pm_ops exynos_rng_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(exynos_rng_suspend, exynos_rng_resume) + SET_RUNTIME_PM_OPS(exynos_rng_runtime_suspend, + exynos_rng_runtime_resume, NULL) +}; + +static const struct of_device_id exynos_rng_dt_match[] = { + { + .compatible = "samsung,exynos4-rng", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, exynos_rng_dt_match); static struct platform_driver exynos_rng_driver = { .driver = { .name = "exynos-rng", .pm = &exynos_rng_pm_ops, + .of_match_table = exynos_rng_dt_match, }, .probe = exynos_rng_probe, }; diff --git a/drivers/char/hw_random/mxc-rnga.c b/drivers/char/hw_random/mxc-rnga.c index 6cbb72ec6013..467362262651 100644 --- a/drivers/char/hw_random/mxc-rnga.c +++ b/drivers/char/hw_random/mxc-rnga.c @@ -141,12 +141,11 @@ static void mxc_rnga_cleanup(struct hwrng *rng) static int __init mxc_rnga_probe(struct platform_device *pdev) { - int err = -ENODEV; + int err; struct resource *res; struct mxc_rng *mxc_rng; - mxc_rng = devm_kzalloc(&pdev->dev, sizeof(struct mxc_rng), - GFP_KERNEL); + mxc_rng = devm_kzalloc(&pdev->dev, sizeof(*mxc_rng), GFP_KERNEL); if (!mxc_rng) return -ENOMEM; @@ -160,13 +159,12 @@ static int __init mxc_rnga_probe(struct platform_device *pdev) mxc_rng->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(mxc_rng->clk)) { dev_err(&pdev->dev, "Could not get rng_clk!\n"); - err = PTR_ERR(mxc_rng->clk); - goto out; + return PTR_ERR(mxc_rng->clk); } err = clk_prepare_enable(mxc_rng->clk); if (err) - goto out; + return err; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mxc_rng->mem = devm_ioremap_resource(&pdev->dev, res); @@ -181,14 +179,10 @@ static int __init mxc_rnga_probe(struct platform_device *pdev) goto err_ioremap; } - dev_info(&pdev->dev, "MXC RNGA Registered.\n"); - return 0; err_ioremap: clk_disable_unprepare(mxc_rng->clk); - -out: return err; } diff --git a/drivers/char/hw_random/octeon-rng.c b/drivers/char/hw_random/octeon-rng.c index 6234a4a19b56..8c78aa090492 100644 --- a/drivers/char/hw_random/octeon-rng.c +++ b/drivers/char/hw_random/octeon-rng.c @@ -96,7 +96,7 @@ static int octeon_rng_probe(struct platform_device *pdev) rng->ops = ops; platform_set_drvdata(pdev, &rng->ops); - ret = hwrng_register(&rng->ops); + ret = devm_hwrng_register(&pdev->dev, &rng->ops); if (ret) return -ENOENT; @@ -105,21 +105,11 @@ static int octeon_rng_probe(struct platform_device *pdev) return 0; } -static int octeon_rng_remove(struct platform_device *pdev) -{ - struct hwrng *rng = platform_get_drvdata(pdev); - - hwrng_unregister(rng); - - return 0; -} - static struct platform_driver octeon_rng_driver = { .driver = { .name = "octeon_rng", }, .probe = octeon_rng_probe, - .remove = octeon_rng_remove, }; module_platform_driver(octeon_rng_driver); diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c index 51cb1d5cc489..699b7259f5d7 100644 --- a/drivers/char/hw_random/pasemi-rng.c +++ b/drivers/char/hw_random/pasemi-rng.c @@ -138,6 +138,7 @@ static const struct of_device_id rng_match[] = { { .compatible = "pasemi,pwrficient-rng", }, { }, }; +MODULE_DEVICE_TABLE(of, rng_match); static struct platform_driver rng_driver = { .driver = { diff --git a/drivers/char/hw_random/ppc4xx-rng.c b/drivers/char/hw_random/ppc4xx-rng.c index b2cfda0fa93e..c0db4387d2e2 100644 --- a/drivers/char/hw_random/ppc4xx-rng.c +++ b/drivers/char/hw_random/ppc4xx-rng.c @@ -129,6 +129,7 @@ static const struct of_device_id ppc4xx_rng_match[] = { { .compatible = "amcc,ppc440epx-rng", }, {}, }; +MODULE_DEVICE_TABLE(of, ppc4xx_rng_match); static struct platform_driver ppc4xx_rng_driver = { .driver = { diff --git a/drivers/char/hw_random/st-rng.c b/drivers/char/hw_random/st-rng.c new file mode 100644 index 000000000000..1d35363d23c5 --- /dev/null +++ b/drivers/char/hw_random/st-rng.c @@ -0,0 +1,151 @@ +/* + * ST Random Number Generator Driver ST's Platforms + * + * Author: Pankaj Dev: <pankaj.dev@st.com> + * Lee Jones <lee.jones@linaro.org> + * + * Copyright (C) 2015 STMicroelectronics (R&D) Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/hw_random.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +/* Registers */ +#define ST_RNG_STATUS_REG 0x20 +#define ST_RNG_DATA_REG 0x24 + +/* Registers fields */ +#define ST_RNG_STATUS_BAD_SEQUENCE BIT(0) +#define ST_RNG_STATUS_BAD_ALTERNANCE BIT(1) +#define ST_RNG_STATUS_FIFO_FULL BIT(5) + +#define ST_RNG_SAMPLE_SIZE 2 /* 2 Byte (16bit) samples */ +#define ST_RNG_FIFO_DEPTH 4 +#define ST_RNG_FIFO_SIZE (ST_RNG_FIFO_DEPTH * ST_RNG_SAMPLE_SIZE) + +/* + * Samples are documented to be available every 0.667us, so in theory + * the 4 sample deep FIFO should take 2.668us to fill. However, during + * thorough testing, it became apparent that filling the FIFO actually + * takes closer to 12us. We then multiply by 2 in order to account for + * the lack of udelay()'s reliability, suggested by Russell King. + */ +#define ST_RNG_FILL_FIFO_TIMEOUT (12 * 2) + +struct st_rng_data { + void __iomem *base; + struct clk *clk; + struct hwrng ops; +}; + +static int st_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + struct st_rng_data *ddata = (struct st_rng_data *)rng->priv; + u32 status; + int i; + + if (max < sizeof(u16)) + return -EINVAL; + + /* Wait until FIFO is full - max 4uS*/ + for (i = 0; i < ST_RNG_FILL_FIFO_TIMEOUT; i++) { + status = readl_relaxed(ddata->base + ST_RNG_STATUS_REG); + if (status & ST_RNG_STATUS_FIFO_FULL) + break; + udelay(1); + } + + if (i == ST_RNG_FILL_FIFO_TIMEOUT) + return 0; + + for (i = 0; i < ST_RNG_FIFO_SIZE && i < max; i += 2) + *(u16 *)(data + i) = + readl_relaxed(ddata->base + ST_RNG_DATA_REG); + + return i; /* No of bytes read */ +} + +static int st_rng_probe(struct platform_device *pdev) +{ + struct st_rng_data *ddata; + struct resource *res; + struct clk *clk; + void __iomem *base; + int ret; + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + ret = clk_prepare_enable(clk); + if (ret) + return ret; + + ddata->ops.priv = (unsigned long)ddata; + ddata->ops.read = st_rng_read; + ddata->ops.name = pdev->name; + ddata->base = base; + ddata->clk = clk; + + dev_set_drvdata(&pdev->dev, ddata); + + ret = hwrng_register(&ddata->ops); + if (ret) { + dev_err(&pdev->dev, "Failed to register HW RNG\n"); + return ret; + } + + dev_info(&pdev->dev, "Successfully registered HW RNG\n"); + + return 0; +} + +static int st_rng_remove(struct platform_device *pdev) +{ + struct st_rng_data *ddata = dev_get_drvdata(&pdev->dev); + + hwrng_unregister(&ddata->ops); + + clk_disable_unprepare(ddata->clk); + + return 0; +} + +static const struct of_device_id st_rng_match[] = { + { .compatible = "st,rng" }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_rng_match); + +static struct platform_driver st_rng_driver = { + .driver = { + .name = "st-hwrandom", + .of_match_table = of_match_ptr(st_rng_match), + }, + .probe = st_rng_probe, + .remove = st_rng_remove +}; + +module_platform_driver(st_rng_driver); + +MODULE_AUTHOR("Pankaj Dev <pankaj.dev@st.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/hw_random/stm32-rng.c b/drivers/char/hw_random/stm32-rng.c new file mode 100644 index 000000000000..92a810648bd0 --- /dev/null +++ b/drivers/char/hw_random/stm32-rng.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2015, Daniel Thompson + * + * This file 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; either version 2 + * of the License, or (at your option) any later version. + * + * This file 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. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/hw_random.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> + +#define RNG_CR 0x00 +#define RNG_CR_RNGEN BIT(2) + +#define RNG_SR 0x04 +#define RNG_SR_SEIS BIT(6) +#define RNG_SR_CEIS BIT(5) +#define RNG_SR_DRDY BIT(0) + +#define RNG_DR 0x08 + +/* + * It takes 40 cycles @ 48MHz to generate each random number (e.g. <1us). + * At the time of writing STM32 parts max out at ~200MHz meaning a timeout + * of 500 leaves us a very comfortable margin for error. The loop to which + * the timeout applies takes at least 4 instructions per iteration so the + * timeout is enough to take us up to multi-GHz parts! + */ +#define RNG_TIMEOUT 500 + +struct stm32_rng_private { + struct hwrng rng; + void __iomem *base; + struct clk *clk; +}; + +static int stm32_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + struct stm32_rng_private *priv = + container_of(rng, struct stm32_rng_private, rng); + u32 sr; + int retval = 0; + + pm_runtime_get_sync((struct device *) priv->rng.priv); + + while (max > sizeof(u32)) { + sr = readl_relaxed(priv->base + RNG_SR); + if (!sr && wait) { + unsigned int timeout = RNG_TIMEOUT; + + do { + cpu_relax(); + sr = readl_relaxed(priv->base + RNG_SR); + } while (!sr && --timeout); + } + + /* If error detected or data not ready... */ + if (sr != RNG_SR_DRDY) + break; + + *(u32 *)data = readl_relaxed(priv->base + RNG_DR); + + retval += sizeof(u32); + data += sizeof(u32); + max -= sizeof(u32); + } + + if (WARN_ONCE(sr & (RNG_SR_SEIS | RNG_SR_CEIS), + "bad RNG status - %x\n", sr)) + writel_relaxed(0, priv->base + RNG_SR); + + pm_runtime_mark_last_busy((struct device *) priv->rng.priv); + pm_runtime_put_sync_autosuspend((struct device *) priv->rng.priv); + + return retval || !wait ? retval : -EIO; +} + +static int stm32_rng_init(struct hwrng *rng) +{ + struct stm32_rng_private *priv = + container_of(rng, struct stm32_rng_private, rng); + int err; + + err = clk_prepare_enable(priv->clk); + if (err) + return err; + + writel_relaxed(RNG_CR_RNGEN, priv->base + RNG_CR); + + /* clear error indicators */ + writel_relaxed(0, priv->base + RNG_SR); + + return 0; +} + +static void stm32_rng_cleanup(struct hwrng *rng) +{ + struct stm32_rng_private *priv = + container_of(rng, struct stm32_rng_private, rng); + + writel_relaxed(0, priv->base + RNG_CR); + clk_disable_unprepare(priv->clk); +} + +static int stm32_rng_probe(struct platform_device *ofdev) +{ + struct device *dev = &ofdev->dev; + struct device_node *np = ofdev->dev.of_node; + struct stm32_rng_private *priv; + struct resource res; + int err; + + priv = devm_kzalloc(dev, sizeof(struct stm32_rng_private), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + err = of_address_to_resource(np, 0, &res); + if (err) + return err; + + priv->base = devm_ioremap_resource(dev, &res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->clk = devm_clk_get(&ofdev->dev, NULL); + if (IS_ERR(priv->clk)) + return PTR_ERR(priv->clk); + + dev_set_drvdata(dev, priv); + + priv->rng.name = dev_driver_string(dev), +#ifndef CONFIG_PM + priv->rng.init = stm32_rng_init, + priv->rng.cleanup = stm32_rng_cleanup, +#endif + priv->rng.read = stm32_rng_read, + priv->rng.priv = (unsigned long) dev; + + pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + return devm_hwrng_register(dev, &priv->rng); +} + +#ifdef CONFIG_PM +static int stm32_rng_runtime_suspend(struct device *dev) +{ + struct stm32_rng_private *priv = dev_get_drvdata(dev); + + stm32_rng_cleanup(&priv->rng); + + return 0; +} + +static int stm32_rng_runtime_resume(struct device *dev) +{ + struct stm32_rng_private *priv = dev_get_drvdata(dev); + + return stm32_rng_init(&priv->rng); +} +#endif + +static UNIVERSAL_DEV_PM_OPS(stm32_rng_pm_ops, stm32_rng_runtime_suspend, + stm32_rng_runtime_resume, NULL); + +static const struct of_device_id stm32_rng_match[] = { + { + .compatible = "st,stm32-rng", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, stm32_rng_match); + +static struct platform_driver stm32_rng_driver = { + .driver = { + .name = "stm32-rng", + .pm = &stm32_rng_pm_ops, + .of_match_table = stm32_rng_match, + }, + .probe = stm32_rng_probe, +}; + +module_platform_driver(stm32_rng_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Daniel Thompson <daniel.thompson@linaro.org>"); +MODULE_DESCRIPTION("STMicroelectronics STM32 RNG device driver"); diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 877205d22046..90e624662257 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -52,6 +52,7 @@ #include <linux/kthread.h> #include <linux/acpi.h> #include <linux/ctype.h> +#include <linux/time64.h> #define PFX "ipmi_ssif: " #define DEVICE_NAME "ipmi_ssif" @@ -1041,12 +1042,12 @@ static void sender(void *send_info, start_next_msg(ssif_info, flags); if (ssif_info->ssif_debug & SSIF_DEBUG_TIMING) { - struct timeval t; + struct timespec64 t; - do_gettimeofday(&t); - pr_info("**Enqueue %02x %02x: %ld.%6.6ld\n", + ktime_get_real_ts64(&t); + pr_info("**Enqueue %02x %02x: %lld.%6.6ld\n", msg->data[0], msg->data[1], - (long) t.tv_sec, (long) t.tv_usec); + (long long) t.tv_sec, (long) t.tv_nsec / NSEC_PER_USEC); } } diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 7680d5213ff8..45df4bf914f8 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -2507,15 +2507,6 @@ static int mgslpc_open(struct tty_struct *tty, struct file * filp) printk("%s(%d):mgslpc_open(%s), old ref count = %d\n", __FILE__, __LINE__, tty->driver->name, port->count); - /* If port is closing, signal caller to try again */ - if (port->flags & ASYNC_CLOSING){ - wait_event_interruptible_tty(tty, port->close_wait, - !(port->flags & ASYNC_CLOSING)); - retval = ((port->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - goto cleanup; - } - port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0; spin_lock_irqsave(&info->netlock, flags); diff --git a/drivers/char/snsc.c b/drivers/char/snsc.c index 8a80ead8d316..94006f9c2e43 100644 --- a/drivers/char/snsc.c +++ b/drivers/char/snsc.c @@ -19,7 +19,7 @@ #include <linux/sched.h> #include <linux/device.h> #include <linux/poll.h> -#include <linux/module.h> +#include <linux/init.h> #include <linux/slab.h> #include <linux/mutex.h> #include <asm/sn/io.h> @@ -461,5 +461,4 @@ scdrv_init(void) } return 0; } - -module_init(scdrv_init); +device_initcall(scdrv_init); diff --git a/drivers/char/tpm/st33zp24/Kconfig b/drivers/char/tpm/st33zp24/Kconfig index 09cb727864f0..19c007461d1c 100644 --- a/drivers/char/tpm/st33zp24/Kconfig +++ b/drivers/char/tpm/st33zp24/Kconfig @@ -1,6 +1,6 @@ config TCG_TIS_ST33ZP24 tristate "STMicroelectronics TPM Interface Specification 1.2 Interface" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST ---help--- STMicroelectronics ST33ZP24 core driver. It implements the core TPM1.2 logic and hooks into the TPM kernel APIs. Physical layers will diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index ad1ee180e0c2..309d2767c6a1 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -258,7 +258,6 @@ static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend, static struct i2c_driver st33zp24_i2c_driver = { .driver = { - .owner = THIS_MODULE, .name = TPM_ST33_I2C, .pm = &st33zp24_i2c_ops, .of_match_table = of_match_ptr(of_st33zp24_i2c_match), diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index f0184a1b0c1c..f974c945c97a 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -381,7 +381,6 @@ static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend, static struct spi_driver tpm_st33_spi_driver = { .driver = { - .owner = THIS_MODULE, .name = TPM_ST33_SPI, .pm = &st33zp24_spi_ops, .of_match_table = of_match_ptr(of_st33zp24_spi_match), diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 1082d4bb016a..f26b0ae23bea 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -119,6 +119,9 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, chip->dev.class = tpm_class; chip->dev.release = tpm_dev_release; chip->dev.parent = chip->pdev; +#ifdef CONFIG_ACPI + chip->dev.groups = chip->groups; +#endif if (chip->dev_num == 0) chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR); @@ -182,12 +185,6 @@ static int tpm1_chip_register(struct tpm_chip *chip) if (rc) return rc; - rc = tpm_add_ppi(chip); - if (rc) { - tpm_sysfs_del_device(chip); - return rc; - } - chip->bios_dir = tpm_bios_log_setup(chip->devname); return 0; @@ -201,8 +198,6 @@ static void tpm1_chip_unregister(struct tpm_chip *chip) if (chip->bios_dir) tpm_bios_log_teardown(chip->bios_dir); - tpm_remove_ppi(chip); - tpm_sysfs_del_device(chip); } @@ -225,10 +220,20 @@ int tpm_chip_register(struct tpm_chip *chip) if (rc) return rc; + tpm_add_ppi(chip); + rc = tpm_dev_add_device(chip); if (rc) goto out_err; + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { + rc = __compat_only_sysfs_link_entry_to_kobj(&chip->pdev->kobj, + &chip->dev.kobj, + "ppi"); + if (rc) + goto out_err; + } + /* Make the chip available. */ spin_lock(&driver_lock); list_add_rcu(&chip->list, &tpm_chip_list); @@ -263,6 +268,9 @@ void tpm_chip_unregister(struct tpm_chip *chip) spin_unlock(&driver_lock); synchronize_rcu(); + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) + sysfs_remove_link(&chip->pdev->kobj, "ppi"); + tpm1_chip_unregister(chip); tpm_dev_del_device(chip); } diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index e85d3416d899..c50637db3a8a 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -666,6 +666,30 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) } /** + * tpm_is_tpm2 - is the chip a TPM2 chip? + * @chip_num: tpm idx # or ANY + * + * Returns < 0 on error, and 1 or 0 on success depending whether the chip + * is a TPM2 chip. + */ +int tpm_is_tpm2(u32 chip_num) +{ + struct tpm_chip *chip; + int rc; + + chip = tpm_chip_find_get(chip_num); + if (chip == NULL) + return -ENODEV; + + rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0; + + tpm_chip_put(chip); + + return rc; +} +EXPORT_SYMBOL_GPL(tpm_is_tpm2); + +/** * tpm_pcr_read - read a pcr value * @chip_num: tpm idx # or ANY * @pcr_idx: pcr idx to retrieve @@ -1021,6 +1045,58 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) } EXPORT_SYMBOL_GPL(tpm_get_random); +/** + * tpm_seal_trusted() - seal a trusted key + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @options: authentication values and other options + * @payload: the key data in clear and encrypted form + * + * Returns < 0 on error and 0 on success. At the moment, only TPM 2.0 chips + * are supported. + */ +int tpm_seal_trusted(u32 chip_num, struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + struct tpm_chip *chip; + int rc; + + chip = tpm_chip_find_get(chip_num); + if (chip == NULL || !(chip->flags & TPM_CHIP_FLAG_TPM2)) + return -ENODEV; + + rc = tpm2_seal_trusted(chip, payload, options); + + tpm_chip_put(chip); + return rc; +} +EXPORT_SYMBOL_GPL(tpm_seal_trusted); + +/** + * tpm_unseal_trusted() - unseal a trusted key + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @options: authentication values and other options + * @payload: the key data in clear and encrypted form + * + * Returns < 0 on error and 0 on success. At the moment, only TPM 2.0 chips + * are supported. + */ +int tpm_unseal_trusted(u32 chip_num, struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + struct tpm_chip *chip; + int rc; + + chip = tpm_chip_find_get(chip_num); + if (chip == NULL || !(chip->flags & TPM_CHIP_FLAG_TPM2)) + return -ENODEV; + + rc = tpm2_unseal_trusted(chip, payload, options); + + tpm_chip_put(chip); + return rc; +} +EXPORT_SYMBOL_GPL(tpm_unseal_trusted); + static int __init tpm_init(void) { int rc; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index f8319a0860fd..a4257a32964f 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2004 IBM Corporation + * Copyright (C) 2015 Intel Corporation * * Authors: * Leendert van Doorn <leendert@watson.ibm.com> @@ -28,6 +29,7 @@ #include <linux/tpm.h> #include <linux/acpi.h> #include <linux/cdev.h> +#include <linux/highmem.h> enum tpm_const { TPM_MINOR = 224, /* officially assigned */ @@ -88,6 +90,9 @@ enum tpm2_return_codes { enum tpm2_algorithms { TPM2_ALG_SHA1 = 0x0004, + TPM2_ALG_KEYEDHASH = 0x0008, + TPM2_ALG_SHA256 = 0x000B, + TPM2_ALG_NULL = 0x0010 }; enum tpm2_command_codes { @@ -95,6 +100,10 @@ enum tpm2_command_codes { TPM2_CC_SELF_TEST = 0x0143, TPM2_CC_STARTUP = 0x0144, TPM2_CC_SHUTDOWN = 0x0145, + TPM2_CC_CREATE = 0x0153, + TPM2_CC_LOAD = 0x0157, + TPM2_CC_UNSEAL = 0x015E, + TPM2_CC_FLUSH_CONTEXT = 0x0165, TPM2_CC_GET_CAPABILITY = 0x017A, TPM2_CC_GET_RANDOM = 0x017B, TPM2_CC_PCR_READ = 0x017E, @@ -115,6 +124,13 @@ enum tpm2_startup_types { TPM2_SU_STATE = 0x0001, }; +enum tpm2_start_method { + TPM2_START_ACPI = 2, + TPM2_START_FIFO = 6, + TPM2_START_CRB = 7, + TPM2_START_CRB_WITH_ACPI = 8, +}; + struct tpm_chip; struct tpm_vendor_specific { @@ -151,8 +167,7 @@ struct tpm_vendor_specific { enum tpm_chip_flags { TPM_CHIP_FLAG_REGISTERED = BIT(0), - TPM_CHIP_FLAG_PPI = BIT(1), - TPM_CHIP_FLAG_TPM2 = BIT(2), + TPM_CHIP_FLAG_TPM2 = BIT(1), }; struct tpm_chip { @@ -175,6 +190,8 @@ struct tpm_chip { struct dentry **bios_dir; #ifdef CONFIG_ACPI + const struct attribute_group *groups[2]; + unsigned int groups_cnt; acpi_handle acpi_dev_handle; char ppi_version[TPM_PPI_VERSION_LEN + 1]; #endif /* CONFIG_ACPI */ @@ -182,7 +199,7 @@ struct tpm_chip { struct list_head list; }; -#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor) +#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) static inline void tpm_chip_put(struct tpm_chip *chip) { @@ -382,6 +399,101 @@ struct tpm_cmd_t { tpm_cmd_params params; } __packed; +/* A string buffer type for constructing TPM commands. This is based on the + * ideas of string buffer code in security/keys/trusted.h but is heap based + * in order to keep the stack usage minimal. + */ + +enum tpm_buf_flags { + TPM_BUF_OVERFLOW = BIT(0), +}; + +struct tpm_buf { + struct page *data_page; + unsigned int flags; + u8 *data; +}; + +static inline int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal) +{ + struct tpm_input_header *head; + + buf->data_page = alloc_page(GFP_HIGHUSER); + if (!buf->data_page) + return -ENOMEM; + + buf->flags = 0; + buf->data = kmap(buf->data_page); + + head = (struct tpm_input_header *) buf->data; + + head->tag = cpu_to_be16(tag); + head->length = cpu_to_be32(sizeof(*head)); + head->ordinal = cpu_to_be32(ordinal); + + return 0; +} + +static inline void tpm_buf_destroy(struct tpm_buf *buf) +{ + kunmap(buf->data_page); + __free_page(buf->data_page); +} + +static inline u32 tpm_buf_length(struct tpm_buf *buf) +{ + struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + + return be32_to_cpu(head->length); +} + +static inline u16 tpm_buf_tag(struct tpm_buf *buf) +{ + struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + + return be16_to_cpu(head->tag); +} + +static inline void tpm_buf_append(struct tpm_buf *buf, + const unsigned char *new_data, + unsigned int new_len) +{ + struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + u32 len = tpm_buf_length(buf); + + /* Return silently if overflow has already happened. */ + if (buf->flags & TPM_BUF_OVERFLOW) + return; + + if ((len + new_len) > PAGE_SIZE) { + WARN(1, "tpm_buf: overflow\n"); + buf->flags |= TPM_BUF_OVERFLOW; + return; + } + + memcpy(&buf->data[len], new_data, new_len); + head->length = cpu_to_be32(len + new_len); +} + +static inline void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value) +{ + tpm_buf_append(buf, &value, 1); +} + +static inline void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value) +{ + __be16 value2 = cpu_to_be16(value); + + tpm_buf_append(buf, (u8 *) &value2, 2); +} + +static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value) +{ + __be32 value2 = cpu_to_be32(value); + + tpm_buf_append(buf, (u8 *) &value2, 4); +} + extern struct class *tpm_class; extern dev_t tpm_devt; extern const struct file_operations tpm_fops; @@ -412,15 +524,9 @@ void tpm_sysfs_del_device(struct tpm_chip *chip); int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf); #ifdef CONFIG_ACPI -extern int tpm_add_ppi(struct tpm_chip *chip); -extern void tpm_remove_ppi(struct tpm_chip *chip); +extern void tpm_add_ppi(struct tpm_chip *chip); #else -static inline int tpm_add_ppi(struct tpm_chip *chip) -{ - return 0; -} - -static inline void tpm_remove_ppi(struct tpm_chip *chip) +static inline void tpm_add_ppi(struct tpm_chip *chip) { } #endif @@ -428,6 +534,12 @@ static inline void tpm_remove_ppi(struct tpm_chip *chip) int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf); int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash); int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max); +int tpm2_seal_trusted(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options); +int tpm2_unseal_trusted(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options); ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, const char *desc); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 011909a9be96..bd7039fafa8a 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Intel Corporation + * Copyright (C) 2014, 2015 Intel Corporation * * Authors: * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> @@ -16,6 +16,11 @@ */ #include "tpm.h" +#include <keys/trusted-type.h> + +enum tpm2_object_attributes { + TPM2_ATTR_USER_WITH_AUTH = BIT(6), +}; struct tpm2_startup_in { __be16 startup_type; @@ -381,6 +386,249 @@ static const struct tpm_input_header tpm2_get_tpm_pt_header = { }; /** + * Append TPMS_AUTH_COMMAND to the buffer. The buffer must be allocated with + * tpm_buf_alloc(). + * + * @param buf: an allocated tpm_buf instance + * @param nonce: the session nonce, may be NULL if not used + * @param nonce_len: the session nonce length, may be 0 if not used + * @param attributes: the session attributes + * @param hmac: the session HMAC or password, may be NULL if not used + * @param hmac_len: the session HMAC or password length, maybe 0 if not used + */ +static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle, + const u8 *nonce, u16 nonce_len, + u8 attributes, + const u8 *hmac, u16 hmac_len) +{ + tpm_buf_append_u32(buf, 9 + nonce_len + hmac_len); + tpm_buf_append_u32(buf, session_handle); + tpm_buf_append_u16(buf, nonce_len); + + if (nonce && nonce_len) + tpm_buf_append(buf, nonce, nonce_len); + + tpm_buf_append_u8(buf, attributes); + tpm_buf_append_u16(buf, hmac_len); + + if (hmac && hmac_len) + tpm_buf_append(buf, hmac, hmac_len); +} + +/** + * tpm2_seal_trusted() - seal a trusted key + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @options: authentication values and other options + * @payload: the key data in clear and encrypted form + * + * Returns < 0 on error and 0 on success. + */ +int tpm2_seal_trusted(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + unsigned int blob_len; + struct tpm_buf buf; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); + if (rc) + return rc; + + tpm_buf_append_u32(&buf, options->keyhandle); + tpm2_buf_append_auth(&buf, TPM2_RS_PW, + NULL /* nonce */, 0, + 0 /* session_attributes */, + options->keyauth /* hmac */, + TPM_DIGEST_SIZE); + + /* sensitive */ + tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len); + + tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE); + tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE); + tpm_buf_append_u16(&buf, payload->key_len); + tpm_buf_append(&buf, payload->key, payload->key_len); + + /* public */ + tpm_buf_append_u16(&buf, 14); + + tpm_buf_append_u16(&buf, TPM2_ALG_KEYEDHASH); + tpm_buf_append_u16(&buf, TPM2_ALG_SHA256); + tpm_buf_append_u32(&buf, TPM2_ATTR_USER_WITH_AUTH); + tpm_buf_append_u16(&buf, 0); /* policy digest size */ + tpm_buf_append_u16(&buf, TPM2_ALG_NULL); + tpm_buf_append_u16(&buf, 0); + + /* outside info */ + tpm_buf_append_u16(&buf, 0); + + /* creation PCR */ + tpm_buf_append_u32(&buf, 0); + + if (buf.flags & TPM_BUF_OVERFLOW) { + rc = -E2BIG; + goto out; + } + + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "sealing data"); + if (rc) + goto out; + + blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]); + if (blob_len > MAX_BLOB_SIZE) { + rc = -E2BIG; + goto out; + } + + memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len); + payload->blob_len = blob_len; + +out: + tpm_buf_destroy(&buf); + + if (rc > 0) + rc = -EPERM; + + return rc; +} + +static int tpm2_load(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options, + u32 *blob_handle) +{ + struct tpm_buf buf; + unsigned int private_len; + unsigned int public_len; + unsigned int blob_len; + int rc; + + private_len = be16_to_cpup((__be16 *) &payload->blob[0]); + if (private_len > (payload->blob_len - 2)) + return -E2BIG; + + public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]); + blob_len = private_len + public_len + 4; + if (blob_len > payload->blob_len) + return -E2BIG; + + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); + if (rc) + return rc; + + tpm_buf_append_u32(&buf, options->keyhandle); + tpm2_buf_append_auth(&buf, TPM2_RS_PW, + NULL /* nonce */, 0, + 0 /* session_attributes */, + options->keyauth /* hmac */, + TPM_DIGEST_SIZE); + + tpm_buf_append(&buf, payload->blob, blob_len); + + if (buf.flags & TPM_BUF_OVERFLOW) { + rc = -E2BIG; + goto out; + } + + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "loading blob"); + if (!rc) + *blob_handle = be32_to_cpup( + (__be32 *) &buf.data[TPM_HEADER_SIZE]); + +out: + tpm_buf_destroy(&buf); + + if (rc > 0) + rc = -EPERM; + + return rc; +} + +static void tpm2_flush_context(struct tpm_chip *chip, u32 handle) +{ + struct tpm_buf buf; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT); + if (rc) { + dev_warn(chip->pdev, "0x%08x was not flushed, out of memory\n", + handle); + return; + } + + tpm_buf_append_u32(&buf, handle); + + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "flushing context"); + if (rc) + dev_warn(chip->pdev, "0x%08x was not flushed, rc=%d\n", handle, + rc); + + tpm_buf_destroy(&buf); +} + +static int tpm2_unseal(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options, + u32 blob_handle) +{ + struct tpm_buf buf; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); + if (rc) + return rc; + + tpm_buf_append_u32(&buf, blob_handle); + tpm2_buf_append_auth(&buf, TPM2_RS_PW, + NULL /* nonce */, 0, + 0 /* session_attributes */, + options->blobauth /* hmac */, + TPM_DIGEST_SIZE); + + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "unsealing"); + if (rc > 0) + rc = -EPERM; + + if (!rc) { + payload->key_len = be16_to_cpup( + (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]); + + memcpy(payload->key, &buf.data[TPM_HEADER_SIZE + 6], + payload->key_len); + } + + tpm_buf_destroy(&buf); + return rc; +} + +/** + * tpm_unseal_trusted() - unseal a trusted key + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @options: authentication values and other options + * @payload: the key data in clear and encrypted form + * + * Returns < 0 on error and 0 on success. + */ +int tpm2_unseal_trusted(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + u32 blob_handle; + int rc; + + rc = tpm2_load(chip, payload, options, &blob_handle); + if (rc) + return rc; + + rc = tpm2_unseal(chip, payload, options, blob_handle); + + tpm2_flush_context(chip, blob_handle); + + return rc; +} + +/** * tpm2_get_tpm_pt() - get value of a TPM_CAP_TPM_PROPERTIES type property * @chip: TPM chip to use. * @property_id: property ID. diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 1267322595da..4bb9727c1047 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -34,12 +34,6 @@ enum crb_defaults { CRB_ACPI_START_INDEX = 1, }; -enum crb_start_method { - CRB_SM_ACPI_START = 2, - CRB_SM_CRB = 7, - CRB_SM_CRB_WITH_ACPI_START = 8, -}; - struct acpi_tpm2 { struct acpi_table_header hdr; u16 platform_class; @@ -74,7 +68,8 @@ struct crb_control_area { u32 int_enable; u32 int_sts; u32 cmd_size; - u64 cmd_pa; + u32 cmd_pa_low; + u32 cmd_pa_high; u32 rsp_size; u64 rsp_pa; } __packed; @@ -220,12 +215,6 @@ static int crb_acpi_add(struct acpi_device *device) u64 pa; int rc; - chip = tpmm_chip_alloc(dev, &tpm_crb); - if (IS_ERR(chip)) - return PTR_ERR(chip); - - chip->flags = TPM_CHIP_FLAG_TPM2; - status = acpi_get_table(ACPI_SIG_TPM2, 1, (struct acpi_table_header **) &buf); if (ACPI_FAILURE(status)) { @@ -233,13 +222,15 @@ static int crb_acpi_add(struct acpi_device *device) return -ENODEV; } - /* At least some versions of AMI BIOS have a bug that TPM2 table has - * zero address for the control area and therefore we must fail. - */ - if (!buf->control_area_pa) { - dev_err(dev, "TPM2 ACPI table has a zero address for the control area\n"); - return -EINVAL; - } + /* Should the FIFO driver handle this? */ + if (buf->start_method == TPM2_START_FIFO) + return -ENODEV; + + chip = tpmm_chip_alloc(dev, &tpm_crb); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + chip->flags = TPM_CHIP_FLAG_TPM2; if (buf->hdr.length < sizeof(struct acpi_tpm2)) { dev_err(dev, "TPM2 ACPI table has wrong size"); @@ -259,11 +250,11 @@ static int crb_acpi_add(struct acpi_device *device) * report only ACPI start but in practice seems to require both * ACPI start and CRB start. */ - if (sm == CRB_SM_CRB || sm == CRB_SM_CRB_WITH_ACPI_START || + if (sm == TPM2_START_CRB || sm == TPM2_START_FIFO || !strcmp(acpi_device_hid(device), "MSFT0101")) priv->flags |= CRB_FL_CRB_START; - if (sm == CRB_SM_ACPI_START || sm == CRB_SM_CRB_WITH_ACPI_START) + if (sm == TPM2_START_ACPI || sm == TPM2_START_CRB_WITH_ACPI) priv->flags |= CRB_FL_ACPI_START; priv->cca = (struct crb_control_area __iomem *) @@ -273,8 +264,8 @@ static int crb_acpi_add(struct acpi_device *device) return -ENOMEM; } - memcpy_fromio(&pa, &priv->cca->cmd_pa, 8); - pa = le64_to_cpu(pa); + pa = ((u64) le32_to_cpu(ioread32(&priv->cca->cmd_pa_high)) << 32) | + (u64) le32_to_cpu(ioread32(&priv->cca->cmd_pa_low)); priv->cmd = devm_ioremap_nocache(dev, pa, ioread32(&priv->cca->cmd_size)); if (!priv->cmd) { diff --git a/drivers/char/tpm/tpm_eventlog.c b/drivers/char/tpm/tpm_eventlog.c index 3a56a131586c..bd72fb04225e 100644 --- a/drivers/char/tpm/tpm_eventlog.c +++ b/drivers/char/tpm/tpm_eventlog.c @@ -76,15 +76,25 @@ static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos) void *addr = log->bios_event_log; void *limit = log->bios_event_log_end; struct tcpa_event *event; + u32 converted_event_size; + u32 converted_event_type; + /* read over *pos measurements */ for (i = 0; i < *pos; i++) { event = addr; + converted_event_size = + do_endian_conversion(event->event_size); + converted_event_type = + do_endian_conversion(event->event_type); + if ((addr + sizeof(struct tcpa_event)) < limit) { - if (event->event_type == 0 && event->event_size == 0) + if ((converted_event_type == 0) && + (converted_event_size == 0)) return NULL; - addr += sizeof(struct tcpa_event) + event->event_size; + addr += (sizeof(struct tcpa_event) + + converted_event_size); } } @@ -94,8 +104,12 @@ static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos) event = addr; - if ((event->event_type == 0 && event->event_size == 0) || - ((addr + sizeof(struct tcpa_event) + event->event_size) >= limit)) + converted_event_size = do_endian_conversion(event->event_size); + converted_event_type = do_endian_conversion(event->event_type); + + if (((converted_event_type == 0) && (converted_event_size == 0)) + || ((addr + sizeof(struct tcpa_event) + converted_event_size) + >= limit)) return NULL; return addr; @@ -107,8 +121,12 @@ static void *tpm_bios_measurements_next(struct seq_file *m, void *v, struct tcpa_event *event = v; struct tpm_bios_log *log = m->private; void *limit = log->bios_event_log_end; + u32 converted_event_size; + u32 converted_event_type; - v += sizeof(struct tcpa_event) + event->event_size; + converted_event_size = do_endian_conversion(event->event_size); + + v += sizeof(struct tcpa_event) + converted_event_size; /* now check if current entry is valid */ if ((v + sizeof(struct tcpa_event)) >= limit) @@ -116,11 +134,11 @@ static void *tpm_bios_measurements_next(struct seq_file *m, void *v, event = v; - if (event->event_type == 0 && event->event_size == 0) - return NULL; + converted_event_size = do_endian_conversion(event->event_size); + converted_event_type = do_endian_conversion(event->event_type); - if ((event->event_type == 0 && event->event_size == 0) || - ((v + sizeof(struct tcpa_event) + event->event_size) >= limit)) + if (((converted_event_type == 0) && (converted_event_size == 0)) || + ((v + sizeof(struct tcpa_event) + converted_event_size) >= limit)) return NULL; (*pos)++; @@ -140,7 +158,7 @@ static int get_event_name(char *dest, struct tcpa_event *event, int i, n_len = 0, d_len = 0; struct tcpa_pc_event *pc_event; - switch(event->event_type) { + switch (do_endian_conversion(event->event_type)) { case PREBOOT: case POST_CODE: case UNUSED: @@ -156,14 +174,16 @@ static int get_event_name(char *dest, struct tcpa_event *event, case NONHOST_CODE: case NONHOST_CONFIG: case NONHOST_INFO: - name = tcpa_event_type_strings[event->event_type]; + name = tcpa_event_type_strings[do_endian_conversion + (event->event_type)]; n_len = strlen(name); break; case SEPARATOR: case ACTION: - if (MAX_TEXT_EVENT > event->event_size) { + if (MAX_TEXT_EVENT > + do_endian_conversion(event->event_size)) { name = event_entry; - n_len = event->event_size; + n_len = do_endian_conversion(event->event_size); } break; case EVENT_TAG: @@ -171,7 +191,7 @@ static int get_event_name(char *dest, struct tcpa_event *event, /* ToDo Row data -> Base64 */ - switch (pc_event->event_id) { + switch (do_endian_conversion(pc_event->event_id)) { case SMBIOS: case BIS_CERT: case CMOS: @@ -179,7 +199,8 @@ static int get_event_name(char *dest, struct tcpa_event *event, case OPTION_ROM_EXEC: case OPTION_ROM_CONFIG: case S_CRTM_VERSION: - name = tcpa_pc_event_id_strings[pc_event->event_id]; + name = tcpa_pc_event_id_strings[do_endian_conversion + (pc_event->event_id)]; n_len = strlen(name); break; /* hash data */ @@ -188,7 +209,8 @@ static int get_event_name(char *dest, struct tcpa_event *event, case OPTION_ROM_MICROCODE: case S_CRTM_CONTENTS: case POST_CONTENTS: - name = tcpa_pc_event_id_strings[pc_event->event_id]; + name = tcpa_pc_event_id_strings[do_endian_conversion + (pc_event->event_id)]; n_len = strlen(name); for (i = 0; i < 20; i++) d_len += sprintf(&data[2*i], "%02x", @@ -209,13 +231,24 @@ static int get_event_name(char *dest, struct tcpa_event *event, static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v) { struct tcpa_event *event = v; - char *data = v; + struct tcpa_event temp_event; + char *tempPtr; int i; - for (i = 0; i < sizeof(struct tcpa_event) + event->event_size; i++) - seq_putc(m, data[i]); + memcpy(&temp_event, event, sizeof(struct tcpa_event)); + + /* convert raw integers for endianness */ + temp_event.pcr_index = do_endian_conversion(event->pcr_index); + temp_event.event_type = do_endian_conversion(event->event_type); + temp_event.event_size = do_endian_conversion(event->event_size); + + tempPtr = (char *)&temp_event; + + for (i = 0; i < sizeof(struct tcpa_event) + temp_event.event_size; i++) + seq_putc(m, tempPtr[i]); return 0; + } static int tpm_bios_measurements_release(struct inode *inode, @@ -238,7 +271,7 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) char *eventname; struct tcpa_event *event = v; unsigned char *event_entry = - (unsigned char *) (v + sizeof(struct tcpa_event)); + (unsigned char *)(v + sizeof(struct tcpa_event)); eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL); if (!eventname) { @@ -247,13 +280,14 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) return -EFAULT; } - seq_printf(m, "%2d ", event->pcr_index); + /* 1st: PCR */ + seq_printf(m, "%2d ", do_endian_conversion(event->pcr_index)); /* 2nd: SHA1 */ seq_printf(m, "%20phN", event->pcr_value); /* 3rd: event type identifier */ - seq_printf(m, " %02x", event->event_type); + seq_printf(m, " %02x", do_endian_conversion(event->event_type)); len += get_event_name(eventname, event, event_entry); diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h index e7da086d6928..267bfbd1b7bb 100644 --- a/drivers/char/tpm/tpm_eventlog.h +++ b/drivers/char/tpm/tpm_eventlog.h @@ -6,6 +6,12 @@ #define MAX_TEXT_EVENT 1000 /* Max event string length */ #define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ +#ifdef CONFIG_PPC64 +#define do_endian_conversion(x) be32_to_cpu(x) +#else +#define do_endian_conversion(x) x +#endif + enum bios_platform_class { BIOS_CLIENT = 0x00, BIOS_SERVER = 0x01, diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index 7a0ca78ad3c6..8dfb88b9739c 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -217,7 +217,6 @@ static struct i2c_driver i2c_atmel_driver = { .remove = i2c_atmel_remove, .driver = { .name = I2C_DRIVER_NAME, - .owner = THIS_MODULE, .pm = &i2c_atmel_pm_ops, .of_match_table = of_match_ptr(i2c_atmel_of_match), }, diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 33c5f360ab01..63d5d22e9e60 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -711,7 +711,6 @@ static struct i2c_driver tpm_tis_i2c_driver = { .remove = tpm_tis_i2c_remove, .driver = { .name = "tpm_i2c_infineon", - .owner = THIS_MODULE, .pm = &tpm_tis_i2c_ops, .of_match_table = of_match_ptr(tpm_tis_i2c_of_match), }, diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 9d42b7d78e50..847f1597fe9b 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -641,7 +641,6 @@ static struct i2c_driver i2c_nuvoton_driver = { .remove = i2c_nuvoton_remove, .driver = { .name = I2C_DRIVER_NAME, - .owner = THIS_MODULE, .pm = &i2c_nuvoton_pm_ops, .of_match_table = of_match_ptr(i2c_nuvoton_of_match), }, diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 27ebf9511cb4..3e6a22658b63 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -491,7 +491,7 @@ static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq, } ibmvtpm->rtce_size = be16_to_cpu(crq->len); ibmvtpm->rtce_buf = kmalloc(ibmvtpm->rtce_size, - GFP_KERNEL); + GFP_ATOMIC); if (!ibmvtpm->rtce_buf) { dev_err(ibmvtpm->dev, "Failed to allocate memory for rtce buffer\n"); return; diff --git a/drivers/char/tpm/tpm_of.c b/drivers/char/tpm/tpm_of.c index eebe6256918f..1141456a4b1f 100644 --- a/drivers/char/tpm/tpm_of.c +++ b/drivers/char/tpm/tpm_of.c @@ -24,14 +24,14 @@ int read_log(struct tpm_bios_log *log) { struct device_node *np; const u32 *sizep; - const __be64 *basep; + const u64 *basep; if (log->bios_event_log != NULL) { pr_err("%s: ERROR - Eventlog already initialized\n", __func__); return -EFAULT; } - np = of_find_node_by_name(NULL, "ibm,vtpm"); + np = of_find_node_by_name(NULL, "vtpm"); if (!np) { pr_err("%s: ERROR - IBMVTPM not supported\n", __func__); return -ENODEV; @@ -63,7 +63,7 @@ int read_log(struct tpm_bios_log *log) log->bios_event_log_end = log->bios_event_log + *sizep; - memcpy(log->bios_event_log, __va(be64_to_cpup(basep)), *sizep); + memcpy(log->bios_event_log, __va(*basep), *sizep); return 0; diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index 6ca9b5d78144..692a2c6ae036 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -53,7 +53,7 @@ tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type, static ssize_t tpm_show_ppi_version(struct device *dev, struct device_attribute *attr, char *buf) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); return scnprintf(buf, PAGE_SIZE, "%s\n", chip->ppi_version); } @@ -63,7 +63,7 @@ static ssize_t tpm_show_ppi_request(struct device *dev, { ssize_t size = -EINVAL; union acpi_object *obj; - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ, ACPI_TYPE_PACKAGE, NULL); @@ -100,7 +100,7 @@ static ssize_t tpm_store_ppi_request(struct device *dev, int func = TPM_PPI_FN_SUBREQ; union acpi_object *obj, tmp; union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp); - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); /* * the function to submit TPM operation request to pre-os environment @@ -156,7 +156,7 @@ static ssize_t tpm_show_ppi_transition_action(struct device *dev, .buffer.length = 0, .buffer.pointer = NULL }; - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); static char *info[] = { "None", @@ -197,7 +197,7 @@ static ssize_t tpm_show_ppi_response(struct device *dev, acpi_status status = -EINVAL; union acpi_object *obj, *ret_obj; u64 req, res; - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETRSP, ACPI_TYPE_PACKAGE, NULL); @@ -296,7 +296,7 @@ static ssize_t tpm_show_ppi_tcg_operations(struct device *dev, struct device_attribute *attr, char *buf) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); return show_ppi_operations(chip->acpi_dev_handle, buf, 0, PPI_TPM_REQ_MAX); @@ -306,7 +306,7 @@ static ssize_t tpm_show_ppi_vs_operations(struct device *dev, struct device_attribute *attr, char *buf) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); return show_ppi_operations(chip->acpi_dev_handle, buf, PPI_VS_REQ_START, PPI_VS_REQ_END); @@ -334,17 +334,16 @@ static struct attribute_group ppi_attr_grp = { .attrs = ppi_attrs }; -int tpm_add_ppi(struct tpm_chip *chip) +void tpm_add_ppi(struct tpm_chip *chip) { union acpi_object *obj; - int rc; if (!chip->acpi_dev_handle) - return 0; + return; if (!acpi_check_dsm(chip->acpi_dev_handle, tpm_ppi_uuid, TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_VERSION)) - return 0; + return; /* Cache PPI version string. */ obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, tpm_ppi_uuid, @@ -356,16 +355,5 @@ int tpm_add_ppi(struct tpm_chip *chip) ACPI_FREE(obj); } - rc = sysfs_create_group(&chip->pdev->kobj, &ppi_attr_grp); - - if (!rc) - chip->flags |= TPM_CHIP_FLAG_PPI; - - return rc; -} - -void tpm_remove_ppi(struct tpm_chip *chip) -{ - if (chip->flags & TPM_CHIP_FLAG_PPI) - sysfs_remove_group(&chip->pdev->kobj, &ppi_attr_grp); + chip->groups[chip->groups_cnt++] = &ppi_attr_grp; } diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index f2dffa770b8e..696ef1d56b4f 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2005, 2006 IBM Corporation - * Copyright (C) 2014 Intel Corporation + * Copyright (C) 2014, 2015 Intel Corporation * * Authors: * Leendert van Doorn <leendert@watson.ibm.com> @@ -28,6 +28,7 @@ #include <linux/wait.h> #include <linux/acpi.h> #include <linux/freezer.h> +#include <acpi/actbl2.h> #include "tpm.h" enum tis_access { @@ -65,6 +66,17 @@ enum tis_defaults { TIS_LONG_TIMEOUT = 2000, /* 2 sec */ }; +struct tpm_info { + unsigned long start; + unsigned long len; + unsigned int irq; +}; + +static struct tpm_info tis_default_info = { + .start = TIS_MEM_BASE, + .len = TIS_MEM_LEN, + .irq = 0, +}; /* Some timeout values are needed before it is known whether the chip is * TPM 1.0 or TPM 2.0. @@ -91,26 +103,54 @@ struct priv_data { }; #if defined(CONFIG_PNP) && defined(CONFIG_ACPI) -static int is_itpm(struct pnp_dev *dev) +static int has_hid(struct acpi_device *dev, const char *hid) { - struct acpi_device *acpi = pnp_acpi_device(dev); struct acpi_hardware_id *id; - if (!acpi) - return 0; - - list_for_each_entry(id, &acpi->pnp.ids, list) { - if (!strcmp("INTC0102", id->id)) + list_for_each_entry(id, &dev->pnp.ids, list) + if (!strcmp(hid, id->id)) return 1; - } return 0; } + +static inline int is_itpm(struct acpi_device *dev) +{ + return has_hid(dev, "INTC0102"); +} + +static inline int is_fifo(struct acpi_device *dev) +{ + struct acpi_table_tpm2 *tbl; + acpi_status st; + + /* TPM 1.2 FIFO */ + if (!has_hid(dev, "MSFT0101")) + return 1; + + st = acpi_get_table(ACPI_SIG_TPM2, 1, + (struct acpi_table_header **) &tbl); + if (ACPI_FAILURE(st)) { + dev_err(&dev->dev, "failed to get TPM2 ACPI table\n"); + return 0; + } + + if (le32_to_cpu(tbl->start_method) != TPM2_START_FIFO) + return 0; + + /* TPM 2.0 FIFO */ + return 1; +} #else -static inline int is_itpm(struct pnp_dev *dev) +static inline int is_itpm(struct acpi_device *dev) { return 0; } + +static inline int is_fifo(struct acpi_device *dev) +{ + return 1; +} #endif /* Before we attempt to access the TPM we must see that the valid bit is set. @@ -600,9 +640,8 @@ static void tpm_tis_remove(struct tpm_chip *chip) release_locality(chip, chip->vendor.locality, 1); } -static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, - resource_size_t start, resource_size_t len, - unsigned int irq) +static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, + acpi_handle acpi_dev_handle) { u32 vendor, intfcaps, intmask; int rc, i, irq_s, irq_e, probe; @@ -622,7 +661,7 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, chip->acpi_dev_handle = acpi_dev_handle; #endif - chip->vendor.iobase = devm_ioremap(dev, start, len); + chip->vendor.iobase = devm_ioremap(dev, tpm_info->start, tpm_info->len); if (!chip->vendor.iobase) return -EIO; @@ -707,7 +746,7 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); if (interrupts) - chip->vendor.irq = irq; + chip->vendor.irq = tpm_info->irq; if (interrupts && !chip->vendor.irq) { irq_s = ioread8(chip->vendor.iobase + @@ -890,27 +929,27 @@ static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume); static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev, const struct pnp_device_id *pnp_id) { - resource_size_t start, len; - unsigned int irq = 0; + struct tpm_info tpm_info = tis_default_info; acpi_handle acpi_dev_handle = NULL; - start = pnp_mem_start(pnp_dev, 0); - len = pnp_mem_len(pnp_dev, 0); + tpm_info.start = pnp_mem_start(pnp_dev, 0); + tpm_info.len = pnp_mem_len(pnp_dev, 0); if (pnp_irq_valid(pnp_dev, 0)) - irq = pnp_irq(pnp_dev, 0); + tpm_info.irq = pnp_irq(pnp_dev, 0); else interrupts = false; - if (is_itpm(pnp_dev)) - itpm = true; - #ifdef CONFIG_ACPI - if (pnp_acpi_device(pnp_dev)) + if (pnp_acpi_device(pnp_dev)) { + if (is_itpm(pnp_acpi_device(pnp_dev))) + itpm = true; + acpi_dev_handle = pnp_acpi_device(pnp_dev)->handle; + } #endif - return tpm_tis_init(&pnp_dev->dev, acpi_dev_handle, start, len, irq); + return tpm_tis_init(&pnp_dev->dev, &tpm_info, acpi_dev_handle); } static struct pnp_device_id tpm_pnp_tbl[] = { @@ -930,6 +969,7 @@ MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl); static void tpm_tis_pnp_remove(struct pnp_dev *dev) { struct tpm_chip *chip = pnp_get_drvdata(dev); + tpm_chip_unregister(chip); tpm_tis_remove(chip); } @@ -950,6 +990,79 @@ module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id, MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe"); #endif +#ifdef CONFIG_ACPI +static int tpm_check_resource(struct acpi_resource *ares, void *data) +{ + struct tpm_info *tpm_info = (struct tpm_info *) data; + struct resource res; + + if (acpi_dev_resource_interrupt(ares, 0, &res)) { + tpm_info->irq = res.start; + } else if (acpi_dev_resource_memory(ares, &res)) { + tpm_info->start = res.start; + tpm_info->len = resource_size(&res); + } + + return 1; +} + +static int tpm_tis_acpi_init(struct acpi_device *acpi_dev) +{ + struct list_head resources; + struct tpm_info tpm_info = tis_default_info; + int ret; + + if (!is_fifo(acpi_dev)) + return -ENODEV; + + INIT_LIST_HEAD(&resources); + ret = acpi_dev_get_resources(acpi_dev, &resources, tpm_check_resource, + &tpm_info); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&resources); + + if (!tpm_info.irq) + interrupts = false; + + if (is_itpm(acpi_dev)) + itpm = true; + + return tpm_tis_init(&acpi_dev->dev, &tpm_info, acpi_dev->handle); +} + +static int tpm_tis_acpi_remove(struct acpi_device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(&dev->dev); + + tpm_chip_unregister(chip); + tpm_tis_remove(chip); + + return 0; +} + +static struct acpi_device_id tpm_acpi_tbl[] = { + {"MSFT0101", 0}, /* TPM 2.0 */ + /* Add new here */ + {"", 0}, /* User Specified */ + {"", 0} /* Terminator */ +}; +MODULE_DEVICE_TABLE(acpi, tpm_acpi_tbl); + +static struct acpi_driver tis_acpi_driver = { + .name = "tpm_tis", + .ids = tpm_acpi_tbl, + .ops = { + .add = tpm_tis_acpi_init, + .remove = tpm_tis_acpi_remove, + }, + .drv = { + .pm = &tpm_tis_pm, + }, +}; +#endif + static struct platform_driver tis_drv = { .driver = { .name = "tpm_tis", @@ -966,9 +1079,25 @@ static int __init init_tis(void) { int rc; #ifdef CONFIG_PNP - if (!force) - return pnp_register_driver(&tis_pnp_driver); + if (!force) { + rc = pnp_register_driver(&tis_pnp_driver); + if (rc) + return rc; + } +#endif +#ifdef CONFIG_ACPI + if (!force) { + rc = acpi_bus_register_driver(&tis_acpi_driver); + if (rc) { +#ifdef CONFIG_PNP + pnp_unregister_driver(&tis_pnp_driver); #endif + return rc; + } + } +#endif + if (!force) + return 0; rc = platform_driver_register(&tis_drv); if (rc < 0) @@ -978,7 +1107,7 @@ static int __init init_tis(void) rc = PTR_ERR(pdev); goto err_dev; } - rc = tpm_tis_init(&pdev->dev, NULL, TIS_MEM_BASE, TIS_MEM_LEN, 0); + rc = tpm_tis_init(&pdev->dev, &tis_default_info, NULL); if (rc) goto err_init; return 0; @@ -992,9 +1121,14 @@ err_dev: static void __exit cleanup_tis(void) { struct tpm_chip *chip; -#ifdef CONFIG_PNP +#if defined(CONFIG_PNP) || defined(CONFIG_ACPI) if (!force) { +#ifdef CONFIG_ACPI + acpi_bus_unregister_driver(&tis_acpi_driver); +#endif +#ifdef CONFIG_PNP pnp_unregister_driver(&tis_pnp_driver); +#endif return; } #endif |