diff options
Diffstat (limited to 'drivers/char')
40 files changed, 2222 insertions, 835 deletions
diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index 0ef350010766..c99cd19d9147 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -14,7 +14,7 @@ #include <linux/agp_backend.h> #include <linux/mmzone.h> #include <asm/page.h> /* PAGE_SIZE */ -#include <asm/e820.h> +#include <asm/e820/api.h> #include <asm/amd_nb.h> #include <asm/gart.h> #include "agp.h" diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 9702c78f458d..7fcc2a9d1d5a 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -332,14 +332,6 @@ static void i810_write_entry(dma_addr_t addr, unsigned int entry, writel_relaxed(addr | pte_flags, intel_private.gtt + entry); } -static const struct aper_size_info_fixed intel_fake_agp_sizes[] = { - {32, 8192, 3}, - {64, 16384, 4}, - {128, 32768, 5}, - {256, 65536, 6}, - {512, 131072, 7}, -}; - static unsigned int intel_gtt_stolen_size(void) { u16 gmch_ctrl; @@ -670,6 +662,14 @@ static int intel_gtt_init(void) } #if IS_ENABLED(CONFIG_AGP_INTEL) +static const struct aper_size_info_fixed intel_fake_agp_sizes[] = { + {32, 8192, 3}, + {64, 16384, 4}, + {128, 32768, 5}, + {256, 65536, 6}, + {512, 131072, 7}, +}; + static int intel_fake_agp_fetch_size(void) { int num_sizes = ARRAY_SIZE(intel_fake_agp_sizes); diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 0cafe08919c9..1b223c32a8ae 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -294,20 +294,6 @@ config HW_RANDOM_POWERNV If unsure, say Y. -config HW_RANDOM_EXYNOS - tristate "EXYNOS HW random number generator support" - depends on ARCH_EXYNOS || COMPILE_TEST - depends on HAS_IOMEM - default HW_RANDOM - ---help--- - This driver provides kernel-side support for the Random Number - Generator hardware found on EXYNOS SOCs. - - To compile this driver as a module, choose M here: the - module will be called exynos-rng. - - If unsure, say Y. - config HW_RANDOM_TPM tristate "TPM HW Random Number Generator support" depends on TCG_TPM @@ -423,6 +409,34 @@ config HW_RANDOM_CAVIUM If unsure, say Y. +config HW_RANDOM_MTK + tristate "Mediatek Random Number Generator support" + depends on HW_RANDOM + depends on ARCH_MEDIATEK || COMPILE_TEST + default y + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on Mediatek SoCs. + + To compile this driver as a module, choose M here. the + module will be called mtk-rng. + + If unsure, say Y. + +config HW_RANDOM_S390 + tristate "S390 True Random Number Generator support" + depends on S390 + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the True + Random Number Generator available as CPACF extension + on modern s390 hardware platforms. + + To compile this driver as a module, choose M here: the + module will be called s390-trng. + + If unsure, say Y. + endif # HW_RANDOM config UML_RANDOM diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 5f52b1e4e7be..b085975ec1d2 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -24,7 +24,6 @@ obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o obj-$(CONFIG_HW_RANDOM_NOMADIK) += nomadik-rng.o obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o -obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o obj-$(CONFIG_HW_RANDOM_HISI) += hisi-rng.o obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o @@ -36,3 +35,5 @@ obj-$(CONFIG_HW_RANDOM_STM32) += stm32-rng.o obj-$(CONFIG_HW_RANDOM_PIC32) += pic32-rng.o obj-$(CONFIG_HW_RANDOM_MESON) += meson-rng.o obj-$(CONFIG_HW_RANDOM_CAVIUM) += cavium-rng.o cavium-rng-vf.o +obj-$(CONFIG_HW_RANDOM_MTK) += mtk-rng.o +obj-$(CONFIG_HW_RANDOM_S390) += s390-trng.o diff --git a/drivers/char/hw_random/exynos-rng.c b/drivers/char/hw_random/exynos-rng.c deleted file mode 100644 index 23d358553b21..000000000000 --- a/drivers/char/hw_random/exynos-rng.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - * exynos-rng.c - Random Number Generator driver for the exynos - * - * Copyright (C) 2012 Samsung Electronics - * Jonghwa Lee <jonghwa3.lee@samsung.com> - * - * This program 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; - * - * This program 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include <linux/hw_random.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/io.h> -#include <linux/platform_device.h> -#include <linux/clk.h> -#include <linux/pm_runtime.h> -#include <linux/err.h> - -#define EXYNOS_PRNG_STATUS_OFFSET 0x10 -#define EXYNOS_PRNG_SEED_OFFSET 0x140 -#define EXYNOS_PRNG_OUT1_OFFSET 0x160 -#define SEED_SETTING_DONE BIT(1) -#define PRNG_START 0x18 -#define PRNG_DONE BIT(5) -#define EXYNOS_AUTOSUSPEND_DELAY 100 - -struct exynos_rng { - struct device *dev; - struct hwrng rng; - void __iomem *mem; - struct clk *clk; -}; - -static u32 exynos_rng_readl(struct exynos_rng *rng, u32 offset) -{ - return readl_relaxed(rng->mem + offset); -} - -static void exynos_rng_writel(struct exynos_rng *rng, u32 val, u32 offset) -{ - writel_relaxed(val, rng->mem + offset); -} - -static int exynos_rng_configure(struct exynos_rng *exynos_rng) -{ - int i; - int ret = 0; - - for (i = 0 ; i < 5 ; i++) - exynos_rng_writel(exynos_rng, jiffies, - EXYNOS_PRNG_SEED_OFFSET + 4*i); - - if (!(exynos_rng_readl(exynos_rng, EXYNOS_PRNG_STATUS_OFFSET) - & 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_mark_last_busy(exynos_rng->dev); - pm_runtime_put_autosuspend(exynos_rng->dev); - - return ret; -} - -static int exynos_read(struct hwrng *rng, void *buf, - size_t max, bool wait) -{ - struct exynos_rng *exynos_rng = container_of(rng, - struct exynos_rng, rng); - u32 *data = buf; - int retry = 100; - int ret = 4; - - 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) && --retry) - cpu_relax(); - if (!retry) { - ret = -ETIMEDOUT; - goto out; - } - - exynos_rng_writel(exynos_rng, PRNG_DONE, EXYNOS_PRNG_STATUS_OFFSET); - - *data = exynos_rng_readl(exynos_rng, EXYNOS_PRNG_OUT1_OFFSET); - -out: - pm_runtime_mark_last_busy(exynos_rng->dev); - pm_runtime_put_sync_autosuspend(exynos_rng->dev); - - return ret; -} - -static int exynos_rng_probe(struct platform_device *pdev) -{ - struct exynos_rng *exynos_rng; - struct resource *res; - int ret; - - exynos_rng = devm_kzalloc(&pdev->dev, sizeof(struct exynos_rng), - GFP_KERNEL); - if (!exynos_rng) - return -ENOMEM; - - exynos_rng->dev = &pdev->dev; - exynos_rng->rng.name = "exynos"; - exynos_rng->rng.init = exynos_init; - exynos_rng->rng.read = exynos_read; - exynos_rng->clk = devm_clk_get(&pdev->dev, "secss"); - if (IS_ERR(exynos_rng->clk)) { - dev_err(&pdev->dev, "Couldn't get clock.\n"); - return -ENOENT; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - exynos_rng->mem = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(exynos_rng->mem)) - return PTR_ERR(exynos_rng->mem); - - platform_set_drvdata(pdev, exynos_rng); - - pm_runtime_set_autosuspend_delay(&pdev->dev, EXYNOS_AUTOSUSPEND_DELAY); - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_enable(&pdev->dev); - - ret = devm_hwrng_register(&pdev->dev, &exynos_rng->rng); - if (ret) { - pm_runtime_dont_use_autosuspend(&pdev->dev); - pm_runtime_disable(&pdev->dev); - } - - return ret; -} - -static int exynos_rng_remove(struct platform_device *pdev) -{ - pm_runtime_dont_use_autosuspend(&pdev->dev); - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static int __maybe_unused exynos_rng_runtime_suspend(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct exynos_rng *exynos_rng = platform_get_drvdata(pdev); - - clk_disable_unprepare(exynos_rng->clk); - - return 0; -} - -static int __maybe_unused exynos_rng_runtime_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - struct exynos_rng *exynos_rng = platform_get_drvdata(pdev); - - return clk_prepare_enable(exynos_rng->clk); -} - -static int __maybe_unused exynos_rng_suspend(struct device *dev) -{ - return pm_runtime_force_suspend(dev); -} - -static int __maybe_unused 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); -} - -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, - .remove = exynos_rng_remove, -}; - -module_platform_driver(exynos_rng_driver); - -MODULE_DESCRIPTION("EXYNOS 4 H/W Random Number Generator driver"); -MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/meson-rng.c b/drivers/char/hw_random/meson-rng.c index 119d698439ae..2e23be802a62 100644 --- a/drivers/char/hw_random/meson-rng.c +++ b/drivers/char/hw_random/meson-rng.c @@ -62,6 +62,7 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/of.h> +#include <linux/clk.h> #define RNG_DATA 0x00 @@ -69,6 +70,7 @@ struct meson_rng_data { void __iomem *base; struct platform_device *pdev; struct hwrng rng; + struct clk *core_clk; }; static int meson_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) @@ -81,11 +83,17 @@ static int meson_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) return sizeof(u32); } +static void meson_rng_clk_disable(void *data) +{ + clk_disable_unprepare(data); +} + static int meson_rng_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct meson_rng_data *data; struct resource *res; + int ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -98,6 +106,20 @@ static int meson_rng_probe(struct platform_device *pdev) if (IS_ERR(data->base)) return PTR_ERR(data->base); + data->core_clk = devm_clk_get(dev, "core"); + if (IS_ERR(data->core_clk)) + data->core_clk = NULL; + + if (data->core_clk) { + ret = clk_prepare_enable(data->core_clk); + if (ret) + return ret; + ret = devm_add_action_or_reset(dev, meson_rng_clk_disable, + data->core_clk); + if (ret) + return ret; + } + data->rng.name = pdev->name; data->rng.read = meson_rng_read; diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c new file mode 100644 index 000000000000..df8eb54fd5a3 --- /dev/null +++ b/drivers/char/hw_random/mtk-rng.c @@ -0,0 +1,168 @@ +/* + * Driver for Mediatek Hardware Random Number Generator + * + * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com> + * + * This program 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 program 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. + */ +#define MTK_RNG_DEV KBUILD_MODNAME + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/hw_random.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#define USEC_POLL 2 +#define TIMEOUT_POLL 20 + +#define RNG_CTRL 0x00 +#define RNG_EN BIT(0) +#define RNG_READY BIT(31) + +#define RNG_DATA 0x08 + +#define to_mtk_rng(p) container_of(p, struct mtk_rng, rng) + +struct mtk_rng { + void __iomem *base; + struct clk *clk; + struct hwrng rng; +}; + +static int mtk_rng_init(struct hwrng *rng) +{ + struct mtk_rng *priv = to_mtk_rng(rng); + u32 val; + int err; + + err = clk_prepare_enable(priv->clk); + if (err) + return err; + + val = readl(priv->base + RNG_CTRL); + val |= RNG_EN; + writel(val, priv->base + RNG_CTRL); + + return 0; +} + +static void mtk_rng_cleanup(struct hwrng *rng) +{ + struct mtk_rng *priv = to_mtk_rng(rng); + u32 val; + + val = readl(priv->base + RNG_CTRL); + val &= ~RNG_EN; + writel(val, priv->base + RNG_CTRL); + + clk_disable_unprepare(priv->clk); +} + +static bool mtk_rng_wait_ready(struct hwrng *rng, bool wait) +{ + struct mtk_rng *priv = to_mtk_rng(rng); + int ready; + + ready = readl(priv->base + RNG_CTRL) & RNG_READY; + if (!ready && wait) + readl_poll_timeout_atomic(priv->base + RNG_CTRL, ready, + ready & RNG_READY, USEC_POLL, + TIMEOUT_POLL); + return !!ready; +} + +static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + struct mtk_rng *priv = to_mtk_rng(rng); + int retval = 0; + + while (max >= sizeof(u32)) { + if (!mtk_rng_wait_ready(rng, wait)) + break; + + *(u32 *)buf = readl(priv->base + RNG_DATA); + retval += sizeof(u32); + buf += sizeof(u32); + max -= sizeof(u32); + } + + return retval || !wait ? retval : -EIO; +} + +static int mtk_rng_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret; + struct mtk_rng *priv; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no iomem resource\n"); + return -ENXIO; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->rng.name = pdev->name; + priv->rng.init = mtk_rng_init; + priv->rng.cleanup = mtk_rng_cleanup; + priv->rng.read = mtk_rng_read; + + priv->clk = devm_clk_get(&pdev->dev, "rng"); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + dev_err(&pdev->dev, "no clock for device: %d\n", ret); + return ret; + } + + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + ret = devm_hwrng_register(&pdev->dev, &priv->rng); + if (ret) { + dev_err(&pdev->dev, "failed to register rng device: %d\n", + ret); + return ret; + } + + dev_info(&pdev->dev, "registered RNG driver\n"); + + return 0; +} + +static const struct of_device_id mtk_rng_match[] = { + { .compatible = "mediatek,mt7623-rng" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_rng_match); + +static struct platform_driver mtk_rng_driver = { + .probe = mtk_rng_probe, + .driver = { + .name = MTK_RNG_DEV, + .of_match_table = mtk_rng_match, + }, +}; + +module_platform_driver(mtk_rng_driver); + +MODULE_DESCRIPTION("Mediatek Random Number Generator Driver"); +MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c index 31cbdbbaebfc..92dd4e925315 100644 --- a/drivers/char/hw_random/n2-drv.c +++ b/drivers/char/hw_random/n2-drv.c @@ -748,9 +748,7 @@ static int n2rng_probe(struct platform_device *op) dev_info(&op->dev, "Registered RNG HVAPI major %lu minor %lu\n", np->hvapi_major, np->hvapi_minor); - - np->units = devm_kzalloc(&op->dev, - sizeof(struct n2rng_unit) * np->num_units, + np->units = devm_kcalloc(&op->dev, np->num_units, sizeof(*np->units), GFP_KERNEL); err = -ENOMEM; if (!np->units) diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index b1ad12552b56..74d11ae6abe9 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -398,16 +398,6 @@ static int of_get_omap_rng_device_details(struct omap_rng_dev *priv, return err; } - priv->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(priv->clk) && PTR_ERR(priv->clk) == -EPROBE_DEFER) - return -EPROBE_DEFER; - if (!IS_ERR(priv->clk)) { - err = clk_prepare_enable(priv->clk); - if (err) - dev_err(&pdev->dev, "unable to enable the clk, " - "err = %d\n", err); - } - /* * On OMAP4, enabling the shutdown_oflo interrupt is * done in the interrupt mask register. There is no @@ -478,6 +468,18 @@ static int omap_rng_probe(struct platform_device *pdev) goto err_ioremap; } + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk) && PTR_ERR(priv->clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (!IS_ERR(priv->clk)) { + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(&pdev->dev, + "Unable to enable the clk: %d\n", ret); + goto err_register; + } + } + ret = (dev->of_node) ? of_get_omap_rng_device_details(priv, pdev) : get_omap_rng_device_details(priv); if (ret) diff --git a/drivers/char/hw_random/s390-trng.c b/drivers/char/hw_random/s390-trng.c new file mode 100644 index 000000000000..aca48e893fca --- /dev/null +++ b/drivers/char/hw_random/s390-trng.c @@ -0,0 +1,268 @@ +/* + * s390 TRNG device driver + * + * Driver for the TRNG (true random number generation) command + * available via CPACF extension MSA 7 on the s390 arch. + + * Copyright IBM Corp. 2017 + * Author(s): Harald Freudenberger <freude@de.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + * + */ + +#define KMSG_COMPONENT "trng" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include <linux/hw_random.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/cpufeature.h> +#include <linux/miscdevice.h> +#include <linux/debugfs.h> +#include <linux/atomic.h> +#include <linux/random.h> +#include <linux/sched/signal.h> +#include <asm/debug.h> +#include <asm/cpacf.h> + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("IBM Corporation"); +MODULE_DESCRIPTION("s390 CPACF TRNG device driver"); + + +/* trng related debug feature things */ + +static debug_info_t *debug_info; + +#define DEBUG_DBG(...) debug_sprintf_event(debug_info, 6, ##__VA_ARGS__) +#define DEBUG_INFO(...) debug_sprintf_event(debug_info, 5, ##__VA_ARGS__) +#define DEBUG_WARN(...) debug_sprintf_event(debug_info, 4, ##__VA_ARGS__) +#define DEBUG_ERR(...) debug_sprintf_event(debug_info, 3, ##__VA_ARGS__) + + +/* trng helpers */ + +static atomic64_t trng_dev_counter = ATOMIC64_INIT(0); +static atomic64_t trng_hwrng_counter = ATOMIC64_INIT(0); + + +/* file io functions */ + +static int trng_open(struct inode *inode, struct file *file) +{ + return nonseekable_open(inode, file); +} + +static ssize_t trng_read(struct file *file, char __user *ubuf, + size_t nbytes, loff_t *ppos) +{ + u8 buf[32]; + u8 *p = buf; + unsigned int n; + ssize_t ret = 0; + + /* + * use buf for requests <= sizeof(buf), + * otherwise allocate one page and fetch + * pagewise. + */ + + if (nbytes > sizeof(buf)) { + p = (u8 *) __get_free_page(GFP_KERNEL); + if (!p) + return -ENOMEM; + } + + while (nbytes) { + if (need_resched()) { + if (signal_pending(current)) { + if (ret == 0) + ret = -ERESTARTSYS; + break; + } + schedule(); + } + n = nbytes > PAGE_SIZE ? PAGE_SIZE : nbytes; + cpacf_trng(NULL, 0, p, n); + atomic64_add(n, &trng_dev_counter); + if (copy_to_user(ubuf, p, n)) { + ret = -EFAULT; + break; + } + nbytes -= n; + ubuf += n; + ret += n; + } + + if (p != buf) + free_page((unsigned long) p); + + DEBUG_DBG("trng_read()=%zd\n", ret); + return ret; +} + + +/* sysfs */ + +static ssize_t trng_counter_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u64 dev_counter = atomic64_read(&trng_dev_counter); + u64 hwrng_counter = atomic64_read(&trng_hwrng_counter); +#if IS_ENABLED(CONFIG_ARCH_RANDOM) + u64 arch_counter = atomic64_read(&s390_arch_random_counter); + + return snprintf(buf, PAGE_SIZE, + "trng: %llu\n" + "hwrng: %llu\n" + "arch: %llu\n" + "total: %llu\n", + dev_counter, hwrng_counter, arch_counter, + dev_counter + hwrng_counter + arch_counter); +#else + return snprintf(buf, PAGE_SIZE, + "trng: %llu\n" + "hwrng: %llu\n" + "total: %llu\n", + dev_counter, hwrng_counter, + dev_counter + hwrng_counter); +#endif +} +static DEVICE_ATTR(byte_counter, 0444, trng_counter_show, NULL); + +static struct attribute *trng_dev_attrs[] = { + &dev_attr_byte_counter.attr, + NULL +}; + +static const struct attribute_group trng_dev_attr_group = { + .attrs = trng_dev_attrs +}; + +static const struct attribute_group *trng_dev_attr_groups[] = { + &trng_dev_attr_group, + NULL +}; + +static const struct file_operations trng_fops = { + .owner = THIS_MODULE, + .open = &trng_open, + .release = NULL, + .read = &trng_read, + .llseek = noop_llseek, +}; + +static struct miscdevice trng_dev = { + .name = "trng", + .minor = MISC_DYNAMIC_MINOR, + .mode = 0444, + .fops = &trng_fops, + .groups = trng_dev_attr_groups, +}; + + +/* hwrng_register */ + +static inline void _trng_hwrng_read(u8 *buf, size_t len) +{ + cpacf_trng(NULL, 0, buf, len); + atomic64_add(len, &trng_hwrng_counter); +} + +static int trng_hwrng_data_read(struct hwrng *rng, u32 *data) +{ + size_t len = sizeof(*data); + + _trng_hwrng_read((u8 *) data, len); + + DEBUG_DBG("trng_hwrng_data_read()=%zu\n", len); + + return len; +} + +static int trng_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + size_t len = max <= PAGE_SIZE ? max : PAGE_SIZE; + + _trng_hwrng_read((u8 *) data, len); + + DEBUG_DBG("trng_hwrng_read()=%zu\n", len); + + return len; +} + +/* + * hwrng register struct + * The trng is suppost to have 100% entropy, and thus + * we register with a very high quality value. + */ +static struct hwrng trng_hwrng_dev = { + .name = "s390-trng", + .data_read = trng_hwrng_data_read, + .read = trng_hwrng_read, + .quality = 999, +}; + + +/* init and exit */ + +static void __init trng_debug_init(void) +{ + debug_info = debug_register("trng", 1, 1, 4 * sizeof(long)); + debug_register_view(debug_info, &debug_sprintf_view); + debug_set_level(debug_info, 3); +} + +static void trng_debug_exit(void) +{ + debug_unregister(debug_info); +} + +static int __init trng_init(void) +{ + int ret; + + trng_debug_init(); + + /* check if subfunction CPACF_PRNO_TRNG is available */ + if (!cpacf_query_func(CPACF_PRNO, CPACF_PRNO_TRNG)) { + DEBUG_INFO("trng_init CPACF_PRNO_TRNG not available\n"); + ret = -ENODEV; + goto out_dbg; + } + + ret = misc_register(&trng_dev); + if (ret) { + DEBUG_WARN("trng_init misc_register() failed rc=%d\n", ret); + goto out_dbg; + } + + ret = hwrng_register(&trng_hwrng_dev); + if (ret) { + DEBUG_WARN("trng_init hwrng_register() failed rc=%d\n", ret); + goto out_misc; + } + + DEBUG_DBG("trng_init successful\n"); + + return 0; + +out_misc: + misc_deregister(&trng_dev); +out_dbg: + trng_debug_exit(); + return ret; +} + +static void __exit trng_exit(void) +{ + hwrng_unregister(&trng_hwrng_dev); + misc_deregister(&trng_dev); + trng_debug_exit(); +} + +module_cpu_feature_match(MSA, trng_init); +module_exit(trng_exit); diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c index cf37db263ecd..a0faa5f05deb 100644 --- a/drivers/char/hw_random/timeriomem-rng.c +++ b/drivers/char/hw_random/timeriomem-rng.c @@ -20,84 +20,100 @@ * TODO: add support for reading sizes other than 32bits and masking */ -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/platform_device.h> -#include <linux/of.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/hrtimer.h> #include <linux/hw_random.h> #include <linux/io.h> +#include <linux/ktime.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/time.h> #include <linux/timeriomem-rng.h> -#include <linux/jiffies.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/completion.h> -struct timeriomem_rng_private_data { +struct timeriomem_rng_private { void __iomem *io_base; - unsigned int expires; - unsigned int period; + ktime_t period; unsigned int present:1; - struct timer_list timer; + struct hrtimer timer; struct completion completion; - struct hwrng timeriomem_rng_ops; + struct hwrng rng_ops; }; -#define to_rng_priv(rng) \ - ((struct timeriomem_rng_private_data *)rng->priv) - -/* - * have data return 1, however return 0 if we have nothing - */ -static int timeriomem_rng_data_present(struct hwrng *rng, int wait) +static int timeriomem_rng_read(struct hwrng *hwrng, void *data, + size_t max, bool wait) { - struct timeriomem_rng_private_data *priv = to_rng_priv(rng); - - if (!wait || priv->present) - return priv->present; + struct timeriomem_rng_private *priv = + container_of(hwrng, struct timeriomem_rng_private, rng_ops); + int retval = 0; + int period_us = ktime_to_us(priv->period); + + /* + * The RNG provides 32-bits per read. Ensure there is enough space for + * at minimum one read. + */ + if (max < sizeof(u32)) + return 0; + + /* + * There may not have been enough time for new data to be generated + * since the last request. If the caller doesn't want to wait, let them + * bail out. Otherwise, wait for the completion. If the new data has + * already been generated, the completion should already be available. + */ + if (!wait && !priv->present) + return 0; wait_for_completion(&priv->completion); - return 1; -} - -static int timeriomem_rng_data_read(struct hwrng *rng, u32 *data) -{ - struct timeriomem_rng_private_data *priv = to_rng_priv(rng); - unsigned long cur; - s32 delay; - - *data = readl(priv->io_base); - - cur = jiffies; - - delay = cur - priv->expires; - delay = priv->period - (delay % priv->period); - - priv->expires = cur + delay; + do { + /* + * After the first read, all additional reads will need to wait + * for the RNG to generate new data. Since the period can have + * a wide range of values (1us to 1s have been observed), allow + * for 1% tolerance in the sleep time rather than a fixed value. + */ + if (retval > 0) + usleep_range(period_us, + period_us + min(1, period_us / 100)); + + *(u32 *)data = readl(priv->io_base); + retval += sizeof(u32); + data += sizeof(u32); + max -= sizeof(u32); + } while (wait && max > sizeof(u32)); + + /* + * Block any new callers until the RNG has had time to generate new + * data. + */ priv->present = 0; - reinit_completion(&priv->completion); - mod_timer(&priv->timer, priv->expires); + hrtimer_forward_now(&priv->timer, priv->period); + hrtimer_restart(&priv->timer); - return 4; + return retval; } -static void timeriomem_rng_trigger(unsigned long data) +static enum hrtimer_restart timeriomem_rng_trigger(struct hrtimer *timer) { - struct timeriomem_rng_private_data *priv - = (struct timeriomem_rng_private_data *)data; + struct timeriomem_rng_private *priv + = container_of(timer, struct timeriomem_rng_private, timer); priv->present = 1; complete(&priv->completion); + + return HRTIMER_NORESTART; } static int timeriomem_rng_probe(struct platform_device *pdev) { struct timeriomem_rng_data *pdata = pdev->dev.platform_data; - struct timeriomem_rng_private_data *priv; + struct timeriomem_rng_private *priv; struct resource *res; int err = 0; int period; @@ -119,7 +135,7 @@ static int timeriomem_rng_probe(struct platform_device *pdev) /* Allocate memory for the device structure (and zero it) */ priv = devm_kzalloc(&pdev->dev, - sizeof(struct timeriomem_rng_private_data), GFP_KERNEL); + sizeof(struct timeriomem_rng_private), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -139,54 +155,41 @@ static int timeriomem_rng_probe(struct platform_device *pdev) period = pdata->period; } - priv->period = usecs_to_jiffies(period); - if (priv->period < 1) { - dev_err(&pdev->dev, "period is less than one jiffy\n"); - return -EINVAL; - } - - priv->expires = jiffies; - priv->present = 1; - + priv->period = ns_to_ktime(period * NSEC_PER_USEC); init_completion(&priv->completion); - complete(&priv->completion); + hrtimer_init(&priv->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + priv->timer.function = timeriomem_rng_trigger; - setup_timer(&priv->timer, timeriomem_rng_trigger, (unsigned long)priv); - - priv->timeriomem_rng_ops.name = dev_name(&pdev->dev); - priv->timeriomem_rng_ops.data_present = timeriomem_rng_data_present; - priv->timeriomem_rng_ops.data_read = timeriomem_rng_data_read; - priv->timeriomem_rng_ops.priv = (unsigned long)priv; + priv->rng_ops.name = dev_name(&pdev->dev); + priv->rng_ops.read = timeriomem_rng_read; priv->io_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->io_base)) { - err = PTR_ERR(priv->io_base); - goto out_timer; + return PTR_ERR(priv->io_base); } - err = hwrng_register(&priv->timeriomem_rng_ops); + /* Assume random data is already available. */ + priv->present = 1; + complete(&priv->completion); + + err = hwrng_register(&priv->rng_ops); if (err) { dev_err(&pdev->dev, "problem registering\n"); - goto out_timer; + return err; } dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n", priv->io_base, period); return 0; - -out_timer: - del_timer_sync(&priv->timer); - return err; } static int timeriomem_rng_remove(struct platform_device *pdev) { - struct timeriomem_rng_private_data *priv = platform_get_drvdata(pdev); - - hwrng_unregister(&priv->timeriomem_rng_ops); + struct timeriomem_rng_private *priv = platform_get_drvdata(pdev); - del_timer_sync(&priv->timer); + hwrng_unregister(&priv->rng_ops); + hrtimer_cancel(&priv->timer); return 0; } diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c index d6f5d9eb102d..70d434bc1cbf 100644 --- a/drivers/char/ipmi/bt-bmc.c +++ b/drivers/char/ipmi/bt-bmc.c @@ -523,6 +523,7 @@ static int bt_bmc_remove(struct platform_device *pdev) static const struct of_device_id bt_bmc_match[] = { { .compatible = "aspeed,ast2400-ibt-bmc" }, + { .compatible = "aspeed,ast2500-ibt-bmc" }, { }, }; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 2a7c425ddfa7..b2b618f066e0 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1954,7 +1954,9 @@ static int hotmod_handler(const char *val, struct kernel_param *kp) kfree(info); goto out; } + mutex_lock(&smi_infos_lock); rv = try_smi_init(info); + mutex_unlock(&smi_infos_lock); if (rv) { cleanup_one_si(info); goto out; @@ -2042,8 +2044,10 @@ static int hardcode_find_bmc(void) info->slave_addr = slave_addrs[i]; if (!add_smi(info)) { + mutex_lock(&smi_infos_lock); if (try_smi_init(info)) cleanup_one_si(info); + mutex_unlock(&smi_infos_lock); ret = 0; } else { kfree(info); @@ -3492,6 +3496,11 @@ out_err: return rv; } +/* + * Try to start up an interface. Must be called with smi_infos_lock + * held, primarily to keep smi_num consistent, we only one to do these + * one at a time. + */ static int try_smi_init(struct smi_info *new_smi) { int rv = 0; @@ -3524,9 +3533,12 @@ static int try_smi_init(struct smi_info *new_smi) goto out_err; } + new_smi->intf_num = smi_num; + /* Do this early so it's available for logs. */ if (!new_smi->dev) { - init_name = kasprintf(GFP_KERNEL, "ipmi_si.%d", 0); + init_name = kasprintf(GFP_KERNEL, "ipmi_si.%d", + new_smi->intf_num); /* * If we don't already have a device from something @@ -3593,8 +3605,6 @@ static int try_smi_init(struct smi_info *new_smi) new_smi->interrupt_disabled = true; atomic_set(&new_smi->need_watch, 0); - new_smi->intf_num = smi_num; - smi_num++; rv = try_enable_event_buffer(new_smi); if (rv == 0) @@ -3661,6 +3671,9 @@ static int try_smi_init(struct smi_info *new_smi) goto out_err_stop_timer; } + /* Don't increment till we know we have succeeded. */ + smi_num++; + dev_info(new_smi->dev, "IPMI %s interface initialized\n", si_to_str[new_smi->si_type]); diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index cca6e5bc1cea..0b22a9be5029 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -891,6 +891,7 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, * for details on the intricacies of this. */ int left; + unsigned char *data_to_send; ssif_inc_stat(ssif_info, sent_messages_parts); @@ -899,6 +900,7 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, left = 32; /* Length byte. */ ssif_info->multi_data[ssif_info->multi_pos] = left; + data_to_send = ssif_info->multi_data + ssif_info->multi_pos; ssif_info->multi_pos += left; if (left < 32) /* @@ -912,7 +914,7 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, rv = ssif_i2c_send(ssif_info, msg_written_handler, I2C_SMBUS_WRITE, SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE, - ssif_info->multi_data + ssif_info->multi_pos, + data_to_send, I2C_SMBUS_BLOCK_DATA); if (rv < 0) { /* request failed, just return the error. */ @@ -1642,9 +1644,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) spin_lock_init(&ssif_info->lock); ssif_info->ssif_state = SSIF_NORMAL; - init_timer(&ssif_info->retry_timer); - ssif_info->retry_timer.data = (unsigned long) ssif_info; - ssif_info->retry_timer.function = retry_timeout; + setup_timer(&ssif_info->retry_timer, retry_timeout, + (unsigned long)ssif_info); for (i = 0; i < SSIF_NUM_STATS; i++) atomic_set(&ssif_info->stats[i], 0); diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 5ca24d9b101b..d165af8abe36 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -516,7 +516,7 @@ static void panic_halt_ipmi_heartbeat(void) msg.cmd = IPMI_WDOG_RESET_TIMER; msg.data = NULL; msg.data_len = 0; - atomic_add(2, &panic_done_count); + atomic_add(1, &panic_done_count); rv = ipmi_request_supply_msgs(watchdog_user, (struct ipmi_addr *) &addr, 0, @@ -526,7 +526,7 @@ static void panic_halt_ipmi_heartbeat(void) &panic_halt_heartbeat_recv_msg, 1); if (rv) - atomic_sub(2, &panic_done_count); + atomic_sub(1, &panic_done_count); } static struct ipmi_smi_msg panic_halt_smi_msg = { @@ -550,12 +550,12 @@ static void panic_halt_ipmi_set_timeout(void) /* Wait for the messages to be free. */ while (atomic_read(&panic_done_count) != 0) ipmi_poll_interface(watchdog_user); - atomic_add(2, &panic_done_count); + atomic_add(1, &panic_done_count); rv = i_ipmi_set_timeout(&panic_halt_smi_msg, &panic_halt_recv_msg, &send_heartbeat_now); if (rv) { - atomic_sub(2, &panic_done_count); + atomic_sub(1, &panic_done_count); printk(KERN_WARNING PFX "Unable to extend the watchdog timeout."); } else { diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 6d9cc2d39d22..7e4a9d1296bb 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -60,6 +60,10 @@ static inline int valid_mmap_phys_addr_range(unsigned long pfn, size_t size) #endif #ifdef CONFIG_STRICT_DEVMEM +static inline int page_is_allowed(unsigned long pfn) +{ + return devmem_is_allowed(pfn); +} static inline int range_is_allowed(unsigned long pfn, unsigned long size) { u64 from = ((u64)pfn) << PAGE_SHIFT; @@ -75,6 +79,10 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size) return 1; } #else +static inline int page_is_allowed(unsigned long pfn) +{ + return 1; +} static inline int range_is_allowed(unsigned long pfn, unsigned long size) { return 1; @@ -122,23 +130,31 @@ static ssize_t read_mem(struct file *file, char __user *buf, while (count > 0) { unsigned long remaining; + int allowed; sz = size_inside_page(p, count); - if (!range_is_allowed(p >> PAGE_SHIFT, count)) + allowed = page_is_allowed(p >> PAGE_SHIFT); + if (!allowed) return -EPERM; + if (allowed == 2) { + /* Show zeros for restricted memory. */ + remaining = clear_user(buf, sz); + } else { + /* + * On ia64 if a page has been mapped somewhere as + * uncached, then it must also be accessed uncached + * by the kernel or data corruption may occur. + */ + ptr = xlate_dev_mem_ptr(p); + if (!ptr) + return -EFAULT; - /* - * On ia64 if a page has been mapped somewhere as uncached, then - * it must also be accessed uncached by the kernel or data - * corruption may occur. - */ - ptr = xlate_dev_mem_ptr(p); - if (!ptr) - return -EFAULT; + remaining = copy_to_user(buf, ptr, sz); + + unxlate_dev_mem_ptr(p, ptr); + } - remaining = copy_to_user(buf, ptr, sz); - unxlate_dev_mem_ptr(p, ptr); if (remaining) return -EFAULT; @@ -181,30 +197,36 @@ static ssize_t write_mem(struct file *file, const char __user *buf, #endif while (count > 0) { + int allowed; + sz = size_inside_page(p, count); - if (!range_is_allowed(p >> PAGE_SHIFT, sz)) + allowed = page_is_allowed(p >> PAGE_SHIFT); + if (!allowed) return -EPERM; - /* - * On ia64 if a page has been mapped somewhere as uncached, then - * it must also be accessed uncached by the kernel or data - * corruption may occur. - */ - ptr = xlate_dev_mem_ptr(p); - if (!ptr) { - if (written) - break; - return -EFAULT; - } + /* Skip actual writing when a page is marked as restricted. */ + if (allowed == 1) { + /* + * On ia64 if a page has been mapped somewhere as + * uncached, then it must also be accessed uncached + * by the kernel or data corruption may occur. + */ + ptr = xlate_dev_mem_ptr(p); + if (!ptr) { + if (written) + break; + return -EFAULT; + } - copied = copy_from_user(ptr, buf, sz); - unxlate_dev_mem_ptr(p, ptr); - if (copied) { - written += sz - copied; - if (written) - break; - return -EFAULT; + copied = copy_from_user(ptr, buf, sz); + unxlate_dev_mem_ptr(p, ptr); + if (copied) { + written += sz - copied; + if (written) + break; + return -EFAULT; + } } buf += sz; diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c index b708c85dc9c1..0e7fcb04f01e 100644 --- a/drivers/char/mmtimer.c +++ b/drivers/char/mmtimer.c @@ -478,18 +478,18 @@ static int sgi_clock_period; static struct timespec sgi_clock_offset; static int sgi_clock_period; -static int sgi_clock_get(clockid_t clockid, struct timespec *tp) +static int sgi_clock_get(clockid_t clockid, struct timespec64 *tp) { u64 nsec; nsec = rtc_time() * sgi_clock_period + sgi_clock_offset.tv_nsec; - *tp = ns_to_timespec(nsec); + *tp = ns_to_timespec64(nsec); tp->tv_sec += sgi_clock_offset.tv_sec; return 0; }; -static int sgi_clock_set(const clockid_t clockid, const struct timespec *tp) +static int sgi_clock_set(const clockid_t clockid, const struct timespec64 *tp) { u64 nsec; @@ -657,7 +657,7 @@ static int sgi_timer_del(struct k_itimer *timr) } /* Assumption: it_lock is already held with irq's disabled */ -static void sgi_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting) +static void sgi_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting) { if (timr->it.mmtimer.clock == TIMER_OFF) { @@ -668,14 +668,14 @@ static void sgi_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting) return; } - cur_setting->it_interval = ns_to_timespec(timr->it.mmtimer.incr * sgi_clock_period); - cur_setting->it_value = ns_to_timespec((timr->it.mmtimer.expires - rtc_time()) * sgi_clock_period); + cur_setting->it_interval = ns_to_timespec64(timr->it.mmtimer.incr * sgi_clock_period); + cur_setting->it_value = ns_to_timespec64((timr->it.mmtimer.expires - rtc_time()) * sgi_clock_period); } static int sgi_timer_set(struct k_itimer *timr, int flags, - struct itimerspec * new_setting, - struct itimerspec * old_setting) + struct itimerspec64 *new_setting, + struct itimerspec64 *old_setting) { unsigned long when, period, irqflags; int err = 0; @@ -687,8 +687,8 @@ static int sgi_timer_set(struct k_itimer *timr, int flags, sgi_timer_get(timr, old_setting); sgi_timer_del(timr); - when = timespec_to_ns(&new_setting->it_value); - period = timespec_to_ns(&new_setting->it_interval); + when = timespec64_to_ns(&new_setting->it_value); + period = timespec64_to_ns(&new_setting->it_interval); if (when == 0) /* Clear timer */ @@ -699,11 +699,11 @@ static int sgi_timer_set(struct k_itimer *timr, int flags, return -ENOMEM; if (flags & TIMER_ABSTIME) { - struct timespec n; + struct timespec64 n; unsigned long now; - getnstimeofday(&n); - now = timespec_to_ns(&n); + getnstimeofday64(&n); + now = timespec64_to_ns(&n); if (when > now) when -= now; else @@ -765,7 +765,7 @@ static int sgi_timer_set(struct k_itimer *timr, int flags, return err; } -static int sgi_clock_getres(const clockid_t which_clock, struct timespec *tp) +static int sgi_clock_getres(const clockid_t which_clock, struct timespec64 *tp) { tp->tv_sec = 0; tp->tv_nsec = sgi_clock_period; diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index af985cca413c..a30352202f1f 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -6,6 +6,7 @@ menuconfig TCG_TPM tristate "TPM Hardware Support" depends on HAS_IOMEM select SECURITYFS + select CRYPTO select CRYPTO_HASH_INFO ---help--- If you have a TPM security chip in your system, which @@ -135,7 +136,7 @@ config TCG_XEN config TCG_CRB tristate "TPM 2.0 CRB Interface" - depends on X86 && ACPI + depends on ACPI ---help--- If you have a TPM security chip that is compliant with the TCG CRB 2.0 TPM specification say Yes and it will be accessible diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 3d386a8c579f..23681f01f95a 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -3,7 +3,8 @@ # obj-$(CONFIG_TCG_TPM) += tpm.o tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o \ - tpm1_eventlog.o tpm2_eventlog.o + tpm-dev-common.o tpmrm-dev.o tpm1_eventlog.o tpm2_eventlog.o \ + tpm2-space.o tpm-$(CONFIG_ACPI) += tpm_ppi.o tpm_acpi.o tpm-$(CONFIG_OF) += tpm_of.o obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index 028a9cd76b63..1b10e38f214e 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -111,6 +111,13 @@ static const struct st33zp24_phy_ops i2c_phy_ops = { .recv = st33zp24_i2c_recv, }; +static const struct acpi_gpio_params lpcpd_gpios = { 1, 0, false }; + +static const struct acpi_gpio_mapping acpi_st33zp24_gpios[] = { + { "lpcpd-gpios", &lpcpd_gpios, 1 }, + {}, +}; + static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client) { struct tpm_chip *chip = i2c_get_clientdata(client); @@ -118,10 +125,14 @@ static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client) struct st33zp24_i2c_phy *phy = tpm_dev->phy_id; struct gpio_desc *gpiod_lpcpd; struct device *dev = &client->dev; + int ret; + + ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), acpi_st33zp24_gpios); + if (ret) + return ret; /* Get LPCPD GPIO from ACPI */ - gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1, - GPIOD_OUT_HIGH); + gpiod_lpcpd = devm_gpiod_get(dev, "lpcpd", GPIOD_OUT_HIGH); if (IS_ERR(gpiod_lpcpd)) { dev_err(&client->dev, "Failed to retrieve lpcpd-gpios from acpi.\n"); @@ -268,8 +279,14 @@ static int st33zp24_i2c_probe(struct i2c_client *client, static int st33zp24_i2c_remove(struct i2c_client *client) { struct tpm_chip *chip = i2c_get_clientdata(client); + int ret; - return st33zp24_remove(chip); + ret = st33zp24_remove(chip); + if (ret) + return ret; + + acpi_dev_remove_driver_gpios(ACPI_COMPANION(&client->dev)); + return 0; } static const struct i2c_device_id st33zp24_i2c_id[] = { diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index 9f5a0117098c..c69d15198f84 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -230,6 +230,13 @@ static const struct st33zp24_phy_ops spi_phy_ops = { .recv = st33zp24_spi_recv, }; +static const struct acpi_gpio_params lpcpd_gpios = { 1, 0, false }; + +static const struct acpi_gpio_mapping acpi_st33zp24_gpios[] = { + { "lpcpd-gpios", &lpcpd_gpios, 1 }, + {}, +}; + static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev) { struct tpm_chip *chip = spi_get_drvdata(spi_dev); @@ -237,10 +244,14 @@ static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev) struct st33zp24_spi_phy *phy = tpm_dev->phy_id; struct gpio_desc *gpiod_lpcpd; struct device *dev = &spi_dev->dev; + int ret; + + ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev), acpi_st33zp24_gpios); + if (ret) + return ret; /* Get LPCPD GPIO from ACPI */ - gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1, - GPIOD_OUT_HIGH); + gpiod_lpcpd = devm_gpiod_get(dev, "lpcpd", GPIOD_OUT_HIGH); if (IS_ERR(gpiod_lpcpd)) { dev_err(dev, "Failed to retrieve lpcpd-gpios from acpi.\n"); phy->io_lpcpd = -1; @@ -385,8 +396,14 @@ static int st33zp24_spi_probe(struct spi_device *dev) static int st33zp24_spi_remove(struct spi_device *dev) { struct tpm_chip *chip = spi_get_drvdata(dev); + int ret; - return st33zp24_remove(chip); + ret = st33zp24_remove(chip); + if (ret) + return ret; + + acpi_dev_remove_driver_gpios(ACPI_COMPANION(&dev->dev)); + return 0; } static const struct spi_device_id st33zp24_spi_id[] = { diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index e8e0f7c02686..4d1dc8b46877 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -117,9 +117,9 @@ static u8 st33zp24_status(struct tpm_chip *chip) /* * check_locality if the locality is active * @param: chip, the tpm chip description - * @return: the active locality or -EACCESS. + * @return: true if LOCALITY0 is active, otherwise false */ -static int check_locality(struct tpm_chip *chip) +static bool check_locality(struct tpm_chip *chip) { struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); u8 data; @@ -129,9 +129,9 @@ static int check_locality(struct tpm_chip *chip) if (status && (data & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) - return tpm_dev->locality; + return true; - return -EACCES; + return false; } /* check_locality() */ /* @@ -146,7 +146,7 @@ static int request_locality(struct tpm_chip *chip) long ret; u8 data; - if (check_locality(chip) == tpm_dev->locality) + if (check_locality(chip)) return tpm_dev->locality; data = TPM_ACCESS_REQUEST_USE; @@ -158,7 +158,7 @@ static int request_locality(struct tpm_chip *chip) /* Request locality is usually effective after the request */ do { - if (check_locality(chip) >= 0) + if (check_locality(chip)) return tpm_dev->locality; msleep(TPM_TIMEOUT); } while (time_before(jiffies, stop)); diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 935f0e92ad61..322b8a51ffc6 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -33,6 +33,7 @@ DEFINE_IDR(dev_nums_idr); static DEFINE_MUTEX(idr_lock); struct class *tpm_class; +struct class *tpmrm_class; dev_t tpm_devt; /** @@ -128,9 +129,19 @@ static void tpm_dev_release(struct device *dev) mutex_unlock(&idr_lock); kfree(chip->log.bios_event_log); + kfree(chip->work_space.context_buf); + kfree(chip->work_space.session_buf); kfree(chip); } +static void tpm_devs_release(struct device *dev) +{ + struct tpm_chip *chip = container_of(dev, struct tpm_chip, devs); + + /* release the master device reference */ + put_device(&chip->dev); +} + /** * tpm_chip_alloc() - allocate a new struct tpm_chip instance * @pdev: device to which the chip is associated @@ -167,30 +178,63 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev, chip->dev_num = rc; device_initialize(&chip->dev); + device_initialize(&chip->devs); chip->dev.class = tpm_class; chip->dev.release = tpm_dev_release; chip->dev.parent = pdev; chip->dev.groups = chip->groups; + chip->devs.parent = pdev; + chip->devs.class = tpmrm_class; + chip->devs.release = tpm_devs_release; + /* get extra reference on main device to hold on + * behalf of devs. This holds the chip structure + * while cdevs is in use. The corresponding put + * is in the tpm_devs_release (TPM2 only) + */ + if (chip->flags & TPM_CHIP_FLAG_TPM2) + get_device(&chip->dev); + if (chip->dev_num == 0) chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR); else chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num); + chip->devs.devt = + MKDEV(MAJOR(tpm_devt), chip->dev_num + TPM_NUM_DEVICES); + rc = dev_set_name(&chip->dev, "tpm%d", chip->dev_num); if (rc) goto out; + rc = dev_set_name(&chip->devs, "tpmrm%d", chip->dev_num); + if (rc) + goto out; if (!pdev) chip->flags |= TPM_CHIP_FLAG_VIRTUAL; cdev_init(&chip->cdev, &tpm_fops); + cdev_init(&chip->cdevs, &tpmrm_fops); chip->cdev.owner = THIS_MODULE; + chip->cdevs.owner = THIS_MODULE; + + chip->work_space.context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!chip->work_space.context_buf) { + rc = -ENOMEM; + goto out; + } + chip->work_space.session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!chip->work_space.session_buf) { + rc = -ENOMEM; + goto out; + } + chip->locality = -1; return chip; out: + put_device(&chip->devs); put_device(&chip->dev); return ERR_PTR(rc); } @@ -235,10 +279,20 @@ static int tpm_add_char_device(struct tpm_chip *chip) "unable to cdev_device_add() %s, major %d, minor %d, err=%d\n", dev_name(&chip->dev), MAJOR(chip->dev.devt), MINOR(chip->dev.devt), rc); - return rc; } + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + rc = cdev_device_add(&chip->cdevs, &chip->devs); + if (rc) { + dev_err(&chip->devs, + "unable to cdev_device_add() %s, major %d, minor %d, err=%d\n", + dev_name(&chip->devs), MAJOR(chip->devs.devt), + MINOR(chip->devs.devt), rc); + return rc; + } + } + /* Make the chip available. */ mutex_lock(&idr_lock); idr_replace(&dev_nums_idr, chip, chip->dev_num); @@ -371,6 +425,8 @@ void tpm_chip_unregister(struct tpm_chip *chip) { tpm_del_legacy_sysfs(chip); tpm_bios_log_teardown(chip); + if (chip->flags & TPM_CHIP_FLAG_TPM2) + cdev_device_del(&chip->cdevs, &chip->devs); tpm_del_char_device(chip); } EXPORT_SYMBOL_GPL(tpm_chip_unregister); diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-common.c new file mode 100644 index 000000000000..610638a80383 --- /dev/null +++ b/drivers/char/tpm/tpm-dev-common.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2004 IBM Corporation + * Authors: + * Leendert van Doorn <leendert@watson.ibm.com> + * Dave Safford <safford@watson.ibm.com> + * Reiner Sailer <sailer@watson.ibm.com> + * Kylene Hall <kjhall@us.ibm.com> + * + * Copyright (C) 2013 Obsidian Research Corp + * Jason Gunthorpe <jgunthorpe@obsidianresearch.com> + * + * Device file system interface to the TPM + * + * This program 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, version 2 of the + * License. + * + */ +#include <linux/slab.h> +#include <linux/uaccess.h> +#include "tpm.h" +#include "tpm-dev.h" + +static void user_reader_timeout(unsigned long ptr) +{ + struct file_priv *priv = (struct file_priv *)ptr; + + pr_warn("TPM user space timeout is deprecated (pid=%d)\n", + task_tgid_nr(current)); + + schedule_work(&priv->work); +} + +static void timeout_work(struct work_struct *work) +{ + struct file_priv *priv = container_of(work, struct file_priv, work); + + mutex_lock(&priv->buffer_mutex); + atomic_set(&priv->data_pending, 0); + memset(priv->data_buffer, 0, sizeof(priv->data_buffer)); + mutex_unlock(&priv->buffer_mutex); +} + +void tpm_common_open(struct file *file, struct tpm_chip *chip, + struct file_priv *priv) +{ + priv->chip = chip; + atomic_set(&priv->data_pending, 0); + mutex_init(&priv->buffer_mutex); + setup_timer(&priv->user_read_timer, user_reader_timeout, + (unsigned long)priv); + INIT_WORK(&priv->work, timeout_work); + + file->private_data = priv; +} + +ssize_t tpm_common_read(struct file *file, char __user *buf, + size_t size, loff_t *off) +{ + struct file_priv *priv = file->private_data; + ssize_t ret_size; + ssize_t orig_ret_size; + int rc; + + del_singleshot_timer_sync(&priv->user_read_timer); + flush_work(&priv->work); + ret_size = atomic_read(&priv->data_pending); + if (ret_size > 0) { /* relay data */ + orig_ret_size = ret_size; + if (size < ret_size) + ret_size = size; + + mutex_lock(&priv->buffer_mutex); + rc = copy_to_user(buf, priv->data_buffer, ret_size); + memset(priv->data_buffer, 0, orig_ret_size); + if (rc) + ret_size = -EFAULT; + + mutex_unlock(&priv->buffer_mutex); + } + + atomic_set(&priv->data_pending, 0); + + return ret_size; +} + +ssize_t tpm_common_write(struct file *file, const char __user *buf, + size_t size, loff_t *off, struct tpm_space *space) +{ + struct file_priv *priv = file->private_data; + size_t in_size = size; + ssize_t out_size; + + /* Cannot perform a write until the read has cleared either via + * tpm_read or a user_read_timer timeout. This also prevents split + * buffered writes from blocking here. + */ + if (atomic_read(&priv->data_pending) != 0) + return -EBUSY; + + if (in_size > TPM_BUFSIZE) + return -E2BIG; + + mutex_lock(&priv->buffer_mutex); + + if (copy_from_user + (priv->data_buffer, (void __user *) buf, in_size)) { + mutex_unlock(&priv->buffer_mutex); + return -EFAULT; + } + + /* atomic tpm command send and result receive. We only hold the ops + * lock during this period so that the tpm can be unregistered even if + * the char dev is held open. + */ + if (tpm_try_get_ops(priv->chip)) { + mutex_unlock(&priv->buffer_mutex); + return -EPIPE; + } + out_size = tpm_transmit(priv->chip, space, priv->data_buffer, + sizeof(priv->data_buffer), 0); + + tpm_put_ops(priv->chip); + if (out_size < 0) { + mutex_unlock(&priv->buffer_mutex); + return out_size; + } + + atomic_set(&priv->data_pending, out_size); + mutex_unlock(&priv->buffer_mutex); + + /* Set a timeout by which the reader must come claim the result */ + mod_timer(&priv->user_read_timer, jiffies + (120 * HZ)); + + return in_size; +} + +/* + * Called on file close + */ +void tpm_common_release(struct file *file, struct file_priv *priv) +{ + del_singleshot_timer_sync(&priv->user_read_timer); + flush_work(&priv->work); + file->private_data = NULL; + atomic_set(&priv->data_pending, 0); +} diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c index 02a8850d3a69..ebd74ab5abef 100644 --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -18,48 +18,15 @@ * */ #include <linux/slab.h> -#include <linux/uaccess.h> -#include "tpm.h" - -struct file_priv { - struct tpm_chip *chip; - - /* Data passed to and from the tpm via the read/write calls */ - atomic_t data_pending; - struct mutex buffer_mutex; - - struct timer_list user_read_timer; /* user needs to claim result */ - struct work_struct work; - - u8 data_buffer[TPM_BUFSIZE]; -}; - -static void user_reader_timeout(unsigned long ptr) -{ - struct file_priv *priv = (struct file_priv *)ptr; - - pr_warn("TPM user space timeout is deprecated (pid=%d)\n", - task_tgid_nr(current)); - - schedule_work(&priv->work); -} - -static void timeout_work(struct work_struct *work) -{ - struct file_priv *priv = container_of(work, struct file_priv, work); - - mutex_lock(&priv->buffer_mutex); - atomic_set(&priv->data_pending, 0); - memset(priv->data_buffer, 0, sizeof(priv->data_buffer)); - mutex_unlock(&priv->buffer_mutex); -} +#include "tpm-dev.h" static int tpm_open(struct inode *inode, struct file *file) { - struct tpm_chip *chip = - container_of(inode->i_cdev, struct tpm_chip, cdev); + struct tpm_chip *chip; struct file_priv *priv; + chip = container_of(inode->i_cdev, struct tpm_chip, cdev); + /* It's assured that the chip will be opened just once, * by the check of is_open variable, which is protected * by driver_lock. */ @@ -69,100 +36,22 @@ static int tpm_open(struct inode *inode, struct file *file) } priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (priv == NULL) { - clear_bit(0, &chip->is_open); - return -ENOMEM; - } + if (priv == NULL) + goto out; - priv->chip = chip; - atomic_set(&priv->data_pending, 0); - mutex_init(&priv->buffer_mutex); - setup_timer(&priv->user_read_timer, user_reader_timeout, - (unsigned long)priv); - INIT_WORK(&priv->work, timeout_work); + tpm_common_open(file, chip, priv); - file->private_data = priv; return 0; -} - -static ssize_t tpm_read(struct file *file, char __user *buf, - size_t size, loff_t *off) -{ - struct file_priv *priv = file->private_data; - ssize_t ret_size; - int rc; - del_singleshot_timer_sync(&priv->user_read_timer); - flush_work(&priv->work); - ret_size = atomic_read(&priv->data_pending); - if (ret_size > 0) { /* relay data */ - ssize_t orig_ret_size = ret_size; - if (size < ret_size) - ret_size = size; - - mutex_lock(&priv->buffer_mutex); - rc = copy_to_user(buf, priv->data_buffer, ret_size); - memset(priv->data_buffer, 0, orig_ret_size); - if (rc) - ret_size = -EFAULT; - - mutex_unlock(&priv->buffer_mutex); - } - - atomic_set(&priv->data_pending, 0); - - return ret_size; + out: + clear_bit(0, &chip->is_open); + return -ENOMEM; } static ssize_t tpm_write(struct file *file, const char __user *buf, size_t size, loff_t *off) { - struct file_priv *priv = file->private_data; - size_t in_size = size; - ssize_t out_size; - - /* cannot perform a write until the read has cleared - either via tpm_read or a user_read_timer timeout. - This also prevents splitted buffered writes from blocking here. - */ - if (atomic_read(&priv->data_pending) != 0) - return -EBUSY; - - if (in_size > TPM_BUFSIZE) - return -E2BIG; - - mutex_lock(&priv->buffer_mutex); - - if (copy_from_user - (priv->data_buffer, (void __user *) buf, in_size)) { - mutex_unlock(&priv->buffer_mutex); - return -EFAULT; - } - - /* atomic tpm command send and result receive. We only hold the ops - * lock during this period so that the tpm can be unregistered even if - * the char dev is held open. - */ - if (tpm_try_get_ops(priv->chip)) { - mutex_unlock(&priv->buffer_mutex); - return -EPIPE; - } - out_size = tpm_transmit(priv->chip, priv->data_buffer, - sizeof(priv->data_buffer), 0); - - tpm_put_ops(priv->chip); - if (out_size < 0) { - mutex_unlock(&priv->buffer_mutex); - return out_size; - } - - atomic_set(&priv->data_pending, out_size); - mutex_unlock(&priv->buffer_mutex); - - /* Set a timeout by which the reader must come claim the result */ - mod_timer(&priv->user_read_timer, jiffies + (120 * HZ)); - - return in_size; + return tpm_common_write(file, buf, size, off, NULL); } /* @@ -172,12 +61,10 @@ static int tpm_release(struct inode *inode, struct file *file) { struct file_priv *priv = file->private_data; - del_singleshot_timer_sync(&priv->user_read_timer); - flush_work(&priv->work); - file->private_data = NULL; - atomic_set(&priv->data_pending, 0); + tpm_common_release(file, priv); clear_bit(0, &priv->chip->is_open); kfree(priv); + return 0; } @@ -185,9 +72,7 @@ const struct file_operations tpm_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .open = tpm_open, - .read = tpm_read, + .read = tpm_common_read, .write = tpm_write, .release = tpm_release, }; - - diff --git a/drivers/char/tpm/tpm-dev.h b/drivers/char/tpm/tpm-dev.h new file mode 100644 index 000000000000..ff15cf719bad --- /dev/null +++ b/drivers/char/tpm/tpm-dev.h @@ -0,0 +1,27 @@ +#ifndef _TPM_DEV_H +#define _TPM_DEV_H + +#include "tpm.h" + +struct file_priv { + struct tpm_chip *chip; + + /* Data passed to and from the tpm via the read/write calls */ + atomic_t data_pending; + struct mutex buffer_mutex; + + struct timer_list user_read_timer; /* user needs to claim result */ + struct work_struct work; + + u8 data_buffer[TPM_BUFSIZE]; +}; + +void tpm_common_open(struct file *file, struct tpm_chip *chip, + struct file_priv *priv); +ssize_t tpm_common_read(struct file *file, char __user *buf, + size_t size, loff_t *off); +ssize_t tpm_common_write(struct file *file, const char __user *buf, + size_t size, loff_t *off, struct tpm_space *space); +void tpm_common_release(struct file *file, struct file_priv *priv); + +#endif diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index bd2128e0b56c..158c1db83f05 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -328,6 +328,47 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); +static bool tpm_validate_command(struct tpm_chip *chip, + struct tpm_space *space, + const u8 *cmd, + size_t len) +{ + const struct tpm_input_header *header = (const void *)cmd; + int i; + u32 cc; + u32 attrs; + unsigned int nr_handles; + + if (len < TPM_HEADER_SIZE) + return false; + + if (!space) + return true; + + if (chip->flags & TPM_CHIP_FLAG_TPM2 && chip->nr_commands) { + cc = be32_to_cpu(header->ordinal); + + i = tpm2_find_cc(chip, cc); + if (i < 0) { + dev_dbg(&chip->dev, "0x%04X is an invalid command\n", + cc); + return false; + } + + attrs = chip->cc_attrs_tbl[i]; + nr_handles = + 4 * ((attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0)); + if (len < TPM_HEADER_SIZE + 4 * nr_handles) + goto err_len; + } + + return true; +err_len: + dev_dbg(&chip->dev, + "%s: insufficient command length %zu", __func__, len); + return false; +} + /** * tmp_transmit - Internal kernel interface to transmit TPM commands. * @@ -340,14 +381,17 @@ EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); * 0 when the operation is successful. * A negative number for system errors (errno). */ -ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, - unsigned int flags) +ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, + u8 *buf, size_t bufsiz, unsigned int flags) { - ssize_t rc; + struct tpm_output_header *header = (void *)buf; + int rc; + ssize_t len = 0; u32 count, ordinal; unsigned long stop; + bool need_locality; - if (bufsiz < TPM_HEADER_SIZE) + if (!tpm_validate_command(chip, space, buf, bufsiz)) return -EINVAL; if (bufsiz > TPM_BUFSIZE) @@ -369,10 +413,24 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, if (chip->dev.parent) pm_runtime_get_sync(chip->dev.parent); + /* Store the decision as chip->locality will be changed. */ + need_locality = chip->locality == -1; + + if (need_locality && chip->ops->request_locality) { + rc = chip->ops->request_locality(chip, 0); + if (rc < 0) + goto out_no_locality; + chip->locality = rc; + } + + rc = tpm2_prepare_space(chip, space, ordinal, buf); + if (rc) + goto out; + rc = chip->ops->send(chip, (u8 *) buf, count); if (rc < 0) { dev_err(&chip->dev, - "tpm_transmit: tpm_send: error %zd\n", rc); + "tpm_transmit: tpm_send: error %d\n", rc); goto out; } @@ -405,17 +463,36 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, goto out; out_recv: - rc = chip->ops->recv(chip, (u8 *) buf, bufsiz); - if (rc < 0) + len = chip->ops->recv(chip, (u8 *) buf, bufsiz); + if (len < 0) { + rc = len; dev_err(&chip->dev, - "tpm_transmit: tpm_recv: error %zd\n", rc); + "tpm_transmit: tpm_recv: error %d\n", rc); + goto out; + } else if (len < TPM_HEADER_SIZE) { + rc = -EFAULT; + goto out; + } + + if (len != be32_to_cpu(header->length)) { + rc = -EFAULT; + goto out; + } + + rc = tpm2_commit_space(chip, space, ordinal, buf, &len); + out: + if (need_locality && chip->ops->relinquish_locality) { + chip->ops->relinquish_locality(chip, chip->locality); + chip->locality = -1; + } +out_no_locality: if (chip->dev.parent) pm_runtime_put_sync(chip->dev.parent); if (!(flags & TPM_TRANSMIT_UNLOCKED)) mutex_unlock(&chip->tpm_mutex); - return rc; + return rc ? rc : len; } /** @@ -434,23 +511,18 @@ out: * A negative number for system errors (errno). * A positive number for a TPM error. */ -ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *buf, - size_t bufsiz, size_t min_rsp_body_length, - unsigned int flags, const char *desc) +ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, + const void *buf, size_t bufsiz, + size_t min_rsp_body_length, unsigned int flags, + const char *desc) { - const struct tpm_output_header *header; + const struct tpm_output_header *header = buf; int err; ssize_t len; - len = tpm_transmit(chip, (const u8 *)buf, bufsiz, flags); + len = tpm_transmit(chip, space, (u8 *)buf, bufsiz, flags); if (len < 0) return len; - else if (len < TPM_HEADER_SIZE) - return -EFAULT; - - header = buf; - if (len != be32_to_cpu(header->length)) - return -EFAULT; err = be32_to_cpu(header->return_code); if (err != 0 && desc) @@ -501,7 +573,7 @@ ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap = cpu_to_be32(subcap_id); } - rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, + rc = tpm_transmit_cmd(chip, NULL, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, min_cap_length, 0, desc); if (!rc) *cap = tpm_cmd.params.getcap_out.cap; @@ -525,7 +597,8 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) start_cmd.header.in = tpm_startup_header; start_cmd.params.startup_in.startup_type = startup_type; - return tpm_transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, 0, + return tpm_transmit_cmd(chip, NULL, &start_cmd, + TPM_INTERNAL_RESULT_SIZE, 0, 0, "attempting to start the TPM"); } @@ -682,8 +755,8 @@ static int tpm_continue_selftest(struct tpm_chip *chip) struct tpm_cmd_t cmd; cmd.header.in = continue_selftest_header; - rc = tpm_transmit_cmd(chip, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, 0, 0, - "continue selftest"); + rc = tpm_transmit_cmd(chip, NULL, &cmd, CONTINUE_SELFTEST_RESULT_SIZE, + 0, 0, "continue selftest"); return rc; } @@ -703,7 +776,7 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) cmd.header.in = pcrread_header; cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx); - rc = tpm_transmit_cmd(chip, &cmd, READ_PCR_RESULT_SIZE, + rc = tpm_transmit_cmd(chip, NULL, &cmd, READ_PCR_RESULT_SIZE, READ_PCR_RESULT_BODY_SIZE, 0, "attempting to read a pcr value"); @@ -815,7 +888,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) cmd.header.in = pcrextend_header; cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, + rc = tpm_transmit_cmd(chip, NULL, &cmd, EXTEND_PCR_RESULT_SIZE, EXTEND_PCR_RESULT_BODY_SIZE, 0, "attempting extend a PCR value"); @@ -920,8 +993,8 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen) if (chip == NULL) return -ENODEV; - rc = tpm_transmit_cmd(chip, cmd, buflen, 0, 0, "attempting tpm_cmd"); - + rc = tpm_transmit_cmd(chip, NULL, cmd, buflen, 0, 0, + "attempting tpm_cmd"); tpm_put_ops(chip); return rc; } @@ -1022,16 +1095,16 @@ int tpm_pm_suspend(struct device *dev) cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(tpm_suspend_pcr); memcpy(cmd.params.pcrextend_in.hash, dummy_hash, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, - EXTEND_PCR_RESULT_BODY_SIZE, 0, + rc = tpm_transmit_cmd(chip, NULL, &cmd, EXTEND_PCR_RESULT_SIZE, + EXTEND_PCR_RESULT_BODY_SIZE, 0, "extending dummy pcr before suspend"); } /* now do the actual savestate */ for (try = 0; try < TPM_RETRY; try++) { cmd.header.in = savestate_header; - rc = tpm_transmit_cmd(chip, &cmd, SAVESTATE_RESULT_SIZE, 0, - 0, NULL); + rc = tpm_transmit_cmd(chip, NULL, &cmd, SAVESTATE_RESULT_SIZE, + 0, 0, NULL); /* * If the TPM indicates that it is too busy to respond to @@ -1114,7 +1187,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) tpm_cmd.header.in = tpm_getrandom_header; tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes); - err = tpm_transmit_cmd(chip, &tpm_cmd, + err = tpm_transmit_cmd(chip, NULL, &tpm_cmd, TPM_GETRANDOM_RESULT_SIZE + num_bytes, offsetof(struct tpm_getrandom_out, rng_data), @@ -1205,9 +1278,17 @@ static int __init tpm_init(void) return PTR_ERR(tpm_class); } - rc = alloc_chrdev_region(&tpm_devt, 0, TPM_NUM_DEVICES, "tpm"); + tpmrm_class = class_create(THIS_MODULE, "tpmrm"); + if (IS_ERR(tpmrm_class)) { + pr_err("couldn't create tpmrm class\n"); + class_destroy(tpm_class); + return PTR_ERR(tpmrm_class); + } + + rc = alloc_chrdev_region(&tpm_devt, 0, 2*TPM_NUM_DEVICES, "tpm"); if (rc < 0) { pr_err("tpm: failed to allocate char dev region\n"); + class_destroy(tpmrm_class); class_destroy(tpm_class); return rc; } @@ -1219,7 +1300,8 @@ static void __exit tpm_exit(void) { idr_destroy(&dev_nums_idr); class_destroy(tpm_class); - unregister_chrdev_region(tpm_devt, TPM_NUM_DEVICES); + class_destroy(tpmrm_class); + unregister_chrdev_region(tpm_devt, 2*TPM_NUM_DEVICES); } subsys_initcall(tpm_init); diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index 2f596d74f80c..55405dbe43fa 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -40,7 +40,7 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, struct tpm_chip *chip = to_tpm_chip(dev); tpm_cmd.header.in = tpm_readpubek_header; - err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, + err = tpm_transmit_cmd(chip, NULL, &tpm_cmd, READ_PUBEK_RESULT_SIZE, READ_PUBEK_RESULT_MIN_BODY_SIZE, 0, "attempting to read the PUBEK"); if (err) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 4937b56a275c..4b4c8dee3096 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -89,10 +89,13 @@ enum tpm2_structures { }; enum tpm2_return_codes { + TPM2_RC_SUCCESS = 0x0000, TPM2_RC_HASH = 0x0083, /* RC_FMT1 */ + TPM2_RC_HANDLE = 0x008B, TPM2_RC_INITIALIZE = 0x0100, /* RC_VER1 */ TPM2_RC_DISABLED = 0x0120, TPM2_RC_TESTING = 0x090A, /* RC_WARN */ + TPM2_RC_REFERENCE_H0 = 0x0910, }; enum tpm2_algorithms { @@ -114,6 +117,8 @@ enum tpm2_command_codes { TPM2_CC_CREATE = 0x0153, TPM2_CC_LOAD = 0x0157, TPM2_CC_UNSEAL = 0x015E, + TPM2_CC_CONTEXT_LOAD = 0x0161, + TPM2_CC_CONTEXT_SAVE = 0x0162, TPM2_CC_FLUSH_CONTEXT = 0x0165, TPM2_CC_GET_CAPABILITY = 0x017A, TPM2_CC_GET_RANDOM = 0x017B, @@ -127,21 +132,39 @@ enum tpm2_permanent_handles { }; enum tpm2_capabilities { + TPM2_CAP_HANDLES = 1, + TPM2_CAP_COMMANDS = 2, TPM2_CAP_PCRS = 5, TPM2_CAP_TPM_PROPERTIES = 6, }; +enum tpm2_properties { + TPM_PT_TOTAL_COMMANDS = 0x0129, +}; + enum tpm2_startup_types { TPM2_SU_CLEAR = 0x0000, TPM2_SU_STATE = 0x0001, }; +enum tpm2_cc_attrs { + TPM2_CC_ATTR_CHANDLES = 25, + TPM2_CC_ATTR_RHANDLE = 28, +}; + #define TPM_VID_INTEL 0x8086 #define TPM_VID_WINBOND 0x1050 #define TPM_VID_STM 0x104A #define TPM_PPI_VERSION_LEN 3 +struct tpm_space { + u32 context_tbl[3]; + u8 *context_buf; + u32 session_tbl[3]; + u8 *session_buf; +}; + enum tpm_chip_flags { TPM_CHIP_FLAG_TPM2 = BIT(1), TPM_CHIP_FLAG_IRQ = BIT(2), @@ -161,7 +184,9 @@ struct tpm_chip_seqops { struct tpm_chip { struct device dev; + struct device devs; struct cdev cdev; + struct cdev cdevs; /* A driver callback under ops cannot be run unless ops_sem is held * (sometimes implicitly, eg for the sysfs code). ops becomes null @@ -199,6 +224,13 @@ struct tpm_chip { acpi_handle acpi_dev_handle; char ppi_version[TPM_PPI_VERSION_LEN + 1]; #endif /* CONFIG_ACPI */ + + struct tpm_space work_space; + u32 nr_commands; + u32 *cc_attrs_tbl; + + /* active locality */ + int locality; }; #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) @@ -485,18 +517,21 @@ static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value) } extern struct class *tpm_class; +extern struct class *tpmrm_class; extern dev_t tpm_devt; extern const struct file_operations tpm_fops; +extern const struct file_operations tpmrm_fops; extern struct idr dev_nums_idr; enum tpm_transmit_flags { TPM_TRANSMIT_UNLOCKED = BIT(0), }; -ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, - unsigned int flags); -ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *buf, size_t bufsiz, - size_t min_rsp_body_len, unsigned int flags, +ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space, + u8 *buf, size_t bufsiz, unsigned int flags); +ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space, + const void *buf, size_t bufsiz, + size_t min_rsp_body_length, unsigned int flags, const char *desc); ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap, const char *desc, size_t min_cap_length); @@ -541,6 +576,8 @@ 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, u32 count, struct tpm2_digest *digests); int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max); +void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, + unsigned int flags); int tpm2_seal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, struct trusted_key_options *options); @@ -554,4 +591,11 @@ int tpm2_auto_startup(struct tpm_chip *chip); void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type); unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal); int tpm2_probe(struct tpm_chip *chip); +int tpm2_find_cc(struct tpm_chip *chip, u32 cc); +int tpm2_init_space(struct tpm_space *space); +void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space); +int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, + u8 *cmd); +int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, + u32 cc, u8 *buf, size_t *bufsiz); #endif diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 881aea9732bf..3ee6883f26c1 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -266,7 +266,7 @@ int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) sizeof(cmd.params.pcrread_in.pcr_select)); cmd.params.pcrread_in.pcr_select[pcr_idx >> 3] = 1 << (pcr_idx & 0x7); - rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), + rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), TPM2_PCR_READ_RESP_BODY_SIZE, 0, "attempting to read a pcr value"); if (rc == 0) { @@ -333,7 +333,7 @@ int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, u32 count, } } - rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 0, 0, + rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0, "attempting extend a PCR value"); tpm_buf_destroy(&buf); @@ -382,7 +382,7 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max) cmd.header.in = tpm2_getrandom_header; cmd.params.getrandom_in.size = cpu_to_be16(num_bytes); - err = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), + err = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), offsetof(struct tpm2_get_random_out, buffer), 0, "attempting get random"); @@ -419,6 +419,35 @@ static const struct tpm_input_header tpm2_get_tpm_pt_header = { }; /** + * tpm2_flush_context_cmd() - execute a TPM2_FlushContext command + * @chip: TPM chip to use + * @payload: the key data in clear and encrypted form + * @options: authentication values and other options + * + * Return: same as with tpm_transmit_cmd + */ +void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, + unsigned int flags) +{ + struct tpm_buf buf; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT); + if (rc) { + dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n", + handle); + return; + } + + tpm_buf_append_u32(&buf, handle); + + (void) tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, flags, + "flushing context"); + + tpm_buf_destroy(&buf); +} + +/** * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer. * * @buf: an allocated tpm_buf instance @@ -528,7 +557,7 @@ int tpm2_seal_trusted(struct tpm_chip *chip, goto out; } - rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 4, 0, + rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, 0, "sealing data"); if (rc) goto out; @@ -612,7 +641,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip, goto out; } - rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 4, flags, + rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 4, flags, "loading blob"); if (!rc) *blob_handle = be32_to_cpup( @@ -628,39 +657,6 @@ out: } /** - * tpm2_flush_context_cmd() - execute a TPM2_FlushContext command - * - * @chip: TPM chip to use - * @handle: the key data in clear and encrypted form - * @flags: tpm transmit flags - * - * Return: Same as with tpm_transmit_cmd. - */ -static void tpm2_flush_context_cmd(struct tpm_chip *chip, u32 handle, - unsigned int flags) -{ - struct tpm_buf buf; - int rc; - - rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT); - if (rc) { - dev_warn(&chip->dev, "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, 0, flags, - "flushing context"); - if (rc) - dev_warn(&chip->dev, "0x%08x was not flushed, rc=%d\n", handle, - rc); - - tpm_buf_destroy(&buf); -} - -/** * tpm2_unseal_cmd() - execute a TPM2_Unload command * * @chip: TPM chip to use @@ -697,7 +693,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, options->blobauth /* hmac */, TPM_DIGEST_SIZE); - rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 6, flags, + rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 6, flags, "unsealing"); if (rc > 0) rc = -EPERM; @@ -774,7 +770,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(property_id); cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1); - rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), + rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), TPM2_GET_TPM_PT_OUT_BODY_SIZE, 0, desc); if (!rc) *value = be32_to_cpu(cmd.params.get_tpm_pt_out.value); @@ -809,7 +805,7 @@ static int tpm2_startup(struct tpm_chip *chip, u16 startup_type) cmd.header.in = tpm2_startup_header; cmd.params.startup_in.startup_type = cpu_to_be16(startup_type); - return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0, + return tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0, "attempting to start the TPM"); } @@ -838,7 +834,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type) cmd.header.in = tpm2_shutdown_header; cmd.params.startup_in.startup_type = cpu_to_be16(shutdown_type); - rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0, + rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0, "stopping the TPM"); /* In places where shutdown command is sent there's no much we can do @@ -902,7 +898,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full) cmd.header.in = tpm2_selftest_header; cmd.params.selftest_in.full_test = full; - rc = tpm_transmit_cmd(chip, &cmd, TPM2_SELF_TEST_IN_SIZE, 0, 0, + rc = tpm_transmit_cmd(chip, NULL, &cmd, TPM2_SELF_TEST_IN_SIZE, 0, 0, "continue selftest"); /* At least some prototype chips seem to give RC_TESTING error @@ -953,7 +949,8 @@ static int tpm2_do_selftest(struct tpm_chip *chip) cmd.params.pcrread_in.pcr_select[1] = 0x00; cmd.params.pcrread_in.pcr_select[2] = 0x00; - rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0, NULL); + rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0, + NULL); if (rc < 0) break; @@ -986,7 +983,7 @@ int tpm2_probe(struct tpm_chip *chip) cmd.params.get_tpm_pt_in.property_id = cpu_to_be32(0x100); cmd.params.get_tpm_pt_in.property_cnt = cpu_to_be32(1); - rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), 0, 0, NULL); + rc = tpm_transmit_cmd(chip, NULL, &cmd, sizeof(cmd), 0, 0, NULL); if (rc < 0) return rc; @@ -1024,7 +1021,7 @@ static ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip) tpm_buf_append_u32(&buf, 0); tpm_buf_append_u32(&buf, 1); - rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, 9, 0, + rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 9, 0, "get tpm pcr allocation"); if (rc) goto out; @@ -1067,15 +1064,76 @@ out: return rc; } +static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip) +{ + struct tpm_buf buf; + u32 nr_commands; + u32 *attrs; + u32 cc; + int i; + int rc; + + rc = tpm2_get_tpm_pt(chip, TPM_PT_TOTAL_COMMANDS, &nr_commands, NULL); + if (rc) + goto out; + + if (nr_commands > 0xFFFFF) { + rc = -EFAULT; + goto out; + } + + chip->cc_attrs_tbl = devm_kzalloc(&chip->dev, 4 * nr_commands, + GFP_KERNEL); + + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY); + if (rc) + goto out; + + tpm_buf_append_u32(&buf, TPM2_CAP_COMMANDS); + tpm_buf_append_u32(&buf, TPM2_CC_FIRST); + tpm_buf_append_u32(&buf, nr_commands); + + rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, + 9 + 4 * nr_commands, 0, NULL); + if (rc) { + tpm_buf_destroy(&buf); + goto out; + } + + if (nr_commands != + be32_to_cpup((__be32 *)&buf.data[TPM_HEADER_SIZE + 5])) { + tpm_buf_destroy(&buf); + goto out; + } + + chip->nr_commands = nr_commands; + + attrs = (u32 *)&buf.data[TPM_HEADER_SIZE + 9]; + for (i = 0; i < nr_commands; i++, attrs++) { + chip->cc_attrs_tbl[i] = be32_to_cpup(attrs); + cc = chip->cc_attrs_tbl[i] & 0xFFFF; + + if (cc == TPM2_CC_CONTEXT_SAVE || cc == TPM2_CC_FLUSH_CONTEXT) { + chip->cc_attrs_tbl[i] &= + ~(GENMASK(2, 0) << TPM2_CC_ATTR_CHANDLES); + chip->cc_attrs_tbl[i] |= 1 << TPM2_CC_ATTR_CHANDLES; + } + } + + tpm_buf_destroy(&buf); + +out: + if (rc > 0) + rc = -ENODEV; + return rc; +} + /** * tpm2_auto_startup - Perform the standard automatic TPM initialization * sequence * @chip: TPM chip to use * - * Initializes timeout values for operation and command durations, conducts - * a self-test and reads the list of active PCR banks. - * - * Return: 0 on success. Otherwise, a system error code is returned. + * Returns 0 on success, < 0 in case of fatal error. */ int tpm2_auto_startup(struct tpm_chip *chip) { @@ -1104,9 +1162,24 @@ int tpm2_auto_startup(struct tpm_chip *chip) } rc = tpm2_get_pcr_allocation(chip); + if (rc) + goto out; + + rc = tpm2_get_cc_attrs_tbl(chip); out: if (rc > 0) rc = -ENODEV; return rc; } + +int tpm2_find_cc(struct tpm_chip *chip, u32 cc) +{ + int i; + + for (i = 0; i < chip->nr_commands; i++) + if (cc == (chip->cc_attrs_tbl[i] & GENMASK(15, 0))) + return i; + + return -1; +} diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c new file mode 100644 index 000000000000..e2e059d8ffec --- /dev/null +++ b/drivers/char/tpm/tpm2-space.c @@ -0,0 +1,528 @@ +/* + * Copyright (C) 2016 Intel Corporation + * + * Authors: + * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> + * + * Maintained by: <tpmdd-devel@lists.sourceforge.net> + * + * This file contains TPM2 protocol implementations of the commands + * used by the kernel internally. + * + * This program 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; version 2 + * of the License. + */ + +#include <linux/gfp.h> +#include <asm/unaligned.h> +#include "tpm.h" + +enum tpm2_handle_types { + TPM2_HT_HMAC_SESSION = 0x02000000, + TPM2_HT_POLICY_SESSION = 0x03000000, + TPM2_HT_TRANSIENT = 0x80000000, +}; + +struct tpm2_context { + __be64 sequence; + __be32 saved_handle; + __be32 hierarchy; + __be16 blob_size; +} __packed; + +static void tpm2_flush_sessions(struct tpm_chip *chip, struct tpm_space *space) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) { + if (space->session_tbl[i]) + tpm2_flush_context_cmd(chip, space->session_tbl[i], + TPM_TRANSMIT_UNLOCKED); + } +} + +int tpm2_init_space(struct tpm_space *space) +{ + space->context_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!space->context_buf) + return -ENOMEM; + + space->session_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (space->session_buf == NULL) { + kfree(space->context_buf); + return -ENOMEM; + } + + return 0; +} + +void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space) +{ + mutex_lock(&chip->tpm_mutex); + tpm2_flush_sessions(chip, space); + mutex_unlock(&chip->tpm_mutex); + kfree(space->context_buf); + kfree(space->session_buf); +} + +static int tpm2_load_context(struct tpm_chip *chip, u8 *buf, + unsigned int *offset, u32 *handle) +{ + struct tpm_buf tbuf; + struct tpm2_context *ctx; + unsigned int body_size; + int rc; + + rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_LOAD); + if (rc) + return rc; + + ctx = (struct tpm2_context *)&buf[*offset]; + body_size = sizeof(*ctx) + be16_to_cpu(ctx->blob_size); + tpm_buf_append(&tbuf, &buf[*offset], body_size); + + rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 4, + TPM_TRANSMIT_UNLOCKED, NULL); + if (rc < 0) { + dev_warn(&chip->dev, "%s: failed with a system error %d\n", + __func__, rc); + tpm_buf_destroy(&tbuf); + return -EFAULT; + } else if (tpm2_rc_value(rc) == TPM2_RC_HANDLE || + rc == TPM2_RC_REFERENCE_H0) { + /* + * TPM_RC_HANDLE means that the session context can't + * be loaded because of an internal counter mismatch + * that makes the TPM think there might have been a + * replay. This might happen if the context was saved + * and loaded outside the space. + * + * TPM_RC_REFERENCE_H0 means the session has been + * flushed outside the space + */ + rc = -ENOENT; + tpm_buf_destroy(&tbuf); + } else if (rc > 0) { + dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n", + __func__, rc); + tpm_buf_destroy(&tbuf); + return -EFAULT; + } + + *handle = be32_to_cpup((__be32 *)&tbuf.data[TPM_HEADER_SIZE]); + *offset += body_size; + + tpm_buf_destroy(&tbuf); + return 0; +} + +static int tpm2_save_context(struct tpm_chip *chip, u32 handle, u8 *buf, + unsigned int buf_size, unsigned int *offset) +{ + struct tpm_buf tbuf; + unsigned int body_size; + int rc; + + rc = tpm_buf_init(&tbuf, TPM2_ST_NO_SESSIONS, TPM2_CC_CONTEXT_SAVE); + if (rc) + return rc; + + tpm_buf_append_u32(&tbuf, handle); + + rc = tpm_transmit_cmd(chip, NULL, tbuf.data, PAGE_SIZE, 0, + TPM_TRANSMIT_UNLOCKED, NULL); + if (rc < 0) { + dev_warn(&chip->dev, "%s: failed with a system error %d\n", + __func__, rc); + tpm_buf_destroy(&tbuf); + return -EFAULT; + } else if (tpm2_rc_value(rc) == TPM2_RC_REFERENCE_H0) { + tpm_buf_destroy(&tbuf); + return -ENOENT; + } else if (rc) { + dev_warn(&chip->dev, "%s: failed with a TPM error 0x%04X\n", + __func__, rc); + tpm_buf_destroy(&tbuf); + return -EFAULT; + } + + body_size = tpm_buf_length(&tbuf) - TPM_HEADER_SIZE; + if ((*offset + body_size) > buf_size) { + dev_warn(&chip->dev, "%s: out of backing storage\n", __func__); + tpm_buf_destroy(&tbuf); + return -ENOMEM; + } + + memcpy(&buf[*offset], &tbuf.data[TPM_HEADER_SIZE], body_size); + *offset += body_size; + tpm_buf_destroy(&tbuf); + return 0; +} + +static void tpm2_flush_space(struct tpm_chip *chip) +{ + struct tpm_space *space = &chip->work_space; + int i; + + for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) + if (space->context_tbl[i] && ~space->context_tbl[i]) + tpm2_flush_context_cmd(chip, space->context_tbl[i], + TPM_TRANSMIT_UNLOCKED); + + tpm2_flush_sessions(chip, space); +} + +static int tpm2_load_space(struct tpm_chip *chip) +{ + struct tpm_space *space = &chip->work_space; + unsigned int offset; + int i; + int rc; + + for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) { + if (!space->context_tbl[i]) + continue; + + /* sanity check, should never happen */ + if (~space->context_tbl[i]) { + dev_err(&chip->dev, "context table is inconsistent"); + return -EFAULT; + } + + rc = tpm2_load_context(chip, space->context_buf, &offset, + &space->context_tbl[i]); + if (rc) + return rc; + } + + for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) { + u32 handle; + + if (!space->session_tbl[i]) + continue; + + rc = tpm2_load_context(chip, space->session_buf, + &offset, &handle); + if (rc == -ENOENT) { + /* load failed, just forget session */ + space->session_tbl[i] = 0; + } else if (rc) { + tpm2_flush_space(chip); + return rc; + } + if (handle != space->session_tbl[i]) { + dev_warn(&chip->dev, "session restored to wrong handle\n"); + tpm2_flush_space(chip); + return -EFAULT; + } + } + + return 0; +} + +static bool tpm2_map_to_phandle(struct tpm_space *space, void *handle) +{ + u32 vhandle = be32_to_cpup((__be32 *)handle); + u32 phandle; + int i; + + i = 0xFFFFFF - (vhandle & 0xFFFFFF); + if (i >= ARRAY_SIZE(space->context_tbl) || !space->context_tbl[i]) + return false; + + phandle = space->context_tbl[i]; + *((__be32 *)handle) = cpu_to_be32(phandle); + return true; +} + +static int tpm2_map_command(struct tpm_chip *chip, u32 cc, u8 *cmd) +{ + struct tpm_space *space = &chip->work_space; + unsigned int nr_handles; + u32 attrs; + u32 *handle; + int i; + + i = tpm2_find_cc(chip, cc); + if (i < 0) + return -EINVAL; + + attrs = chip->cc_attrs_tbl[i]; + nr_handles = (attrs >> TPM2_CC_ATTR_CHANDLES) & GENMASK(2, 0); + + handle = (u32 *)&cmd[TPM_HEADER_SIZE]; + for (i = 0; i < nr_handles; i++, handle++) { + if ((be32_to_cpu(*handle) & 0xFF000000) == TPM2_HT_TRANSIENT) { + if (!tpm2_map_to_phandle(space, handle)) + return -EINVAL; + } + } + + return 0; +} + +int tpm2_prepare_space(struct tpm_chip *chip, struct tpm_space *space, u32 cc, + u8 *cmd) +{ + int rc; + + if (!space) + return 0; + + memcpy(&chip->work_space.context_tbl, &space->context_tbl, + sizeof(space->context_tbl)); + memcpy(&chip->work_space.session_tbl, &space->session_tbl, + sizeof(space->session_tbl)); + memcpy(chip->work_space.context_buf, space->context_buf, PAGE_SIZE); + memcpy(chip->work_space.session_buf, space->session_buf, PAGE_SIZE); + + rc = tpm2_load_space(chip); + if (rc) { + tpm2_flush_space(chip); + return rc; + } + + rc = tpm2_map_command(chip, cc, cmd); + if (rc) { + tpm2_flush_space(chip); + return rc; + } + + return 0; +} + +static bool tpm2_add_session(struct tpm_chip *chip, u32 handle) +{ + struct tpm_space *space = &chip->work_space; + int i; + + for (i = 0; i < ARRAY_SIZE(space->session_tbl); i++) + if (space->session_tbl[i] == 0) + break; + + if (i == ARRAY_SIZE(space->session_tbl)) + return false; + + space->session_tbl[i] = handle; + return true; +} + +static u32 tpm2_map_to_vhandle(struct tpm_space *space, u32 phandle, bool alloc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(space->context_tbl); i++) { + if (alloc) { + if (!space->context_tbl[i]) { + space->context_tbl[i] = phandle; + break; + } + } else if (space->context_tbl[i] == phandle) + break; + } + + if (i == ARRAY_SIZE(space->context_tbl)) + return 0; + + return TPM2_HT_TRANSIENT | (0xFFFFFF - i); +} + +static int tpm2_map_response_header(struct tpm_chip *chip, u32 cc, u8 *rsp, + size_t len) +{ + struct tpm_space *space = &chip->work_space; + struct tpm_output_header *header = (void *)rsp; + u32 phandle; + u32 phandle_type; + u32 vhandle; + u32 attrs; + int i; + + if (be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) + return 0; + + i = tpm2_find_cc(chip, cc); + /* sanity check, should never happen */ + if (i < 0) + return -EFAULT; + + attrs = chip->cc_attrs_tbl[i]; + if (!((attrs >> TPM2_CC_ATTR_RHANDLE) & 1)) + return 0; + + phandle = be32_to_cpup((__be32 *)&rsp[TPM_HEADER_SIZE]); + phandle_type = phandle & 0xFF000000; + + switch (phandle_type) { + case TPM2_HT_TRANSIENT: + vhandle = tpm2_map_to_vhandle(space, phandle, true); + if (!vhandle) + goto out_no_slots; + + *(__be32 *)&rsp[TPM_HEADER_SIZE] = cpu_to_be32(vhandle); + break; + case TPM2_HT_HMAC_SESSION: + case TPM2_HT_POLICY_SESSION: + if (!tpm2_add_session(chip, phandle)) + goto out_no_slots; + break; + default: + dev_err(&chip->dev, "%s: unknown handle 0x%08X\n", + __func__, phandle); + break; + }; + + return 0; +out_no_slots: + tpm2_flush_context_cmd(chip, phandle, TPM_TRANSMIT_UNLOCKED); + dev_warn(&chip->dev, "%s: out of slots for 0x%08X\n", __func__, + phandle); + return -ENOMEM; +} + +struct tpm2_cap_handles { + u8 more_data; + __be32 capability; + __be32 count; + __be32 handles[]; +} __packed; + +static int tpm2_map_response_body(struct tpm_chip *chip, u32 cc, u8 *rsp, + size_t len) +{ + struct tpm_space *space = &chip->work_space; + struct tpm_output_header *header = (void *)rsp; + struct tpm2_cap_handles *data; + u32 phandle; + u32 phandle_type; + u32 vhandle; + int i; + int j; + + if (cc != TPM2_CC_GET_CAPABILITY || + be32_to_cpu(header->return_code) != TPM2_RC_SUCCESS) { + return 0; + } + + if (len < TPM_HEADER_SIZE + 9) + return -EFAULT; + + data = (void *)&rsp[TPM_HEADER_SIZE]; + if (be32_to_cpu(data->capability) != TPM2_CAP_HANDLES) + return 0; + + if (len != TPM_HEADER_SIZE + 9 + 4 * be32_to_cpu(data->count)) + return -EFAULT; + + for (i = 0, j = 0; i < be32_to_cpu(data->count); i++) { + phandle = be32_to_cpup((__be32 *)&data->handles[i]); + phandle_type = phandle & 0xFF000000; + + switch (phandle_type) { + case TPM2_HT_TRANSIENT: + vhandle = tpm2_map_to_vhandle(space, phandle, false); + if (!vhandle) + break; + + data->handles[j] = cpu_to_be32(vhandle); + j++; + break; + + default: + data->handles[j] = cpu_to_be32(phandle); + j++; + break; + } + + } + + header->length = cpu_to_be32(TPM_HEADER_SIZE + 9 + 4 * j); + data->count = cpu_to_be32(j); + return 0; +} + +static int tpm2_save_space(struct tpm_chip *chip) +{ + struct tpm_space *space = &chip->work_space; + unsigned int offset; + int i; + int rc; + + for (i = 0, offset = 0; i < ARRAY_SIZE(space->context_tbl); i++) { + if (!(space->context_tbl[i] && ~space->context_tbl[i])) + continue; + + rc = tpm2_save_context(chip, space->context_tbl[i], + space->context_buf, PAGE_SIZE, + &offset); + if (rc == -ENOENT) { + space->context_tbl[i] = 0; + continue; + } else if (rc) + return rc; + + tpm2_flush_context_cmd(chip, space->context_tbl[i], + TPM_TRANSMIT_UNLOCKED); + space->context_tbl[i] = ~0; + } + + for (i = 0, offset = 0; i < ARRAY_SIZE(space->session_tbl); i++) { + if (!space->session_tbl[i]) + continue; + + rc = tpm2_save_context(chip, space->session_tbl[i], + space->session_buf, PAGE_SIZE, + &offset); + + if (rc == -ENOENT) { + /* handle error saving session, just forget it */ + space->session_tbl[i] = 0; + } else if (rc < 0) { + tpm2_flush_space(chip); + return rc; + } + } + + return 0; +} + +int tpm2_commit_space(struct tpm_chip *chip, struct tpm_space *space, + u32 cc, u8 *buf, size_t *bufsiz) +{ + struct tpm_output_header *header = (void *)buf; + int rc; + + if (!space) + return 0; + + rc = tpm2_map_response_header(chip, cc, buf, *bufsiz); + if (rc) { + tpm2_flush_space(chip); + return rc; + } + + rc = tpm2_map_response_body(chip, cc, buf, *bufsiz); + if (rc) { + tpm2_flush_space(chip); + return rc; + } + + rc = tpm2_save_space(chip); + if (rc) { + tpm2_flush_space(chip); + return rc; + } + + *bufsiz = be32_to_cpu(header->length); + + memcpy(&space->context_tbl, &chip->work_space.context_tbl, + sizeof(space->context_tbl)); + memcpy(&space->session_tbl, &chip->work_space.session_tbl, + sizeof(space->session_tbl)); + memcpy(space->context_buf, chip->work_space.context_buf, PAGE_SIZE); + memcpy(space->session_buf, chip->work_space.session_buf, PAGE_SIZE); + + return 0; +} diff --git a/drivers/char/tpm/tpm2_eventlog.c b/drivers/char/tpm/tpm2_eventlog.c index 513897cf9c4b..34a8afa69138 100644 --- a/drivers/char/tpm/tpm2_eventlog.c +++ b/drivers/char/tpm/tpm2_eventlog.c @@ -56,18 +56,24 @@ static int calc_tpm2_event_size(struct tcg_pcr_event2 *event, efispecid = (struct tcg_efi_specid_event *)event_header->event; - for (i = 0; (i < event->count) && (i < TPM2_ACTIVE_PCR_BANKS); - i++) { + /* Check if event is malformed. */ + if (event->count > efispecid->num_algs) + return 0; + + for (i = 0; i < event->count; i++) { halg_size = sizeof(event->digests[i].alg_id); memcpy(&halg, marker, halg_size); marker = marker + halg_size; - for (j = 0; (j < efispecid->num_algs); j++) { + for (j = 0; j < efispecid->num_algs; j++) { if (halg == efispecid->digest_sizes[j].alg_id) { - marker = marker + + marker += efispecid->digest_sizes[j].digest_size; break; } } + /* Algorithm without known length. Such event is unparseable. */ + if (j == efispecid->num_algs) + return 0; } event_field = (struct tcg_event_field *)marker; diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 86f355b6df1d..b917b9d5f710 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -20,6 +20,9 @@ #include <linux/rculist.h> #include <linux/module.h> #include <linux/pm_runtime.h> +#ifdef CONFIG_ARM64 +#include <linux/arm-smccc.h> +#endif #include "tpm.h" #define ACPI_SIG_TPM2 "TPM2" @@ -34,6 +37,16 @@ enum crb_defaults { CRB_ACPI_START_INDEX = 1, }; +enum crb_loc_ctrl { + CRB_LOC_CTRL_REQUEST_ACCESS = BIT(0), + CRB_LOC_CTRL_RELINQUISH = BIT(1), +}; + +enum crb_loc_state { + CRB_LOC_STATE_LOC_ASSIGNED = BIT(1), + CRB_LOC_STATE_TPM_REG_VALID_STS = BIT(7), +}; + enum crb_ctrl_req { CRB_CTRL_REQ_CMD_READY = BIT(0), CRB_CTRL_REQ_GO_IDLE = BIT(1), @@ -52,18 +65,28 @@ enum crb_cancel { CRB_CANCEL_INVOKE = BIT(0), }; -struct crb_control_area { - u32 req; - u32 sts; - u32 cancel; - u32 start; - u32 int_enable; - u32 int_sts; - u32 cmd_size; - u32 cmd_pa_low; - u32 cmd_pa_high; - u32 rsp_size; - u64 rsp_pa; +struct crb_regs_head { + u32 loc_state; + u32 reserved1; + u32 loc_ctrl; + u32 loc_sts; + u8 reserved2[32]; + u64 intf_id; + u64 ctrl_ext; +} __packed; + +struct crb_regs_tail { + u32 ctrl_req; + u32 ctrl_sts; + u32 ctrl_cancel; + u32 ctrl_start; + u32 ctrl_int_enable; + u32 ctrl_int_sts; + u32 ctrl_cmd_size; + u32 ctrl_cmd_pa_low; + u32 ctrl_cmd_pa_high; + u32 ctrl_rsp_size; + u64 ctrl_rsp_pa; } __packed; enum crb_status { @@ -73,15 +96,26 @@ enum crb_status { enum crb_flags { CRB_FL_ACPI_START = BIT(0), CRB_FL_CRB_START = BIT(1), + CRB_FL_CRB_SMC_START = BIT(2), }; struct crb_priv { unsigned int flags; void __iomem *iobase; - struct crb_control_area __iomem *cca; + struct crb_regs_head __iomem *regs_h; + struct crb_regs_tail __iomem *regs_t; u8 __iomem *cmd; u8 __iomem *rsp; u32 cmd_size; + u32 smc_func_id; +}; + +struct tpm2_crb_smc { + u32 interrupt; + u8 interrupt_flags; + u8 op_flags; + u16 reserved2; + u32 smc_func_id; }; /** @@ -101,15 +135,35 @@ struct crb_priv { */ static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv) { - if (priv->flags & CRB_FL_ACPI_START) + if ((priv->flags & CRB_FL_ACPI_START) || + (priv->flags & CRB_FL_CRB_SMC_START)) return 0; - iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->cca->req); + iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req); /* we don't really care when this settles */ return 0; } +static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, + unsigned long timeout) +{ + ktime_t start; + ktime_t stop; + + start = ktime_get(); + stop = ktime_add(start, ms_to_ktime(timeout)); + + do { + if ((ioread32(reg) & mask) == value) + return true; + + usleep_range(50, 100); + } while (ktime_before(ktime_get(), stop)); + + return false; +} + /** * crb_cmd_ready - request tpm crb device to enter ready state * @@ -127,35 +181,57 @@ static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv) static int __maybe_unused crb_cmd_ready(struct device *dev, struct crb_priv *priv) { - ktime_t stop, start; - - if (priv->flags & CRB_FL_ACPI_START) + if ((priv->flags & CRB_FL_ACPI_START) || + (priv->flags & CRB_FL_CRB_SMC_START)) return 0; - iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->cca->req); + iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->regs_t->ctrl_req); + if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req, + CRB_CTRL_REQ_CMD_READY /* mask */, + 0, /* value */ + TPM2_TIMEOUT_C)) { + dev_warn(dev, "cmdReady timed out\n"); + return -ETIME; + } - start = ktime_get(); - stop = ktime_add(start, ms_to_ktime(TPM2_TIMEOUT_C)); - do { - if (!(ioread32(&priv->cca->req) & CRB_CTRL_REQ_CMD_READY)) - return 0; - usleep_range(50, 100); - } while (ktime_before(ktime_get(), stop)); + return 0; +} - if (ioread32(&priv->cca->req) & CRB_CTRL_REQ_CMD_READY) { - dev_warn(dev, "cmdReady timed out\n"); +static int crb_request_locality(struct tpm_chip *chip, int loc) +{ + struct crb_priv *priv = dev_get_drvdata(&chip->dev); + u32 value = CRB_LOC_STATE_LOC_ASSIGNED | + CRB_LOC_STATE_TPM_REG_VALID_STS; + + if (!priv->regs_h) + return 0; + + iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl); + if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value, + TPM2_TIMEOUT_C)) { + dev_warn(&chip->dev, "TPM_LOC_STATE_x.requestAccess timed out\n"); return -ETIME; } return 0; } +static void crb_relinquish_locality(struct tpm_chip *chip, int loc) +{ + struct crb_priv *priv = dev_get_drvdata(&chip->dev); + + if (!priv->regs_h) + return; + + iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl); +} + static u8 crb_status(struct tpm_chip *chip) { struct crb_priv *priv = dev_get_drvdata(&chip->dev); u8 sts = 0; - if ((ioread32(&priv->cca->start) & CRB_START_INVOKE) != + if ((ioread32(&priv->regs_t->ctrl_start) & CRB_START_INVOKE) != CRB_START_INVOKE) sts |= CRB_DRV_STS_COMPLETE; @@ -171,13 +247,12 @@ static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count) if (count < 6) return -EIO; - if (ioread32(&priv->cca->sts) & CRB_CTRL_STS_ERROR) + if (ioread32(&priv->regs_t->ctrl_sts) & CRB_CTRL_STS_ERROR) return -EIO; memcpy_fromio(buf, priv->rsp, 6); expected = be32_to_cpup((__be32 *) &buf[2]); - - if (expected > count) + if (expected > count || expected < 6) return -EIO; memcpy_fromio(&buf[6], &priv->rsp[6], expected - 6); @@ -202,6 +277,34 @@ static int crb_do_acpi_start(struct tpm_chip *chip) return rc; } +#ifdef CONFIG_ARM64 +/* + * This is a TPM Command Response Buffer start method that invokes a + * Secure Monitor Call to requrest the firmware to execute or cancel + * a TPM 2.0 command. + */ +static int tpm_crb_smc_start(struct device *dev, unsigned long func_id) +{ + struct arm_smccc_res res; + + arm_smccc_smc(func_id, 0, 0, 0, 0, 0, 0, 0, &res); + if (res.a0 != 0) { + dev_err(dev, + FW_BUG "tpm_crb_smc_start() returns res.a0 = 0x%lx\n", + res.a0); + return -EIO; + } + + return 0; +} +#else +static int tpm_crb_smc_start(struct device *dev, unsigned long func_id) +{ + dev_err(dev, FW_BUG "tpm_crb: incorrect start method\n"); + return -EINVAL; +} +#endif + static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) { struct crb_priv *priv = dev_get_drvdata(&chip->dev); @@ -210,7 +313,7 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) /* Zero the cancel register so that the next command will not get * canceled. */ - iowrite32(0, &priv->cca->cancel); + iowrite32(0, &priv->regs_t->ctrl_cancel); if (len > priv->cmd_size) { dev_err(&chip->dev, "invalid command count value %zd %d\n", @@ -224,11 +327,16 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) wmb(); if (priv->flags & CRB_FL_CRB_START) - iowrite32(CRB_START_INVOKE, &priv->cca->start); + iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start); if (priv->flags & CRB_FL_ACPI_START) rc = crb_do_acpi_start(chip); + if (priv->flags & CRB_FL_CRB_SMC_START) { + iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start); + rc = tpm_crb_smc_start(&chip->dev, priv->smc_func_id); + } + return rc; } @@ -236,7 +344,7 @@ static void crb_cancel(struct tpm_chip *chip) { struct crb_priv *priv = dev_get_drvdata(&chip->dev); - iowrite32(CRB_CANCEL_INVOKE, &priv->cca->cancel); + iowrite32(CRB_CANCEL_INVOKE, &priv->regs_t->ctrl_cancel); if ((priv->flags & CRB_FL_ACPI_START) && crb_do_acpi_start(chip)) dev_err(&chip->dev, "ACPI Start failed\n"); @@ -245,7 +353,7 @@ static void crb_cancel(struct tpm_chip *chip) static bool crb_req_canceled(struct tpm_chip *chip, u8 status) { struct crb_priv *priv = dev_get_drvdata(&chip->dev); - u32 cancel = ioread32(&priv->cca->cancel); + u32 cancel = ioread32(&priv->regs_t->ctrl_cancel); return (cancel & CRB_CANCEL_INVOKE) == CRB_CANCEL_INVOKE; } @@ -257,6 +365,8 @@ static const struct tpm_class_ops tpm_crb = { .send = crb_send, .cancel = crb_cancel, .req_canceled = crb_req_canceled, + .request_locality = crb_request_locality, + .relinquish_locality = crb_relinquish_locality, .req_complete_mask = CRB_DRV_STS_COMPLETE, .req_complete_val = CRB_DRV_STS_COMPLETE, }; @@ -295,6 +405,27 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv, return priv->iobase + (new_res.start - io_res->start); } +/* + * Work around broken BIOSs that return inconsistent values from the ACPI + * region vs the registers. Trust the ACPI region. Such broken systems + * probably cannot send large TPM commands since the buffer will be truncated. + */ +static u64 crb_fixup_cmd_size(struct device *dev, struct resource *io_res, + u64 start, u64 size) +{ + if (io_res->start > start || io_res->end < start) + return size; + + if (start + size - 1 <= io_res->end) + return size; + + dev_err(dev, + FW_BUG "ACPI region does not cover the entire command/response buffer. %pr vs %llx %llx\n", + io_res, start, size); + + return io_res->end - start + 1; +} + static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, struct acpi_table_tpm2 *buf) { @@ -324,10 +455,22 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, if (IS_ERR(priv->iobase)) return PTR_ERR(priv->iobase); - priv->cca = crb_map_res(dev, priv, &io_res, buf->control_address, - sizeof(struct crb_control_area)); - if (IS_ERR(priv->cca)) - return PTR_ERR(priv->cca); + /* The ACPI IO region starts at the head area and continues to include + * the control area, as one nice sane region except for some older + * stuff that puts the control area outside the ACPI IO region. + */ + if (!(priv->flags & CRB_FL_ACPI_START)) { + if (buf->control_address == io_res.start + + sizeof(*priv->regs_h)) + priv->regs_h = priv->iobase; + else + dev_warn(dev, FW_BUG "Bad ACPI memory layout"); + } + + priv->regs_t = crb_map_res(dev, priv, &io_res, buf->control_address, + sizeof(struct crb_regs_tail)); + if (IS_ERR(priv->regs_t)) + return PTR_ERR(priv->regs_t); /* * PTT HW bug w/a: wake up the device to access @@ -337,10 +480,11 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, if (ret) return ret; - pa_high = ioread32(&priv->cca->cmd_pa_high); - pa_low = ioread32(&priv->cca->cmd_pa_low); + pa_high = ioread32(&priv->regs_t->ctrl_cmd_pa_high); + pa_low = ioread32(&priv->regs_t->ctrl_cmd_pa_low); cmd_pa = ((u64)pa_high << 32) | pa_low; - cmd_size = ioread32(&priv->cca->cmd_size); + cmd_size = crb_fixup_cmd_size(dev, &io_res, cmd_pa, + ioread32(&priv->regs_t->ctrl_cmd_size)); dev_dbg(dev, "cmd_hi = %X cmd_low = %X cmd_size %X\n", pa_high, pa_low, cmd_size); @@ -351,9 +495,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, goto out; } - memcpy_fromio(&rsp_pa, &priv->cca->rsp_pa, 8); + memcpy_fromio(&rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8); rsp_pa = le64_to_cpu(rsp_pa); - rsp_size = ioread32(&priv->cca->rsp_size); + rsp_size = crb_fixup_cmd_size(dev, &io_res, rsp_pa, + ioread32(&priv->regs_t->ctrl_rsp_size)); if (cmd_pa != rsp_pa) { priv->rsp = crb_map_res(dev, priv, &io_res, rsp_pa, rsp_size); @@ -386,6 +531,7 @@ static int crb_acpi_add(struct acpi_device *device) struct crb_priv *priv; struct tpm_chip *chip; struct device *dev = &device->dev; + struct tpm2_crb_smc *crb_smc; acpi_status status; u32 sm; int rc; @@ -418,6 +564,19 @@ static int crb_acpi_add(struct acpi_device *device) sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) priv->flags |= CRB_FL_ACPI_START; + if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_SMC) { + if (buf->header.length < (sizeof(*buf) + sizeof(*crb_smc))) { + dev_err(dev, + FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n", + buf->header.length, + ACPI_TPM2_COMMAND_BUFFER_WITH_SMC); + return -EINVAL; + } + crb_smc = ACPI_ADD_PTR(struct tpm2_crb_smc, buf, sizeof(*buf)); + priv->smc_func_id = crb_smc->smc_func_id; + priv->flags |= CRB_FL_CRB_SMC_START; + } + rc = crb_map_io(device, priv, buf); if (rc) return rc; @@ -463,8 +622,7 @@ static int crb_acpi_remove(struct acpi_device *device) return 0; } -#ifdef CONFIG_PM -static int crb_pm_runtime_suspend(struct device *dev) +static int __maybe_unused crb_pm_runtime_suspend(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); struct crb_priv *priv = dev_get_drvdata(&chip->dev); @@ -472,17 +630,38 @@ static int crb_pm_runtime_suspend(struct device *dev) return crb_go_idle(dev, priv); } -static int crb_pm_runtime_resume(struct device *dev) +static int __maybe_unused crb_pm_runtime_resume(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); struct crb_priv *priv = dev_get_drvdata(&chip->dev); return crb_cmd_ready(dev, priv); } -#endif /* CONFIG_PM */ + +static int __maybe_unused crb_pm_suspend(struct device *dev) +{ + int ret; + + ret = tpm_pm_suspend(dev); + if (ret) + return ret; + + return crb_pm_runtime_suspend(dev); +} + +static int __maybe_unused crb_pm_resume(struct device *dev) +{ + int ret; + + ret = crb_pm_runtime_resume(dev); + if (ret) + return ret; + + return tpm_pm_resume(dev); +} static const struct dev_pm_ops crb_pm = { - SET_SYSTEM_SLEEP_PM_OPS(tpm_pm_suspend, tpm_pm_resume) + SET_SYSTEM_SLEEP_PM_OPS(crb_pm_suspend, crb_pm_resume) SET_RUNTIME_PM_OPS(crb_pm_runtime_suspend, crb_pm_runtime_resume, NULL) }; diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 62ee44e57ddc..dc47fa222a26 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -278,22 +278,22 @@ enum tis_defaults { #define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4)) #define TPM_DID_VID(l) (0x0006 | ((l) << 4)) -static int check_locality(struct tpm_chip *chip, int loc) +static bool check_locality(struct tpm_chip *chip, int loc) { u8 buf; int rc; rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1); if (rc < 0) - return rc; + return false; if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) { tpm_dev.locality = loc; - return loc; + return true; } - return -EIO; + return false; } /* implementation similar to tpm_tis */ @@ -315,7 +315,7 @@ static int request_locality(struct tpm_chip *chip, int loc) unsigned long stop; u8 buf = TPM_ACCESS_REQUEST_USE; - if (check_locality(chip, loc) >= 0) + if (check_locality(chip, loc)) return loc; iic_tpm_write(TPM_ACCESS(loc), &buf, 1); @@ -323,7 +323,7 @@ static int request_locality(struct tpm_chip *chip, int loc) /* wait for burstcount */ stop = jiffies + chip->timeout_a; do { - if (check_locality(chip, loc) >= 0) + if (check_locality(chip, loc)) return loc; usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI); } while (time_before(jiffies, stop)); diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index e3a9155ee671..c6428771841f 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -49,9 +49,10 @@ */ #define TPM_I2C_MAX_BUF_SIZE 32 #define TPM_I2C_RETRY_COUNT 32 -#define TPM_I2C_BUS_DELAY 1 /* msec */ -#define TPM_I2C_RETRY_DELAY_SHORT 2 /* msec */ -#define TPM_I2C_RETRY_DELAY_LONG 10 /* msec */ +#define TPM_I2C_BUS_DELAY 1000 /* usec */ +#define TPM_I2C_RETRY_DELAY_SHORT (2 * 1000) /* usec */ +#define TPM_I2C_RETRY_DELAY_LONG (10 * 1000) /* usec */ +#define TPM_I2C_DELAY_RANGE 300 /* usec */ #define OF_IS_TPM2 ((void *)1) #define I2C_IS_TPM2 1 @@ -123,7 +124,9 @@ static s32 i2c_nuvoton_write_status(struct i2c_client *client, u8 data) /* this causes the current command to be aborted */ for (i = 0, status = -1; i < TPM_I2C_RETRY_COUNT && status < 0; i++) { status = i2c_nuvoton_write_buf(client, TPM_STS, 1, &data); - msleep(TPM_I2C_BUS_DELAY); + if (status < 0) + usleep_range(TPM_I2C_BUS_DELAY, TPM_I2C_BUS_DELAY + + TPM_I2C_DELAY_RANGE); } return status; } @@ -160,7 +163,8 @@ static int i2c_nuvoton_get_burstcount(struct i2c_client *client, burst_count = min_t(u8, TPM_I2C_MAX_BUF_SIZE, data); break; } - msleep(TPM_I2C_BUS_DELAY); + usleep_range(TPM_I2C_BUS_DELAY, TPM_I2C_BUS_DELAY + + TPM_I2C_DELAY_RANGE); } while (time_before(jiffies, stop)); return burst_count; @@ -203,13 +207,17 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value, return 0; /* use polling to wait for the event */ - ten_msec = jiffies + msecs_to_jiffies(TPM_I2C_RETRY_DELAY_LONG); + ten_msec = jiffies + usecs_to_jiffies(TPM_I2C_RETRY_DELAY_LONG); stop = jiffies + timeout; do { if (time_before(jiffies, ten_msec)) - msleep(TPM_I2C_RETRY_DELAY_SHORT); + usleep_range(TPM_I2C_RETRY_DELAY_SHORT, + TPM_I2C_RETRY_DELAY_SHORT + + TPM_I2C_DELAY_RANGE); else - msleep(TPM_I2C_RETRY_DELAY_LONG); + usleep_range(TPM_I2C_RETRY_DELAY_LONG, + TPM_I2C_RETRY_DELAY_LONG + + TPM_I2C_DELAY_RANGE); status_valid = i2c_nuvoton_check_status(chip, mask, value); if (status_valid) diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 1b9d61ffe991..f01d083eced2 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -299,6 +299,8 @@ static int tpm_ibmvtpm_remove(struct vio_dev *vdev) } kfree(ibmvtpm); + /* For tpm_ibmvtpm_get_desired_dma */ + dev_set_drvdata(&vdev->dev, NULL); return 0; } @@ -313,14 +315,16 @@ static int tpm_ibmvtpm_remove(struct vio_dev *vdev) static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev) { struct tpm_chip *chip = dev_get_drvdata(&vdev->dev); - struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); + struct ibmvtpm_dev *ibmvtpm; /* * ibmvtpm initializes at probe time, so the data we are * asking for may not be set yet. Estimate that 4K required * for TCE-mapped buffer in addition to CRQ. */ - if (!ibmvtpm) + if (chip) + ibmvtpm = dev_get_drvdata(&chip->dev); + else return CRQ_RES_BUF_SIZE + PAGE_SIZE; return CRQ_RES_BUF_SIZE + ibmvtpm->rtce_size; diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index c0f296b5d413..b617b2eeb080 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -56,7 +56,7 @@ static int wait_startup(struct tpm_chip *chip, int l) return -1; } -static int check_locality(struct tpm_chip *chip, int l) +static bool check_locality(struct tpm_chip *chip, int l) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); int rc; @@ -64,30 +64,22 @@ static int check_locality(struct tpm_chip *chip, int l) rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access); if (rc < 0) - return rc; + return false; if ((access & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) - return priv->locality = l; + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) { + priv->locality = l; + return true; + } - return -1; + return false; } -static void release_locality(struct tpm_chip *chip, int l, int force) +static void release_locality(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; - - if (force || (access & - (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == - (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) - tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY); + tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY); } static int request_locality(struct tpm_chip *chip, int l) @@ -96,7 +88,7 @@ static int request_locality(struct tpm_chip *chip, int l) unsigned long stop, timeout; long rc; - if (check_locality(chip, l) >= 0) + if (check_locality(chip, l)) return l; rc = tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_REQUEST_USE); @@ -112,7 +104,7 @@ again: return -1; rc = wait_event_interruptible_timeout(priv->int_queue, (check_locality - (chip, l) >= 0), + (chip, l)), timeout); if (rc > 0) return l; @@ -123,7 +115,7 @@ again: } else { /* wait for burstcount */ do { - if (check_locality(chip, l) >= 0) + if (check_locality(chip, l)) return l; msleep(TPM_TIMEOUT); } while (time_before(jiffies, stop)); @@ -160,8 +152,10 @@ static int get_burstcount(struct tpm_chip *chip) u32 value; /* wait for burstcount */ - /* which timeout value, spec has 2 answers (c & d) */ - stop = jiffies + chip->timeout_d; + if (chip->flags & TPM_CHIP_FLAG_TPM2) + stop = jiffies + chip->timeout_a; + else + stop = jiffies + chip->timeout_d; do { rc = tpm_tis_read32(priv, TPM_STS(priv->locality), &value); if (rc < 0) @@ -250,7 +244,6 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) out: tpm_tis_ready(chip); - release_locality(chip, priv->locality, 0); return size; } @@ -266,9 +259,6 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) size_t count = 0; bool itpm = priv->flags & TPM_TIS_ITPM_WORKAROUND; - if (request_locality(chip, 0) < 0) - return -EBUSY; - status = tpm_tis_status(chip); if ((status & TPM_STS_COMMAND_READY) == 0) { tpm_tis_ready(chip); @@ -327,7 +317,6 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) out_err: tpm_tis_ready(chip); - release_locality(chip, priv->locality, 0); return rc; } @@ -388,7 +377,6 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) return len; out_err: tpm_tis_ready(chip); - release_locality(chip, priv->locality, 0); return rc; } @@ -475,12 +463,14 @@ static int probe_itpm(struct tpm_chip *chip) if (vendor != TPM_VID_INTEL) return 0; + if (request_locality(chip, 0) != 0) + return -EBUSY; + rc = tpm_tis_send_data(chip, cmd_getticks, len); if (rc == 0) goto out; tpm_tis_ready(chip); - release_locality(chip, priv->locality, 0); priv->flags |= TPM_TIS_ITPM_WORKAROUND; @@ -494,7 +484,7 @@ static int probe_itpm(struct tpm_chip *chip) out: tpm_tis_ready(chip); - release_locality(chip, priv->locality, 0); + release_locality(chip, priv->locality); return rc; } @@ -533,7 +523,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) wake_up_interruptible(&priv->read_queue); if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT) for (i = 0; i < 5; i++) - if (check_locality(chip, i) >= 0) + if (check_locality(chip, i)) break; if (interrupt & (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT | @@ -668,7 +658,6 @@ void tpm_tis_remove(struct tpm_chip *chip) interrupt = 0; tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt); - release_locality(chip, priv->locality, 1); } EXPORT_SYMBOL_GPL(tpm_tis_remove); @@ -682,6 +671,8 @@ static const struct tpm_class_ops tpm_tis = { .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_canceled = tpm_tis_req_canceled, + .request_locality = request_locality, + .relinquish_locality = release_locality, }; int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, @@ -724,11 +715,6 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, intmask &= ~TPM_GLOBAL_INT_ENABLE; tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); - if (request_locality(chip, 0) != 0) { - rc = -ENODEV; - goto out_err; - } - rc = tpm2_probe(chip); if (rc) goto out_err; diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c index 5292e5768a7e..88fe72ae967f 100644 --- a/drivers/char/tpm/tpm_tis_spi.c +++ b/drivers/char/tpm/tpm_tis_spi.c @@ -47,8 +47,8 @@ struct tpm_tis_spi_phy { struct tpm_tis_data priv; struct spi_device *spi_device; - u8 tx_buf[MAX_SPI_FRAMESIZE + 4]; - u8 rx_buf[MAX_SPI_FRAMESIZE + 4]; + u8 tx_buf[4]; + u8 rx_buf[4]; }; static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data) @@ -56,122 +56,98 @@ static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *da return container_of(data, struct tpm_tis_spi_phy, priv); } -static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr, - u16 len, u8 *result) +static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, + u8 *buffer, u8 direction) { struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); - int ret, i; + int ret = 0; + int i; struct spi_message m; - struct spi_transfer spi_xfer = { - .tx_buf = phy->tx_buf, - .rx_buf = phy->rx_buf, - .len = 4, - }; + struct spi_transfer spi_xfer; + u8 transfer_len; - if (len > MAX_SPI_FRAMESIZE) - return -ENOMEM; + spi_bus_lock(phy->spi_device->master); - phy->tx_buf[0] = 0x80 | (len - 1); - phy->tx_buf[1] = 0xd4; - phy->tx_buf[2] = (addr >> 8) & 0xFF; - phy->tx_buf[3] = addr & 0xFF; + while (len) { + transfer_len = min_t(u16, len, MAX_SPI_FRAMESIZE); - spi_xfer.cs_change = 1; - spi_message_init(&m); - spi_message_add_tail(&spi_xfer, &m); + phy->tx_buf[0] = direction | (transfer_len - 1); + phy->tx_buf[1] = 0xd4; + phy->tx_buf[2] = addr >> 8; + phy->tx_buf[3] = addr; + + memset(&spi_xfer, 0, sizeof(spi_xfer)); + spi_xfer.tx_buf = phy->tx_buf; + spi_xfer.rx_buf = phy->rx_buf; + spi_xfer.len = 4; + spi_xfer.cs_change = 1; - spi_bus_lock(phy->spi_device->master); - ret = spi_sync_locked(phy->spi_device, &m); - if (ret < 0) - goto exit; - - memset(phy->tx_buf, 0, len); - - /* According to TCG PTP specification, if there is no TPM present at - * all, then the design has a weak pull-up on MISO. If a TPM is not - * present, a pull-up on MISO means that the SB controller sees a 1, - * and will latch in 0xFF on the read. - */ - for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) { - spi_xfer.len = 1; spi_message_init(&m); spi_message_add_tail(&spi_xfer, &m); ret = spi_sync_locked(phy->spi_device, &m); if (ret < 0) goto exit; - } - - spi_xfer.cs_change = 0; - spi_xfer.len = len; - spi_xfer.rx_buf = result; - - spi_message_init(&m); - spi_message_add_tail(&spi_xfer, &m); - ret = spi_sync_locked(phy->spi_device, &m); - -exit: - spi_bus_unlock(phy->spi_device->master); - return ret; -} - -static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr, - u16 len, u8 *value) -{ - struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); - int ret, i; - struct spi_message m; - struct spi_transfer spi_xfer = { - .tx_buf = phy->tx_buf, - .rx_buf = phy->rx_buf, - .len = 4, - }; - - if (len > MAX_SPI_FRAMESIZE) - return -ENOMEM; - - phy->tx_buf[0] = len - 1; - phy->tx_buf[1] = 0xd4; - phy->tx_buf[2] = (addr >> 8) & 0xFF; - phy->tx_buf[3] = addr & 0xFF; - spi_xfer.cs_change = 1; - spi_message_init(&m); - spi_message_add_tail(&spi_xfer, &m); + if ((phy->rx_buf[3] & 0x01) == 0) { + // handle SPI wait states + phy->tx_buf[0] = 0; + + for (i = 0; i < TPM_RETRY; i++) { + spi_xfer.len = 1; + spi_message_init(&m); + spi_message_add_tail(&spi_xfer, &m); + ret = spi_sync_locked(phy->spi_device, &m); + if (ret < 0) + goto exit; + if (phy->rx_buf[0] & 0x01) + break; + } + + if (i == TPM_RETRY) { + ret = -ETIMEDOUT; + goto exit; + } + } + + spi_xfer.cs_change = 0; + spi_xfer.len = transfer_len; + spi_xfer.delay_usecs = 5; + + if (direction) { + spi_xfer.tx_buf = NULL; + spi_xfer.rx_buf = buffer; + } else { + spi_xfer.tx_buf = buffer; + spi_xfer.rx_buf = NULL; + } - spi_bus_lock(phy->spi_device->master); - ret = spi_sync_locked(phy->spi_device, &m); - if (ret < 0) - goto exit; - - memset(phy->tx_buf, 0, len); - - /* According to TCG PTP specification, if there is no TPM present at - * all, then the design has a weak pull-up on MISO. If a TPM is not - * present, a pull-up on MISO means that the SB controller sees a 1, - * and will latch in 0xFF on the read. - */ - for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) { - spi_xfer.len = 1; spi_message_init(&m); spi_message_add_tail(&spi_xfer, &m); ret = spi_sync_locked(phy->spi_device, &m); if (ret < 0) goto exit; - } - spi_xfer.len = len; - spi_xfer.tx_buf = value; - spi_xfer.cs_change = 0; - spi_xfer.tx_buf = value; - spi_message_init(&m); - spi_message_add_tail(&spi_xfer, &m); - ret = spi_sync_locked(phy->spi_device, &m); + len -= transfer_len; + buffer += transfer_len; + } exit: spi_bus_unlock(phy->spi_device->master); return ret; } +static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, u8 *result) +{ + return tpm_tis_spi_transfer(data, addr, len, result, 0x80); +} + +static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, u8 *value) +{ + return tpm_tis_spi_transfer(data, addr, len, value, 0); +} + static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result) { int rc; diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c new file mode 100644 index 000000000000..c636e7fdd1f5 --- /dev/null +++ b/drivers/char/tpm/tpmrm-dev.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017 James.Bottomley@HansenPartnership.com + * + * GPLv2 + */ +#include <linux/slab.h> +#include "tpm-dev.h" + +struct tpmrm_priv { + struct file_priv priv; + struct tpm_space space; +}; + +static int tpmrm_open(struct inode *inode, struct file *file) +{ + struct tpm_chip *chip; + struct tpmrm_priv *priv; + int rc; + + chip = container_of(inode->i_cdev, struct tpm_chip, cdevs); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + rc = tpm2_init_space(&priv->space); + if (rc) { + kfree(priv); + return -ENOMEM; + } + + tpm_common_open(file, chip, &priv->priv); + + return 0; +} + +static int tpmrm_release(struct inode *inode, struct file *file) +{ + struct file_priv *fpriv = file->private_data; + struct tpmrm_priv *priv = container_of(fpriv, struct tpmrm_priv, priv); + + tpm_common_release(file, fpriv); + tpm2_del_space(fpriv->chip, &priv->space); + kfree(priv); + + return 0; +} + +ssize_t tpmrm_write(struct file *file, const char __user *buf, + size_t size, loff_t *off) +{ + struct file_priv *fpriv = file->private_data; + struct tpmrm_priv *priv = container_of(fpriv, struct tpmrm_priv, priv); + + return tpm_common_write(file, buf, size, off, &priv->space); +} + +const struct file_operations tpmrm_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = tpmrm_open, + .read = tpm_common_read, + .write = tpmrm_write, + .release = tpmrm_release, +}; + diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index c6f760b3d9d7..7d041d026680 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -2202,14 +2202,16 @@ static int virtcons_freeze(struct virtio_device *vdev) vdev->config->reset(vdev); - virtqueue_disable_cb(portdev->c_ivq); + if (use_multiport(portdev)) + virtqueue_disable_cb(portdev->c_ivq); cancel_work_sync(&portdev->control_work); cancel_work_sync(&portdev->config_work); /* * Once more: if control_work_handler() was running, it would * enable the cb as the last step. */ - virtqueue_disable_cb(portdev->c_ivq); + if (use_multiport(portdev)) + virtqueue_disable_cb(portdev->c_ivq); remove_controlq_data(portdev); list_for_each_entry(port, &portdev->ports, list) { |