diff options
author | Jani Nikula <jani.nikula@intel.com> | 2020-06-25 18:05:03 +0300 |
---|---|---|
committer | Jani Nikula <jani.nikula@intel.com> | 2020-06-25 18:05:03 +0300 |
commit | 0f69403d2535ffc7200a8414cf3ca66a49b0d741 (patch) | |
tree | 3ce85dd08359ea872aa8fb9bd12072efdb80a787 /drivers/char | |
parent | 580fbdc5136822208f107500682e50a1cb232e94 (diff) | |
parent | 0a19b068acc47d05212f03e494381926dc0381e2 (diff) |
Merge drm/drm-next into drm-intel-next-queued
Catch up with upstream, in particular to get c1e8d7c6a7a6 ("mmap locking
API: convert mmap_sem comments").
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Diffstat (limited to 'drivers/char')
36 files changed, 1117 insertions, 226 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index d4665fe9ccd2..98c3a5d8003e 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -11,7 +11,7 @@ config TTY_PRINTK tristate "TTY driver to output user messages via printk" depends on EXPERT && TTY default n - ---help--- + help If you say Y here, the support for writing user messages (i.e. console messages) via printk is available. @@ -33,7 +33,7 @@ config TTY_PRINTK_LEVEL config PRINTER tristate "Parallel printer support" depends on PARPORT - ---help--- + help If you intend to attach a printer to the parallel port of your Linux box (as opposed to using a serial printer; if the connector at the printer has 9 or 25 holes ["female"], then it's serial), say Y. @@ -59,7 +59,7 @@ config PRINTER config LP_CONSOLE bool "Support for console on line printer" depends on PRINTER - ---help--- + help If you want kernel messages to be printed out as they occur, you can have a console on the printer. This option adds support for doing that; to actually get it to happen you need to pass the @@ -76,7 +76,7 @@ config LP_CONSOLE config PPDEV tristate "Support for user-space parallel port device drivers" depends on PARPORT - ---help--- + help Saying Y to this adds support for /dev/parport device nodes. This is needed for programs that want portable access to the parallel port, for instance deviceid (which displays Plug-and-Play device @@ -146,7 +146,7 @@ config DS1620 config NWBUTTON tristate "NetWinder Button" depends on ARCH_NETWINDER - ---help--- + help If you say Y here and create a character device node /dev/nwbutton with major and minor numbers 10 and 158 ("man mknod"), then every time the orange button is pressed a number of times, the number of @@ -182,7 +182,7 @@ config NWBUTTON_REBOOT config NWFLASH tristate "NetWinder flash support" depends on ARCH_NETWINDER - ---help--- + help If you say Y here and create a character device /dev/flash with major 10 and minor 160 you can manipulate the flash ROM containing the NetWinder firmware. Be careful as accidentally overwriting the @@ -209,7 +209,7 @@ config DTLK config XILINX_HWICAP tristate "Xilinx HWICAP Support" - depends on XILINX_VIRTEX || MICROBLAZE + depends on MICROBLAZE help This option enables support for Xilinx Internal Configuration Access Port (ICAP) driver. The ICAP is used on Xilinx Virtex @@ -220,7 +220,7 @@ config XILINX_HWICAP config R3964 tristate "Siemens R3964 line discipline" depends on TTY && BROKEN - ---help--- + help This driver allows synchronous communication with devices using the Siemens R3964 packet protocol. Unless you are dealing with special hardware like PLCs, you are unlikely to need this. @@ -233,7 +233,7 @@ config R3964 config APPLICOM tristate "Applicom intelligent fieldbus card support" depends on PCI - ---help--- + help This driver provides the kernel-side support for the intelligent fieldbus cards made by Applicom International. More information about these cards can be found on the WWW at the address @@ -248,7 +248,7 @@ config APPLICOM config SONYPI tristate "Sony Vaio Programmable I/O Control Device support" depends on X86_32 && PCI && INPUT - ---help--- + help This driver enables access to the Sony Programmable I/O Control Device which can be found in many (all ?) Sony Vaio laptops. @@ -269,7 +269,7 @@ config MWAVE tristate "ACP Modem (Mwave) support" depends on X86 && TTY select SERIAL_8250 - ---help--- + help The ACP modem (Mwave) for Linux is a WinModem. It is composed of a kernel driver and a user level application. Together these components support direct attachment to public switched telephone networks (PSTNs) @@ -347,7 +347,7 @@ config NVRAM tristate "/dev/nvram support" depends on X86 || HAVE_ARCH_NVRAM_OPS default M68K || PPC - ---help--- + help If you say Y here and create a character special file /dev/nvram with major number 10 and minor number 144 using mknod ("man mknod"), you get read and write access to the non-volatile memory. diff --git a/drivers/char/agp/Kconfig b/drivers/char/agp/Kconfig index bc54235a7022..a086dd34f932 100644 --- a/drivers/char/agp/Kconfig +++ b/drivers/char/agp/Kconfig @@ -3,7 +3,7 @@ menuconfig AGP tristate "/dev/agpgart (AGP Support)" depends on ALPHA || IA64 || PARISC || PPC || X86 depends on PCI - ---help--- + help AGP (Accelerated Graphics Port) is a bus system mainly used to connect graphics cards to the rest of the system. @@ -30,7 +30,7 @@ menuconfig AGP config AGP_ALI tristate "ALI chipset support" depends on AGP && X86_32 - ---help--- + help This option gives you AGP support for the GLX component of X on the following ALi chipsets. The supported chipsets include M1541, M1621, M1631, M1632, M1641,M1647,and M1651. @@ -45,7 +45,7 @@ config AGP_ALI config AGP_ATI tristate "ATI chipset support" depends on AGP && X86_32 - ---help--- + help This option gives you AGP support for the GLX component of X on the ATI RadeonIGP family of chipsets. diff --git a/drivers/char/agp/frontend.c b/drivers/char/agp/frontend.c index 47098648502d..00ff5fcb808a 100644 --- a/drivers/char/agp/frontend.c +++ b/drivers/char/agp/frontend.c @@ -39,7 +39,6 @@ #include <linux/fs.h> #include <linux/sched.h> #include <linux/uaccess.h> -#include <asm/pgtable.h> #include "agp.h" struct agp_front_data agp_fe; diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 9e84239f88d4..3ffbb1c80c5c 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -42,7 +42,6 @@ #ifdef CONFIG_X86 #include <asm/set_memory.h> #endif -#include <asm/pgtable.h> #include "agp.h" __u32 *agp_gatt_table; diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index e5e5333f302d..cce2af5df7b4 100644 --- a/drivers/char/bsr.c +++ b/drivers/char/bsr.c @@ -17,7 +17,6 @@ #include <linux/list.h> #include <linux/mm.h> #include <linux/slab.h> -#include <asm/pgtable.h> #include <asm/io.h> /* diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 9bc46da8d77a..0ad17efc96df 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -6,7 +6,7 @@ menuconfig HW_RANDOM tristate "Hardware Random Number Generator Core support" default m - ---help--- + help Hardware Random Number Generator Core infrastructure. To compile this driver as a module, choose M here: the @@ -24,7 +24,7 @@ if HW_RANDOM config HW_RANDOM_TIMERIOMEM tristate "Timer IOMEM HW Random Number Generator support" depends on HAS_IOMEM - ---help--- + help This driver provides kernel-side support for a generic Random Number Generator used by reading a 'dumb' iomem address that is to be read no faster than, for example, once a second; @@ -39,7 +39,7 @@ config HW_RANDOM_INTEL tristate "Intel HW Random Number Generator support" depends on (X86 || IA64) && PCI default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on Intel i8xx-based motherboards. @@ -52,7 +52,7 @@ config HW_RANDOM_AMD tristate "AMD HW Random Number Generator support" depends on (X86 || PPC_MAPLE) && PCI default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on AMD 76x-based motherboards. @@ -65,7 +65,7 @@ config HW_RANDOM_ATMEL tristate "Atmel Random Number Generator support" depends on ARCH_AT91 && HAVE_CLK && OF default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on Atmel AT91 devices. @@ -79,7 +79,7 @@ config HW_RANDOM_BCM2835 depends on ARCH_BCM2835 || ARCH_BCM_NSP || ARCH_BCM_5301X || \ ARCH_BCM_63XX || BCM63XX || BMIPS_GENERIC default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on the Broadcom BCM2835 and BCM63xx SoCs. @@ -92,7 +92,7 @@ config HW_RANDOM_IPROC_RNG200 tristate "Broadcom iProc/STB RNG200 support" depends on ARCH_BCM_IPROC || ARCH_BCM2835 || ARCH_BRCMSTB default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the RNG200 hardware found on the Broadcom iProc and STB SoCs. @@ -105,7 +105,7 @@ config HW_RANDOM_GEODE tristate "AMD Geode HW Random Number Generator support" depends on X86_32 && PCI default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on the AMD Geode LX. @@ -118,7 +118,7 @@ config HW_RANDOM_N2RNG tristate "Niagara2 Random Number Generator support" depends on SPARC64 default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on Niagara2 cpus. @@ -131,7 +131,7 @@ config HW_RANDOM_VIA tristate "VIA HW Random Number Generator support" depends on X86 default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on VIA based motherboards. @@ -144,7 +144,7 @@ config HW_RANDOM_IXP4XX tristate "Intel IXP4xx NPU HW Pseudo-Random Number Generator support" depends on ARCH_IXP4XX default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Pseudo-Random Number Generator hardware found on the Intel IXP45x/46x NPU. @@ -157,7 +157,7 @@ config HW_RANDOM_OMAP tristate "OMAP Random Number Generator support" depends on ARCH_OMAP16XX || ARCH_OMAP2PLUS || ARCH_MVEBU default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on OMAP16xx, OMAP2/3/4/5, AM33xx/AM43xx multimedia processors, and Marvell Armada 7k/8k SoCs. @@ -171,7 +171,7 @@ config HW_RANDOM_OMAP3_ROM tristate "OMAP3 ROM Random Number Generator support" depends on ARCH_OMAP3 default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on OMAP34xx processors. @@ -184,7 +184,7 @@ config HW_RANDOM_OCTEON tristate "Octeon Random Number Generator support" depends on CAVIUM_OCTEON_SOC default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on Octeon processors. @@ -197,7 +197,7 @@ config HW_RANDOM_PASEMI tristate "PA Semi HW Random Number Generator support" depends on PPC_PASEMI default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on PA Semi PWRficient SoCs. @@ -209,7 +209,7 @@ config HW_RANDOM_PASEMI config HW_RANDOM_VIRTIO tristate "VirtIO Random Number Generator support" depends on VIRTIO - ---help--- + help This driver provides kernel-side support for the virtual Random Number Generator hardware. @@ -220,7 +220,7 @@ config HW_RANDOM_TX4939 tristate "TX4939 Random Number Generator support" depends on SOC_TX4939 default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on TX4939 SoC. @@ -233,7 +233,7 @@ config HW_RANDOM_MXC_RNGA tristate "Freescale i.MX RNGA Random Number Generator" depends on SOC_IMX31 default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on Freescale i.MX processors. @@ -247,7 +247,7 @@ config HW_RANDOM_IMX_RNGC depends on HAS_IOMEM && HAVE_CLK depends on SOC_IMX25 || COMPILE_TEST default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator Version C hardware found on some Freescale i.MX processors. Version B is also supported by this driver. @@ -261,7 +261,7 @@ config HW_RANDOM_NOMADIK tristate "ST-Ericsson Nomadik Random Number Generator support" depends on ARCH_NOMADIK default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on ST-Ericsson SoCs (8815 and 8500). @@ -274,7 +274,7 @@ config HW_RANDOM_PSERIES tristate "pSeries HW Random Number Generator support" depends on PPC64 && IBMVIO default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on POWER7+ machines and above @@ -287,7 +287,7 @@ config HW_RANDOM_POWERNV tristate "PowerNV Random Number Generator support" depends on PPC_POWERNV default HW_RANDOM - ---help--- + help This is the driver for Random Number Generator hardware found in POWER7+ and above machines for PowerNV platform. @@ -300,7 +300,7 @@ config HW_RANDOM_HISI tristate "Hisilicon Random Number Generator support" depends on HW_RANDOM && ARCH_HISI default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on Hisilicon Hip04 and Hip05 SoC. @@ -325,7 +325,7 @@ config HW_RANDOM_HISI_V2 config HW_RANDOM_ST tristate "ST Microelectronics HW Random Number Generator support" depends on HW_RANDOM && ARCH_STI - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on STi series of SoCs. @@ -336,7 +336,7 @@ config HW_RANDOM_XGENE tristate "APM X-Gene True Random Number Generator (TRNG) support" depends on HW_RANDOM && ARCH_XGENE default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on APM X-Gene SoC. @@ -363,7 +363,7 @@ config HW_RANDOM_PIC32 tristate "Microchip PIC32 Random Number Generator support" depends on HW_RANDOM && MACH_PIC32 default y - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on a PIC32. @@ -377,7 +377,7 @@ config HW_RANDOM_MESON depends on HW_RANDOM depends on ARCH_MESON || COMPILE_TEST default y - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on Amlogic Meson SoCs. @@ -390,7 +390,7 @@ config HW_RANDOM_CAVIUM tristate "Cavium ThunderX Random Number Generator support" depends on HW_RANDOM && PCI && (ARM64 || (COMPILE_TEST && 64BIT)) default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on Cavium SoCs. @@ -404,7 +404,7 @@ config HW_RANDOM_MTK depends on HW_RANDOM depends on ARCH_MEDIATEK || COMPILE_TEST default y - ---help--- + help This driver provides kernel-side support for the Random Number Generator hardware found on Mediatek SoCs. @@ -417,7 +417,7 @@ config HW_RANDOM_S390 tristate "S390 True Random Number Generator support" depends on S390 default HW_RANDOM - ---help--- + help This driver provides kernel-side support for the True Random Number Generator available as CPACF extension on modern s390 hardware platforms. @@ -431,7 +431,7 @@ config HW_RANDOM_EXYNOS tristate "Samsung Exynos True Random Number Generator support" depends on ARCH_EXYNOS || COMPILE_TEST default HW_RANDOM - ---help--- + help This driver provides support for the True Random Number Generator available in Exynos SoCs. @@ -474,6 +474,19 @@ config HW_RANDOM_KEYSTONE help This option enables Keystone's hardware random generator. +config HW_RANDOM_CCTRNG + tristate "Arm CryptoCell True Random Number Generator support" + depends on HAS_IOMEM && OF + help + Say 'Y' to enable the True Random Number Generator driver for the + Arm TrustZone CryptoCell family of processors. + Currently the CryptoCell 713 and 703 are supported. + The driver is supported only in SoC where Trusted Execution + Environment is not used. + Choose 'M' to compile this driver as a module. The module + will be called cctrng. + If unsure, say 'N'. + endif # HW_RANDOM config UML_RANDOM diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index a7801b49ce6c..2c6724735345 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -41,3 +41,4 @@ obj-$(CONFIG_HW_RANDOM_S390) += s390-trng.o obj-$(CONFIG_HW_RANDOM_KEYSTONE) += ks-sa-rng.o obj-$(CONFIG_HW_RANDOM_OPTEE) += optee-rng.o obj-$(CONFIG_HW_RANDOM_NPCM) += npcm-rng.o +obj-$(CONFIG_HW_RANDOM_CCTRNG) += cctrng.o diff --git a/drivers/char/hw_random/cctrng.c b/drivers/char/hw_random/cctrng.c new file mode 100644 index 000000000000..619148fb2dc9 --- /dev/null +++ b/drivers/char/hw_random/cctrng.c @@ -0,0 +1,735 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2019-2020 ARM Limited or its affiliates. */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/hw_random.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/workqueue.h> +#include <linux/circ_buf.h> +#include <linux/completion.h> +#include <linux/of.h> +#include <linux/bitfield.h> +#include <linux/fips.h> + +#include "cctrng.h" + +#define CC_REG_LOW(name) (name ## _BIT_SHIFT) +#define CC_REG_HIGH(name) (CC_REG_LOW(name) + name ## _BIT_SIZE - 1) +#define CC_GENMASK(name) GENMASK(CC_REG_HIGH(name), CC_REG_LOW(name)) + +#define CC_REG_FLD_GET(reg_name, fld_name, reg_val) \ + (FIELD_GET(CC_GENMASK(CC_ ## reg_name ## _ ## fld_name), reg_val)) + +#define CC_HW_RESET_LOOP_COUNT 10 +#define CC_TRNG_SUSPEND_TIMEOUT 3000 + +/* data circular buffer in words must be: + * - of a power-of-2 size (limitation of circ_buf.h macros) + * - at least 6, the size generated in the EHR according to HW implementation + */ +#define CCTRNG_DATA_BUF_WORDS 32 + +/* The timeout for the TRNG operation should be calculated with the formula: + * Timeout = EHR_NUM * VN_COEFF * EHR_LENGTH * SAMPLE_CNT * SCALE_VALUE + * while: + * - SAMPLE_CNT is input value from the characterisation process + * - all the rest are constants + */ +#define EHR_NUM 1 +#define VN_COEFF 4 +#define EHR_LENGTH CC_TRNG_EHR_IN_BITS +#define SCALE_VALUE 2 +#define CCTRNG_TIMEOUT(smpl_cnt) \ + (EHR_NUM * VN_COEFF * EHR_LENGTH * smpl_cnt * SCALE_VALUE) + +struct cctrng_drvdata { + struct platform_device *pdev; + void __iomem *cc_base; + struct clk *clk; + struct hwrng rng; + u32 active_rosc; + /* Sampling interval for each ring oscillator: + * count of ring oscillator cycles between consecutive bits sampling. + * Value of 0 indicates non-valid rosc + */ + u32 smpl_ratio[CC_TRNG_NUM_OF_ROSCS]; + + u32 data_buf[CCTRNG_DATA_BUF_WORDS]; + struct circ_buf circ; + struct work_struct compwork; + struct work_struct startwork; + + /* pending_hw - 1 when HW is pending, 0 when it is idle */ + atomic_t pending_hw; + + /* protects against multiple concurrent consumers of data_buf */ + spinlock_t read_lock; +}; + + +/* functions for write/read CC registers */ +static inline void cc_iowrite(struct cctrng_drvdata *drvdata, u32 reg, u32 val) +{ + iowrite32(val, (drvdata->cc_base + reg)); +} +static inline u32 cc_ioread(struct cctrng_drvdata *drvdata, u32 reg) +{ + return ioread32(drvdata->cc_base + reg); +} + + +static int cc_trng_pm_get(struct device *dev) +{ + int rc = 0; + + rc = pm_runtime_get_sync(dev); + + /* pm_runtime_get_sync() can return 1 as a valid return code */ + return (rc == 1 ? 0 : rc); +} + +static void cc_trng_pm_put_suspend(struct device *dev) +{ + int rc = 0; + + pm_runtime_mark_last_busy(dev); + rc = pm_runtime_put_autosuspend(dev); + if (rc) + dev_err(dev, "pm_runtime_put_autosuspend returned %x\n", rc); +} + +static int cc_trng_pm_init(struct cctrng_drvdata *drvdata) +{ + struct device *dev = &(drvdata->pdev->dev); + + /* must be before the enabling to avoid redundant suspending */ + pm_runtime_set_autosuspend_delay(dev, CC_TRNG_SUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(dev); + /* set us as active - note we won't do PM ops until cc_trng_pm_go()! */ + return pm_runtime_set_active(dev); +} + +static void cc_trng_pm_go(struct cctrng_drvdata *drvdata) +{ + struct device *dev = &(drvdata->pdev->dev); + + /* enable the PM module*/ + pm_runtime_enable(dev); +} + +static void cc_trng_pm_fini(struct cctrng_drvdata *drvdata) +{ + struct device *dev = &(drvdata->pdev->dev); + + pm_runtime_disable(dev); +} + + +static inline int cc_trng_parse_sampling_ratio(struct cctrng_drvdata *drvdata) +{ + struct device *dev = &(drvdata->pdev->dev); + struct device_node *np = drvdata->pdev->dev.of_node; + int rc; + int i; + /* ret will be set to 0 if at least one rosc has (sampling ratio > 0) */ + int ret = -EINVAL; + + rc = of_property_read_u32_array(np, "arm,rosc-ratio", + drvdata->smpl_ratio, + CC_TRNG_NUM_OF_ROSCS); + if (rc) { + /* arm,rosc-ratio was not found in device tree */ + return rc; + } + + /* verify that at least one rosc has (sampling ratio > 0) */ + for (i = 0; i < CC_TRNG_NUM_OF_ROSCS; ++i) { + dev_dbg(dev, "rosc %d sampling ratio %u", + i, drvdata->smpl_ratio[i]); + + if (drvdata->smpl_ratio[i] > 0) + ret = 0; + } + + return ret; +} + +static int cc_trng_change_rosc(struct cctrng_drvdata *drvdata) +{ + struct device *dev = &(drvdata->pdev->dev); + + dev_dbg(dev, "cctrng change rosc (was %d)\n", drvdata->active_rosc); + drvdata->active_rosc += 1; + + while (drvdata->active_rosc < CC_TRNG_NUM_OF_ROSCS) { + if (drvdata->smpl_ratio[drvdata->active_rosc] > 0) + return 0; + + drvdata->active_rosc += 1; + } + return -EINVAL; +} + + +static void cc_trng_enable_rnd_source(struct cctrng_drvdata *drvdata) +{ + u32 max_cycles; + + /* Set watchdog threshold to maximal allowed time (in CPU cycles) */ + max_cycles = CCTRNG_TIMEOUT(drvdata->smpl_ratio[drvdata->active_rosc]); + cc_iowrite(drvdata, CC_RNG_WATCHDOG_VAL_REG_OFFSET, max_cycles); + + /* enable the RND source */ + cc_iowrite(drvdata, CC_RND_SOURCE_ENABLE_REG_OFFSET, 0x1); + + /* unmask RNG interrupts */ + cc_iowrite(drvdata, CC_RNG_IMR_REG_OFFSET, (u32)~CC_RNG_INT_MASK); +} + + +/* increase circular data buffer index (head/tail) */ +static inline void circ_idx_inc(int *idx, int bytes) +{ + *idx += (bytes + 3) >> 2; + *idx &= (CCTRNG_DATA_BUF_WORDS - 1); +} + +static inline size_t circ_buf_space(struct cctrng_drvdata *drvdata) +{ + return CIRC_SPACE(drvdata->circ.head, + drvdata->circ.tail, CCTRNG_DATA_BUF_WORDS); + +} + +static int cctrng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + /* current implementation ignores "wait" */ + + struct cctrng_drvdata *drvdata = (struct cctrng_drvdata *)rng->priv; + struct device *dev = &(drvdata->pdev->dev); + u32 *buf = (u32 *)drvdata->circ.buf; + size_t copied = 0; + size_t cnt_w; + size_t size; + size_t left; + + if (!spin_trylock(&drvdata->read_lock)) { + /* concurrent consumers from data_buf cannot be served */ + dev_dbg_ratelimited(dev, "unable to hold lock\n"); + return 0; + } + + /* copy till end of data buffer (without wrap back) */ + cnt_w = CIRC_CNT_TO_END(drvdata->circ.head, + drvdata->circ.tail, CCTRNG_DATA_BUF_WORDS); + size = min((cnt_w<<2), max); + memcpy(data, &(buf[drvdata->circ.tail]), size); + copied = size; + circ_idx_inc(&drvdata->circ.tail, size); + /* copy rest of data in data buffer */ + left = max - copied; + if (left > 0) { + cnt_w = CIRC_CNT(drvdata->circ.head, + drvdata->circ.tail, CCTRNG_DATA_BUF_WORDS); + size = min((cnt_w<<2), left); + memcpy(data, &(buf[drvdata->circ.tail]), size); + copied += size; + circ_idx_inc(&drvdata->circ.tail, size); + } + + spin_unlock(&drvdata->read_lock); + + if (circ_buf_space(drvdata) >= CC_TRNG_EHR_IN_WORDS) { + if (atomic_cmpxchg(&drvdata->pending_hw, 0, 1) == 0) { + /* re-check space in buffer to avoid potential race */ + if (circ_buf_space(drvdata) >= CC_TRNG_EHR_IN_WORDS) { + /* increment device's usage counter */ + int rc = cc_trng_pm_get(dev); + + if (rc) { + dev_err(dev, + "cc_trng_pm_get returned %x\n", + rc); + return rc; + } + + /* schedule execution of deferred work handler + * for filling of data buffer + */ + schedule_work(&drvdata->startwork); + } else { + atomic_set(&drvdata->pending_hw, 0); + } + } + } + + return copied; +} + +static void cc_trng_hw_trigger(struct cctrng_drvdata *drvdata) +{ + u32 tmp_smpl_cnt = 0; + struct device *dev = &(drvdata->pdev->dev); + + dev_dbg(dev, "cctrng hw trigger.\n"); + + /* enable the HW RND clock */ + cc_iowrite(drvdata, CC_RNG_CLK_ENABLE_REG_OFFSET, 0x1); + + /* do software reset */ + cc_iowrite(drvdata, CC_RNG_SW_RESET_REG_OFFSET, 0x1); + /* in order to verify that the reset has completed, + * the sample count need to be verified + */ + do { + /* enable the HW RND clock */ + cc_iowrite(drvdata, CC_RNG_CLK_ENABLE_REG_OFFSET, 0x1); + + /* set sampling ratio (rng_clocks) between consecutive bits */ + cc_iowrite(drvdata, CC_SAMPLE_CNT1_REG_OFFSET, + drvdata->smpl_ratio[drvdata->active_rosc]); + + /* read the sampling ratio */ + tmp_smpl_cnt = cc_ioread(drvdata, CC_SAMPLE_CNT1_REG_OFFSET); + + } while (tmp_smpl_cnt != drvdata->smpl_ratio[drvdata->active_rosc]); + + /* disable the RND source for setting new parameters in HW */ + cc_iowrite(drvdata, CC_RND_SOURCE_ENABLE_REG_OFFSET, 0); + + cc_iowrite(drvdata, CC_RNG_ICR_REG_OFFSET, 0xFFFFFFFF); + + cc_iowrite(drvdata, CC_TRNG_CONFIG_REG_OFFSET, drvdata->active_rosc); + + /* Debug Control register: set to 0 - no bypasses */ + cc_iowrite(drvdata, CC_TRNG_DEBUG_CONTROL_REG_OFFSET, 0); + + cc_trng_enable_rnd_source(drvdata); +} + +static void cc_trng_compwork_handler(struct work_struct *w) +{ + u32 isr = 0; + u32 ehr_valid = 0; + struct cctrng_drvdata *drvdata = + container_of(w, struct cctrng_drvdata, compwork); + struct device *dev = &(drvdata->pdev->dev); + int i; + + /* stop DMA and the RNG source */ + cc_iowrite(drvdata, CC_RNG_DMA_ENABLE_REG_OFFSET, 0); + cc_iowrite(drvdata, CC_RND_SOURCE_ENABLE_REG_OFFSET, 0); + + /* read RNG_ISR and check for errors */ + isr = cc_ioread(drvdata, CC_RNG_ISR_REG_OFFSET); + ehr_valid = CC_REG_FLD_GET(RNG_ISR, EHR_VALID, isr); + dev_dbg(dev, "Got RNG_ISR=0x%08X (EHR_VALID=%u)\n", isr, ehr_valid); + + if (fips_enabled && CC_REG_FLD_GET(RNG_ISR, CRNGT_ERR, isr)) { + fips_fail_notify(); + /* FIPS error is fatal */ + panic("Got HW CRNGT error while fips is enabled!\n"); + } + + /* Clear all pending RNG interrupts */ + cc_iowrite(drvdata, CC_RNG_ICR_REG_OFFSET, isr); + + + if (!ehr_valid) { + /* in case of AUTOCORR/TIMEOUT error, try the next ROSC */ + if (CC_REG_FLD_GET(RNG_ISR, AUTOCORR_ERR, isr) || + CC_REG_FLD_GET(RNG_ISR, WATCHDOG, isr)) { + dev_dbg(dev, "cctrng autocorr/timeout error.\n"); + goto next_rosc; + } + + /* in case of VN error, ignore it */ + } + + /* read EHR data from registers */ + for (i = 0; i < CC_TRNG_EHR_IN_WORDS; i++) { + /* calc word ptr in data_buf */ + u32 *buf = (u32 *)drvdata->circ.buf; + + buf[drvdata->circ.head] = cc_ioread(drvdata, + CC_EHR_DATA_0_REG_OFFSET + (i*sizeof(u32))); + + /* EHR_DATA registers are cleared on read. In case 0 value was + * returned, restart the entropy collection. + */ + if (buf[drvdata->circ.head] == 0) { + dev_dbg(dev, "Got 0 value in EHR. active_rosc %u\n", + drvdata->active_rosc); + goto next_rosc; + } + + circ_idx_inc(&drvdata->circ.head, 1<<2); + } + + atomic_set(&drvdata->pending_hw, 0); + + /* continue to fill data buffer if needed */ + if (circ_buf_space(drvdata) >= CC_TRNG_EHR_IN_WORDS) { + if (atomic_cmpxchg(&drvdata->pending_hw, 0, 1) == 0) { + /* Re-enable rnd source */ + cc_trng_enable_rnd_source(drvdata); + return; + } + } + + cc_trng_pm_put_suspend(dev); + + dev_dbg(dev, "compwork handler done\n"); + return; + +next_rosc: + if ((circ_buf_space(drvdata) >= CC_TRNG_EHR_IN_WORDS) && + (cc_trng_change_rosc(drvdata) == 0)) { + /* trigger trng hw with next rosc */ + cc_trng_hw_trigger(drvdata); + } else { + atomic_set(&drvdata->pending_hw, 0); + cc_trng_pm_put_suspend(dev); + } +} + +static irqreturn_t cc_isr(int irq, void *dev_id) +{ + struct cctrng_drvdata *drvdata = (struct cctrng_drvdata *)dev_id; + struct device *dev = &(drvdata->pdev->dev); + u32 irr; + + /* if driver suspended return, probably shared interrupt */ + if (pm_runtime_suspended(dev)) + return IRQ_NONE; + + /* read the interrupt status */ + irr = cc_ioread(drvdata, CC_HOST_RGF_IRR_REG_OFFSET); + dev_dbg(dev, "Got IRR=0x%08X\n", irr); + + if (irr == 0) /* Probably shared interrupt line */ + return IRQ_NONE; + + /* clear interrupt - must be before processing events */ + cc_iowrite(drvdata, CC_HOST_RGF_ICR_REG_OFFSET, irr); + + /* RNG interrupt - most probable */ + if (irr & CC_HOST_RNG_IRQ_MASK) { + /* Mask RNG interrupts - will be unmasked in deferred work */ + cc_iowrite(drvdata, CC_RNG_IMR_REG_OFFSET, 0xFFFFFFFF); + + /* We clear RNG interrupt here, + * to avoid it from firing as we'll unmask RNG interrupts. + */ + cc_iowrite(drvdata, CC_HOST_RGF_ICR_REG_OFFSET, + CC_HOST_RNG_IRQ_MASK); + + irr &= ~CC_HOST_RNG_IRQ_MASK; + + /* schedule execution of deferred work handler */ + schedule_work(&drvdata->compwork); + } + + if (irr) { + dev_dbg_ratelimited(dev, + "IRR includes unknown cause bits (0x%08X)\n", + irr); + /* Just warning */ + } + + return IRQ_HANDLED; +} + +static void cc_trng_startwork_handler(struct work_struct *w) +{ + struct cctrng_drvdata *drvdata = + container_of(w, struct cctrng_drvdata, startwork); + + drvdata->active_rosc = 0; + cc_trng_hw_trigger(drvdata); +} + + +static int cc_trng_clk_init(struct cctrng_drvdata *drvdata) +{ + struct clk *clk; + struct device *dev = &(drvdata->pdev->dev); + int rc = 0; + + clk = devm_clk_get_optional(dev, NULL); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_err(dev, "Error getting clock: %pe\n", clk); + return PTR_ERR(clk); + } + drvdata->clk = clk; + + rc = clk_prepare_enable(drvdata->clk); + if (rc) { + dev_err(dev, "Failed to enable clock\n"); + return rc; + } + + return 0; +} + +static void cc_trng_clk_fini(struct cctrng_drvdata *drvdata) +{ + clk_disable_unprepare(drvdata->clk); +} + + +static int cctrng_probe(struct platform_device *pdev) +{ + struct resource *req_mem_cc_regs = NULL; + struct cctrng_drvdata *drvdata; + struct device *dev = &pdev->dev; + int rc = 0; + u32 val; + int irq; + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->rng.name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); + if (!drvdata->rng.name) + return -ENOMEM; + + drvdata->rng.read = cctrng_read; + drvdata->rng.priv = (unsigned long)drvdata; + drvdata->rng.quality = CC_TRNG_QUALITY; + + platform_set_drvdata(pdev, drvdata); + drvdata->pdev = pdev; + + drvdata->circ.buf = (char *)drvdata->data_buf; + + /* Get device resources */ + /* First CC registers space */ + req_mem_cc_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + /* Map registers space */ + drvdata->cc_base = devm_ioremap_resource(dev, req_mem_cc_regs); + if (IS_ERR(drvdata->cc_base)) { + dev_err(dev, "Failed to ioremap registers"); + return PTR_ERR(drvdata->cc_base); + } + + dev_dbg(dev, "Got MEM resource (%s): %pR\n", req_mem_cc_regs->name, + req_mem_cc_regs); + dev_dbg(dev, "CC registers mapped from %pa to 0x%p\n", + &req_mem_cc_regs->start, drvdata->cc_base); + + /* Then IRQ */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "Failed getting IRQ resource\n"); + return irq; + } + + /* parse sampling rate from device tree */ + rc = cc_trng_parse_sampling_ratio(drvdata); + if (rc) { + dev_err(dev, "Failed to get legal sampling ratio for rosc\n"); + return rc; + } + + rc = cc_trng_clk_init(drvdata); + if (rc) { + dev_err(dev, "cc_trng_clk_init failed\n"); + return rc; + } + + INIT_WORK(&drvdata->compwork, cc_trng_compwork_handler); + INIT_WORK(&drvdata->startwork, cc_trng_startwork_handler); + spin_lock_init(&drvdata->read_lock); + + /* register the driver isr function */ + rc = devm_request_irq(dev, irq, cc_isr, IRQF_SHARED, "cctrng", drvdata); + if (rc) { + dev_err(dev, "Could not register to interrupt %d\n", irq); + goto post_clk_err; + } + dev_dbg(dev, "Registered to IRQ: %d\n", irq); + + /* Clear all pending interrupts */ + val = cc_ioread(drvdata, CC_HOST_RGF_IRR_REG_OFFSET); + dev_dbg(dev, "IRR=0x%08X\n", val); + cc_iowrite(drvdata, CC_HOST_RGF_ICR_REG_OFFSET, val); + + /* unmask HOST RNG interrupt */ + cc_iowrite(drvdata, CC_HOST_RGF_IMR_REG_OFFSET, + cc_ioread(drvdata, CC_HOST_RGF_IMR_REG_OFFSET) & + ~CC_HOST_RNG_IRQ_MASK); + + /* init PM */ + rc = cc_trng_pm_init(drvdata); + if (rc) { + dev_err(dev, "cc_trng_pm_init failed\n"); + goto post_clk_err; + } + + /* increment device's usage counter */ + rc = cc_trng_pm_get(dev); + if (rc) { + dev_err(dev, "cc_trng_pm_get returned %x\n", rc); + goto post_pm_err; + } + + /* set pending_hw to verify that HW won't be triggered from read */ + atomic_set(&drvdata->pending_hw, 1); + + /* registration of the hwrng device */ + rc = hwrng_register(&drvdata->rng); + if (rc) { + dev_err(dev, "Could not register hwrng device.\n"); + goto post_pm_err; + } + + /* trigger HW to start generate data */ + drvdata->active_rosc = 0; + cc_trng_hw_trigger(drvdata); + + /* All set, we can allow auto-suspend */ + cc_trng_pm_go(drvdata); + + dev_info(dev, "ARM cctrng device initialized\n"); + + return 0; + +post_pm_err: + cc_trng_pm_fini(drvdata); + +post_clk_err: + cc_trng_clk_fini(drvdata); + + return rc; +} + +static int cctrng_remove(struct platform_device *pdev) +{ + struct cctrng_drvdata *drvdata = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + dev_dbg(dev, "Releasing cctrng resources...\n"); + + hwrng_unregister(&drvdata->rng); + + cc_trng_pm_fini(drvdata); + + cc_trng_clk_fini(drvdata); + + dev_info(dev, "ARM cctrng device terminated\n"); + + return 0; +} + +static int __maybe_unused cctrng_suspend(struct device *dev) +{ + struct cctrng_drvdata *drvdata = dev_get_drvdata(dev); + + dev_dbg(dev, "set HOST_POWER_DOWN_EN\n"); + cc_iowrite(drvdata, CC_HOST_POWER_DOWN_EN_REG_OFFSET, + POWER_DOWN_ENABLE); + + clk_disable_unprepare(drvdata->clk); + + return 0; +} + +static bool cctrng_wait_for_reset_completion(struct cctrng_drvdata *drvdata) +{ + unsigned int val; + unsigned int i; + + for (i = 0; i < CC_HW_RESET_LOOP_COUNT; i++) { + /* in cc7x3 NVM_IS_IDLE indicates that CC reset is + * completed and device is fully functional + */ + val = cc_ioread(drvdata, CC_NVM_IS_IDLE_REG_OFFSET); + if (val & BIT(CC_NVM_IS_IDLE_VALUE_BIT_SHIFT)) { + /* hw indicate reset completed */ + return true; + } + /* allow scheduling other process on the processor */ + schedule(); + } + /* reset not completed */ + return false; +} + +static int __maybe_unused cctrng_resume(struct device *dev) +{ + struct cctrng_drvdata *drvdata = dev_get_drvdata(dev); + int rc; + + dev_dbg(dev, "unset HOST_POWER_DOWN_EN\n"); + /* Enables the device source clk */ + rc = clk_prepare_enable(drvdata->clk); + if (rc) { + dev_err(dev, "failed getting clock back on. We're toast.\n"); + return rc; + } + + /* wait for Cryptocell reset completion */ + if (!cctrng_wait_for_reset_completion(drvdata)) { + dev_err(dev, "Cryptocell reset not completed"); + return -EBUSY; + } + + /* unmask HOST RNG interrupt */ + cc_iowrite(drvdata, CC_HOST_RGF_IMR_REG_OFFSET, + cc_ioread(drvdata, CC_HOST_RGF_IMR_REG_OFFSET) & + ~CC_HOST_RNG_IRQ_MASK); + + cc_iowrite(drvdata, CC_HOST_POWER_DOWN_EN_REG_OFFSET, + POWER_DOWN_DISABLE); + + return 0; +} + +static UNIVERSAL_DEV_PM_OPS(cctrng_pm, cctrng_suspend, cctrng_resume, NULL); + +static const struct of_device_id arm_cctrng_dt_match[] = { + { .compatible = "arm,cryptocell-713-trng", }, + { .compatible = "arm,cryptocell-703-trng", }, + {}, +}; +MODULE_DEVICE_TABLE(of, arm_cctrng_dt_match); + +static struct platform_driver cctrng_driver = { + .driver = { + .name = "cctrng", + .of_match_table = arm_cctrng_dt_match, + .pm = &cctrng_pm, + }, + .probe = cctrng_probe, + .remove = cctrng_remove, +}; + +static int __init cctrng_mod_init(void) +{ + /* Compile time assertion checks */ + BUILD_BUG_ON(CCTRNG_DATA_BUF_WORDS < 6); + BUILD_BUG_ON((CCTRNG_DATA_BUF_WORDS & (CCTRNG_DATA_BUF_WORDS-1)) != 0); + + return platform_driver_register(&cctrng_driver); +} +module_init(cctrng_mod_init); + +static void __exit cctrng_mod_exit(void) +{ + platform_driver_unregister(&cctrng_driver); +} +module_exit(cctrng_mod_exit); + +/* Module description */ +MODULE_DESCRIPTION("ARM CryptoCell TRNG Driver"); +MODULE_AUTHOR("ARM"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/hw_random/cctrng.h b/drivers/char/hw_random/cctrng.h new file mode 100644 index 000000000000..1f2fde95adcb --- /dev/null +++ b/drivers/char/hw_random/cctrng.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2019-2020 ARM Limited or its affiliates. */ + +#include <linux/bitops.h> + +#define POWER_DOWN_ENABLE 0x01 +#define POWER_DOWN_DISABLE 0x00 + +/* hwrng quality: bits of true entropy per 1024 bits of input */ +#define CC_TRNG_QUALITY 1024 + +/* CryptoCell TRNG HW definitions */ +#define CC_TRNG_NUM_OF_ROSCS 4 +/* The number of words generated in the entropy holding register (EHR) + * 6 words (192 bit) according to HW implementation + */ +#define CC_TRNG_EHR_IN_WORDS 6 +#define CC_TRNG_EHR_IN_BITS (CC_TRNG_EHR_IN_WORDS * BITS_PER_TYPE(u32)) + +#define CC_HOST_RNG_IRQ_MASK BIT(CC_HOST_RGF_IRR_RNG_INT_BIT_SHIFT) + +/* RNG interrupt mask */ +#define CC_RNG_INT_MASK (BIT(CC_RNG_IMR_EHR_VALID_INT_MASK_BIT_SHIFT) | \ + BIT(CC_RNG_IMR_AUTOCORR_ERR_INT_MASK_BIT_SHIFT) | \ + BIT(CC_RNG_IMR_CRNGT_ERR_INT_MASK_BIT_SHIFT) | \ + BIT(CC_RNG_IMR_VN_ERR_INT_MASK_BIT_SHIFT) | \ + BIT(CC_RNG_IMR_WATCHDOG_INT_MASK_BIT_SHIFT)) + +// -------------------------------------- +// BLOCK: RNG +// -------------------------------------- +#define CC_RNG_IMR_REG_OFFSET 0x0100UL +#define CC_RNG_IMR_EHR_VALID_INT_MASK_BIT_SHIFT 0x0UL +#define CC_RNG_IMR_AUTOCORR_ERR_INT_MASK_BIT_SHIFT 0x1UL +#define CC_RNG_IMR_CRNGT_ERR_INT_MASK_BIT_SHIFT 0x2UL +#define CC_RNG_IMR_VN_ERR_INT_MASK_BIT_SHIFT 0x3UL +#define CC_RNG_IMR_WATCHDOG_INT_MASK_BIT_SHIFT 0x4UL +#define CC_RNG_ISR_REG_OFFSET 0x0104UL +#define CC_RNG_ISR_EHR_VALID_BIT_SHIFT 0x0UL +#define CC_RNG_ISR_EHR_VALID_BIT_SIZE 0x1UL +#define CC_RNG_ISR_AUTOCORR_ERR_BIT_SHIFT 0x1UL +#define CC_RNG_ISR_AUTOCORR_ERR_BIT_SIZE 0x1UL +#define CC_RNG_ISR_CRNGT_ERR_BIT_SHIFT 0x2UL +#define CC_RNG_ISR_CRNGT_ERR_BIT_SIZE 0x1UL +#define CC_RNG_ISR_WATCHDOG_BIT_SHIFT 0x4UL +#define CC_RNG_ISR_WATCHDOG_BIT_SIZE 0x1UL +#define CC_RNG_ICR_REG_OFFSET 0x0108UL +#define CC_TRNG_CONFIG_REG_OFFSET 0x010CUL +#define CC_EHR_DATA_0_REG_OFFSET 0x0114UL +#define CC_RND_SOURCE_ENABLE_REG_OFFSET 0x012CUL +#define CC_SAMPLE_CNT1_REG_OFFSET 0x0130UL +#define CC_TRNG_DEBUG_CONTROL_REG_OFFSET 0x0138UL +#define CC_RNG_SW_RESET_REG_OFFSET 0x0140UL +#define CC_RNG_CLK_ENABLE_REG_OFFSET 0x01C4UL +#define CC_RNG_DMA_ENABLE_REG_OFFSET 0x01C8UL +#define CC_RNG_WATCHDOG_VAL_REG_OFFSET 0x01D8UL +// -------------------------------------- +// BLOCK: SEC_HOST_RGF +// -------------------------------------- +#define CC_HOST_RGF_IRR_REG_OFFSET 0x0A00UL +#define CC_HOST_RGF_IRR_RNG_INT_BIT_SHIFT 0xAUL +#define CC_HOST_RGF_IMR_REG_OFFSET 0x0A04UL +#define CC_HOST_RGF_ICR_REG_OFFSET 0x0A08UL + +#define CC_HOST_POWER_DOWN_EN_REG_OFFSET 0x0A78UL + +// -------------------------------------- +// BLOCK: NVM +// -------------------------------------- +#define CC_NVM_IS_IDLE_REG_OFFSET 0x0F10UL +#define CC_NVM_IS_IDLE_VALUE_BIT_SHIFT 0x0UL +#define CC_NVM_IS_IDLE_VALUE_BIT_SIZE 0x1UL diff --git a/drivers/char/hw_random/ks-sa-rng.c b/drivers/char/hw_random/ks-sa-rng.c index e2330e757f1f..001617033d6a 100644 --- a/drivers/char/hw_random/ks-sa-rng.c +++ b/drivers/char/hw_random/ks-sa-rng.c @@ -244,6 +244,7 @@ static int ks_sa_rng_probe(struct platform_device *pdev) ret = pm_runtime_get_sync(dev); if (ret < 0) { dev_err(dev, "Failed to enable SA power-domain\n"); + pm_runtime_put_noidle(dev); pm_runtime_disable(dev); return ret; } diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index 65952393e1bb..7290c603fcb8 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -392,11 +392,8 @@ static int of_get_omap_rng_device_details(struct omap_rng_dev *priv, if (of_device_is_compatible(dev->of_node, "ti,omap4-rng") || of_device_is_compatible(dev->of_node, "inside-secure,safexcel-eip76")) { irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "%s: error getting IRQ resource - %d\n", - __func__, irq); + if (irq < 0) return irq; - } err = devm_request_irq(dev, irq, omap4_rng_irq, IRQF_TRIGGER_NONE, dev_name(dev), priv); diff --git a/drivers/char/hw_random/optee-rng.c b/drivers/char/hw_random/optee-rng.c index ddfbabaa5f8f..49b2e02537dd 100644 --- a/drivers/char/hw_random/optee-rng.c +++ b/drivers/char/hw_random/optee-rng.c @@ -226,7 +226,7 @@ static int optee_rng_probe(struct device *dev) return -ENODEV; /* Open session with hwrng Trusted App */ - memcpy(sess_arg.uuid, rng_device->id.uuid.b, TEE_IOCTL_UUID_LEN); + export_uuid(sess_arg.uuid, &rng_device->id.uuid); sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; sess_arg.num_params = 0; diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 718d8c087650..79a6e47b5fbc 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -11,6 +11,7 @@ #include <linux/virtio.h> #include <linux/virtio_rng.h> #include <linux/module.h> +#include <linux/slab.h> static DEFINE_IDA(rng_index_ida); diff --git a/drivers/char/hw_random/xgene-rng.c b/drivers/char/hw_random/xgene-rng.c index d7516a446987..008e6db9ce01 100644 --- a/drivers/char/hw_random/xgene-rng.c +++ b/drivers/char/hw_random/xgene-rng.c @@ -328,10 +328,8 @@ static int xgene_rng_probe(struct platform_device *pdev) return PTR_ERR(ctx->csr_base); rc = platform_get_irq(pdev, 0); - if (rc < 0) { - dev_err(&pdev->dev, "No IRQ resource\n"); + if (rc < 0) return rc; - } ctx->irq = rc; dev_dbg(&pdev->dev, "APM X-Gene RNG BASE %p ALARM IRQ %d", diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 7dc2c3ec4051..07847d9a459a 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -14,7 +14,7 @@ menuconfig IPMI_HANDLER IPMI is a standard for managing sensors (temperature, voltage, etc.) in a system. - See <file:Documentation/IPMI.txt> for more details on the driver. + See <file:Documentation/driver-api/ipmi.rst> for more details on the driver. If unsure, say N. diff --git a/drivers/char/ipmi/bt-bmc.c b/drivers/char/ipmi/bt-bmc.c index d36aeacb290e..a395e2e70dc5 100644 --- a/drivers/char/ipmi/bt-bmc.c +++ b/drivers/char/ipmi/bt-bmc.c @@ -399,15 +399,15 @@ static int bt_bmc_config_irq(struct bt_bmc *bt_bmc, struct device *dev = &pdev->dev; int rc; - bt_bmc->irq = platform_get_irq(pdev, 0); - if (!bt_bmc->irq) - return -ENODEV; + bt_bmc->irq = platform_get_irq_optional(pdev, 0); + if (bt_bmc->irq < 0) + return bt_bmc->irq; rc = devm_request_irq(dev, bt_bmc->irq, bt_bmc_irq, IRQF_SHARED, DEVICE_NAME, bt_bmc); if (rc < 0) { dev_warn(dev, "Unable to request IRQ %d\n", bt_bmc->irq); - bt_bmc->irq = 0; + bt_bmc->irq = rc; return rc; } @@ -430,9 +430,6 @@ static int bt_bmc_probe(struct platform_device *pdev) struct device *dev; int rc; - if (!pdev || !pdev->dev.of_node) - return -ENODEV; - dev = &pdev->dev; dev_info(dev, "Found bt bmc device\n"); @@ -466,9 +463,9 @@ static int bt_bmc_probe(struct platform_device *pdev) init_waitqueue_head(&bt_bmc->queue); bt_bmc->miscdev.minor = MISC_DYNAMIC_MINOR, - bt_bmc->miscdev.name = DEVICE_NAME, - bt_bmc->miscdev.fops = &bt_bmc_fops, - bt_bmc->miscdev.parent = dev; + bt_bmc->miscdev.name = DEVICE_NAME, + bt_bmc->miscdev.fops = &bt_bmc_fops, + bt_bmc->miscdev.parent = dev; rc = misc_register(&bt_bmc->miscdev); if (rc) { dev_err(dev, "Unable to register misc device\n"); @@ -477,7 +474,7 @@ static int bt_bmc_probe(struct platform_device *pdev) bt_bmc_config_irq(bt_bmc, pdev); - if (bt_bmc->irq) { + if (bt_bmc->irq >= 0) { dev_info(dev, "Using IRQ %d\n", bt_bmc->irq); } else { dev_info(dev, "No IRQ; using timer\n"); @@ -503,7 +500,7 @@ static int bt_bmc_remove(struct platform_device *pdev) struct bt_bmc *bt_bmc = dev_get_drvdata(&pdev->dev); misc_deregister(&bt_bmc->miscdev); - if (!bt_bmc->irq) + if (bt_bmc->irq < 0) del_timer_sync(&bt_bmc->poll_timer); return 0; } diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index c48d8f086382..e1b22fe0916c 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -33,6 +33,7 @@ #include <linux/workqueue.h> #include <linux/uuid.h> #include <linux/nospec.h> +#include <linux/vmalloc.h> #define IPMI_DRIVER_VERSION "39.2" @@ -1153,7 +1154,7 @@ static void free_user_work(struct work_struct *work) remove_work); cleanup_srcu_struct(&user->release_barrier); - kfree(user); + vfree(user); } int ipmi_create_user(unsigned int if_num, @@ -1185,7 +1186,7 @@ int ipmi_create_user(unsigned int if_num, if (rv) return rv; - new_user = kmalloc(sizeof(*new_user), GFP_KERNEL); + new_user = vzalloc(sizeof(*new_user)); if (!new_user) return -ENOMEM; @@ -1232,7 +1233,7 @@ int ipmi_create_user(unsigned int if_num, out_kfree: srcu_read_unlock(&ipmi_interfaces_srcu, index); - kfree(new_user); + vfree(new_user); return rv; } EXPORT_SYMBOL(ipmi_create_user); @@ -3171,7 +3172,7 @@ static void guid_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) goto out; } - guid_copy(&bmc->fetch_guid, (guid_t *)(msg->msg.data + 1)); + import_guid(&bmc->fetch_guid, msg->msg.data + 1); /* * Make sure the guid data is available before setting * dyn_guid_set. diff --git a/drivers/char/ipmi/ipmi_si_hotmod.c b/drivers/char/ipmi/ipmi_si_hotmod.c index 42a925f8cf69..4fbb4e18bae2 100644 --- a/drivers/char/ipmi/ipmi_si_hotmod.c +++ b/drivers/char/ipmi/ipmi_si_hotmod.c @@ -18,7 +18,7 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp); module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200); MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" - " Documentation/IPMI.txt in the kernel sources for the" + " Documentation/driver-api/ipmi.rst in the kernel sources for the" " gory details."); /* diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index c7cc8538b84a..77b8d551ae7f 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -968,7 +968,7 @@ static inline bool ipmi_thread_busy_wait(enum si_sm_result smi_result, * that are not BT and do not have interrupts. It starts spinning * when an operation is complete or until max_busy tells it to stop * (if that is enabled). See the paragraph on kimid_max_busy_us in - * Documentation/IPMI.txt for details. + * Documentation/driver-api/ipmi.rst for details. */ static int ipmi_thread(void *data) { diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c index 638c693e17ad..129b5713f187 100644 --- a/drivers/char/ipmi/ipmi_si_platform.c +++ b/drivers/char/ipmi/ipmi_si_platform.c @@ -393,6 +393,8 @@ static int acpi_ipmi_probe(struct platform_device *pdev) dev_info(io.dev, "%pR regsize %d spacing %d irq %d\n", res, io.regsize, io.regspacing, io.irq); + request_module("acpi_ipmi"); + return ipmi_si_add_smi(&io); err_free: diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index b7145f370d3b..198b65d45c5e 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -189,8 +189,6 @@ struct ssif_addr_info { struct device *dev; struct i2c_client *client; - struct i2c_client *added_client; - struct mutex clients_mutex; struct list_head clients; @@ -1472,6 +1470,7 @@ static bool check_acpi(struct ssif_info *ssif_info, struct device *dev) if (acpi_handle) { ssif_info->addr_source = SI_ACPI; ssif_info->addr_info.acpi_info.acpi_handle = acpi_handle; + request_module("acpi_ipmi"); return true; } #endif @@ -1940,21 +1939,6 @@ out_remove_attr: goto out; } -static int ssif_adapter_handler(struct device *adev, void *opaque) -{ - struct ssif_addr_info *addr_info = opaque; - - if (adev->type != &i2c_adapter_type) - return 0; - - addr_info->added_client = i2c_new_device(to_i2c_adapter(adev), - &addr_info->binfo); - - if (!addr_info->adapter_name) - return 1; /* Only try the first I2C adapter by default. */ - return 0; -} - static int new_ssif_client(int addr, char *adapter_name, int debug, int slave_addr, enum ipmi_addr_src addr_src, @@ -1998,9 +1982,7 @@ static int new_ssif_client(int addr, char *adapter_name, list_add_tail(&addr_info->link, &ssif_infos); - if (initialized) - i2c_for_each_dev(addr_info, ssif_adapter_handler); - /* Otherwise address list will get it */ + /* Address list will get it */ out_unlock: mutex_unlock(&ssif_infos_mutex); @@ -2120,8 +2102,6 @@ static int ssif_platform_remove(struct platform_device *dev) return 0; mutex_lock(&ssif_infos_mutex); - i2c_unregister_device(addr_info->added_client); - list_del(&addr_info->link); kfree(addr_info); mutex_unlock(&ssif_infos_mutex); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 43dd0891ca1e..934c92dcb9ab 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -31,11 +31,15 @@ #include <linux/uio.h> #include <linux/uaccess.h> #include <linux/security.h> +#include <linux/pseudo_fs.h> +#include <uapi/linux/magic.h> +#include <linux/mount.h> #ifdef CONFIG_IA64 # include <linux/efi.h> #endif +#define DEVMEM_MINOR 1 #define DEVPORT_MINOR 4 static inline unsigned long size_inside_page(unsigned long start, @@ -167,7 +171,7 @@ static ssize_t read_mem(struct file *file, char __user *buf, if (!ptr) goto failed; - probe = probe_kernel_read(bounce, ptr, sz); + probe = copy_from_kernel_nofault(bounce, ptr, sz); unxlate_dev_mem_ptr(p, ptr); if (probe) goto failed; @@ -805,12 +809,64 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig) return ret; } +static struct inode *devmem_inode; + +#ifdef CONFIG_IO_STRICT_DEVMEM +void revoke_devmem(struct resource *res) +{ + struct inode *inode = READ_ONCE(devmem_inode); + + /* + * Check that the initialization has completed. Losing the race + * is ok because it means drivers are claiming resources before + * the fs_initcall level of init and prevent /dev/mem from + * establishing mappings. + */ + if (!inode) + return; + + /* + * The expectation is that the driver has successfully marked + * the resource busy by this point, so devmem_is_allowed() + * should start returning false, however for performance this + * does not iterate the entire resource range. + */ + if (devmem_is_allowed(PHYS_PFN(res->start)) && + devmem_is_allowed(PHYS_PFN(res->end))) { + /* + * *cringe* iomem=relaxed says "go ahead, what's the + * worst that can happen?" + */ + return; + } + + unmap_mapping_range(inode->i_mapping, res->start, resource_size(res), 1); +} +#endif + static int open_port(struct inode *inode, struct file *filp) { + int rc; + if (!capable(CAP_SYS_RAWIO)) return -EPERM; - return security_locked_down(LOCKDOWN_DEV_MEM); + rc = security_locked_down(LOCKDOWN_DEV_MEM); + if (rc) + return rc; + + if (iminor(inode) != DEVMEM_MINOR) + return 0; + + /* + * Use a unified address space to have a single point to manage + * revocations when drivers want to take over a /dev/mem mapped + * range. + */ + inode->i_mapping = devmem_inode->i_mapping; + filp->f_mapping = inode->i_mapping; + + return 0; } #define zero_lseek null_lseek @@ -885,7 +941,7 @@ static const struct memdev { fmode_t fmode; } devlist[] = { #ifdef CONFIG_DEVMEM - [1] = { "mem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET }, + [DEVMEM_MINOR] = { "mem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET }, #endif #ifdef CONFIG_DEVKMEM [2] = { "kmem", 0, &kmem_fops, FMODE_UNSIGNED_OFFSET }, @@ -939,6 +995,45 @@ static char *mem_devnode(struct device *dev, umode_t *mode) static struct class *mem_class; +static int devmem_fs_init_fs_context(struct fs_context *fc) +{ + return init_pseudo(fc, DEVMEM_MAGIC) ? 0 : -ENOMEM; +} + +static struct file_system_type devmem_fs_type = { + .name = "devmem", + .owner = THIS_MODULE, + .init_fs_context = devmem_fs_init_fs_context, + .kill_sb = kill_anon_super, +}; + +static int devmem_init_inode(void) +{ + static struct vfsmount *devmem_vfs_mount; + static int devmem_fs_cnt; + struct inode *inode; + int rc; + + rc = simple_pin_fs(&devmem_fs_type, &devmem_vfs_mount, &devmem_fs_cnt); + if (rc < 0) { + pr_err("Cannot mount /dev/mem pseudo filesystem: %d\n", rc); + return rc; + } + + inode = alloc_anon_inode(devmem_vfs_mount->mnt_sb); + if (IS_ERR(inode)) { + rc = PTR_ERR(inode); + pr_err("Cannot allocate inode for /dev/mem: %d\n", rc); + simple_release_fs(&devmem_vfs_mount, &devmem_fs_cnt); + return rc; + } + + /* publish /dev/mem initialized */ + WRITE_ONCE(devmem_inode, inode); + + return 0; +} + static int __init chr_dev_init(void) { int minor; @@ -960,6 +1055,8 @@ static int __init chr_dev_init(void) */ if ((minor == DEVPORT_MINOR) && !arch_has_dev_port()) continue; + if ((minor == DEVMEM_MINOR) && devmem_init_inode() != 0) + continue; device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor), NULL, devlist[minor].name); diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index 7d583222e8fa..0fae33319d2e 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -39,7 +39,6 @@ #include <linux/numa.h> #include <linux/refcount.h> #include <asm/page.h> -#include <asm/pgtable.h> #include <linux/atomic.h> #include <asm/tlbflush.h> #include <asm/uncached.h> @@ -65,7 +64,7 @@ enum mspec_page_type { * This structure is shared by all vma's that are split off from the * original vma when split_vma()'s are done. * - * The refcnt is incremented atomically because mm->mmap_sem does not + * The refcnt is incremented atomically because mm->mmap_lock does not * protect in fork case where multiple tasks share the vma_data. */ struct vma_data { diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index 4667844eee69..8206412d25ba 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -232,8 +232,6 @@ static ssize_t nvram_misc_read(struct file *file, char __user *buf, ssize_t ret; - if (!access_ok(buf, count)) - return -EFAULT; if (*ppos >= nvram_size) return 0; @@ -264,8 +262,6 @@ static ssize_t nvram_misc_write(struct file *file, const char __user *buf, char *tmp; ssize_t ret; - if (!access_ok(buf, count)) - return -EFAULT; if (*ppos >= nvram_size) return 0; diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 4edb4174a1e2..89681f07bc78 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -1404,7 +1404,6 @@ static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) unsigned int iobase = dev->p_dev->resource[0]->start; struct inode *inode = file_inode(filp); struct pcmcia_device *link; - int size; int rc; void __user *argp = (void __user *)arg; #ifdef CM4000_DEBUG @@ -1441,19 +1440,6 @@ static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) DEBUGP(4, dev, "iocnr mismatch\n"); goto out; } - size = _IOC_SIZE(cmd); - rc = -EFAULT; - DEBUGP(4, dev, "iocdir=%.4x iocr=%.4x iocw=%.4x iocsize=%d cmd=%.4x\n", - _IOC_DIR(cmd), _IOC_READ, _IOC_WRITE, size, cmd); - - if (_IOC_DIR(cmd) & _IOC_READ) { - if (!access_ok(argp, size)) - goto out; - } - if (_IOC_DIR(cmd) & _IOC_WRITE) { - if (!access_ok(argp, size)) - goto out; - } rc = 0; switch (cmd) { diff --git a/drivers/char/random.c b/drivers/char/random.c index 0d10e31fd342..2a41b21623ae 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -327,7 +327,6 @@ #include <linux/spinlock.h> #include <linux/kthread.h> #include <linux/percpu.h> -#include <linux/cryptohash.h> #include <linux/fips.h> #include <linux/ptrace.h> #include <linux/workqueue.h> @@ -337,6 +336,7 @@ #include <linux/completion.h> #include <linux/uuid.h> #include <crypto/chacha.h> +#include <crypto/sha.h> #include <asm/processor.h> #include <linux/uaccess.h> @@ -1397,14 +1397,14 @@ static void extract_buf(struct entropy_store *r, __u8 *out) __u32 w[5]; unsigned long l[LONGS(20)]; } hash; - __u32 workspace[SHA_WORKSPACE_WORDS]; + __u32 workspace[SHA1_WORKSPACE_WORDS]; unsigned long flags; /* * If we have an architectural hardware random number * generator, use it for SHA's initial vector */ - sha_init(hash.w); + sha1_init(hash.w); for (i = 0; i < LONGS(20); i++) { unsigned long v; if (!arch_get_random_long(&v)) @@ -1415,7 +1415,7 @@ static void extract_buf(struct entropy_store *r, __u8 *out) /* Generate a hash across the pool, 16 words (512 bits) at a time */ spin_lock_irqsave(&r->lock, flags); for (i = 0; i < r->poolinfo->poolwords; i += 16) - sha_transform(hash.w, (__u8 *)(r->pool + i), workspace); + sha1_transform(hash.w, (__u8 *)(r->pool + i), workspace); /* * We mix the hash back into the pool to prevent backtracking @@ -2057,7 +2057,7 @@ static char sysctl_bootid[16]; * sysctl system call, as 16 bytes of binary data. */ static int proc_do_uuid(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) + void *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table fake_table; unsigned char buf[64], tmp_uuid[16], *uuid; @@ -2087,7 +2087,7 @@ static int proc_do_uuid(struct ctl_table *table, int write, * Return entropy available scaled to integral bits */ static int proc_do_entropy(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) + void *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table fake_table; int entropy_count; diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index 6d81bb3bb503..896a3550fba9 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -777,17 +777,21 @@ static int __init tlclk_init(void) { int ret; + telclk_interrupt = (inb(TLCLK_REG7) & 0x0f); + + alarm_events = kzalloc( sizeof(struct tlclk_alarms), GFP_KERNEL); + if (!alarm_events) { + ret = -ENOMEM; + goto out1; + } + ret = register_chrdev(tlclk_major, "telco_clock", &tlclk_fops); if (ret < 0) { printk(KERN_ERR "tlclk: can't get major %d.\n", tlclk_major); + kfree(alarm_events); return ret; } tlclk_major = ret; - alarm_events = kzalloc( sizeof(struct tlclk_alarms), GFP_KERNEL); - if (!alarm_events) { - ret = -ENOMEM; - goto out1; - } /* Read telecom clock IRQ number (Set by BIOS) */ if (!request_region(TLCLK_BASE, 8, "telco_clock")) { @@ -796,7 +800,6 @@ static int __init tlclk_init(void) ret = -EBUSY; goto out2; } - telclk_interrupt = (inb(TLCLK_REG7) & 0x0f); if (0x0F == telclk_interrupt ) { /* not MCPBL0010 ? */ printk(KERN_ERR "telclk_interrupt = 0x%x non-mcpbl0010 hw.\n", @@ -837,8 +840,8 @@ out3: release_region(TLCLK_BASE, 8); out2: kfree(alarm_events); -out1: unregister_chrdev(tlclk_major, "telco_clock"); +out1: return ret; } diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index aacdeed93320..58b4c573d176 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -9,7 +9,7 @@ menuconfig TCG_TPM imply SECURITYFS select CRYPTO select CRYPTO_HASH_INFO - ---help--- + help If you have a TPM security chip in your system, which implements the Trusted Computing Group's specification, say Yes and it will be accessible from within Linux. For @@ -31,7 +31,7 @@ config HW_RANDOM_TPM bool "TPM HW Random Number Generator support" depends on TCG_TPM && HW_RANDOM && !(TCG_TPM=y && HW_RANDOM=m) default y - ---help--- + help This setting exposes the TPM's Random Number Generator as a hwrng device. This allows the kernel to collect randomness from the TPM at boot, and provides the TPM randomines in /dev/hwrng. @@ -40,7 +40,7 @@ config HW_RANDOM_TPM config TCG_TIS_CORE tristate - ---help--- + help TCG TIS TPM core driver. It implements the TPM TCG TIS logic and hooks into the TPM kernel APIs. Physical layers will register against it. @@ -48,7 +48,7 @@ config TCG_TIS tristate "TPM Interface Specification 1.2 Interface / TPM 2.0 FIFO Interface" depends on X86 || OF select TCG_TIS_CORE - ---help--- + help If you have a TPM security chip that is compliant with the TCG TIS 1.2 TPM specification (TPM1.2) or the TCG PTP FIFO specification (TPM2.0) say Yes and it will be accessible from @@ -59,7 +59,7 @@ config TCG_TIS_SPI tristate "TPM Interface Specification 1.3 Interface / TPM 2.0 FIFO Interface - (SPI)" depends on SPI select TCG_TIS_CORE - ---help--- + help If you have a TPM security chip which is connected to a regular, non-tcg SPI master (i.e. most embedded platforms) that is compliant with the TCG TIS 1.3 TPM specification (TPM1.2) or the TCG PTP FIFO @@ -77,7 +77,7 @@ config TCG_TIS_SPI_CR50 config TCG_TIS_I2C_ATMEL tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)" depends on I2C - ---help--- + help If you have an Atmel I2C TPM security chip say Yes and it will be accessible from within Linux. To compile this driver as a module, choose M here; the module will @@ -86,7 +86,7 @@ config TCG_TIS_I2C_ATMEL config TCG_TIS_I2C_INFINEON tristate "TPM Interface Specification 1.2 Interface (I2C - Infineon)" depends on I2C - ---help--- + help If you have a TPM security chip that is compliant with the TCG TIS 1.2 TPM specification and Infineon's I2C Protocol Stack Specification 0.20 say Yes and it will be accessible from within @@ -97,7 +97,7 @@ config TCG_TIS_I2C_INFINEON config TCG_TIS_I2C_NUVOTON tristate "TPM Interface Specification 1.2 Interface (I2C - Nuvoton)" depends on I2C - ---help--- + help If you have a TPM security chip with an I2C interface from Nuvoton Technology Corp. say Yes and it will be accessible from within Linux. @@ -107,7 +107,7 @@ config TCG_TIS_I2C_NUVOTON config TCG_NSC tristate "National Semiconductor TPM Interface" depends on X86 - ---help--- + help If you have a TPM security chip from National Semiconductor say Yes and it will be accessible from within Linux. To compile this driver as a module, choose M here; the module @@ -116,7 +116,7 @@ config TCG_NSC config TCG_ATMEL tristate "Atmel TPM Interface" depends on PPC64 || HAS_IOPORT_MAP - ---help--- + help If you have a TPM security chip from Atmel say Yes and it will be accessible from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_atmel. @@ -124,7 +124,7 @@ config TCG_ATMEL config TCG_INFINEON tristate "Infineon Technologies TPM Interface" depends on PNP - ---help--- + help If you have a TPM security chip from Infineon Technologies (either SLD 9630 TT 1.1 or SLB 9635 TT 1.2) say Yes and it will be accessible from within Linux. @@ -136,7 +136,7 @@ config TCG_INFINEON config TCG_IBMVTPM tristate "IBM VTPM Interface" depends on PPC_PSERIES - ---help--- + help If you have IBM virtual TPM (VTPM) support say Yes and it will be accessible from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_ibmvtpm. @@ -145,7 +145,7 @@ config TCG_XEN tristate "XEN TPM Interface" depends on TCG_TPM && XEN select XEN_XENBUS_FRONTEND - ---help--- + help If you want to make TPM support available to a Xen user domain, say Yes and it will be accessible from within Linux. See the manpages for xl, xl.conf, and docs/misc/vtpm.txt in @@ -156,7 +156,7 @@ config TCG_XEN config TCG_CRB tristate "TPM 2.0 CRB Interface" depends on ACPI - ---help--- + 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 from within Linux. To compile this driver as a module, choose @@ -165,7 +165,7 @@ config TCG_CRB config TCG_VTPM_PROXY tristate "VTPM Proxy Interface" depends on TCG_TPM - ---help--- + help This driver proxies for an emulated TPM (vTPM) running in userspace. A device /dev/vtpmx is provided that creates a device pair /dev/vtpmX and a server-side file descriptor on which the vTPM diff --git a/drivers/char/tpm/eventlog/tpm2.c b/drivers/char/tpm/eventlog/tpm2.c index e741b1157525..37a05800980c 100644 --- a/drivers/char/tpm/eventlog/tpm2.c +++ b/drivers/char/tpm/eventlog/tpm2.c @@ -51,8 +51,7 @@ static void *tpm2_bios_measurements_start(struct seq_file *m, loff_t *pos) int i; event_header = addr; - size = sizeof(struct tcg_pcr_event) - sizeof(event_header->event) - + event_header->event_size; + size = struct_size(event_header, event, event_header->event_size); if (*pos == 0) { if (addr + size < limit) { @@ -98,8 +97,8 @@ static void *tpm2_bios_measurements_next(struct seq_file *m, void *v, event_header = log->bios_event_log; if (v == SEQ_START_TOKEN) { - event_size = sizeof(struct tcg_pcr_event) - - sizeof(event_header->event) + event_header->event_size; + event_size = struct_size(event_header, event, + event_header->event_size); marker = event_header; } else { event = v; @@ -136,9 +135,8 @@ static int tpm2_binary_bios_measurements_show(struct seq_file *m, void *v) size_t size; if (v == SEQ_START_TOKEN) { - size = sizeof(struct tcg_pcr_event) - - sizeof(event_header->event) + event_header->event_size; - + size = struct_size(event_header, event, + event_header->event_size); temp_ptr = event_header; if (size > 0) diff --git a/drivers/char/tpm/st33zp24/Kconfig b/drivers/char/tpm/st33zp24/Kconfig index e582145076dc..601c2ae5bfcb 100644 --- a/drivers/char/tpm/st33zp24/Kconfig +++ b/drivers/char/tpm/st33zp24/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config TCG_TIS_ST33ZP24 tristate - ---help--- + help STMicroelectronics ST33ZP24 core driver. It implements the core TPM1.2 logic and hooks into the TPM kernel APIs. Physical layers will register against it. @@ -13,7 +13,7 @@ config TCG_TIS_ST33ZP24_I2C tristate "STMicroelectronics TPM Interface Specification 1.2 Interface (I2C)" depends on I2C select TCG_TIS_ST33ZP24 - ---help--- + help This module adds support for the STMicroelectronics TPM security chip ST33ZP24 with i2c interface. To compile this driver as a module, choose M here; the module will be @@ -23,7 +23,7 @@ config TCG_TIS_ST33ZP24_SPI tristate "STMicroelectronics TPM Interface Specification 1.2 Interface (SPI)" depends on SPI select TCG_TIS_ST33ZP24 - ---help--- + help This module adds support for the STMicroelectronics TPM security chip ST33ZP24 with spi interface. To compile this driver as a module, choose M here; the module will be diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index a438b1206fcb..1621ce818705 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -323,7 +323,7 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, for (i = 0; i < chip->nr_allocated_banks; i++) { if (digests[i].alg_id != chip->allocated_banks[i].alg_id) { - rc = EINVAL; + rc = -EINVAL; goto out; } } diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 76f67b155bd5..eff1f12d981a 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -681,6 +681,7 @@ out: rc = -ENODEV; return rc; } +EXPORT_SYMBOL_GPL(tpm2_get_cc_attrs_tbl); /** * tpm2_startup - turn on the TPM diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c index 22bf553ccf9d..2491a2cb54a2 100644 --- a/drivers/char/tpm/tpm_ftpm_tee.c +++ b/drivers/char/tpm/tpm_ftpm_tee.c @@ -241,7 +241,7 @@ static int ftpm_tee_probe(struct platform_device *pdev) /* Open a session with fTPM TA */ memset(&sess_arg, 0, sizeof(sess_arg)); - memcpy(sess_arg.uuid, ftpm_ta_uuid.b, TEE_IOCTL_UUID_LEN); + export_uuid(sess_arg.uuid, &ftpm_ta_uuid); sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; sess_arg.num_params = 0; diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 1a49db9e108e..09fe45246b8c 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2012 IBM Corporation + * Copyright (C) 2012-2020 IBM Corporation * * Author: Ashley Lai <ashleydlai@gmail.com> * @@ -135,6 +135,64 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) } /** + * ibmvtpm_crq_send_init - Send a CRQ initialize message + * @ibmvtpm: vtpm device struct + * + * Return: + * 0 on success. + * Non-zero on failure. + */ +static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm) +{ + int rc; + + rc = ibmvtpm_send_crq_word(ibmvtpm->vdev, INIT_CRQ_CMD); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "%s failed rc=%d\n", __func__, rc); + + return rc; +} + +/** + * tpm_ibmvtpm_resume - Resume from suspend + * + * @dev: device struct + * + * Return: Always 0. + */ +static int tpm_ibmvtpm_resume(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); + int rc = 0; + + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_ENABLE_CRQ, + ibmvtpm->vdev->unit_address); + } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + if (rc) { + dev_err(dev, "Error enabling ibmvtpm rc=%d\n", rc); + return rc; + } + + rc = vio_enable_interrupts(ibmvtpm->vdev); + if (rc) { + dev_err(dev, "Error vio_enable_interrupts rc=%d\n", rc); + return rc; + } + + rc = ibmvtpm_crq_send_init(ibmvtpm); + if (rc) + dev_err(dev, "Error send_init rc=%d\n", rc); + + return rc; +} + +/** * tpm_ibmvtpm_send() - Send a TPM command * @chip: tpm chip struct * @buf: buffer contains data to send @@ -147,6 +205,7 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) { struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); + bool retry = true; int rc, sig; if (!ibmvtpm->rtce_buf) { @@ -180,18 +239,27 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) */ ibmvtpm->tpm_processing_cmd = true; +again: rc = ibmvtpm_send_crq(ibmvtpm->vdev, IBMVTPM_VALID_CMD, VTPM_TPM_COMMAND, count, ibmvtpm->rtce_dma_handle); if (rc != H_SUCCESS) { + /* + * H_CLOSED can be returned after LPM resume. Call + * tpm_ibmvtpm_resume() to re-enable the CRQ then retry + * ibmvtpm_send_crq() once before failing. + */ + if (rc == H_CLOSED && retry) { + tpm_ibmvtpm_resume(ibmvtpm->dev); + retry = false; + goto again; + } dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc); - rc = 0; ibmvtpm->tpm_processing_cmd = false; - } else - rc = 0; + } spin_unlock(&ibmvtpm->rtce_lock); - return rc; + return 0; } static void tpm_ibmvtpm_cancel(struct tpm_chip *chip) @@ -270,26 +338,6 @@ static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm) } /** - * ibmvtpm_crq_send_init - Send a CRQ initialize message - * @ibmvtpm: vtpm device struct - * - * Return: - * 0 on success. - * Non-zero on failure. - */ -static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm) -{ - int rc; - - rc = ibmvtpm_send_crq_word(ibmvtpm->vdev, INIT_CRQ_CMD); - if (rc != H_SUCCESS) - dev_err(ibmvtpm->dev, - "ibmvtpm_crq_send_init failed rc=%d\n", rc); - - return rc; -} - -/** * tpm_ibmvtpm_remove - ibm vtpm remove entry point * @vdev: vio device struct * @@ -401,44 +449,6 @@ static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm) ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE); } -/** - * tpm_ibmvtpm_resume - Resume from suspend - * - * @dev: device struct - * - * Return: Always 0. - */ -static int tpm_ibmvtpm_resume(struct device *dev) -{ - struct tpm_chip *chip = dev_get_drvdata(dev); - struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); - int rc = 0; - - do { - if (rc) - msleep(100); - rc = plpar_hcall_norets(H_ENABLE_CRQ, - ibmvtpm->vdev->unit_address); - } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc)); - - if (rc) { - dev_err(dev, "Error enabling ibmvtpm rc=%d\n", rc); - return rc; - } - - rc = vio_enable_interrupts(ibmvtpm->vdev); - if (rc) { - dev_err(dev, "Error vio_enable_interrupts rc=%d\n", rc); - return rc; - } - - rc = ibmvtpm_crq_send_init(ibmvtpm); - if (rc) - dev_err(dev, "Error send_init rc=%d\n", rc); - - return rc; -} - static bool tpm_ibmvtpm_req_canceled(struct tpm_chip *chip, u8 status) { return (status == 0); diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 27c6ca031e23..2435216bd10a 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -433,6 +433,9 @@ static void disable_interrupts(struct tpm_chip *chip) u32 intmask; int rc; + if (priv->irq == 0) + return; + rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask); if (rc < 0) intmask = 0; @@ -1062,9 +1065,12 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, if (irq) { tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED, irq); - if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) + if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { dev_err(&chip->dev, FW_BUG "TPM interrupt not working, polling instead\n"); + + disable_interrupts(chip); + } } else { tpm_tis_probe_irq(chip, intmask); } diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 3cbaec925606..00c5e3acee46 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -871,7 +871,7 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf, return 0; /* Try lock this page */ - if (pipe_buf_steal(pipe, buf) == 0) { + if (pipe_buf_try_steal(pipe, buf)) { /* Get reference and unlock page for moving */ get_page(buf->page); unlock_page(buf->page); |