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