diff options
Diffstat (limited to 'drivers/char')
39 files changed, 1324 insertions, 962 deletions
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 59f25286befe..3daae8ddd511 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -308,6 +308,19 @@ config HW_RANDOM_HISI If unsure, say Y. +config HW_RANDOM_HISI_V2 + tristate "HiSilicon True Random Number Generator V2 support" + depends on HW_RANDOM && ARM64 && ACPI + default HW_RANDOM + help + This driver provides kernel-side support for the True Random Number + Generator V2 hardware found on HiSilicon Hi1620 SoC. + + To compile this driver as a module, choose M here: the + module will be called hisi-trng-v2. + + If unsure, say Y. + config HW_RANDOM_ST tristate "ST Microelectronics HW Random Number Generator support" depends on HW_RANDOM && ARCH_STI @@ -440,6 +453,19 @@ config HW_RANDOM_OPTEE If unsure, say Y. +config HW_RANDOM_NPCM + tristate "NPCM Random Number Generator support" + depends on ARCH_NPCM || COMPILE_TEST + default HW_RANDOM + help + This driver provides support for the Random Number + Generator hardware available in Nuvoton NPCM SoCs. + + To compile this driver as a module, choose M here: the + module will be called npcm-rng. + + If unsure, say Y. + endif # HW_RANDOM config UML_RANDOM @@ -458,7 +484,7 @@ config UML_RANDOM /dev/hwrng and injects the entropy into /dev/random. config HW_RANDOM_KEYSTONE - depends on ARCH_KEYSTONE + depends on ARCH_KEYSTONE || COMPILE_TEST default HW_RANDOM tristate "TI Keystone NETCP SA Hardware random number generator" help diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 7c9ef4a7667f..a7801b49ce6c 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -27,6 +27,7 @@ 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_HISI) += hisi-rng.o +obj-$(CONFIG_HW_RANDOM_HISI_V2) += hisi-trng-v2.o obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o obj-$(CONFIG_HW_RANDOM_ST) += st-rng.o @@ -39,3 +40,4 @@ obj-$(CONFIG_HW_RANDOM_MTK) += mtk-rng.o obj-$(CONFIG_HW_RANDOM_S390) += s390-trng.o obj-$(CONFIG_HW_RANDOM_KEYSTONE) += ks-sa-rng.o obj-$(CONFIG_HW_RANDOM_OPTEE) += optee-rng.o +obj-$(CONFIG_HW_RANDOM_NPCM) += npcm-rng.o diff --git a/drivers/char/hw_random/atmel-rng.c b/drivers/char/hw_random/atmel-rng.c index e55705745d5e..ecb71c4317a5 100644 --- a/drivers/char/hw_random/atmel-rng.c +++ b/drivers/char/hw_random/atmel-rng.c @@ -14,14 +14,22 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/hw_random.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #define TRNG_CR 0x00 +#define TRNG_MR 0x04 #define TRNG_ISR 0x1c #define TRNG_ODATA 0x50 #define TRNG_KEY 0x524e4700 /* RNG */ +#define TRNG_HALFR BIT(0) /* generate RN every 168 cycles */ + +struct atmel_trng_data { + bool has_half_rate; +}; + struct atmel_trng { struct clk *clk; void __iomem *base; @@ -62,21 +70,31 @@ static void atmel_trng_disable(struct atmel_trng *trng) static int atmel_trng_probe(struct platform_device *pdev) { struct atmel_trng *trng; - struct resource *res; + const struct atmel_trng_data *data; int ret; trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); if (!trng) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - trng->base = devm_ioremap_resource(&pdev->dev, res); + trng->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(trng->base)) return PTR_ERR(trng->base); trng->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(trng->clk)) return PTR_ERR(trng->clk); + data = of_device_get_match_data(&pdev->dev); + if (!data) + return -ENODEV; + + if (data->has_half_rate) { + unsigned long rate = clk_get_rate(trng->clk); + + /* if peripheral clk is above 100MHz, set HALFR */ + if (rate > 100000000) + writel(TRNG_HALFR, trng->base + TRNG_MR); + } ret = clk_prepare_enable(trng->clk); if (ret) @@ -141,9 +159,24 @@ static const struct dev_pm_ops atmel_trng_pm_ops = { }; #endif /* CONFIG_PM */ +static const struct atmel_trng_data at91sam9g45_config = { + .has_half_rate = false, +}; + +static const struct atmel_trng_data sam9x60_config = { + .has_half_rate = true, +}; + static const struct of_device_id atmel_trng_dt_ids[] = { - { .compatible = "atmel,at91sam9g45-trng" }, - { /* sentinel */ } + { + .compatible = "atmel,at91sam9g45-trng", + .data = &at91sam9g45_config, + }, { + .compatible = "microchip,sam9x60-trng", + .data = &sam9x60_config, + }, { + /* sentinel */ + } }; MODULE_DEVICE_TABLE(of, atmel_trng_dt_ids); diff --git a/drivers/char/hw_random/bcm2835-rng.c b/drivers/char/hw_random/bcm2835-rng.c index f759790c3cdb..d2a5791eb49f 100644 --- a/drivers/char/hw_random/bcm2835-rng.c +++ b/drivers/char/hw_random/bcm2835-rng.c @@ -142,7 +142,6 @@ static int bcm2835_rng_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; const struct of_device_id *rng_id; struct bcm2835_rng_priv *priv; - struct resource *r; int err; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -151,10 +150,8 @@ static int bcm2835_rng_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - /* map peripheral */ - priv->base = devm_ioremap_resource(dev, r); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 80b850ef1bf6..d2d7a42d7e0d 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -13,7 +13,6 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> -#include <linux/freezer.h> #include <linux/fs.h> #include <linux/hw_random.h> #include <linux/kernel.h> @@ -112,6 +111,14 @@ static void drop_current_rng(void) } /* Returns ERR_PTR(), NULL or refcounted hwrng */ +static struct hwrng *get_current_rng_nolock(void) +{ + if (current_rng) + kref_get(¤t_rng->ref); + + return current_rng; +} + static struct hwrng *get_current_rng(void) { struct hwrng *rng; @@ -119,9 +126,7 @@ static struct hwrng *get_current_rng(void) if (mutex_lock_interruptible(&rng_mutex)) return ERR_PTR(-ERESTARTSYS); - rng = current_rng; - if (rng) - kref_get(&rng->ref); + rng = get_current_rng_nolock(); mutex_unlock(&rng_mutex); return rng; @@ -156,8 +161,6 @@ static int hwrng_init(struct hwrng *rng) reinit_completion(&rng->cleanup_done); skip_init: - add_early_randomness(rng); - current_quality = rng->quality ? : default_quality; if (current_quality > 1024) current_quality = 1024; @@ -321,12 +324,13 @@ static ssize_t hwrng_attr_current_store(struct device *dev, const char *buf, size_t len) { int err = -ENODEV; - struct hwrng *rng; + struct hwrng *rng, *old_rng, *new_rng; err = mutex_lock_interruptible(&rng_mutex); if (err) return -ERESTARTSYS; + old_rng = current_rng; if (sysfs_streq(buf, "")) { err = enable_best_rng(); } else { @@ -338,9 +342,15 @@ static ssize_t hwrng_attr_current_store(struct device *dev, } } } - + new_rng = get_current_rng_nolock(); mutex_unlock(&rng_mutex); + if (new_rng) { + if (new_rng != old_rng) + add_early_randomness(new_rng); + put_rng(new_rng); + } + return err ? : len; } @@ -422,9 +432,7 @@ static int hwrng_fillfn(void *unused) { long rc; - set_freezable(); - - while (!kthread_freezable_should_stop(NULL)) { + while (!kthread_should_stop()) { struct hwrng *rng; rng = get_current_rng(); @@ -460,13 +468,15 @@ static void start_khwrngd(void) int hwrng_register(struct hwrng *rng) { int err = -EINVAL; - struct hwrng *old_rng, *tmp; + struct hwrng *tmp; struct list_head *rng_list_ptr; + bool is_new_current = false; if (!rng->name || (!rng->data_read && !rng->read)) goto out; mutex_lock(&rng_mutex); + /* Must not register two RNGs with the same name. */ err = -EEXIST; list_for_each_entry(tmp, &rng_list, list) { @@ -485,10 +495,8 @@ int hwrng_register(struct hwrng *rng) } list_add_tail(&rng->list, rng_list_ptr); - old_rng = current_rng; - err = 0; - if (!old_rng || - (!cur_rng_set_by_user && rng->quality > old_rng->quality)) { + if (!current_rng || + (!cur_rng_set_by_user && rng->quality > current_rng->quality)) { /* * Set new rng as current as the new rng source * provides better entropy quality and was not @@ -497,19 +505,26 @@ int hwrng_register(struct hwrng *rng) err = set_current_rng(rng); if (err) goto out_unlock; + /* to use current_rng in add_early_randomness() we need + * to take a ref + */ + is_new_current = true; + kref_get(&rng->ref); } - - if (old_rng && !rng->init) { + mutex_unlock(&rng_mutex); + if (is_new_current || !rng->init) { /* * Use a new device's input to add some randomness to * the system. If this rng device isn't going to be * used right away, its init function hasn't been - * called yet; so only use the randomness from devices - * that don't need an init callback. + * called yet by set_current_rng(); so only use the + * randomness from devices that don't need an init callback */ add_early_randomness(rng); } - + if (is_new_current) + put_rng(rng); + return 0; out_unlock: mutex_unlock(&rng_mutex); out: @@ -519,10 +534,12 @@ EXPORT_SYMBOL_GPL(hwrng_register); void hwrng_unregister(struct hwrng *rng) { + struct hwrng *old_rng, *new_rng; int err; mutex_lock(&rng_mutex); + old_rng = current_rng; list_del(&rng->list); if (current_rng == rng) { err = enable_best_rng(); @@ -532,6 +549,7 @@ void hwrng_unregister(struct hwrng *rng) } } + new_rng = get_current_rng_nolock(); if (list_empty(&rng_list)) { mutex_unlock(&rng_mutex); if (hwrng_fill) @@ -539,6 +557,12 @@ void hwrng_unregister(struct hwrng *rng) } else mutex_unlock(&rng_mutex); + if (new_rng) { + if (old_rng != new_rng) + add_early_randomness(new_rng); + put_rng(new_rng); + } + wait_for_completion(&rng->cleanup_done); } EXPORT_SYMBOL_GPL(hwrng_unregister); diff --git a/drivers/char/hw_random/exynos-trng.c b/drivers/char/hw_random/exynos-trng.c index b4b52ab23b6b..8e1fe3f8dd2d 100644 --- a/drivers/char/hw_random/exynos-trng.c +++ b/drivers/char/hw_random/exynos-trng.c @@ -109,7 +109,6 @@ static int exynos_trng_init(struct hwrng *rng) static int exynos_trng_probe(struct platform_device *pdev) { struct exynos_trng_dev *trng; - struct resource *res; int ret = -ENOMEM; trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); @@ -128,8 +127,7 @@ static int exynos_trng_probe(struct platform_device *pdev) platform_set_drvdata(pdev, trng); trng->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - trng->mem = devm_ioremap_resource(&pdev->dev, res); + trng->mem = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(trng->mem)) return PTR_ERR(trng->mem); diff --git a/drivers/char/hw_random/hisi-rng.c b/drivers/char/hw_random/hisi-rng.c index c663d5dd85bb..6815e17a9834 100644 --- a/drivers/char/hw_random/hisi-rng.c +++ b/drivers/char/hw_random/hisi-rng.c @@ -73,7 +73,6 @@ static int hisi_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) static int hisi_rng_probe(struct platform_device *pdev) { struct hisi_rng *rng; - struct resource *res; int ret; rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); @@ -82,8 +81,7 @@ static int hisi_rng_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rng); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - rng->base = devm_ioremap_resource(&pdev->dev, res); + rng->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rng->base)) return PTR_ERR(rng->base); diff --git a/drivers/char/hw_random/hisi-trng-v2.c b/drivers/char/hw_random/hisi-trng-v2.c new file mode 100644 index 000000000000..6a65b8232ce0 --- /dev/null +++ b/drivers/char/hw_random/hisi-trng-v2.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2019 HiSilicon Limited. */ + +#include <linux/acpi.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/platform_device.h> +#include <linux/random.h> + +#define HISI_TRNG_REG 0x00F0 +#define HISI_TRNG_BYTES 4 +#define HISI_TRNG_QUALITY 512 +#define SLEEP_US 10 +#define TIMEOUT_US 10000 + +struct hisi_trng { + void __iomem *base; + struct hwrng rng; +}; + +static int hisi_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + struct hisi_trng *trng; + int currsize = 0; + u32 val = 0; + u32 ret; + + trng = container_of(rng, struct hisi_trng, rng); + + do { + ret = readl_poll_timeout(trng->base + HISI_TRNG_REG, val, + val, SLEEP_US, TIMEOUT_US); + if (ret) + return currsize; + + if (max - currsize >= HISI_TRNG_BYTES) { + memcpy(buf + currsize, &val, HISI_TRNG_BYTES); + currsize += HISI_TRNG_BYTES; + if (currsize == max) + return currsize; + continue; + } + + /* copy remaining bytes */ + memcpy(buf + currsize, &val, max - currsize); + currsize = max; + } while (currsize < max); + + return currsize; +} + +static int hisi_trng_probe(struct platform_device *pdev) +{ + struct hisi_trng *trng; + int ret; + + trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL); + if (!trng) + return -ENOMEM; + + trng->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(trng->base)) + return PTR_ERR(trng->base); + + trng->rng.name = pdev->name; + trng->rng.read = hisi_trng_read; + trng->rng.quality = HISI_TRNG_QUALITY; + + ret = devm_hwrng_register(&pdev->dev, &trng->rng); + if (ret) + dev_err(&pdev->dev, "failed to register hwrng!\n"); + + return ret; +} + +static const struct acpi_device_id hisi_trng_acpi_match[] = { + { "HISI02B3", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hisi_trng_acpi_match); + +static struct platform_driver hisi_trng_driver = { + .probe = hisi_trng_probe, + .driver = { + .name = "hisi-trng-v2", + .acpi_match_table = ACPI_PTR(hisi_trng_acpi_match), + }, +}; + +module_platform_driver(hisi_trng_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Weili Qian <qianweili@huawei.com>"); +MODULE_AUTHOR("Zaibo Xu <xuzaibo@huawei.com>"); +MODULE_DESCRIPTION("HiSilicon true random number generator V2 driver"); diff --git a/drivers/char/hw_random/iproc-rng200.c b/drivers/char/hw_random/iproc-rng200.c index 92be1c0ab99f..899ff25f4f28 100644 --- a/drivers/char/hw_random/iproc-rng200.c +++ b/drivers/char/hw_random/iproc-rng200.c @@ -181,7 +181,6 @@ static void iproc_rng200_cleanup(struct hwrng *rng) static int iproc_rng200_probe(struct platform_device *pdev) { struct iproc_rng200_dev *priv; - struct resource *res; struct device *dev = &pdev->dev; int ret; @@ -190,13 +189,7 @@ static int iproc_rng200_probe(struct platform_device *pdev) return -ENOMEM; /* Map peripheral */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "failed to get rng resources\n"); - return -EINVAL; - } - - priv->base = devm_ioremap_resource(dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) { dev_err(dev, "failed to remap rng regs\n"); return PTR_ERR(priv->base); diff --git a/drivers/char/hw_random/ks-sa-rng.c b/drivers/char/hw_random/ks-sa-rng.c index a67430010aa6..e2330e757f1f 100644 --- a/drivers/char/hw_random/ks-sa-rng.c +++ b/drivers/char/hw_random/ks-sa-rng.c @@ -21,6 +21,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/delay.h> +#include <linux/timekeeping.h> #define SA_CMD_STATUS_OFS 0x8 @@ -84,14 +85,37 @@ struct ks_sa_rng { struct hwrng rng; struct clk *clk; struct regmap *regmap_cfg; - struct trng_regs *reg_rng; + struct trng_regs __iomem *reg_rng; + u64 ready_ts; + unsigned int refill_delay_ns; }; +static unsigned int cycles_to_ns(unsigned long clk_rate, unsigned int cycles) +{ + return DIV_ROUND_UP_ULL((TRNG_DEF_CLK_DIV_CYCLES + 1) * 1000000000ull * + cycles, clk_rate); +} + +static unsigned int startup_delay_ns(unsigned long clk_rate) +{ + if (!TRNG_DEF_STARTUP_CYCLES) + return cycles_to_ns(clk_rate, BIT(24)); + return cycles_to_ns(clk_rate, 256 * TRNG_DEF_STARTUP_CYCLES); +} + +static unsigned int refill_delay_ns(unsigned long clk_rate) +{ + if (!TRNG_DEF_MAX_REFILL_CYCLES) + return cycles_to_ns(clk_rate, BIT(24)); + return cycles_to_ns(clk_rate, 256 * TRNG_DEF_MAX_REFILL_CYCLES); +} + static int ks_sa_rng_init(struct hwrng *rng) { u32 value; struct device *dev = (struct device *)rng->priv; struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); + unsigned long clk_rate = clk_get_rate(ks_sa_rng->clk); /* Enable RNG module */ regmap_write_bits(ks_sa_rng->regmap_cfg, SA_CMD_STATUS_OFS, @@ -120,6 +144,10 @@ static int ks_sa_rng_init(struct hwrng *rng) value |= TRNG_CNTL_REG_TRNG_ENABLE; writel(value, &ks_sa_rng->reg_rng->control); + ks_sa_rng->refill_delay_ns = refill_delay_ns(clk_rate); + ks_sa_rng->ready_ts = ktime_get_ns() + + startup_delay_ns(clk_rate); + return 0; } @@ -144,6 +172,7 @@ static int ks_sa_rng_data_read(struct hwrng *rng, u32 *data) data[1] = readl(&ks_sa_rng->reg_rng->output_h); writel(TRNG_INTACK_REG_READY, &ks_sa_rng->reg_rng->intack); + ks_sa_rng->ready_ts = ktime_get_ns() + ks_sa_rng->refill_delay_ns; return sizeof(u32) * 2; } @@ -152,10 +181,19 @@ static int ks_sa_rng_data_present(struct hwrng *rng, int wait) { struct device *dev = (struct device *)rng->priv; struct ks_sa_rng *ks_sa_rng = dev_get_drvdata(dev); + u64 now = ktime_get_ns(); u32 ready; int j; + if (wait && now < ks_sa_rng->ready_ts) { + /* Max delay expected here is 81920000 ns */ + unsigned long min_delay = + DIV_ROUND_UP((u32)(ks_sa_rng->ready_ts - now), 1000); + + usleep_range(min_delay, min_delay + SA_RNG_DATA_RETRY_DELAY); + } + for (j = 0; j < SA_MAX_RNG_DATA_RETRIES; j++) { ready = readl(&ks_sa_rng->reg_rng->status); ready &= TRNG_STATUS_REG_READY; @@ -174,7 +212,6 @@ static int ks_sa_rng_probe(struct platform_device *pdev) struct ks_sa_rng *ks_sa_rng; struct device *dev = &pdev->dev; int ret; - struct resource *mem; ks_sa_rng = devm_kzalloc(dev, sizeof(*ks_sa_rng), GFP_KERNEL); if (!ks_sa_rng) @@ -190,8 +227,7 @@ static int ks_sa_rng_probe(struct platform_device *pdev) }; ks_sa_rng->rng.priv = (unsigned long)dev; - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ks_sa_rng->reg_rng = devm_ioremap_resource(dev, mem); + ks_sa_rng->reg_rng = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ks_sa_rng->reg_rng)) return PTR_ERR(ks_sa_rng->reg_rng); diff --git a/drivers/char/hw_random/meson-rng.c b/drivers/char/hw_random/meson-rng.c index 76e693da5dde..e446236e81f2 100644 --- a/drivers/char/hw_random/meson-rng.c +++ b/drivers/char/hw_random/meson-rng.c @@ -42,7 +42,6 @@ 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); @@ -51,8 +50,7 @@ static int meson_rng_probe(struct platform_device *pdev) data->pdev = pdev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->base = devm_ioremap_resource(dev, res); + data->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->base)) return PTR_ERR(data->base); diff --git a/drivers/char/hw_random/mtk-rng.c b/drivers/char/hw_random/mtk-rng.c index e649be5a5f13..8ad7b515a51b 100644 --- a/drivers/char/hw_random/mtk-rng.c +++ b/drivers/char/hw_random/mtk-rng.c @@ -105,16 +105,9 @@ static int mtk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 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; @@ -135,7 +128,7 @@ static int mtk_rng_probe(struct platform_device *pdev) return ret; } - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); diff --git a/drivers/char/hw_random/npcm-rng.c b/drivers/char/hw_random/npcm-rng.c new file mode 100644 index 000000000000..01d04404d8c0 --- /dev/null +++ b/drivers/char/hw_random/npcm-rng.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2019 Nuvoton Technology corporation. + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/init.h> +#include <linux/random.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/hw_random.h> +#include <linux/delay.h> +#include <linux/of_irq.h> +#include <linux/pm_runtime.h> + +#define NPCM_RNGCS_REG 0x00 /* Control and status register */ +#define NPCM_RNGD_REG 0x04 /* Data register */ +#define NPCM_RNGMODE_REG 0x08 /* Mode register */ + +#define NPCM_RNG_CLK_SET_25MHZ GENMASK(4, 3) /* 20-25 MHz */ +#define NPCM_RNG_DATA_VALID BIT(1) +#define NPCM_RNG_ENABLE BIT(0) +#define NPCM_RNG_M1ROSEL BIT(1) + +#define NPCM_RNG_TIMEOUT_USEC 20000 +#define NPCM_RNG_POLL_USEC 1000 + +#define to_npcm_rng(p) container_of(p, struct npcm_rng, rng) + +struct npcm_rng { + void __iomem *base; + struct hwrng rng; +}; + +static int npcm_rng_init(struct hwrng *rng) +{ + struct npcm_rng *priv = to_npcm_rng(rng); + + writel(NPCM_RNG_CLK_SET_25MHZ | NPCM_RNG_ENABLE, + priv->base + NPCM_RNGCS_REG); + + return 0; +} + +static void npcm_rng_cleanup(struct hwrng *rng) +{ + struct npcm_rng *priv = to_npcm_rng(rng); + + writel(NPCM_RNG_CLK_SET_25MHZ, priv->base + NPCM_RNGCS_REG); +} + +static int npcm_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + struct npcm_rng *priv = to_npcm_rng(rng); + int retval = 0; + int ready; + + pm_runtime_get_sync((struct device *)priv->rng.priv); + + while (max >= sizeof(u32)) { + if (wait) { + if (readl_poll_timeout(priv->base + NPCM_RNGCS_REG, + ready, + ready & NPCM_RNG_DATA_VALID, + NPCM_RNG_POLL_USEC, + NPCM_RNG_TIMEOUT_USEC)) + break; + } else { + if ((readl(priv->base + NPCM_RNGCS_REG) & + NPCM_RNG_DATA_VALID) == 0) + break; + } + + *(u32 *)buf = readl(priv->base + NPCM_RNGD_REG); + retval += sizeof(u32); + buf += sizeof(u32); + max -= sizeof(u32); + } + + pm_runtime_mark_last_busy((struct device *)priv->rng.priv); + pm_runtime_put_sync_autosuspend((struct device *)priv->rng.priv); + + return retval || !wait ? retval : -EIO; +} + +static int npcm_rng_probe(struct platform_device *pdev) +{ + struct npcm_rng *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + dev_set_drvdata(&pdev->dev, priv); + pm_runtime_set_autosuspend_delay(&pdev->dev, 100); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + +#ifndef CONFIG_PM + priv->rng.init = npcm_rng_init; + priv->rng.cleanup = npcm_rng_cleanup; +#endif + priv->rng.name = pdev->name; + priv->rng.read = npcm_rng_read; + priv->rng.priv = (unsigned long)&pdev->dev; + priv->rng.quality = 1000; + + writel(NPCM_RNG_M1ROSEL, priv->base + NPCM_RNGMODE_REG); + + ret = devm_hwrng_register(&pdev->dev, &priv->rng); + if (ret) { + dev_err(&pdev->dev, "Failed to register rng device: %d\n", + ret); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + return ret; + } + + return 0; +} + +static int npcm_rng_remove(struct platform_device *pdev) +{ + struct npcm_rng *priv = platform_get_drvdata(pdev); + + devm_hwrng_unregister(&pdev->dev, &priv->rng); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM +static int npcm_rng_runtime_suspend(struct device *dev) +{ + struct npcm_rng *priv = dev_get_drvdata(dev); + + npcm_rng_cleanup(&priv->rng); + + return 0; +} + +static int npcm_rng_runtime_resume(struct device *dev) +{ + struct npcm_rng *priv = dev_get_drvdata(dev); + + return npcm_rng_init(&priv->rng); +} +#endif + +static const struct dev_pm_ops npcm_rng_pm_ops = { + SET_RUNTIME_PM_OPS(npcm_rng_runtime_suspend, + npcm_rng_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) +}; + +static const struct of_device_id rng_dt_id[] = { + { .compatible = "nuvoton,npcm750-rng", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rng_dt_id); + +static struct platform_driver npcm_rng_driver = { + .driver = { + .name = "npcm-rng", + .pm = &npcm_rng_pm_ops, + .of_match_table = of_match_ptr(rng_dt_id), + }, + .probe = npcm_rng_probe, + .remove = npcm_rng_remove, +}; + +module_platform_driver(npcm_rng_driver); + +MODULE_DESCRIPTION("Nuvoton NPCM Random Number Generator Driver"); +MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index b27f39688b5e..0ed07d16ec8e 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -66,6 +66,13 @@ #define OMAP4_RNG_OUTPUT_SIZE 0x8 #define EIP76_RNG_OUTPUT_SIZE 0x10 +/* + * EIP76 RNG takes approx. 700us to produce 16 bytes of output data + * as per testing results. And to account for the lack of udelay()'s + * reliability, we keep the timeout as 1000us. + */ +#define RNG_DATA_FILL_TIMEOUT 100 + enum { RNG_OUTPUT_0_REG = 0, RNG_OUTPUT_1_REG, @@ -176,7 +183,7 @@ static int omap_rng_do_read(struct hwrng *rng, void *data, size_t max, if (max < priv->pdata->data_size) return 0; - for (i = 0; i < 20; i++) { + for (i = 0; i < RNG_DATA_FILL_TIMEOUT; i++) { present = priv->pdata->data_present(priv); if (present || !wait) break; @@ -432,7 +439,6 @@ static int get_omap_rng_device_details(struct omap_rng_dev *omap_rng) static int omap_rng_probe(struct platform_device *pdev) { struct omap_rng_dev *priv; - struct resource *res; struct device *dev = &pdev->dev; int ret; @@ -449,8 +455,7 @@ static int omap_rng_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); priv->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) { ret = PTR_ERR(priv->base); goto err_ioremap; diff --git a/drivers/char/hw_random/omap3-rom-rng.c b/drivers/char/hw_random/omap3-rom-rng.c index 38b719017186..e08a8887e718 100644 --- a/drivers/char/hw_random/omap3-rom-rng.c +++ b/drivers/char/hw_random/omap3-rom-rng.c @@ -11,8 +11,6 @@ * warranty of any kind, whether express or implied. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include <linux/module.h> #include <linux/init.h> #include <linux/random.h> @@ -20,117 +18,159 @@ #include <linux/workqueue.h> #include <linux/clk.h> #include <linux/err.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #define RNG_RESET 0x01 #define RNG_GEN_PRNG_HW_INIT 0x02 #define RNG_GEN_HW 0x08 -/* param1: ptr, param2: count, param3: flag */ -static u32 (*omap3_rom_rng_call)(u32, u32, u32); - -static struct delayed_work idle_work; -static int rng_idle; -static struct clk *rng_clk; +struct omap_rom_rng { + struct clk *clk; + struct device *dev; + struct hwrng ops; + u32 (*rom_rng_call)(u32 ptr, u32 count, u32 flag); +}; -static void omap3_rom_rng_idle(struct work_struct *work) +static int omap3_rom_rng_read(struct hwrng *rng, void *data, size_t max, bool w) { + struct omap_rom_rng *ddata; + u32 ptr; int r; - r = omap3_rom_rng_call(0, 0, RNG_RESET); - if (r != 0) { - pr_err("reset failed: %d\n", r); - return; + ddata = (struct omap_rom_rng *)rng->priv; + + r = pm_runtime_get_sync(ddata->dev); + if (r < 0) { + pm_runtime_put_noidle(ddata->dev); + + return r; } - clk_disable_unprepare(rng_clk); - rng_idle = 1; + + ptr = virt_to_phys(data); + r = ddata->rom_rng_call(ptr, 4, RNG_GEN_HW); + if (r != 0) + r = -EINVAL; + else + r = 4; + + pm_runtime_mark_last_busy(ddata->dev); + pm_runtime_put_autosuspend(ddata->dev); + + return r; } -static int omap3_rom_rng_get_random(void *buf, unsigned int count) +static int __maybe_unused omap_rom_rng_runtime_suspend(struct device *dev) { - u32 r; - u32 ptr; + struct omap_rom_rng *ddata; + int r; - cancel_delayed_work_sync(&idle_work); - if (rng_idle) { - r = clk_prepare_enable(rng_clk); - if (r) - return r; - - r = omap3_rom_rng_call(0, 0, RNG_GEN_PRNG_HW_INIT); - if (r != 0) { - clk_disable_unprepare(rng_clk); - pr_err("HW init failed: %d\n", r); - return -EIO; - } - rng_idle = 0; - } + ddata = dev_get_drvdata(dev); - ptr = virt_to_phys(buf); - r = omap3_rom_rng_call(ptr, count, RNG_GEN_HW); - schedule_delayed_work(&idle_work, msecs_to_jiffies(500)); + r = ddata->rom_rng_call(0, 0, RNG_RESET); if (r != 0) - return -EINVAL; + dev_err(dev, "reset failed: %d\n", r); + + clk_disable_unprepare(ddata->clk); + return 0; } -static int omap3_rom_rng_read(struct hwrng *rng, void *data, size_t max, bool w) +static int __maybe_unused omap_rom_rng_runtime_resume(struct device *dev) { + struct omap_rom_rng *ddata; int r; - r = omap3_rom_rng_get_random(data, 4); + ddata = dev_get_drvdata(dev); + + r = clk_prepare_enable(ddata->clk); if (r < 0) return r; - return 4; + + r = ddata->rom_rng_call(0, 0, RNG_GEN_PRNG_HW_INIT); + if (r != 0) { + clk_disable(ddata->clk); + dev_err(dev, "HW init failed: %d\n", r); + + return -EIO; + } + + return 0; } -static struct hwrng omap3_rom_rng_ops = { - .name = "omap3-rom", - .read = omap3_rom_rng_read, -}; +static void omap_rom_rng_finish(void *data) +{ + struct omap_rom_rng *ddata = data; + + pm_runtime_dont_use_autosuspend(ddata->dev); + pm_runtime_disable(ddata->dev); +} static int omap3_rom_rng_probe(struct platform_device *pdev) { + struct omap_rom_rng *ddata; int ret = 0; - pr_info("initializing\n"); + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; - omap3_rom_rng_call = pdev->dev.platform_data; - if (!omap3_rom_rng_call) { - pr_err("omap3_rom_rng_call is NULL\n"); + ddata->dev = &pdev->dev; + ddata->ops.priv = (unsigned long)ddata; + ddata->ops.name = "omap3-rom"; + ddata->ops.read = of_device_get_match_data(&pdev->dev); + ddata->ops.quality = 900; + if (!ddata->ops.read) { + dev_err(&pdev->dev, "missing rom code handler\n"); + + return -ENODEV; + } + dev_set_drvdata(ddata->dev, ddata); + + ddata->rom_rng_call = pdev->dev.platform_data; + if (!ddata->rom_rng_call) { + dev_err(ddata->dev, "rom_rng_call is NULL\n"); return -EINVAL; } - INIT_DELAYED_WORK(&idle_work, omap3_rom_rng_idle); - rng_clk = devm_clk_get(&pdev->dev, "ick"); - if (IS_ERR(rng_clk)) { - pr_err("unable to get RNG clock\n"); - return PTR_ERR(rng_clk); + ddata->clk = devm_clk_get(ddata->dev, "ick"); + if (IS_ERR(ddata->clk)) { + dev_err(ddata->dev, "unable to get RNG clock\n"); + return PTR_ERR(ddata->clk); } - /* Leave the RNG in reset state. */ - ret = clk_prepare_enable(rng_clk); + pm_runtime_enable(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 500); + pm_runtime_use_autosuspend(&pdev->dev); + + ret = devm_add_action_or_reset(ddata->dev, omap_rom_rng_finish, + ddata); if (ret) return ret; - omap3_rom_rng_idle(0); - return hwrng_register(&omap3_rom_rng_ops); + return devm_hwrng_register(ddata->dev, &ddata->ops); } -static int omap3_rom_rng_remove(struct platform_device *pdev) -{ - cancel_delayed_work_sync(&idle_work); - hwrng_unregister(&omap3_rom_rng_ops); - clk_disable_unprepare(rng_clk); - return 0; -} +static const struct of_device_id omap_rom_rng_match[] = { + { .compatible = "nokia,n900-rom-rng", .data = omap3_rom_rng_read, }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, omap_rom_rng_match); + +static const struct dev_pm_ops omap_rom_rng_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(omap_rom_rng_runtime_suspend, + omap_rom_rng_runtime_resume) +}; static struct platform_driver omap3_rom_rng_driver = { .driver = { .name = "omap3-rom-rng", + .of_match_table = omap_rom_rng_match, + .pm = &omap_rom_rng_pm_ops, }, .probe = omap3_rom_rng_probe, - .remove = omap3_rom_rng_remove, }; module_platform_driver(omap3_rom_rng_driver); diff --git a/drivers/char/hw_random/pasemi-rng.c b/drivers/char/hw_random/pasemi-rng.c index 24b1460b49d4..2498d4ef9fe2 100644 --- a/drivers/char/hw_random/pasemi-rng.c +++ b/drivers/char/hw_random/pasemi-rng.c @@ -86,10 +86,8 @@ static struct hwrng pasemi_rng = { static int rng_probe(struct platform_device *pdev) { void __iomem *rng_regs; - struct resource *res; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - rng_regs = devm_ioremap_resource(&pdev->dev, res); + rng_regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rng_regs)) return PTR_ERR(rng_regs); diff --git a/drivers/char/hw_random/pic32-rng.c b/drivers/char/hw_random/pic32-rng.c index 90f498c98947..81080cb2294e 100644 --- a/drivers/char/hw_random/pic32-rng.c +++ b/drivers/char/hw_random/pic32-rng.c @@ -70,7 +70,6 @@ static int pic32_rng_read(struct hwrng *rng, void *buf, size_t max, static int pic32_rng_probe(struct platform_device *pdev) { struct pic32_rng *priv; - struct resource *res; u32 v; int ret; @@ -78,8 +77,7 @@ static int pic32_rng_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); diff --git a/drivers/char/hw_random/st-rng.c b/drivers/char/hw_random/st-rng.c index 863448360a7d..783c24e3f8b7 100644 --- a/drivers/char/hw_random/st-rng.c +++ b/drivers/char/hw_random/st-rng.c @@ -72,7 +72,6 @@ static int st_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) static int st_rng_probe(struct platform_device *pdev) { struct st_rng_data *ddata; - struct resource *res; struct clk *clk; void __iomem *base; int ret; @@ -81,8 +80,7 @@ static int st_rng_probe(struct platform_device *pdev) if (!ddata) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/char/hw_random/tx4939-rng.c b/drivers/char/hw_random/tx4939-rng.c index 1093583b579c..c8bd34e740fd 100644 --- a/drivers/char/hw_random/tx4939-rng.c +++ b/drivers/char/hw_random/tx4939-rng.c @@ -107,14 +107,12 @@ static int tx4939_rng_data_read(struct hwrng *rng, u32 *buffer) static int __init tx4939_rng_probe(struct platform_device *dev) { struct tx4939_rng *rngdev; - struct resource *r; int i; rngdev = devm_kzalloc(&dev->dev, sizeof(*rngdev), GFP_KERNEL); if (!rngdev) return -ENOMEM; - r = platform_get_resource(dev, IORESOURCE_MEM, 0); - rngdev->base = devm_ioremap_resource(&dev->dev, r); + rngdev->base = devm_platform_ioremap_resource(dev, 0); if (IS_ERR(rngdev->base)) return PTR_ERR(rngdev->base); diff --git a/drivers/char/hw_random/xgene-rng.c b/drivers/char/hw_random/xgene-rng.c index 7e568db87ae2..d7516a446987 100644 --- a/drivers/char/hw_random/xgene-rng.c +++ b/drivers/char/hw_random/xgene-rng.c @@ -313,7 +313,6 @@ static struct hwrng xgene_rng_func = { static int xgene_rng_probe(struct platform_device *pdev) { - struct resource *res; struct xgene_rng_dev *ctx; int rc = 0; @@ -324,8 +323,7 @@ static int xgene_rng_probe(struct platform_device *pdev) ctx->dev = &pdev->dev; platform_set_drvdata(pdev, ctx); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ctx->csr_base = devm_ioremap_resource(&pdev->dev, res); + ctx->csr_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ctx->csr_base)) return PTR_ERR(ctx->csr_base); diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c index 40b9927c072c..d36aeacb290e 100644 --- a/drivers/char/ipmi/bt-bmc.c +++ b/drivers/char/ipmi/bt-bmc.c @@ -444,15 +444,13 @@ static int bt_bmc_probe(struct platform_device *pdev) bt_bmc->map = syscon_node_to_regmap(pdev->dev.parent->of_node); if (IS_ERR(bt_bmc->map)) { - struct resource *res; void __iomem *base; /* * Assume it's not the MFD-based devicetree description, in * which case generate a regmap ourselves */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c index 285e0b8f9a97..1ff4fb1def7c 100644 --- a/drivers/char/ipmi/ipmb_dev_int.c +++ b/drivers/char/ipmi/ipmb_dev_int.c @@ -133,9 +133,6 @@ static ssize_t ipmb_write(struct file *file, const char __user *buf, rq_sa = GET_7BIT_ADDR(msg[RQ_SA_8BIT_IDX]); netf_rq_lun = msg[NETFN_LUN_IDX]; - if (!(netf_rq_lun & NETFN_RSP_BIT_MASK)) - return -EINVAL; - /* * subtract rq_sa and netf_rq_lun from the length of the msg passed to * i2c_smbus_xfer @@ -154,16 +151,16 @@ static ssize_t ipmb_write(struct file *file, const char __user *buf, return ret ? : count; } -static unsigned int ipmb_poll(struct file *file, poll_table *wait) +static __poll_t ipmb_poll(struct file *file, poll_table *wait) { struct ipmb_dev *ipmb_dev = to_ipmb_dev(file); - unsigned int mask = POLLOUT; + __poll_t mask = EPOLLOUT; mutex_lock(&ipmb_dev->file_mutex); poll_wait(file, &ipmb_dev->wait_queue, wait); if (atomic_read(&ipmb_dev->request_queue_len)) - mask |= POLLIN; + mask |= EPOLLIN; mutex_unlock(&ipmb_dev->file_mutex); return mask; @@ -203,25 +200,16 @@ static u8 ipmb_verify_checksum1(struct ipmb_dev *ipmb_dev, u8 rs_sa) ipmb_dev->request.checksum1); } -static bool is_ipmb_request(struct ipmb_dev *ipmb_dev, u8 rs_sa) +/* + * Verify if message has proper ipmb header with minimum length + * and correct checksum byte. + */ +static bool is_ipmb_msg(struct ipmb_dev *ipmb_dev, u8 rs_sa) { - if (ipmb_dev->msg_idx >= IPMB_REQUEST_LEN_MIN) { - if (ipmb_verify_checksum1(ipmb_dev, rs_sa)) - return false; + if ((ipmb_dev->msg_idx >= IPMB_REQUEST_LEN_MIN) && + (!ipmb_verify_checksum1(ipmb_dev, rs_sa))) + return true; - /* - * Check whether this is an IPMB request or - * response. - * The 6 MSB of netfn_rs_lun are dedicated to the netfn - * while the remaining bits are dedicated to the lun. - * If the LSB of the netfn is cleared, it is associated - * with an IPMB request. - * If the LSB of the netfn is set, it is associated with - * an IPMB response. - */ - if (!(ipmb_dev->request.netfn_rs_lun & NETFN_RSP_BIT_MASK)) - return true; - } return false; } @@ -273,8 +261,7 @@ static int ipmb_slave_cb(struct i2c_client *client, case I2C_SLAVE_STOP: ipmb_dev->request.len = ipmb_dev->msg_idx; - - if (is_ipmb_request(ipmb_dev, GET_8BIT_ADDR(client->addr))) + if (is_ipmb_msg(ipmb_dev, GET_8BIT_ADDR(client->addr))) ipmb_handle_request(ipmb_dev); break; diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 2aab80e19ae0..cad9563f8f48 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -44,25 +44,6 @@ static void need_waiter(struct ipmi_smi *intf); static int handle_one_recv_msg(struct ipmi_smi *intf, struct ipmi_smi_msg *msg); -#ifdef DEBUG -static void ipmi_debug_msg(const char *title, unsigned char *data, - unsigned int len) -{ - int i, pos; - char buf[100]; - - pos = snprintf(buf, sizeof(buf), "%s: ", title); - for (i = 0; i < len; i++) - pos += snprintf(buf + pos, sizeof(buf) - pos, - " %2.2x", data[i]); - pr_debug("%s\n", buf); -} -#else -static void ipmi_debug_msg(const char *title, unsigned char *data, - unsigned int len) -{ } -#endif - static bool initialized; static bool drvregistered; @@ -448,6 +429,8 @@ enum ipmi_stat_indexes { #define IPMI_IPMB_NUM_SEQ 64 struct ipmi_smi { + struct module *owner; + /* What interface number are we? */ int intf_num; @@ -1220,6 +1203,11 @@ int ipmi_create_user(unsigned int if_num, if (rv) goto out_kfree; + if (!try_module_get(intf->owner)) { + rv = -ENODEV; + goto out_kfree; + } + /* Note that each existing user holds a refcount to the interface. */ kref_get(&intf->refcount); @@ -1349,6 +1337,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user) } kref_put(&intf->refcount, intf_free); + module_put(intf->owner); } int ipmi_destroy_user(struct ipmi_user *user) @@ -2267,7 +2256,7 @@ out_err: ipmi_free_smi_msg(smi_msg); ipmi_free_recv_msg(recv_msg); } else { - ipmi_debug_msg("Send", smi_msg->data, smi_msg->data_size); + pr_debug("Send: %*ph\n", smi_msg->data_size, smi_msg->data); smi_send(intf, intf->handlers, smi_msg, priority); } @@ -2459,7 +2448,7 @@ static int __get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc) * been recently fetched, this will just use the cached data. Otherwise * it will run a new fetch. * - * Except for the first time this is called (in ipmi_register_smi()), + * Except for the first time this is called (in ipmi_add_smi()), * this will always return good data; */ static int __bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc, @@ -3031,8 +3020,11 @@ static int __ipmi_bmc_register(struct ipmi_smi *intf, bmc->pdev.name = "ipmi_bmc"; rv = ida_simple_get(&ipmi_bmc_ida, 0, 0, GFP_KERNEL); - if (rv < 0) + if (rv < 0) { + kfree(bmc); goto out; + } + bmc->pdev.dev.driver = &ipmidriver.driver; bmc->pdev.id = rv; bmc->pdev.dev.release = release_bmc_device; @@ -3377,10 +3369,11 @@ static void redo_bmc_reg(struct work_struct *work) kref_put(&intf->refcount, intf_free); } -int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, - void *send_info, - struct device *si_dev, - unsigned char slave_addr) +int ipmi_add_smi(struct module *owner, + const struct ipmi_smi_handlers *handlers, + void *send_info, + struct device *si_dev, + unsigned char slave_addr) { int i, j; int rv; @@ -3406,7 +3399,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, return rv; } - + intf->owner = owner; intf->bmc = &intf->tmp_bmc; INIT_LIST_HEAD(&intf->bmc->intfs); mutex_init(&intf->bmc->dyn_mutex); @@ -3514,7 +3507,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, return rv; } -EXPORT_SYMBOL(ipmi_register_smi); +EXPORT_SYMBOL(ipmi_add_smi); static void deliver_smi_err_response(struct ipmi_smi *intf, struct ipmi_smi_msg *msg, @@ -3730,7 +3723,7 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf, msg->data[10] = ipmb_checksum(&msg->data[6], 4); msg->data_size = 11; - ipmi_debug_msg("Invalid command:", msg->data, msg->data_size); + pr_debug("Invalid command: %*ph\n", msg->data_size, msg->data); rcu_read_lock(); if (!intf->in_shutdown) { @@ -4217,7 +4210,7 @@ static int handle_one_recv_msg(struct ipmi_smi *intf, int requeue; int chan; - ipmi_debug_msg("Recv:", msg->rsp, msg->rsp_size); + pr_debug("Recv: %*ph\n", msg->rsp_size, msg->rsp); if ((msg->data_size >= 2) && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2)) @@ -4576,7 +4569,7 @@ smi_from_recv_msg(struct ipmi_smi *intf, struct ipmi_recv_msg *recv_msg, smi_msg->data_size = recv_msg->msg.data_len; smi_msg->msgid = STORE_SEQ_IN_MSGID(seq, seqid); - ipmi_debug_msg("Resend: ", smi_msg->data, smi_msg->data_size); + pr_debug("Resend: %*ph\n", smi_msg->data_size, smi_msg->data); return smi_msg; } diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 6b9a0593d2eb..c7cc8538b84a 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -265,10 +265,10 @@ static void cleanup_ipmi_si(void); #ifdef DEBUG_TIMING void debug_timestamp(char *msg) { - struct timespec t; + struct timespec64 t; - ktime_get_ts(&t); - pr_debug("**%s: %ld.%9.9ld\n", msg, (long) t.tv_sec, t.tv_nsec); + ktime_get_ts64(&t); + pr_debug("**%s: %lld.%9.9ld\n", msg, t.tv_sec, t.tv_nsec); } #else #define debug_timestamp(x) @@ -935,38 +935,25 @@ static void set_run_to_completion(void *send_info, bool i_run_to_completion) } /* - * Use -1 in the nsec value of the busy waiting timespec to tell that - * we are spinning in kipmid looking for something and not delaying - * between checks + * Use -1 as a special constant to tell that we are spinning in kipmid + * looking for something and not delaying between checks */ -static inline void ipmi_si_set_not_busy(struct timespec *ts) -{ - ts->tv_nsec = -1; -} -static inline int ipmi_si_is_busy(struct timespec *ts) -{ - return ts->tv_nsec != -1; -} - +#define IPMI_TIME_NOT_BUSY ns_to_ktime(-1ull) static inline bool ipmi_thread_busy_wait(enum si_sm_result smi_result, const struct smi_info *smi_info, - struct timespec *busy_until) + ktime_t *busy_until) { unsigned int max_busy_us = 0; if (smi_info->si_num < num_max_busy_us) max_busy_us = kipmid_max_busy_us[smi_info->si_num]; if (max_busy_us == 0 || smi_result != SI_SM_CALL_WITH_DELAY) - ipmi_si_set_not_busy(busy_until); - else if (!ipmi_si_is_busy(busy_until)) { - ktime_get_ts(busy_until); - timespec_add_ns(busy_until, max_busy_us * NSEC_PER_USEC); + *busy_until = IPMI_TIME_NOT_BUSY; + else if (*busy_until == IPMI_TIME_NOT_BUSY) { + *busy_until = ktime_get() + max_busy_us * NSEC_PER_USEC; } else { - struct timespec now; - - ktime_get_ts(&now); - if (unlikely(timespec_compare(&now, busy_until) > 0)) { - ipmi_si_set_not_busy(busy_until); + if (unlikely(ktime_get() > *busy_until)) { + *busy_until = IPMI_TIME_NOT_BUSY; return false; } } @@ -988,9 +975,8 @@ static int ipmi_thread(void *data) struct smi_info *smi_info = data; unsigned long flags; enum si_sm_result smi_result; - struct timespec busy_until = { 0, 0 }; + ktime_t busy_until = IPMI_TIME_NOT_BUSY; - ipmi_si_set_not_busy(&busy_until); set_user_nice(current, MAX_NICE); while (!kthread_should_stop()) { int busy_wait; diff --git a/drivers/char/random.c b/drivers/char/random.c index de434feb873a..01b8868b9bed 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -327,7 +327,6 @@ #include <linux/percpu.h> #include <linux/cryptohash.h> #include <linux/fips.h> -#include <linux/freezer.h> #include <linux/ptrace.h> #include <linux/workqueue.h> #include <linux/irq.h> @@ -2500,8 +2499,7 @@ void add_hwgenerator_randomness(const char *buffer, size_t count, * We'll be woken up again once below random_write_wakeup_thresh, * or when the calling thread is about to terminate. */ - wait_event_freezable(random_write_wait, - kthread_should_stop() || + wait_event_interruptible(random_write_wait, kthread_should_stop() || ENTROPY_BITS(&input_pool) <= random_write_wakeup_bits); mix_pool_bytes(poolp, buffer, count); credit_entropy_bits(poolp, entropy); diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 9c37047f4b56..aacdeed93320 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -67,6 +67,13 @@ config TCG_TIS_SPI within Linux. To compile this driver as a module, choose M here; the module will be called tpm_tis_spi. +config TCG_TIS_SPI_CR50 + bool "Cr50 SPI Interface" + depends on TCG_TIS_SPI + help + If you have a H1 secure module running Cr50 firmware on SPI bus, + say Yes and it will be accessible from within Linux. + config TCG_TIS_I2C_ATMEL tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)" depends on I2C diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index c354cdff9c62..5a0d99d4fec0 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -21,7 +21,9 @@ tpm-$(CONFIG_EFI) += eventlog/efi.o tpm-$(CONFIG_OF) += eventlog/of.o obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o obj-$(CONFIG_TCG_TIS) += tpm_tis.o -obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi.o +obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi_mod.o +tpm_tis_spi_mod-y := tpm_tis_spi.o +tpm_tis_spi_mod-$(CONFIG_TCG_TIS_SPI_CR50) += tpm_tis_spi_cr50.o obj-$(CONFIG_TCG_TIS_I2C_ATMEL) += tpm_i2c_atmel.o obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index d7a3888ad80f..a438b1206fcb 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -23,6 +23,7 @@ #include <linux/slab.h> #include <linux/mutex.h> #include <linux/spinlock.h> +#include <linux/suspend.h> #include <linux/freezer.h> #include <linux/tpm_eventlog.h> @@ -394,7 +395,11 @@ int tpm_pm_suspend(struct device *dev) return -ENODEV; if (chip->flags & TPM_CHIP_FLAG_ALWAYS_POWERED) - return 0; + goto suspended; + + if ((chip->flags & TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED) && + !pm_suspend_via_firmware()) + goto suspended; if (!tpm_chip_start(chip)) { if (chip->flags & TPM_CHIP_FLAG_TPM2) @@ -405,6 +410,7 @@ int tpm_pm_suspend(struct device *dev) tpm_chip_stop(chip); } +suspended: return rc; } EXPORT_SYMBOL_GPL(tpm_pm_suspend); @@ -453,62 +459,6 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max) } EXPORT_SYMBOL_GPL(tpm_get_random); -/** - * tpm_seal_trusted() - seal a trusted key payload - * @chip: a &struct tpm_chip instance, %NULL for the default chip - * @options: authentication values and other options - * @payload: the key data in clear and encrypted form - * - * Note: only TPM 2.0 chip are supported. TPM 1.x implementation is located in - * the keyring subsystem. - * - * Return: same as with tpm_transmit_cmd() - */ -int tpm_seal_trusted(struct tpm_chip *chip, struct trusted_key_payload *payload, - struct trusted_key_options *options) -{ - int rc; - - chip = tpm_find_get_ops(chip); - if (!chip || !(chip->flags & TPM_CHIP_FLAG_TPM2)) - return -ENODEV; - - rc = tpm2_seal_trusted(chip, payload, options); - - tpm_put_ops(chip); - return rc; -} -EXPORT_SYMBOL_GPL(tpm_seal_trusted); - -/** - * tpm_unseal_trusted() - unseal a trusted key - * @chip: a &struct tpm_chip instance, %NULL for the default chip - * @options: authentication values and other options - * @payload: the key data in clear and encrypted form - * - * Note: only TPM 2.0 chip are supported. TPM 1.x implementation is located in - * the keyring subsystem. - * - * Return: same as with tpm_transmit_cmd() - */ -int tpm_unseal_trusted(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options) -{ - int rc; - - chip = tpm_find_get_ops(chip); - if (!chip || !(chip->flags & TPM_CHIP_FLAG_TPM2)) - return -ENODEV; - - rc = tpm2_unseal_trusted(chip, payload, options); - - tpm_put_ops(chip); - - return rc; -} -EXPORT_SYMBOL_GPL(tpm_unseal_trusted); - static int __init tpm_init(void) { int rc; diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index edfa89160010..3b53b3e5ec3e 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -217,6 +217,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, char *buf) { struct tpm_chip *chip = to_tpm_chip(dev); + struct tpm1_version *version; ssize_t rc = 0; char *str = buf; cap_t cap; @@ -232,31 +233,31 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, str += sprintf(str, "Manufacturer: 0x%x\n", be32_to_cpu(cap.manufacturer_id)); - /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */ - rc = tpm1_getcap(chip, TPM_CAP_VERSION_1_2, &cap, + /* TPM 1.2 */ + if (!tpm1_getcap(chip, TPM_CAP_VERSION_1_2, &cap, "attempting to determine the 1.2 version", - sizeof(cap.tpm_version_1_2)); - if (!rc) { - str += sprintf(str, - "TCG version: %d.%d\nFirmware version: %d.%d\n", - cap.tpm_version_1_2.Major, - cap.tpm_version_1_2.Minor, - cap.tpm_version_1_2.revMajor, - cap.tpm_version_1_2.revMinor); - } else { - /* Otherwise just use TPM_STRUCT_VER */ - if (tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap, - "attempting to determine the 1.1 version", - sizeof(cap.tpm_version))) - goto out_ops; - str += sprintf(str, - "TCG version: %d.%d\nFirmware version: %d.%d\n", - cap.tpm_version.Major, - cap.tpm_version.Minor, - cap.tpm_version.revMajor, - cap.tpm_version.revMinor); + sizeof(cap.version2))) { + version = &cap.version2.version; + goto out_print; } + + /* TPM 1.1 */ + if (tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap, + "attempting to determine the 1.1 version", + sizeof(cap.version1))) { + goto out_ops; + } + + version = &cap.version1; + +out_print: + str += sprintf(str, + "TCG version: %d.%d\nFirmware version: %d.%d\n", + version->major, version->minor, + version->rev_major, version->rev_minor); + rc = str - buf; + out_ops: tpm_put_ops(chip); return rc; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index a7fea3e0ca86..b9e1547be6b5 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -25,7 +25,6 @@ #include <linux/platform_device.h> #include <linux/io.h> #include <linux/tpm.h> -#include <linux/highmem.h> #include <linux/tpm_eventlog.h> #ifdef CONFIG_X86 @@ -58,123 +57,6 @@ enum tpm_addr { #define TPM_ERR_DISABLED 0x7 #define TPM_ERR_INVALID_POSTINIT 38 -#define TPM_HEADER_SIZE 10 - -enum tpm2_const { - TPM2_PLATFORM_PCR = 24, - TPM2_PCR_SELECT_MIN = ((TPM2_PLATFORM_PCR + 7) / 8), -}; - -enum tpm2_timeouts { - TPM2_TIMEOUT_A = 750, - TPM2_TIMEOUT_B = 2000, - TPM2_TIMEOUT_C = 200, - TPM2_TIMEOUT_D = 30, - TPM2_DURATION_SHORT = 20, - TPM2_DURATION_MEDIUM = 750, - TPM2_DURATION_LONG = 2000, - TPM2_DURATION_LONG_LONG = 300000, - TPM2_DURATION_DEFAULT = 120000, -}; - -enum tpm2_structures { - TPM2_ST_NO_SESSIONS = 0x8001, - TPM2_ST_SESSIONS = 0x8002, -}; - -/* Indicates from what layer of the software stack the error comes from */ -#define TSS2_RC_LAYER_SHIFT 16 -#define TSS2_RESMGR_TPM_RC_LAYER (11 << TSS2_RC_LAYER_SHIFT) - -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_FAILURE = 0x0101, - TPM2_RC_DISABLED = 0x0120, - TPM2_RC_COMMAND_CODE = 0x0143, - TPM2_RC_TESTING = 0x090A, /* RC_WARN */ - TPM2_RC_REFERENCE_H0 = 0x0910, - TPM2_RC_RETRY = 0x0922, -}; - -enum tpm2_command_codes { - TPM2_CC_FIRST = 0x011F, - TPM2_CC_HIERARCHY_CONTROL = 0x0121, - TPM2_CC_HIERARCHY_CHANGE_AUTH = 0x0129, - TPM2_CC_CREATE_PRIMARY = 0x0131, - TPM2_CC_SEQUENCE_COMPLETE = 0x013E, - TPM2_CC_SELF_TEST = 0x0143, - TPM2_CC_STARTUP = 0x0144, - TPM2_CC_SHUTDOWN = 0x0145, - TPM2_CC_NV_READ = 0x014E, - TPM2_CC_CREATE = 0x0153, - TPM2_CC_LOAD = 0x0157, - TPM2_CC_SEQUENCE_UPDATE = 0x015C, - TPM2_CC_UNSEAL = 0x015E, - TPM2_CC_CONTEXT_LOAD = 0x0161, - TPM2_CC_CONTEXT_SAVE = 0x0162, - TPM2_CC_FLUSH_CONTEXT = 0x0165, - TPM2_CC_VERIFY_SIGNATURE = 0x0177, - TPM2_CC_GET_CAPABILITY = 0x017A, - TPM2_CC_GET_RANDOM = 0x017B, - TPM2_CC_PCR_READ = 0x017E, - TPM2_CC_PCR_EXTEND = 0x0182, - TPM2_CC_EVENT_SEQUENCE_COMPLETE = 0x0185, - TPM2_CC_HASH_SEQUENCE_START = 0x0186, - TPM2_CC_CREATE_LOADED = 0x0191, - TPM2_CC_LAST = 0x0193, /* Spec 1.36 */ -}; - -enum tpm2_permanent_handles { - TPM2_RS_PW = 0x40000009, -}; - -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 - -enum tpm_chip_flags { - TPM_CHIP_FLAG_TPM2 = BIT(1), - TPM_CHIP_FLAG_IRQ = BIT(2), - TPM_CHIP_FLAG_VIRTUAL = BIT(3), - TPM_CHIP_FLAG_HAVE_TIMEOUTS = BIT(4), - TPM_CHIP_FLAG_ALWAYS_POWERED = BIT(5), -}; - -#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) - -struct tpm_header { - __be16 tag; - __be32 length; - union { - __be32 ordinal; - __be32 return_code; - }; -} __packed; - #define TPM_TAG_RQU_COMMAND 193 struct stclear_flags_t { @@ -186,19 +68,16 @@ struct stclear_flags_t { u8 bGlobalLock; } __packed; -struct tpm_version_t { - u8 Major; - u8 Minor; - u8 revMajor; - u8 revMinor; +struct tpm1_version { + u8 major; + u8 minor; + u8 rev_major; + u8 rev_minor; } __packed; -struct tpm_version_1_2_t { - __be16 tag; - u8 Major; - u8 Minor; - u8 revMajor; - u8 revMinor; +struct tpm1_version2 { + __be16 tag; + struct tpm1_version version; } __packed; struct timeout_t { @@ -243,8 +122,8 @@ typedef union { struct stclear_flags_t stclear_flags; __u8 owned; __be32 num_pcrs; - struct tpm_version_t tpm_version; - struct tpm_version_1_2_t tpm_version_1_2; + struct tpm1_version version1; + struct tpm1_version2 version2; __be32 manufacturer_id; struct timeout_t timeout; struct duration_t duration; @@ -274,102 +153,6 @@ enum tpm_sub_capabilities { * compiler warnings about stack frame size. */ #define TPM_MAX_RNG_DATA 128 -/* A string buffer type for constructing TPM commands. This is based on the - * ideas of string buffer code in security/keys/trusted.h but is heap based - * in order to keep the stack usage minimal. - */ - -enum tpm_buf_flags { - TPM_BUF_OVERFLOW = BIT(0), -}; - -struct tpm_buf { - struct page *data_page; - unsigned int flags; - u8 *data; -}; - -static inline void tpm_buf_reset(struct tpm_buf *buf, u16 tag, u32 ordinal) -{ - struct tpm_header *head = (struct tpm_header *)buf->data; - - head->tag = cpu_to_be16(tag); - head->length = cpu_to_be32(sizeof(*head)); - head->ordinal = cpu_to_be32(ordinal); -} - -static inline int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal) -{ - buf->data_page = alloc_page(GFP_HIGHUSER); - if (!buf->data_page) - return -ENOMEM; - - buf->flags = 0; - buf->data = kmap(buf->data_page); - tpm_buf_reset(buf, tag, ordinal); - return 0; -} - -static inline void tpm_buf_destroy(struct tpm_buf *buf) -{ - kunmap(buf->data_page); - __free_page(buf->data_page); -} - -static inline u32 tpm_buf_length(struct tpm_buf *buf) -{ - struct tpm_header *head = (struct tpm_header *)buf->data; - - return be32_to_cpu(head->length); -} - -static inline u16 tpm_buf_tag(struct tpm_buf *buf) -{ - struct tpm_header *head = (struct tpm_header *)buf->data; - - return be16_to_cpu(head->tag); -} - -static inline void tpm_buf_append(struct tpm_buf *buf, - const unsigned char *new_data, - unsigned int new_len) -{ - struct tpm_header *head = (struct tpm_header *)buf->data; - u32 len = tpm_buf_length(buf); - - /* Return silently if overflow has already happened. */ - if (buf->flags & TPM_BUF_OVERFLOW) - return; - - if ((len + new_len) > PAGE_SIZE) { - WARN(1, "tpm_buf: overflow\n"); - buf->flags |= TPM_BUF_OVERFLOW; - return; - } - - memcpy(&buf->data[len], new_data, new_len); - head->length = cpu_to_be32(len + new_len); -} - -static inline void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value) -{ - tpm_buf_append(buf, &value, 1); -} - -static inline void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value) -{ - __be16 value2 = cpu_to_be16(value); - - tpm_buf_append(buf, (u8 *) &value2, 2); -} - -static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value) -{ - __be32 value2 = cpu_to_be32(value); - - tpm_buf_append(buf, (u8 *) &value2, 4); -} - extern struct class *tpm_class; extern struct class *tpmrm_class; extern dev_t tpm_devt; @@ -429,11 +212,6 @@ static inline void tpm_add_ppi(struct tpm_chip *chip) } #endif -static inline u32 tpm2_rc_value(u32 rc) -{ - return (rc & BIT(7)) ? rc & 0xff : rc; -} - int tpm2_get_timeouts(struct tpm_chip *chip); int tpm2_pcr_read(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digest, u16 *digest_size_ptr); @@ -441,12 +219,6 @@ int tpm2_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, struct tpm_digest *digests); int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max); void tpm2_flush_context(struct tpm_chip *chip, u32 handle); -int tpm2_seal_trusted(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options); -int tpm2_unseal_trusted(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options); ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, const char *desc); diff --git a/drivers/char/tpm/tpm1-cmd.c b/drivers/char/tpm/tpm1-cmd.c index 149e953ca369..ca7158fa6e6c 100644 --- a/drivers/char/tpm/tpm1-cmd.c +++ b/drivers/char/tpm/tpm1-cmd.c @@ -343,6 +343,7 @@ int tpm1_get_timeouts(struct tpm_chip *chip) { cap_t cap; unsigned long timeout_old[4], timeout_chip[4], timeout_eff[4]; + unsigned long durations[3]; ssize_t rc; rc = tpm1_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap, NULL, @@ -427,6 +428,20 @@ int tpm1_get_timeouts(struct tpm_chip *chip) usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long)); chip->duration[TPM_LONG_LONG] = 0; /* not used under 1.2 */ + /* + * Provide the ability for vendor overrides of duration values in case + * of misreporting. + */ + if (chip->ops->update_durations) + chip->ops->update_durations(chip, durations); + + if (chip->duration_adjusted) { + dev_info(&chip->dev, HW_ERR "Adjusting reported durations."); + chip->duration[TPM_SHORT] = durations[0]; + chip->duration[TPM_MEDIUM] = durations[1]; + chip->duration[TPM_LONG] = durations[2]; + } + /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above * value wrong and apparently reports msecs rather than usecs. So we * fix up the resulting too-small TPM_SHORT value to make things work. diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index ba9acae83bff..fdb457704aa7 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -13,20 +13,6 @@ #include "tpm.h" #include <crypto/hash_info.h> -#include <keys/trusted-type.h> - -enum tpm2_object_attributes { - TPM2_OA_USER_WITH_AUTH = BIT(6), -}; - -enum tpm2_session_attributes { - TPM2_SA_CONTINUE_SESSION = BIT(0), -}; - -struct tpm2_hash { - unsigned int crypto_id; - unsigned int tpm_id; -}; static struct tpm2_hash tpm2_hash_map[] = { {HASH_ALGO_SHA1, TPM_ALG_SHA1}, @@ -377,299 +363,6 @@ void tpm2_flush_context(struct tpm_chip *chip, u32 handle) tpm_buf_destroy(&buf); } -/** - * tpm_buf_append_auth() - append TPMS_AUTH_COMMAND to the buffer. - * - * @buf: an allocated tpm_buf instance - * @session_handle: session handle - * @nonce: the session nonce, may be NULL if not used - * @nonce_len: the session nonce length, may be 0 if not used - * @attributes: the session attributes - * @hmac: the session HMAC or password, may be NULL if not used - * @hmac_len: the session HMAC or password length, maybe 0 if not used - */ -static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle, - const u8 *nonce, u16 nonce_len, - u8 attributes, - const u8 *hmac, u16 hmac_len) -{ - tpm_buf_append_u32(buf, 9 + nonce_len + hmac_len); - tpm_buf_append_u32(buf, session_handle); - tpm_buf_append_u16(buf, nonce_len); - - if (nonce && nonce_len) - tpm_buf_append(buf, nonce, nonce_len); - - tpm_buf_append_u8(buf, attributes); - tpm_buf_append_u16(buf, hmac_len); - - if (hmac && hmac_len) - tpm_buf_append(buf, hmac, hmac_len); -} - -/** - * tpm2_seal_trusted() - seal the payload of a trusted key - * - * @chip: TPM chip to use - * @payload: the key data in clear and encrypted form - * @options: authentication values and other options - * - * Return: < 0 on error and 0 on success. - */ -int tpm2_seal_trusted(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options) -{ - unsigned int blob_len; - struct tpm_buf buf; - u32 hash; - int i; - int rc; - - for (i = 0; i < ARRAY_SIZE(tpm2_hash_map); i++) { - if (options->hash == tpm2_hash_map[i].crypto_id) { - hash = tpm2_hash_map[i].tpm_id; - break; - } - } - - if (i == ARRAY_SIZE(tpm2_hash_map)) - return -EINVAL; - - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); - if (rc) - return rc; - - tpm_buf_append_u32(&buf, options->keyhandle); - tpm2_buf_append_auth(&buf, TPM2_RS_PW, - NULL /* nonce */, 0, - 0 /* session_attributes */, - options->keyauth /* hmac */, - TPM_DIGEST_SIZE); - - /* sensitive */ - tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len + 1); - - tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE); - tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE); - tpm_buf_append_u16(&buf, payload->key_len + 1); - tpm_buf_append(&buf, payload->key, payload->key_len); - tpm_buf_append_u8(&buf, payload->migratable); - - /* public */ - tpm_buf_append_u16(&buf, 14 + options->policydigest_len); - tpm_buf_append_u16(&buf, TPM_ALG_KEYEDHASH); - tpm_buf_append_u16(&buf, hash); - - /* policy */ - if (options->policydigest_len) { - tpm_buf_append_u32(&buf, 0); - tpm_buf_append_u16(&buf, options->policydigest_len); - tpm_buf_append(&buf, options->policydigest, - options->policydigest_len); - } else { - tpm_buf_append_u32(&buf, TPM2_OA_USER_WITH_AUTH); - tpm_buf_append_u16(&buf, 0); - } - - /* public parameters */ - tpm_buf_append_u16(&buf, TPM_ALG_NULL); - tpm_buf_append_u16(&buf, 0); - - /* outside info */ - tpm_buf_append_u16(&buf, 0); - - /* creation PCR */ - tpm_buf_append_u32(&buf, 0); - - if (buf.flags & TPM_BUF_OVERFLOW) { - rc = -E2BIG; - goto out; - } - - rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data"); - if (rc) - goto out; - - blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]); - if (blob_len > MAX_BLOB_SIZE) { - rc = -E2BIG; - goto out; - } - if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 4 + blob_len) { - rc = -EFAULT; - goto out; - } - - memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len); - payload->blob_len = blob_len; - -out: - tpm_buf_destroy(&buf); - - if (rc > 0) { - if (tpm2_rc_value(rc) == TPM2_RC_HASH) - rc = -EINVAL; - else - rc = -EPERM; - } - - return rc; -} - -/** - * tpm2_load_cmd() - execute a TPM2_Load command - * - * @chip: TPM chip to use - * @payload: the key data in clear and encrypted form - * @options: authentication values and other options - * @blob_handle: returned blob handle - * - * Return: 0 on success. - * -E2BIG on wrong payload size. - * -EPERM on tpm error status. - * < 0 error from tpm_transmit_cmd. - */ -static int tpm2_load_cmd(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options, - u32 *blob_handle) -{ - struct tpm_buf buf; - unsigned int private_len; - unsigned int public_len; - unsigned int blob_len; - int rc; - - private_len = be16_to_cpup((__be16 *) &payload->blob[0]); - if (private_len > (payload->blob_len - 2)) - return -E2BIG; - - public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]); - blob_len = private_len + public_len + 4; - if (blob_len > payload->blob_len) - return -E2BIG; - - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); - if (rc) - return rc; - - tpm_buf_append_u32(&buf, options->keyhandle); - tpm2_buf_append_auth(&buf, TPM2_RS_PW, - NULL /* nonce */, 0, - 0 /* session_attributes */, - options->keyauth /* hmac */, - TPM_DIGEST_SIZE); - - tpm_buf_append(&buf, payload->blob, blob_len); - - if (buf.flags & TPM_BUF_OVERFLOW) { - rc = -E2BIG; - goto out; - } - - rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob"); - if (!rc) - *blob_handle = be32_to_cpup( - (__be32 *) &buf.data[TPM_HEADER_SIZE]); - -out: - tpm_buf_destroy(&buf); - - if (rc > 0) - rc = -EPERM; - - return rc; -} - -/** - * tpm2_unseal_cmd() - execute a TPM2_Unload command - * - * @chip: TPM chip to use - * @payload: the key data in clear and encrypted form - * @options: authentication values and other options - * @blob_handle: blob handle - * - * Return: 0 on success - * -EPERM on tpm error status - * < 0 error from tpm_transmit_cmd - */ -static int tpm2_unseal_cmd(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options, - u32 blob_handle) -{ - struct tpm_buf buf; - u16 data_len; - u8 *data; - int rc; - - rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); - if (rc) - return rc; - - tpm_buf_append_u32(&buf, blob_handle); - tpm2_buf_append_auth(&buf, - options->policyhandle ? - options->policyhandle : TPM2_RS_PW, - NULL /* nonce */, 0, - TPM2_SA_CONTINUE_SESSION, - options->blobauth /* hmac */, - TPM_DIGEST_SIZE); - - rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing"); - if (rc > 0) - rc = -EPERM; - - if (!rc) { - data_len = be16_to_cpup( - (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]); - if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE + 1) { - rc = -EFAULT; - goto out; - } - - if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 6 + data_len) { - rc = -EFAULT; - goto out; - } - data = &buf.data[TPM_HEADER_SIZE + 6]; - - memcpy(payload->key, data, data_len - 1); - payload->key_len = data_len - 1; - payload->migratable = data[data_len - 1]; - } - -out: - tpm_buf_destroy(&buf); - return rc; -} - -/** - * tpm2_unseal_trusted() - unseal the payload of a trusted key - * - * @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. - */ -int tpm2_unseal_trusted(struct tpm_chip *chip, - struct trusted_key_payload *payload, - struct trusted_key_options *options) -{ - u32 blob_handle; - int rc; - - rc = tpm2_load_cmd(chip, payload, options, &blob_handle); - if (rc) - return rc; - - rc = tpm2_unseal_cmd(chip, payload, options, blob_handle); - tpm2_flush_context(chip, blob_handle); - return rc; -} - struct tpm2_get_cap_out { u8 more_data; __be32 subcap_id; @@ -939,6 +632,10 @@ static int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip) chip->cc_attrs_tbl = devm_kcalloc(&chip->dev, 4, nr_commands, GFP_KERNEL); + if (!chip->cc_attrs_tbl) { + rc = -ENOMEM; + goto out; + } rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_GET_CAPABILITY); if (rc) diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index e59f1f91d7f3..a9dcf31eadd2 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -22,6 +22,7 @@ #include "tpm.h" #define ACPI_SIG_TPM2 "TPM2" +#define TPM_CRB_MAX_RESOURCES 3 static const guid_t crb_acpi_start_guid = GUID_INIT(0x6BBF6CAB, 0x5463, 0x4714, @@ -91,7 +92,6 @@ enum crb_status { struct crb_priv { u32 sm; const char *hid; - void __iomem *iobase; struct crb_regs_head __iomem *regs_h; struct crb_regs_tail __iomem *regs_t; u8 __iomem *cmd; @@ -434,21 +434,27 @@ static const struct tpm_class_ops tpm_crb = { static int crb_check_resource(struct acpi_resource *ares, void *data) { - struct resource *io_res = data; + struct resource *iores_array = data; struct resource_win win; struct resource *res = &(win.res); + int i; if (acpi_dev_resource_memory(ares, res) || acpi_dev_resource_address_space(ares, &win)) { - *io_res = *res; - io_res->name = NULL; + for (i = 0; i < TPM_CRB_MAX_RESOURCES + 1; ++i) { + if (resource_type(iores_array + i) != IORESOURCE_MEM) { + iores_array[i] = *res; + iores_array[i].name = NULL; + break; + } + } } return 1; } -static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv, - struct resource *io_res, u64 start, u32 size) +static void __iomem *crb_map_res(struct device *dev, struct resource *iores, + void __iomem **iobase_ptr, u64 start, u32 size) { struct resource new_res = { .start = start, @@ -460,10 +466,16 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv, if (start != new_res.start) return (void __iomem *) ERR_PTR(-EINVAL); - if (!resource_contains(io_res, &new_res)) + if (!iores) return devm_ioremap_resource(dev, &new_res); - return priv->iobase + (new_res.start - io_res->start); + if (!*iobase_ptr) { + *iobase_ptr = devm_ioremap_resource(dev, iores); + if (IS_ERR(*iobase_ptr)) + return *iobase_ptr; + } + + return *iobase_ptr + (new_res.start - iores->start); } /* @@ -490,9 +502,13 @@ static u64 crb_fixup_cmd_size(struct device *dev, struct resource *io_res, static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, struct acpi_table_tpm2 *buf) { - struct list_head resources; - struct resource io_res; + struct list_head acpi_resource_list; + struct resource iores_array[TPM_CRB_MAX_RESOURCES + 1] = { {0} }; + void __iomem *iobase_array[TPM_CRB_MAX_RESOURCES] = {NULL}; struct device *dev = &device->dev; + struct resource *iores; + void __iomem **iobase_ptr; + int i; u32 pa_high, pa_low; u64 cmd_pa; u32 cmd_size; @@ -501,21 +517,41 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, u32 rsp_size; int ret; - INIT_LIST_HEAD(&resources); - ret = acpi_dev_get_resources(device, &resources, crb_check_resource, - &io_res); + INIT_LIST_HEAD(&acpi_resource_list); + ret = acpi_dev_get_resources(device, &acpi_resource_list, + crb_check_resource, iores_array); if (ret < 0) return ret; - acpi_dev_free_resource_list(&resources); + acpi_dev_free_resource_list(&acpi_resource_list); - if (resource_type(&io_res) != IORESOURCE_MEM) { + if (resource_type(iores_array) != IORESOURCE_MEM) { dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n"); return -EINVAL; + } else if (resource_type(iores_array + TPM_CRB_MAX_RESOURCES) == + IORESOURCE_MEM) { + dev_warn(dev, "TPM2 ACPI table defines too many memory resources\n"); + memset(iores_array + TPM_CRB_MAX_RESOURCES, + 0, sizeof(*iores_array)); + iores_array[TPM_CRB_MAX_RESOURCES].flags = 0; } - priv->iobase = devm_ioremap_resource(dev, &io_res); - if (IS_ERR(priv->iobase)) - return PTR_ERR(priv->iobase); + iores = NULL; + iobase_ptr = NULL; + for (i = 0; resource_type(iores_array + i) == IORESOURCE_MEM; ++i) { + if (buf->control_address >= iores_array[i].start && + buf->control_address + sizeof(struct crb_regs_tail) - 1 <= + iores_array[i].end) { + iores = iores_array + i; + iobase_ptr = iobase_array + i; + break; + } + } + + priv->regs_t = crb_map_res(dev, iores, iobase_ptr, buf->control_address, + sizeof(struct crb_regs_tail)); + + if (IS_ERR(priv->regs_t)) + return PTR_ERR(priv->regs_t); /* 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 @@ -523,9 +559,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, */ if ((priv->sm == ACPI_TPM2_COMMAND_BUFFER) || (priv->sm == ACPI_TPM2_MEMORY_MAPPED)) { - if (buf->control_address == io_res.start + + if (iores && + buf->control_address == iores->start + sizeof(*priv->regs_h)) - priv->regs_h = priv->iobase; + priv->regs_h = *iobase_ptr; else dev_warn(dev, FW_BUG "Bad ACPI memory layout"); } @@ -534,13 +571,6 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, if (ret) return ret; - priv->regs_t = crb_map_res(dev, priv, &io_res, buf->control_address, - sizeof(struct crb_regs_tail)); - if (IS_ERR(priv->regs_t)) { - ret = PTR_ERR(priv->regs_t); - goto out_relinquish_locality; - } - /* * PTT HW bug w/a: wake up the device to access * possibly not retained registers. @@ -552,13 +582,26 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, 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 = crb_fixup_cmd_size(dev, &io_res, cmd_pa, - ioread32(&priv->regs_t->ctrl_cmd_size)); + cmd_size = ioread32(&priv->regs_t->ctrl_cmd_size); + + iores = NULL; + iobase_ptr = NULL; + for (i = 0; iores_array[i].end; ++i) { + if (cmd_pa >= iores_array[i].start && + cmd_pa <= iores_array[i].end) { + iores = iores_array + i; + iobase_ptr = iobase_array + i; + break; + } + } + + if (iores) + cmd_size = crb_fixup_cmd_size(dev, iores, cmd_pa, cmd_size); dev_dbg(dev, "cmd_hi = %X cmd_low = %X cmd_size %X\n", pa_high, pa_low, cmd_size); - priv->cmd = crb_map_res(dev, priv, &io_res, cmd_pa, cmd_size); + priv->cmd = crb_map_res(dev, iores, iobase_ptr, cmd_pa, cmd_size); if (IS_ERR(priv->cmd)) { ret = PTR_ERR(priv->cmd); goto out; @@ -566,11 +609,25 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, memcpy_fromio(&__rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8); rsp_pa = le64_to_cpu(__rsp_pa); - rsp_size = crb_fixup_cmd_size(dev, &io_res, rsp_pa, - ioread32(&priv->regs_t->ctrl_rsp_size)); + rsp_size = ioread32(&priv->regs_t->ctrl_rsp_size); + + iores = NULL; + iobase_ptr = NULL; + for (i = 0; resource_type(iores_array + i) == IORESOURCE_MEM; ++i) { + if (rsp_pa >= iores_array[i].start && + rsp_pa <= iores_array[i].end) { + iores = iores_array + i; + iobase_ptr = iobase_array + i; + break; + } + } + + if (iores) + rsp_size = crb_fixup_cmd_size(dev, iores, rsp_pa, rsp_size); if (cmd_pa != rsp_pa) { - priv->rsp = crb_map_res(dev, priv, &io_res, rsp_pa, rsp_size); + priv->rsp = crb_map_res(dev, iores, iobase_ptr, + rsp_pa, rsp_size); ret = PTR_ERR_OR_ZERO(priv->rsp); goto out; } diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index e4fdde93ed4c..e7df342a317d 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -286,7 +286,7 @@ static int tpm_tis_plat_probe(struct platform_device *pdev) } tpm_info.res = *res; - tpm_info.irq = platform_get_irq(pdev, 0); + tpm_info.irq = platform_get_irq_optional(pdev, 0); if (tpm_info.irq <= 0) { if (pdev != force_pdev) tpm_info.irq = -1; diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 270f43acbb77..8af2cee1a762 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -506,6 +506,84 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) return rc; } +struct tis_vendor_durations_override { + u32 did_vid; + struct tpm1_version version; + unsigned long durations[3]; +}; + +static const struct tis_vendor_durations_override vendor_dur_overrides[] = { + /* STMicroelectronics 0x104a */ + { 0x0000104a, + { 1, 2, 8, 28 }, + { (2 * 60 * HZ), (2 * 60 * HZ), (2 * 60 * HZ) } }, +}; + +static void tpm_tis_update_durations(struct tpm_chip *chip, + unsigned long *duration_cap) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm1_version *version; + u32 did_vid; + int i, rc; + cap_t cap; + + chip->duration_adjusted = false; + + if (chip->ops->clk_enable != NULL) + chip->ops->clk_enable(chip, true); + + rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid); + if (rc < 0) { + dev_warn(&chip->dev, "%s: failed to read did_vid. %d\n", + __func__, rc); + goto out; + } + + /* Try to get a TPM version 1.2 or 1.1 TPM_CAP_VERSION_INFO */ + rc = tpm1_getcap(chip, TPM_CAP_VERSION_1_2, &cap, + "attempting to determine the 1.2 version", + sizeof(cap.version2)); + if (!rc) { + version = &cap.version2.version; + } else { + rc = tpm1_getcap(chip, TPM_CAP_VERSION_1_1, &cap, + "attempting to determine the 1.1 version", + sizeof(cap.version1)); + + if (rc) + goto out; + + version = &cap.version1; + } + + for (i = 0; i != ARRAY_SIZE(vendor_dur_overrides); i++) { + if (vendor_dur_overrides[i].did_vid != did_vid) + continue; + + if ((version->major == + vendor_dur_overrides[i].version.major) && + (version->minor == + vendor_dur_overrides[i].version.minor) && + (version->rev_major == + vendor_dur_overrides[i].version.rev_major) && + (version->rev_minor == + vendor_dur_overrides[i].version.rev_minor)) { + + memcpy(duration_cap, + vendor_dur_overrides[i].durations, + sizeof(vendor_dur_overrides[i].durations)); + + chip->duration_adjusted = true; + goto out; + } + } + +out: + if (chip->ops->clk_enable != NULL) + chip->ops->clk_enable(chip, false); +} + struct tis_vendor_timeout_override { u32 did_vid; unsigned long timeout_us[4]; @@ -842,6 +920,7 @@ static const struct tpm_class_ops tpm_tis = { .send = tpm_tis_send, .cancel = tpm_tis_ready, .update_timeouts = tpm_tis_update_timeouts, + .update_durations = tpm_tis_update_durations, .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, diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c index 19513e622053..d1754fd6c573 100644 --- a/drivers/char/tpm/tpm_tis_spi.c +++ b/drivers/char/tpm/tpm_tis_spi.c @@ -20,42 +20,64 @@ * Dorn and Kyleen Hall and Jarko Sakkinnen. */ +#include <linux/acpi.h> +#include <linux/completion.h> #include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/wait.h> -#include <linux/acpi.h> -#include <linux/freezer.h> +#include <linux/of_device.h> #include <linux/spi/spi.h> -#include <linux/gpio.h> -#include <linux/of_irq.h> -#include <linux/of_gpio.h> #include <linux/tpm.h> + #include "tpm.h" #include "tpm_tis_core.h" +#include "tpm_tis_spi.h" #define MAX_SPI_FRAMESIZE 64 -struct tpm_tis_spi_phy { - struct tpm_tis_data priv; - struct spi_device *spi_device; - u8 *iobuf; -}; - -static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data) +/* + * TCG SPI flow control is documented in section 6.4 of the spec[1]. In short, + * keep trying to read from the device until MISO goes high indicating the + * wait state has ended. + * + * [1] https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/ + */ +static int tpm_tis_spi_flow_control(struct tpm_tis_spi_phy *phy, + struct spi_transfer *spi_xfer) { - return container_of(data, struct tpm_tis_spi_phy, priv); + struct spi_message m; + int ret, i; + + if ((phy->iobuf[3] & 0x01) == 0) { + // handle SPI wait states + phy->iobuf[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) + return ret; + if (phy->iobuf[0] & 0x01) + break; + } + + if (i == TPM_RETRY) + return -ETIMEDOUT; + } + + return 0; } -static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, - u8 *in, const u8 *out) +int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, + u8 *in, const u8 *out) { struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); int ret = 0; - int i; struct spi_message m; struct spi_transfer spi_xfer; u8 transfer_len; @@ -82,26 +104,9 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, if (ret < 0) goto exit; - if ((phy->iobuf[3] & 0x01) == 0) { - // handle SPI wait states - phy->iobuf[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->iobuf[0] & 0x01) - break; - } - - if (i == TPM_RETRY) { - ret = -ETIMEDOUT; - goto exit; - } - } + ret = phy->flow_control(phy, &spi_xfer); + if (ret < 0) + goto exit; spi_xfer.cs_change = 0; spi_xfer.len = transfer_len; @@ -117,6 +122,7 @@ static int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, spi_message_init(&m); spi_message_add_tail(&spi_xfer, &m); + reinit_completion(&phy->ready); ret = spi_sync_locked(phy->spi_device, &m); if (ret < 0) goto exit; @@ -146,7 +152,7 @@ static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr, return tpm_tis_spi_transfer(data, addr, len, NULL, value); } -static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result) +int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result) { __le16 result_le; int rc; @@ -159,7 +165,7 @@ static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result) return rc; } -static int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result) +int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result) { __le32 result_le; int rc; @@ -172,7 +178,7 @@ static int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result) return rc; } -static int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value) +int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value) { __le32 value_le; int rc; @@ -184,6 +190,18 @@ static int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value) return rc; } +int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy, + int irq, const struct tpm_tis_phy_ops *phy_ops) +{ + phy->iobuf = devm_kmalloc(&spi->dev, MAX_SPI_FRAMESIZE, GFP_KERNEL); + if (!phy->iobuf) + return -ENOMEM; + + phy->spi_device = spi; + + return tpm_tis_core_init(&spi->dev, &phy->priv, irq, phy_ops, NULL); +} + static const struct tpm_tis_phy_ops tpm_spi_phy_ops = { .read_bytes = tpm_tis_spi_read_bytes, .write_bytes = tpm_tis_spi_write_bytes, @@ -202,11 +220,7 @@ static int tpm_tis_spi_probe(struct spi_device *dev) if (!phy) return -ENOMEM; - phy->spi_device = dev; - - phy->iobuf = devm_kmalloc(&dev->dev, MAX_SPI_FRAMESIZE, GFP_KERNEL); - if (!phy->iobuf) - return -ENOMEM; + phy->flow_control = tpm_tis_spi_flow_control; /* If the SPI device has an IRQ then use that */ if (dev->irq > 0) @@ -214,11 +228,27 @@ static int tpm_tis_spi_probe(struct spi_device *dev) else irq = -1; - return tpm_tis_core_init(&dev->dev, &phy->priv, irq, &tpm_spi_phy_ops, - NULL); + init_completion(&phy->ready); + return tpm_tis_spi_init(dev, phy, irq, &tpm_spi_phy_ops); +} + +typedef int (*tpm_tis_spi_probe_func)(struct spi_device *); + +static int tpm_tis_spi_driver_probe(struct spi_device *spi) +{ + const struct spi_device_id *spi_dev_id = spi_get_device_id(spi); + tpm_tis_spi_probe_func probe_func; + + probe_func = of_device_get_match_data(&spi->dev); + if (!probe_func && spi_dev_id) + probe_func = (tpm_tis_spi_probe_func)spi_dev_id->driver_data; + if (!probe_func) + return -ENODEV; + + return probe_func(spi); } -static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume); +static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_spi_resume); static int tpm_tis_spi_remove(struct spi_device *dev) { @@ -230,15 +260,17 @@ static int tpm_tis_spi_remove(struct spi_device *dev) } static const struct spi_device_id tpm_tis_spi_id[] = { - {"tpm_tis_spi", 0}, + { "tpm_tis_spi", (unsigned long)tpm_tis_spi_probe }, + { "cr50", (unsigned long)cr50_spi_probe }, {} }; MODULE_DEVICE_TABLE(spi, tpm_tis_spi_id); static const struct of_device_id of_tis_spi_match[] = { - { .compatible = "st,st33htpm-spi", }, - { .compatible = "infineon,slb9670", }, - { .compatible = "tcg,tpm_tis-spi", }, + { .compatible = "st,st33htpm-spi", .data = tpm_tis_spi_probe }, + { .compatible = "infineon,slb9670", .data = tpm_tis_spi_probe }, + { .compatible = "tcg,tpm_tis-spi", .data = tpm_tis_spi_probe }, + { .compatible = "google,cr50", .data = cr50_spi_probe }, {} }; MODULE_DEVICE_TABLE(of, of_tis_spi_match); @@ -251,13 +283,12 @@ MODULE_DEVICE_TABLE(acpi, acpi_tis_spi_match); static struct spi_driver tpm_tis_spi_driver = { .driver = { - .owner = THIS_MODULE, .name = "tpm_tis_spi", .pm = &tpm_tis_pm, .of_match_table = of_match_ptr(of_tis_spi_match), .acpi_match_table = ACPI_PTR(acpi_tis_spi_match), }, - .probe = tpm_tis_spi_probe, + .probe = tpm_tis_spi_driver_probe, .remove = tpm_tis_spi_remove, .id_table = tpm_tis_spi_id, }; diff --git a/drivers/char/tpm/tpm_tis_spi.h b/drivers/char/tpm/tpm_tis_spi.h new file mode 100644 index 000000000000..bba73979c368 --- /dev/null +++ b/drivers/char/tpm/tpm_tis_spi.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2015 Infineon Technologies AG + * Copyright (C) 2016 STMicroelectronics SAS + */ + +#ifndef TPM_TIS_SPI_H +#define TPM_TIS_SPI_H + +#include "tpm_tis_core.h" + +struct tpm_tis_spi_phy { + struct tpm_tis_data priv; + struct spi_device *spi_device; + int (*flow_control)(struct tpm_tis_spi_phy *phy, + struct spi_transfer *xfer); + struct completion ready; + unsigned long wake_after; + + u8 *iobuf; +}; + +static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data) +{ + return container_of(data, struct tpm_tis_spi_phy, priv); +} + +extern int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy, + int irq, const struct tpm_tis_phy_ops *phy_ops); + +extern int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len, + u8 *in, const u8 *out); + +extern int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result); +extern int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result); +extern int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value); + +#ifdef CONFIG_TCG_TIS_SPI_CR50 +extern int cr50_spi_probe(struct spi_device *spi); +#else +static inline int cr50_spi_probe(struct spi_device *spi) +{ + return -ENODEV; +} +#endif + +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_TCG_TIS_SPI_CR50) +extern int tpm_tis_spi_resume(struct device *dev); +#else +#define tpm_tis_spi_resume NULL +#endif + +#endif diff --git a/drivers/char/tpm/tpm_tis_spi_cr50.c b/drivers/char/tpm/tpm_tis_spi_cr50.c new file mode 100644 index 000000000000..37d72e818335 --- /dev/null +++ b/drivers/char/tpm/tpm_tis_spi_cr50.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016 Google, Inc + * + * This device driver implements a TCG PTP FIFO interface over SPI for chips + * with Cr50 firmware. + * It is based on tpm_tis_spi driver by Peter Huewe and Christophe Ricard. + */ + +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/pm.h> +#include <linux/spi/spi.h> +#include <linux/wait.h> + +#include "tpm_tis_core.h" +#include "tpm_tis_spi.h" + +/* + * Cr50 timing constants: + * - can go to sleep not earlier than after CR50_SLEEP_DELAY_MSEC. + * - needs up to CR50_WAKE_START_DELAY_USEC to wake after sleep. + * - requires waiting for "ready" IRQ, if supported; or waiting for at least + * CR50_NOIRQ_ACCESS_DELAY_MSEC between transactions, if IRQ is not supported. + * - waits for up to CR50_FLOW_CONTROL for flow control 'ready' indication. + */ +#define CR50_SLEEP_DELAY_MSEC 1000 +#define CR50_WAKE_START_DELAY_USEC 1000 +#define CR50_NOIRQ_ACCESS_DELAY msecs_to_jiffies(2) +#define CR50_READY_IRQ_TIMEOUT msecs_to_jiffies(TPM2_TIMEOUT_A) +#define CR50_FLOW_CONTROL msecs_to_jiffies(TPM2_TIMEOUT_A) +#define MAX_IRQ_CONFIRMATION_ATTEMPTS 3 + +#define TPM_CR50_FW_VER(l) (0x0f90 | ((l) << 12)) +#define TPM_CR50_MAX_FW_VER_LEN 64 + +struct cr50_spi_phy { + struct tpm_tis_spi_phy spi_phy; + + struct mutex time_track_mutex; + unsigned long last_access; + + unsigned long access_delay; + + unsigned int irq_confirmation_attempt; + bool irq_needs_confirmation; + bool irq_confirmed; +}; + +static inline struct cr50_spi_phy *to_cr50_spi_phy(struct tpm_tis_spi_phy *phy) +{ + return container_of(phy, struct cr50_spi_phy, spi_phy); +} + +/* + * The cr50 interrupt handler just signals waiting threads that the + * interrupt was asserted. It does not do any processing triggered + * by interrupts but is instead used to avoid fixed delays. + */ +static irqreturn_t cr50_spi_irq_handler(int dummy, void *dev_id) +{ + struct cr50_spi_phy *cr50_phy = dev_id; + + cr50_phy->irq_confirmed = true; + complete(&cr50_phy->spi_phy.ready); + + return IRQ_HANDLED; +} + +/* + * Cr50 needs to have at least some delay between consecutive + * transactions. Make sure we wait. + */ +static void cr50_ensure_access_delay(struct cr50_spi_phy *phy) +{ + unsigned long allowed_access = phy->last_access + phy->access_delay; + unsigned long time_now = jiffies; + struct device *dev = &phy->spi_phy.spi_device->dev; + + /* + * Note: There is a small chance, if Cr50 is not accessed in a few days, + * that time_in_range will not provide the correct result after the wrap + * around for jiffies. In this case, we'll have an unneeded short delay, + * which is fine. + */ + if (time_in_range_open(time_now, phy->last_access, allowed_access)) { + unsigned long remaining, timeout = allowed_access - time_now; + + remaining = wait_for_completion_timeout(&phy->spi_phy.ready, + timeout); + if (!remaining && phy->irq_confirmed) + dev_warn(dev, "Timeout waiting for TPM ready IRQ\n"); + } + + if (phy->irq_needs_confirmation) { + unsigned int attempt = ++phy->irq_confirmation_attempt; + + if (phy->irq_confirmed) { + phy->irq_needs_confirmation = false; + phy->access_delay = CR50_READY_IRQ_TIMEOUT; + dev_info(dev, "TPM ready IRQ confirmed on attempt %u\n", + attempt); + } else if (attempt > MAX_IRQ_CONFIRMATION_ATTEMPTS) { + phy->irq_needs_confirmation = false; + dev_warn(dev, "IRQ not confirmed - will use delays\n"); + } + } +} + +/* + * Cr50 might go to sleep if there is no SPI activity for some time and + * miss the first few bits/bytes on the bus. In such case, wake it up + * by asserting CS and give it time to start up. + */ +static bool cr50_needs_waking(struct cr50_spi_phy *phy) +{ + /* + * Note: There is a small chance, if Cr50 is not accessed in a few days, + * that time_in_range will not provide the correct result after the wrap + * around for jiffies. In this case, we'll probably timeout or read + * incorrect value from TPM_STS and just retry the operation. + */ + return !time_in_range_open(jiffies, phy->last_access, + phy->spi_phy.wake_after); +} + +static void cr50_wake_if_needed(struct cr50_spi_phy *cr50_phy) +{ + struct tpm_tis_spi_phy *phy = &cr50_phy->spi_phy; + + if (cr50_needs_waking(cr50_phy)) { + /* Assert CS, wait 1 msec, deassert CS */ + struct spi_transfer spi_cs_wake = { .delay_usecs = 1000 }; + + spi_sync_transfer(phy->spi_device, &spi_cs_wake, 1); + /* Wait for it to fully wake */ + usleep_range(CR50_WAKE_START_DELAY_USEC, + CR50_WAKE_START_DELAY_USEC * 2); + } + + /* Reset the time when we need to wake Cr50 again */ + phy->wake_after = jiffies + msecs_to_jiffies(CR50_SLEEP_DELAY_MSEC); +} + +/* + * Flow control: clock the bus and wait for cr50 to set LSB before + * sending/receiving data. TCG PTP spec allows it to happen during + * the last byte of header, but cr50 never does that in practice, + * and earlier versions had a bug when it was set too early, so don't + * check for it during header transfer. + */ +static int cr50_spi_flow_control(struct tpm_tis_spi_phy *phy, + struct spi_transfer *spi_xfer) +{ + struct device *dev = &phy->spi_device->dev; + unsigned long timeout = jiffies + CR50_FLOW_CONTROL; + struct spi_message m; + int ret; + + spi_xfer->len = 1; + + do { + spi_message_init(&m); + spi_message_add_tail(spi_xfer, &m); + ret = spi_sync_locked(phy->spi_device, &m); + if (ret < 0) + return ret; + + if (time_after(jiffies, timeout)) { + dev_warn(dev, "Timeout during flow control\n"); + return -EBUSY; + } + } while (!(phy->iobuf[0] & 0x01)); + + return 0; +} + +static int tpm_tis_spi_cr50_transfer(struct tpm_tis_data *data, u32 addr, u16 len, + u8 *in, const u8 *out) +{ + struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); + struct cr50_spi_phy *cr50_phy = to_cr50_spi_phy(phy); + int ret; + + mutex_lock(&cr50_phy->time_track_mutex); + /* + * Do this outside of spi_bus_lock in case cr50 is not the + * only device on that spi bus. + */ + cr50_ensure_access_delay(cr50_phy); + cr50_wake_if_needed(cr50_phy); + + ret = tpm_tis_spi_transfer(data, addr, len, in, out); + + cr50_phy->last_access = jiffies; + mutex_unlock(&cr50_phy->time_track_mutex); + + return ret; +} + +static int tpm_tis_spi_cr50_read_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, u8 *result) +{ + return tpm_tis_spi_cr50_transfer(data, addr, len, result, NULL); +} + +static int tpm_tis_spi_cr50_write_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, const u8 *value) +{ + return tpm_tis_spi_cr50_transfer(data, addr, len, NULL, value); +} + +static const struct tpm_tis_phy_ops tpm_spi_cr50_phy_ops = { + .read_bytes = tpm_tis_spi_cr50_read_bytes, + .write_bytes = tpm_tis_spi_cr50_write_bytes, + .read16 = tpm_tis_spi_read16, + .read32 = tpm_tis_spi_read32, + .write32 = tpm_tis_spi_write32, +}; + +static void cr50_print_fw_version(struct tpm_tis_data *data) +{ + struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); + int i, len = 0; + char fw_ver[TPM_CR50_MAX_FW_VER_LEN + 1]; + char fw_ver_block[4]; + + /* + * Write anything to TPM_CR50_FW_VER to start from the beginning + * of the version string + */ + tpm_tis_write8(data, TPM_CR50_FW_VER(data->locality), 0); + + /* Read the string, 4 bytes at a time, until we get '\0' */ + do { + tpm_tis_read_bytes(data, TPM_CR50_FW_VER(data->locality), 4, + fw_ver_block); + for (i = 0; i < 4 && fw_ver_block[i]; ++len, ++i) + fw_ver[len] = fw_ver_block[i]; + } while (i == 4 && len < TPM_CR50_MAX_FW_VER_LEN); + fw_ver[len] = '\0'; + + dev_info(&phy->spi_device->dev, "Cr50 firmware version: %s\n", fw_ver); +} + +int cr50_spi_probe(struct spi_device *spi) +{ + struct tpm_tis_spi_phy *phy; + struct cr50_spi_phy *cr50_phy; + int ret; + struct tpm_chip *chip; + + cr50_phy = devm_kzalloc(&spi->dev, sizeof(*cr50_phy), GFP_KERNEL); + if (!cr50_phy) + return -ENOMEM; + + phy = &cr50_phy->spi_phy; + phy->flow_control = cr50_spi_flow_control; + phy->wake_after = jiffies; + init_completion(&phy->ready); + + cr50_phy->access_delay = CR50_NOIRQ_ACCESS_DELAY; + cr50_phy->last_access = jiffies; + mutex_init(&cr50_phy->time_track_mutex); + + if (spi->irq > 0) { + ret = devm_request_irq(&spi->dev, spi->irq, + cr50_spi_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "cr50_spi", cr50_phy); + if (ret < 0) { + if (ret == -EPROBE_DEFER) + return ret; + dev_warn(&spi->dev, "Requesting IRQ %d failed: %d\n", + spi->irq, ret); + /* + * This is not fatal, the driver will fall back to + * delays automatically, since ready will never + * be completed without a registered irq handler. + * So, just fall through. + */ + } else { + /* + * IRQ requested, let's verify that it is actually + * triggered, before relying on it. + */ + cr50_phy->irq_needs_confirmation = true; + } + } else { + dev_warn(&spi->dev, + "No IRQ - will use delays between transactions.\n"); + } + + ret = tpm_tis_spi_init(spi, phy, -1, &tpm_spi_cr50_phy_ops); + if (ret) + return ret; + + cr50_print_fw_version(&phy->priv); + + chip = dev_get_drvdata(&spi->dev); + chip->flags |= TPM_CHIP_FLAG_FIRMWARE_POWER_MANAGED; + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +int tpm_tis_spi_resume(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_tis_data *data = dev_get_drvdata(&chip->dev); + struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); + /* + * Jiffies not increased during suspend, so we need to reset + * the time to wake Cr50 after resume. + */ + phy->wake_after = jiffies; + + return tpm_tis_resume(dev); +} +#endif diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 7270e7b69262..3259426f01dc 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1325,24 +1325,24 @@ static void set_console_size(struct port *port, u16 rows, u16 cols) port->cons.ws.ws_col = cols; } -static unsigned int fill_queue(struct virtqueue *vq, spinlock_t *lock) +static int fill_queue(struct virtqueue *vq, spinlock_t *lock) { struct port_buffer *buf; - unsigned int nr_added_bufs; + int nr_added_bufs; int ret; nr_added_bufs = 0; do { buf = alloc_buf(vq->vdev, PAGE_SIZE, 0); if (!buf) - break; + return -ENOMEM; spin_lock_irq(lock); ret = add_inbuf(vq, buf); if (ret < 0) { spin_unlock_irq(lock); free_buf(buf, true); - break; + return ret; } nr_added_bufs++; spin_unlock_irq(lock); @@ -1362,7 +1362,6 @@ static int add_port(struct ports_device *portdev, u32 id) char debugfs_name[16]; struct port *port; dev_t devt; - unsigned int nr_added_bufs; int err; port = kmalloc(sizeof(*port), GFP_KERNEL); @@ -1421,11 +1420,13 @@ static int add_port(struct ports_device *portdev, u32 id) spin_lock_init(&port->outvq_lock); init_waitqueue_head(&port->waitqueue); - /* Fill the in_vq with buffers so the host can send us data. */ - nr_added_bufs = fill_queue(port->in_vq, &port->inbuf_lock); - if (!nr_added_bufs) { + /* We can safely ignore ENOSPC because it means + * the queue already has buffers. Buffers are removed + * only by virtcons_remove(), not by unplug_port() + */ + err = fill_queue(port->in_vq, &port->inbuf_lock); + if (err < 0 && err != -ENOSPC) { dev_err(port->dev, "Error allocating inbufs\n"); - err = -ENOMEM; goto free_device; } @@ -2059,14 +2060,11 @@ static int virtcons_probe(struct virtio_device *vdev) INIT_WORK(&portdev->control_work, &control_work_handler); if (multiport) { - unsigned int nr_added_bufs; - spin_lock_init(&portdev->c_ivq_lock); spin_lock_init(&portdev->c_ovq_lock); - nr_added_bufs = fill_queue(portdev->c_ivq, - &portdev->c_ivq_lock); - if (!nr_added_bufs) { + err = fill_queue(portdev->c_ivq, &portdev->c_ivq_lock); + if (err < 0) { dev_err(&vdev->dev, "Error allocating buffers for control queue\n"); /* @@ -2077,7 +2075,7 @@ static int virtcons_probe(struct virtio_device *vdev) VIRTIO_CONSOLE_DEVICE_READY, 0); /* Device was functional: we need full cleanup. */ virtcons_remove(vdev); - return -ENOMEM; + return err; } } else { /* |