diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/hw_random/Kconfig | 21 | ||||
-rw-r--r-- | drivers/char/hw_random/imx-rngc.c | 69 | ||||
-rw-r--r-- | drivers/char/hw_random/rockchip-rng.c | 250 | ||||
-rw-r--r-- | drivers/char/hw_random/timeriomem-rng.c | 3 | ||||
-rw-r--r-- | drivers/char/random.c | 6 | ||||
-rw-r--r-- | drivers/char/sonypi.c | 11 | ||||
-rw-r--r-- | drivers/char/tpm/Kconfig | 9 | ||||
-rw-r--r-- | drivers/char/tpm/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/tpm/tpm-chip.c | 6 | ||||
-rw-r--r-- | drivers/char/tpm/tpm-interface.c | 37 | ||||
-rw-r--r-- | drivers/char/tpm/tpm2-cmd.c | 1 | ||||
-rw-r--r-- | drivers/char/tpm/tpm2-sessions.c | 2 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_crb.c | 105 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_crb_ffa.c | 348 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_crb_ffa.h | 25 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_ftpm_tee.c | 22 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_ftpm_tee.h | 1 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis_core.c | 20 | ||||
-rw-r--r-- | drivers/char/tpm/tpm_tis_core.h | 1 |
19 files changed, 815 insertions, 123 deletions
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 17854f052386..c85827843447 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -534,10 +534,10 @@ config HW_RANDOM_NPCM If unsure, say Y. config HW_RANDOM_KEYSTONE + tristate "TI Keystone NETCP SA Hardware random number generator" depends on ARCH_KEYSTONE || COMPILE_TEST depends on HAS_IOMEM && OF default HW_RANDOM - tristate "TI Keystone NETCP SA Hardware random number generator" help This option enables Keystone's hardware random generator. @@ -579,15 +579,15 @@ config HW_RANDOM_ARM_SMCCC_TRNG module will be called arm_smccc_trng. config HW_RANDOM_CN10K - tristate "Marvell CN10K Random Number Generator support" - depends on HW_RANDOM && PCI && (ARM64 || (64BIT && COMPILE_TEST)) - default HW_RANDOM if ARCH_THUNDER - help - This driver provides support for the True Random Number - generator available in Marvell CN10K SoCs. + tristate "Marvell CN10K Random Number Generator support" + depends on HW_RANDOM && PCI && (ARM64 || (64BIT && COMPILE_TEST)) + default HW_RANDOM if ARCH_THUNDER + help + This driver provides support for the True Random Number + generator available in Marvell CN10K SoCs. - To compile this driver as a module, choose M here. - The module will be called cn10k_rng. If unsure, say Y. + To compile this driver as a module, choose M here. + The module will be called cn10k_rng. If unsure, say Y. config HW_RANDOM_JH7110 tristate "StarFive JH7110 Random Number Generator support" @@ -606,7 +606,8 @@ config HW_RANDOM_ROCKCHIP default HW_RANDOM help This driver provides kernel-side support for the True Random Number - Generator hardware found on some Rockchip SoC like RK3566 or RK3568. + Generator hardware found on some Rockchip SoCs like RK3566, RK3568 + or RK3588. To compile this driver as a module, choose M here: the module will be called rockchip-rng. diff --git a/drivers/char/hw_random/imx-rngc.c b/drivers/char/hw_random/imx-rngc.c index 118a72acb99b..241664a9b5d9 100644 --- a/drivers/char/hw_random/imx-rngc.c +++ b/drivers/char/hw_random/imx-rngc.c @@ -13,6 +13,8 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/interrupt.h> #include <linux/hw_random.h> #include <linux/completion.h> @@ -53,6 +55,7 @@ #define RNGC_SELFTEST_TIMEOUT 2500 /* us */ #define RNGC_SEED_TIMEOUT 200 /* ms */ +#define RNGC_PM_TIMEOUT 500 /* ms */ static bool self_test = true; module_param(self_test, bool, 0); @@ -123,7 +126,11 @@ static int imx_rngc_read(struct hwrng *rng, void *data, size_t max, bool wait) { struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); unsigned int status; - int retval = 0; + int err, retval = 0; + + err = pm_runtime_resume_and_get(rngc->dev); + if (err) + return err; while (max >= sizeof(u32)) { status = readl(rngc->base + RNGC_STATUS); @@ -141,6 +148,8 @@ static int imx_rngc_read(struct hwrng *rng, void *data, size_t max, bool wait) max -= sizeof(u32); } } + pm_runtime_mark_last_busy(rngc->dev); + pm_runtime_put(rngc->dev); return retval ? retval : -EIO; } @@ -169,7 +178,11 @@ static int imx_rngc_init(struct hwrng *rng) { struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); u32 cmd, ctrl; - int ret; + int ret, err; + + err = pm_runtime_resume_and_get(rngc->dev); + if (err) + return err; /* clear error */ cmd = readl(rngc->base + RNGC_COMMAND); @@ -186,15 +199,15 @@ static int imx_rngc_init(struct hwrng *rng) ret = wait_for_completion_timeout(&rngc->rng_op_done, msecs_to_jiffies(RNGC_SEED_TIMEOUT)); if (!ret) { - ret = -ETIMEDOUT; - goto err; + err = -ETIMEDOUT; + goto out; } } while (rngc->err_reg == RNGC_ERROR_STATUS_STAT_ERR); if (rngc->err_reg) { - ret = -EIO; - goto err; + err = -EIO; + goto out; } /* @@ -205,23 +218,29 @@ static int imx_rngc_init(struct hwrng *rng) ctrl |= RNGC_CTRL_AUTO_SEED; writel(ctrl, rngc->base + RNGC_CONTROL); +out: /* * if initialisation was successful, we keep the interrupt * unmasked until imx_rngc_cleanup is called * we mask the interrupt ourselves if we return an error */ - return 0; + if (err) + imx_rngc_irq_mask_clear(rngc); -err: - imx_rngc_irq_mask_clear(rngc); - return ret; + pm_runtime_put(rngc->dev); + return err; } static void imx_rngc_cleanup(struct hwrng *rng) { struct imx_rngc *rngc = container_of(rng, struct imx_rngc, rng); + int err; - imx_rngc_irq_mask_clear(rngc); + err = pm_runtime_resume_and_get(rngc->dev); + if (!err) { + imx_rngc_irq_mask_clear(rngc); + pm_runtime_put(rngc->dev); + } } static int __init imx_rngc_probe(struct platform_device *pdev) @@ -240,7 +259,7 @@ static int __init imx_rngc_probe(struct platform_device *pdev) if (IS_ERR(rngc->base)) return PTR_ERR(rngc->base); - rngc->clk = devm_clk_get_enabled(&pdev->dev, NULL); + rngc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(rngc->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(rngc->clk), "Cannot get rng_clk\n"); @@ -248,14 +267,18 @@ static int __init imx_rngc_probe(struct platform_device *pdev) if (irq < 0) return irq; + clk_prepare_enable(rngc->clk); + ver_id = readl(rngc->base + RNGC_VER_ID); rng_type = FIELD_GET(RNG_TYPE, ver_id); /* * This driver supports only RNGC and RNGB. (There's a different * driver for RNGA.) */ - if (rng_type != RNGC_TYPE_RNGC && rng_type != RNGC_TYPE_RNGB) + if (rng_type != RNGC_TYPE_RNGC && rng_type != RNGC_TYPE_RNGB) { + clk_disable_unprepare(rngc->clk); return -ENODEV; + } init_completion(&rngc->rng_op_done); @@ -272,15 +295,24 @@ static int __init imx_rngc_probe(struct platform_device *pdev) ret = devm_request_irq(&pdev->dev, irq, imx_rngc_irq, 0, pdev->name, (void *)rngc); - if (ret) + if (ret) { + clk_disable_unprepare(rngc->clk); return dev_err_probe(&pdev->dev, ret, "Can't get interrupt working.\n"); + } if (self_test) { ret = imx_rngc_self_test(rngc); - if (ret) + if (ret) { + clk_disable_unprepare(rngc->clk); return dev_err_probe(&pdev->dev, ret, "self test failed\n"); + } } + pm_runtime_set_autosuspend_delay(&pdev->dev, RNGC_PM_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + devm_pm_runtime_enable(&pdev->dev); + ret = devm_hwrng_register(&pdev->dev, &rngc->rng); if (ret) return dev_err_probe(&pdev->dev, ret, "hwrng registration failed\n"); @@ -310,7 +342,10 @@ static int imx_rngc_resume(struct device *dev) return 0; } -static DEFINE_SIMPLE_DEV_PM_OPS(imx_rngc_pm_ops, imx_rngc_suspend, imx_rngc_resume); +static const struct dev_pm_ops imx_rngc_pm_ops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(imx_rngc_suspend, imx_rngc_resume, NULL) +}; static const struct of_device_id imx_rngc_dt_ids[] = { { .compatible = "fsl,imx25-rngb" }, @@ -321,7 +356,7 @@ MODULE_DEVICE_TABLE(of, imx_rngc_dt_ids); static struct platform_driver imx_rngc_driver = { .driver = { .name = KBUILD_MODNAME, - .pm = pm_sleep_ptr(&imx_rngc_pm_ops), + .pm = pm_ptr(&imx_rngc_pm_ops), .of_match_table = imx_rngc_dt_ids, }, }; diff --git a/drivers/char/hw_random/rockchip-rng.c b/drivers/char/hw_random/rockchip-rng.c index 289b385bbf05..161050591663 100644 --- a/drivers/char/hw_random/rockchip-rng.c +++ b/drivers/char/hw_random/rockchip-rng.c @@ -1,12 +1,14 @@ // SPDX-License-Identifier: GPL-2.0 /* - * rockchip-rng.c True Random Number Generator driver for Rockchip RK3568 SoC + * rockchip-rng.c True Random Number Generator driver for Rockchip SoCs * * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd. * Copyright (c) 2022, Aurelien Jarno + * Copyright (c) 2025, Collabora Ltd. * Authors: * Lin Jinhan <troy.lin@rock-chips.com> * Aurelien Jarno <aurelien@aurel32.net> + * Nicolas Frattaroli <nicolas.frattaroli@collabora.com> */ #include <linux/clk.h> #include <linux/hw_random.h> @@ -32,6 +34,9 @@ */ #define RK_RNG_SAMPLE_CNT 1000 +/* after how many bytes of output TRNGv1 implementations should be reseeded */ +#define RK_TRNG_V1_AUTO_RESEED_CNT 16000 + /* TRNG registers from RK3568 TRM-Part2, section 5.4.1 */ #define TRNG_RST_CTL 0x0004 #define TRNG_RNG_CTL 0x0400 @@ -49,11 +54,64 @@ #define TRNG_RNG_SAMPLE_CNT 0x0404 #define TRNG_RNG_DOUT 0x0410 +/* + * TRNG V1 register definitions + * The TRNG V1 IP is a stand-alone TRNG implementation (not part of a crypto IP) + * and can be found in the Rockchip RK3588 SoC + */ +#define TRNG_V1_CTRL 0x0000 +#define TRNG_V1_CTRL_NOP 0x00 +#define TRNG_V1_CTRL_RAND 0x01 +#define TRNG_V1_CTRL_SEED 0x02 + +#define TRNG_V1_STAT 0x0004 +#define TRNG_V1_STAT_SEEDED BIT(9) +#define TRNG_V1_STAT_GENERATING BIT(30) +#define TRNG_V1_STAT_RESEEDING BIT(31) + +#define TRNG_V1_MODE 0x0008 +#define TRNG_V1_MODE_128_BIT (0x00 << 3) +#define TRNG_V1_MODE_256_BIT (0x01 << 3) + +/* Interrupt Enable register; unused because polling is faster */ +#define TRNG_V1_IE 0x0010 +#define TRNG_V1_IE_GLBL_EN BIT(31) +#define TRNG_V1_IE_SEED_DONE_EN BIT(1) +#define TRNG_V1_IE_RAND_RDY_EN BIT(0) + +#define TRNG_V1_ISTAT 0x0014 +#define TRNG_V1_ISTAT_RAND_RDY BIT(0) + +/* RAND0 ~ RAND7 */ +#define TRNG_V1_RAND0 0x0020 +#define TRNG_V1_RAND7 0x003C + +/* Auto Reseed Register */ +#define TRNG_V1_AUTO_RQSTS 0x0060 + +#define TRNG_V1_VERSION 0x00F0 +#define TRNG_v1_VERSION_CODE 0x46bc +/* end of TRNG_V1 register definitions */ + +/* Before removing this assert, give rk3588_rng_read an upper bound of 32 */ +static_assert(RK_RNG_MAX_BYTE <= (TRNG_V1_RAND7 + 4 - TRNG_V1_RAND0), + "You raised RK_RNG_MAX_BYTE and broke rk3588-rng, congrats."); + struct rk_rng { struct hwrng rng; void __iomem *base; int clk_num; struct clk_bulk_data *clk_bulks; + const struct rk_rng_soc_data *soc_data; + struct device *dev; +}; + +struct rk_rng_soc_data { + int (*rk_rng_init)(struct hwrng *rng); + int (*rk_rng_read)(struct hwrng *rng, void *buf, size_t max, bool wait); + void (*rk_rng_cleanup)(struct hwrng *rng); + unsigned short quality; + bool reset_optional; }; /* The mask in the upper 16 bits determines the bits that are updated */ @@ -62,19 +120,38 @@ static void rk_rng_write_ctl(struct rk_rng *rng, u32 val, u32 mask) writel((mask << 16) | val, rng->base + TRNG_RNG_CTL); } -static int rk_rng_init(struct hwrng *rng) +static inline void rk_rng_writel(struct rk_rng *rng, u32 val, u32 offset) { - struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); - int ret; + writel(val, rng->base + offset); +} +static inline u32 rk_rng_readl(struct rk_rng *rng, u32 offset) +{ + return readl(rng->base + offset); +} + +static int rk_rng_enable_clks(struct rk_rng *rk_rng) +{ + int ret; /* start clocks */ ret = clk_bulk_prepare_enable(rk_rng->clk_num, rk_rng->clk_bulks); if (ret < 0) { - dev_err((struct device *) rk_rng->rng.priv, - "Failed to enable clks %d\n", ret); + dev_err(rk_rng->dev, "Failed to enable clocks: %d\n", ret); return ret; } + return 0; +} + +static int rk3568_rng_init(struct hwrng *rng) +{ + struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); + int ret; + + ret = rk_rng_enable_clks(rk_rng); + if (ret < 0) + return ret; + /* set the sample period */ writel(RK_RNG_SAMPLE_CNT, rk_rng->base + TRNG_RNG_SAMPLE_CNT); @@ -87,7 +164,7 @@ static int rk_rng_init(struct hwrng *rng) return 0; } -static void rk_rng_cleanup(struct hwrng *rng) +static void rk3568_rng_cleanup(struct hwrng *rng) { struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); @@ -98,14 +175,14 @@ static void rk_rng_cleanup(struct hwrng *rng) clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks); } -static int rk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +static int rk3568_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) { struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); size_t to_read = min_t(size_t, max, RK_RNG_MAX_BYTE); u32 reg; int ret = 0; - ret = pm_runtime_resume_and_get((struct device *) rk_rng->rng.priv); + ret = pm_runtime_resume_and_get(rk_rng->dev); if (ret < 0) return ret; @@ -122,12 +199,120 @@ static int rk_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) /* Read random data stored in the registers */ memcpy_fromio(buf, rk_rng->base + TRNG_RNG_DOUT, to_read); out: - pm_runtime_mark_last_busy((struct device *) rk_rng->rng.priv); - pm_runtime_put_sync_autosuspend((struct device *) rk_rng->rng.priv); + pm_runtime_mark_last_busy(rk_rng->dev); + pm_runtime_put_sync_autosuspend(rk_rng->dev); + + return (ret < 0) ? ret : to_read; +} + +static int rk3588_rng_init(struct hwrng *rng) +{ + struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); + u32 version, status, mask, istat; + int ret; + + ret = rk_rng_enable_clks(rk_rng); + if (ret < 0) + return ret; + + version = rk_rng_readl(rk_rng, TRNG_V1_VERSION); + if (version != TRNG_v1_VERSION_CODE) { + dev_err(rk_rng->dev, + "wrong trng version, expected = %08x, actual = %08x\n", + TRNG_V1_VERSION, version); + ret = -EFAULT; + goto err_disable_clk; + } + + mask = TRNG_V1_STAT_SEEDED | TRNG_V1_STAT_GENERATING | + TRNG_V1_STAT_RESEEDING; + if (readl_poll_timeout(rk_rng->base + TRNG_V1_STAT, status, + (status & mask) == TRNG_V1_STAT_SEEDED, + RK_RNG_POLL_PERIOD_US, RK_RNG_POLL_TIMEOUT_US) < 0) { + dev_err(rk_rng->dev, "timed out waiting for hwrng to reseed\n"); + ret = -ETIMEDOUT; + goto err_disable_clk; + } + + /* + * clear ISTAT flag, downstream advises to do this to avoid + * auto-reseeding "on power on" + */ + istat = rk_rng_readl(rk_rng, TRNG_V1_ISTAT); + rk_rng_writel(rk_rng, istat, TRNG_V1_ISTAT); + + /* auto reseed after RK_TRNG_V1_AUTO_RESEED_CNT bytes */ + rk_rng_writel(rk_rng, RK_TRNG_V1_AUTO_RESEED_CNT / 16, TRNG_V1_AUTO_RQSTS); + + return 0; +err_disable_clk: + clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks); + return ret; +} + +static void rk3588_rng_cleanup(struct hwrng *rng) +{ + struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); + + clk_bulk_disable_unprepare(rk_rng->clk_num, rk_rng->clk_bulks); +} + +static int rk3588_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) +{ + struct rk_rng *rk_rng = container_of(rng, struct rk_rng, rng); + size_t to_read = min_t(size_t, max, RK_RNG_MAX_BYTE); + int ret = 0; + u32 reg; + + ret = pm_runtime_resume_and_get(rk_rng->dev); + if (ret < 0) + return ret; + + /* Clear ISTAT, even without interrupts enabled, this will be updated */ + reg = rk_rng_readl(rk_rng, TRNG_V1_ISTAT); + rk_rng_writel(rk_rng, reg, TRNG_V1_ISTAT); + + /* generate 256 bits of random data */ + rk_rng_writel(rk_rng, TRNG_V1_MODE_256_BIT, TRNG_V1_MODE); + rk_rng_writel(rk_rng, TRNG_V1_CTRL_RAND, TRNG_V1_CTRL); + + ret = readl_poll_timeout_atomic(rk_rng->base + TRNG_V1_ISTAT, reg, + (reg & TRNG_V1_ISTAT_RAND_RDY), 0, + RK_RNG_POLL_TIMEOUT_US); + if (ret < 0) + goto out; + + /* Read random data that's in registers TRNG_V1_RAND0 through RAND7 */ + memcpy_fromio(buf, rk_rng->base + TRNG_V1_RAND0, to_read); + +out: + /* Clear ISTAT */ + rk_rng_writel(rk_rng, reg, TRNG_V1_ISTAT); + /* close the TRNG */ + rk_rng_writel(rk_rng, TRNG_V1_CTRL_NOP, TRNG_V1_CTRL); + + pm_runtime_mark_last_busy(rk_rng->dev); + pm_runtime_put_sync_autosuspend(rk_rng->dev); return (ret < 0) ? ret : to_read; } +static const struct rk_rng_soc_data rk3568_soc_data = { + .rk_rng_init = rk3568_rng_init, + .rk_rng_read = rk3568_rng_read, + .rk_rng_cleanup = rk3568_rng_cleanup, + .quality = 900, + .reset_optional = false, +}; + +static const struct rk_rng_soc_data rk3588_soc_data = { + .rk_rng_init = rk3588_rng_init, + .rk_rng_read = rk3588_rng_read, + .rk_rng_cleanup = rk3588_rng_cleanup, + .quality = 999, /* as determined by actual testing */ + .reset_optional = true, +}; + static int rk_rng_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -139,6 +324,7 @@ static int rk_rng_probe(struct platform_device *pdev) if (!rk_rng) return -ENOMEM; + rk_rng->soc_data = of_device_get_match_data(dev); rk_rng->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(rk_rng->base)) return PTR_ERR(rk_rng->base); @@ -148,34 +334,40 @@ static int rk_rng_probe(struct platform_device *pdev) return dev_err_probe(dev, rk_rng->clk_num, "Failed to get clks property\n"); - rst = devm_reset_control_array_get_exclusive(&pdev->dev); - if (IS_ERR(rst)) - return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset property\n"); + if (rk_rng->soc_data->reset_optional) + rst = devm_reset_control_array_get_optional_exclusive(dev); + else + rst = devm_reset_control_array_get_exclusive(dev); - reset_control_assert(rst); - udelay(2); - reset_control_deassert(rst); + if (rst) { + if (IS_ERR(rst)) + return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset property\n"); + + reset_control_assert(rst); + udelay(2); + reset_control_deassert(rst); + } platform_set_drvdata(pdev, rk_rng); rk_rng->rng.name = dev_driver_string(dev); if (!IS_ENABLED(CONFIG_PM)) { - rk_rng->rng.init = rk_rng_init; - rk_rng->rng.cleanup = rk_rng_cleanup; + rk_rng->rng.init = rk_rng->soc_data->rk_rng_init; + rk_rng->rng.cleanup = rk_rng->soc_data->rk_rng_cleanup; } - rk_rng->rng.read = rk_rng_read; - rk_rng->rng.priv = (unsigned long) dev; - rk_rng->rng.quality = 900; + rk_rng->rng.read = rk_rng->soc_data->rk_rng_read; + rk_rng->dev = dev; + rk_rng->rng.quality = rk_rng->soc_data->quality; pm_runtime_set_autosuspend_delay(dev, RK_RNG_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(dev); ret = devm_pm_runtime_enable(dev); if (ret) - return dev_err_probe(&pdev->dev, ret, "Runtime pm activation failed.\n"); + return dev_err_probe(dev, ret, "Runtime pm activation failed.\n"); ret = devm_hwrng_register(dev, &rk_rng->rng); if (ret) - return dev_err_probe(&pdev->dev, ret, "Failed to register Rockchip hwrng\n"); + return dev_err_probe(dev, ret, "Failed to register Rockchip hwrng\n"); return 0; } @@ -184,7 +376,7 @@ static int __maybe_unused rk_rng_runtime_suspend(struct device *dev) { struct rk_rng *rk_rng = dev_get_drvdata(dev); - rk_rng_cleanup(&rk_rng->rng); + rk_rng->soc_data->rk_rng_cleanup(&rk_rng->rng); return 0; } @@ -193,7 +385,7 @@ static int __maybe_unused rk_rng_runtime_resume(struct device *dev) { struct rk_rng *rk_rng = dev_get_drvdata(dev); - return rk_rng_init(&rk_rng->rng); + return rk_rng->soc_data->rk_rng_init(&rk_rng->rng); } static const struct dev_pm_ops rk_rng_pm_ops = { @@ -204,7 +396,8 @@ static const struct dev_pm_ops rk_rng_pm_ops = { }; static const struct of_device_id rk_rng_dt_match[] = { - { .compatible = "rockchip,rk3568-rng", }, + { .compatible = "rockchip,rk3568-rng", .data = (void *)&rk3568_soc_data }, + { .compatible = "rockchip,rk3588-rng", .data = (void *)&rk3588_soc_data }, { /* sentinel */ }, }; @@ -221,8 +414,9 @@ static struct platform_driver rk_rng_driver = { module_platform_driver(rk_rng_driver); -MODULE_DESCRIPTION("Rockchip RK3568 True Random Number Generator driver"); +MODULE_DESCRIPTION("Rockchip True Random Number Generator driver"); MODULE_AUTHOR("Lin Jinhan <troy.lin@rock-chips.com>"); MODULE_AUTHOR("Aurelien Jarno <aurelien@aurel32.net>"); MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>"); +MODULE_AUTHOR("Nicolas Frattaroli <nicolas.frattaroli@collabora.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/char/hw_random/timeriomem-rng.c b/drivers/char/hw_random/timeriomem-rng.c index 7174bfccc7b3..b95f6d0f17ed 100644 --- a/drivers/char/hw_random/timeriomem-rng.c +++ b/drivers/char/hw_random/timeriomem-rng.c @@ -152,8 +152,7 @@ static int timeriomem_rng_probe(struct platform_device *pdev) priv->period = ns_to_ktime(period * NSEC_PER_USEC); init_completion(&priv->completion); - hrtimer_init(&priv->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - priv->timer.function = timeriomem_rng_trigger; + hrtimer_setup(&priv->timer, timeriomem_rng_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); priv->rng_ops.name = dev_name(&pdev->dev); priv->rng_ops.read = timeriomem_rng_read; diff --git a/drivers/char/random.c b/drivers/char/random.c index 2581186fa61b..92cbd24a36d8 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -278,7 +278,7 @@ static void crng_reseed(struct work_struct *work) WRITE_ONCE(base_crng.generation, next_gen); #ifdef CONFIG_VDSO_GETRANDOM /* base_crng.generation's invalid value is ULONG_MAX, while - * _vdso_rng_data.generation's invalid value is 0, so add one to the + * vdso_k_rng_data->generation's invalid value is 0, so add one to the * former to arrive at the latter. Use smp_store_release so that this * is ordered with the write above to base_crng.generation. Pairs with * the smp_rmb() before the syscall in the vDSO code. @@ -290,7 +290,7 @@ static void crng_reseed(struct work_struct *work) * because the vDSO side only checks whether the value changed, without * actually using or interpreting the value. */ - smp_store_release((unsigned long *)&__arch_get_k_vdso_rng_data()->generation, next_gen + 1); + smp_store_release((unsigned long *)&vdso_k_rng_data->generation, next_gen + 1); #endif if (!static_branch_likely(&crng_is_ready)) crng_init = CRNG_READY; @@ -743,7 +743,7 @@ static void __cold _credit_init_bits(size_t bits) queue_work(system_unbound_wq, &set_ready); atomic_notifier_call_chain(&random_ready_notifier, 0, NULL); #ifdef CONFIG_VDSO_GETRANDOM - WRITE_ONCE(__arch_get_k_vdso_rng_data()->is_ready, true); + WRITE_ONCE(vdso_k_rng_data->is_ready, true); #endif wake_up_interruptible(&crng_init_wait); kill_fasync(&fasync, SIGIO, POLL_IN); diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index f887569fd3d0..677bb5ac950a 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -37,6 +37,7 @@ #include <linux/kfifo.h> #include <linux/platform_device.h> #include <linux/gfp.h> +#include <linux/string_choices.h> #include <linux/uaccess.h> #include <asm/io.h> @@ -1268,12 +1269,12 @@ static void sonypi_display_info(void) "compat = %s, mask = 0x%08lx, useinput = %s, acpi = %s\n", sonypi_device.model, verbose, - fnkeyinit ? "on" : "off", - camera ? "on" : "off", - compat ? "on" : "off", + str_on_off(fnkeyinit), + str_on_off(camera), + str_on_off(compat), mask, - useinput ? "on" : "off", - SONYPI_ACPI_ACTIVE ? "on" : "off"); + str_on_off(useinput), + str_on_off(SONYPI_ACPI_ACTIVE)); printk(KERN_INFO "sonypi: enabled at irq=%d, port1=0x%x, port2=0x%x\n", sonypi_device.irq, sonypi_device.ioport1, sonypi_device.ioport2); diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 0fc9a510e059..fe4f3a609934 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -210,6 +210,15 @@ config TCG_CRB from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_crb. +config TCG_ARM_CRB_FFA + tristate "TPM CRB over Arm FF-A Transport" + depends on ARM_FFA_TRANSPORT && TCG_CRB + default TCG_CRB + help + If the Arm FF-A transport is used to access the TPM say Yes. + To compile this driver as a module, choose M here; the module + will be called tpm_crb_ffa. + config TCG_VTPM_PROXY tristate "VTPM Proxy Interface" depends on TCG_TPM diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 9bb142c75243..2b004df8c04b 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -42,5 +42,6 @@ obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/ obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o obj-$(CONFIG_TCG_CRB) += tpm_crb.o +obj-$(CONFIG_TCG_ARM_CRB_FFA) += tpm_crb_ffa.o obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 7df7abaf3e52..e25daf2396d3 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -168,6 +168,11 @@ int tpm_try_get_ops(struct tpm_chip *chip) goto out_ops; mutex_lock(&chip->tpm_mutex); + + /* tmp_chip_start may issue IO that is denied while suspended */ + if (chip->flags & TPM_CHIP_FLAG_SUSPENDED) + goto out_lock; + rc = tpm_chip_start(chip); if (rc) goto out_lock; @@ -300,6 +305,7 @@ int tpm_class_shutdown(struct device *dev) down_write(&chip->ops_sem); if (chip->flags & TPM_CHIP_FLAG_TPM2) { if (!tpm_chip_start(chip)) { + tpm2_end_auth_session(chip); tpm2_shutdown(chip, TPM2_SU_CLEAR); tpm_chip_stop(chip); } diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index b1daa0d7b341..8d7e4da6ed53 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -58,6 +58,30 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); +static void tpm_chip_cancel(struct tpm_chip *chip) +{ + if (!chip->ops->cancel) + return; + + chip->ops->cancel(chip); +} + +static u8 tpm_chip_status(struct tpm_chip *chip) +{ + if (!chip->ops->status) + return 0; + + return chip->ops->status(chip); +} + +static bool tpm_chip_req_canceled(struct tpm_chip *chip, u8 status) +{ + if (!chip->ops->req_canceled) + return false; + + return chip->ops->req_canceled(chip, status); +} + static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) { struct tpm_header *header = buf; @@ -104,12 +128,12 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); do { - u8 status = chip->ops->status(chip); + u8 status = tpm_chip_status(chip); if ((status & chip->ops->req_complete_mask) == chip->ops->req_complete_val) goto out_recv; - if (chip->ops->req_canceled(chip, status)) { + if (tpm_chip_req_canceled(chip, status)) { dev_err(&chip->dev, "Operation Canceled\n"); return -ECANCELED; } @@ -118,7 +142,7 @@ static ssize_t tpm_try_transmit(struct tpm_chip *chip, void *buf, size_t bufsiz) rmb(); } while (time_before(jiffies, stop)); - chip->ops->cancel(chip); + tpm_chip_cancel(chip); dev_err(&chip->dev, "Operation Timed out\n"); return -ETIME; @@ -445,18 +469,11 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, size_t max) if (!chip) return -ENODEV; - /* Give back zero bytes, as TPM chip has not yet fully resumed: */ - if (chip->flags & TPM_CHIP_FLAG_SUSPENDED) { - rc = 0; - goto out; - } - if (chip->flags & TPM_CHIP_FLAG_TPM2) rc = tpm2_get_random(chip, out, max); else rc = tpm1_get_random(chip, out, max); -out: tpm_put_ops(chip); return rc; } diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index dfdcbd009720..524d802ede26 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -359,7 +359,6 @@ int tpm2_get_random(struct tpm_chip *chip, u8 *dest, size_t max) } while (retries-- && total < max); tpm_buf_destroy(&buf); - tpm2_end_auth_session(chip); return total ? total : -EIO; out: diff --git a/drivers/char/tpm/tpm2-sessions.c b/drivers/char/tpm/tpm2-sessions.c index b70165b588ec..3f89635ba5e8 100644 --- a/drivers/char/tpm/tpm2-sessions.c +++ b/drivers/char/tpm/tpm2-sessions.c @@ -982,7 +982,7 @@ int tpm2_start_auth_session(struct tpm_chip *chip) int rc; if (chip->auth) { - dev_warn_once(&chip->dev, "auth session is active\n"); + dev_dbg_once(&chip->dev, "auth session is active\n"); return 0; } diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index ea085b14ab7c..876edf2705ab 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -19,6 +19,7 @@ #ifdef CONFIG_ARM64 #include <linux/arm-smccc.h> #endif +#include "tpm_crb_ffa.h" #include "tpm.h" #define ACPI_SIG_TPM2 "TPM2" @@ -100,6 +101,8 @@ struct crb_priv { u32 smc_func_id; u32 __iomem *pluton_start_addr; u32 __iomem *pluton_reply_addr; + u8 ffa_flags; + u8 ffa_attributes; }; struct tpm2_crb_smc { @@ -110,11 +113,30 @@ struct tpm2_crb_smc { u32 smc_func_id; }; +/* CRB over FFA start method parameters in TCG2 ACPI table */ +struct tpm2_crb_ffa { + u8 flags; + u8 attributes; + u16 partition_id; + u8 reserved[8]; +}; + struct tpm2_crb_pluton { u64 start_addr; u64 reply_addr; }; +/* + * Returns true if the start method supports idle. + */ +static inline bool tpm_crb_has_idle(u32 start_method) +{ + return !(start_method == ACPI_TPM2_START_METHOD || + start_method == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD || + start_method == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC || + start_method == ACPI_TPM2_CRB_WITH_ARM_FFA); +} + static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value, unsigned long timeout) { @@ -173,9 +195,7 @@ static int __crb_go_idle(struct device *dev, struct crb_priv *priv) { int rc; - if ((priv->sm == ACPI_TPM2_START_METHOD) || - (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) || - (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC)) + if (!tpm_crb_has_idle(priv->sm)) return 0; iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req); @@ -222,9 +242,7 @@ static int __crb_cmd_ready(struct device *dev, struct crb_priv *priv) { int rc; - if ((priv->sm == ACPI_TPM2_START_METHOD) || - (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) || - (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC)) + if (!tpm_crb_has_idle(priv->sm)) return 0; iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->regs_t->ctrl_req); @@ -255,13 +273,20 @@ static int crb_cmd_ready(struct tpm_chip *chip) static int __crb_request_locality(struct device *dev, struct crb_priv *priv, int loc) { - u32 value = CRB_LOC_STATE_LOC_ASSIGNED | - CRB_LOC_STATE_TPM_REG_VALID_STS; + u32 value = CRB_LOC_STATE_LOC_ASSIGNED | CRB_LOC_STATE_TPM_REG_VALID_STS; + int rc; if (!priv->regs_h) return 0; iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl); + + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_LOCALITY_REQUEST, loc); + if (rc) + return rc; + } + if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value, TPM2_TIMEOUT_C)) { dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n"); @@ -281,14 +306,21 @@ static int crb_request_locality(struct tpm_chip *chip, int loc) static int __crb_relinquish_locality(struct device *dev, struct crb_priv *priv, int loc) { - u32 mask = CRB_LOC_STATE_LOC_ASSIGNED | - CRB_LOC_STATE_TPM_REG_VALID_STS; + u32 mask = CRB_LOC_STATE_LOC_ASSIGNED | CRB_LOC_STATE_TPM_REG_VALID_STS; u32 value = CRB_LOC_STATE_TPM_REG_VALID_STS; + int rc; if (!priv->regs_h) return 0; iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl); + + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_LOCALITY_REQUEST, loc); + if (rc) + return rc; + } + if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, mask, value, TPM2_TIMEOUT_C)) { dev_warn(dev, "TPM_LOC_STATE_x.Relinquish timed out\n"); @@ -423,13 +455,13 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) * report only ACPI start but in practice seems to require both * CRB start, hence invoking CRB start method if hid == MSFT0101. */ - if ((priv->sm == ACPI_TPM2_COMMAND_BUFFER) || - (priv->sm == ACPI_TPM2_MEMORY_MAPPED) || - (!strcmp(priv->hid, "MSFT0101"))) + if (priv->sm == ACPI_TPM2_COMMAND_BUFFER || + priv->sm == ACPI_TPM2_MEMORY_MAPPED || + !strcmp(priv->hid, "MSFT0101")) iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start); - if ((priv->sm == ACPI_TPM2_START_METHOD) || - (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD)) + if (priv->sm == ACPI_TPM2_START_METHOD || + priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) rc = crb_do_acpi_start(chip); if (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_ARM_SMC) { @@ -437,6 +469,11 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) rc = tpm_crb_smc_start(&chip->dev, priv->smc_func_id); } + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + iowrite32(CRB_START_INVOKE, &priv->regs_t->ctrl_start); + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, chip->locality); + } + if (rc) return rc; @@ -446,13 +483,20 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) static void crb_cancel(struct tpm_chip *chip) { struct crb_priv *priv = dev_get_drvdata(&chip->dev); + int rc; iowrite32(CRB_CANCEL_INVOKE, &priv->regs_t->ctrl_cancel); - if (((priv->sm == ACPI_TPM2_START_METHOD) || - (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD)) && + if ((priv->sm == ACPI_TPM2_START_METHOD || + priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) && crb_do_acpi_start(chip)) dev_err(&chip->dev, "ACPI Start failed\n"); + + if (priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + rc = tpm_crb_ffa_start(CRB_FFA_START_TYPE_COMMAND, chip->locality); + if (rc) + dev_err(&chip->dev, "FF-A Start failed\n"); + } } static bool crb_req_canceled(struct tpm_chip *chip, u8 status) @@ -609,8 +653,9 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, * 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->sm == ACPI_TPM2_COMMAND_BUFFER) || - (priv->sm == ACPI_TPM2_MEMORY_MAPPED)) { + if (priv->sm == ACPI_TPM2_COMMAND_BUFFER || + priv->sm == ACPI_TPM2_CRB_WITH_ARM_FFA || + priv->sm == ACPI_TPM2_MEMORY_MAPPED) { if (iores && buf->control_address == iores->start + sizeof(*priv->regs_h)) @@ -731,6 +776,7 @@ static int crb_acpi_add(struct acpi_device *device) struct tpm_chip *chip; struct device *dev = &device->dev; struct tpm2_crb_smc *crb_smc; + struct tpm2_crb_ffa *crb_ffa; struct tpm2_crb_pluton *crb_pluton; acpi_status status; u32 sm; @@ -769,6 +815,27 @@ static int crb_acpi_add(struct acpi_device *device) priv->smc_func_id = crb_smc->smc_func_id; } + if (sm == ACPI_TPM2_CRB_WITH_ARM_FFA) { + if (buf->header.length < (sizeof(*buf) + sizeof(*crb_ffa))) { + dev_err(dev, + FW_BUG "TPM2 ACPI table has wrong size %u for start method type %d\n", + buf->header.length, + ACPI_TPM2_CRB_WITH_ARM_FFA); + rc = -EINVAL; + goto out; + } + crb_ffa = ACPI_ADD_PTR(struct tpm2_crb_ffa, buf, sizeof(*buf)); + priv->ffa_flags = crb_ffa->flags; + priv->ffa_attributes = crb_ffa->attributes; + rc = tpm_crb_ffa_init(); + if (rc) { + /* If FF-A driver is not available yet, request probe retry */ + if (rc == -ENOENT) + rc = -EPROBE_DEFER; + goto out; + } + } + if (sm == ACPI_TPM2_COMMAND_BUFFER_WITH_PLUTON) { if (buf->header.length < (sizeof(*buf) + sizeof(*crb_pluton))) { dev_err(dev, diff --git a/drivers/char/tpm/tpm_crb_ffa.c b/drivers/char/tpm/tpm_crb_ffa.c new file mode 100644 index 000000000000..3169a87a56b6 --- /dev/null +++ b/drivers/char/tpm/tpm_crb_ffa.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024 Arm Ltd. + * + * This device driver implements the TPM CRB start method + * as defined in the TPM Service Command Response Buffer + * Interface Over FF-A (DEN0138). + */ + +#define pr_fmt(fmt) "CRB_FFA: " fmt + +#include <linux/arm_ffa.h> +#include "tpm_crb_ffa.h" + +/* TPM service function status codes */ +#define CRB_FFA_OK 0x05000001 +#define CRB_FFA_OK_RESULTS_RETURNED 0x05000002 +#define CRB_FFA_NOFUNC 0x8e000001 +#define CRB_FFA_NOTSUP 0x8e000002 +#define CRB_FFA_INVARG 0x8e000005 +#define CRB_FFA_INV_CRB_CTRL_DATA 0x8e000006 +#define CRB_FFA_ALREADY 0x8e000009 +#define CRB_FFA_DENIED 0x8e00000a +#define CRB_FFA_NOMEM 0x8e00000b + +#define CRB_FFA_VERSION_MAJOR 1 +#define CRB_FFA_VERSION_MINOR 0 + +/* version encoding */ +#define CRB_FFA_MAJOR_VERSION_MASK GENMASK(30, 16) +#define CRB_FFA_MINOR_VERSION_MASK GENMASK(15, 0) +#define CRB_FFA_MAJOR_VERSION(x) ((u16)(FIELD_GET(CRB_FFA_MAJOR_VERSION_MASK, (x)))) +#define CRB_FFA_MINOR_VERSION(x) ((u16)(FIELD_GET(CRB_FFA_MINOR_VERSION_MASK, (x)))) + +/* + * Normal world sends requests with FFA_MSG_SEND_DIRECT_REQ and + * responses are returned with FFA_MSG_SEND_DIRECT_RESP for normal + * messages. + * + * All requests with FFA_MSG_SEND_DIRECT_REQ and FFA_MSG_SEND_DIRECT_RESP + * are using the AArch32 SMC calling convention with register usage as + * defined in FF-A specification: + * w0: Function ID (0x8400006F or 0x84000070) + * w1: Source/Destination IDs + * w2: Reserved (MBZ) + * w3-w7: Implementation defined, free to be used below + */ + +/* + * Returns the version of the interface that is available + * Call register usage: + * w3: Not used (MBZ) + * w4: TPM service function ID, CRB_FFA_GET_INTERFACE_VERSION + * w5-w7: Reserved (MBZ) + * + * Return register usage: + * w3: Not used (MBZ) + * w4: TPM service function status + * w5: TPM service interface version + * Bits[31:16]: major version + * Bits[15:0]: minor version + * w6-w7: Reserved (MBZ) + * + * Possible function status codes in register w4: + * CRB_FFA_OK_RESULTS_RETURNED: The version of the interface has been + * returned. + */ +#define CRB_FFA_GET_INTERFACE_VERSION 0x0f000001 + +/* + * Return information on a given feature of the TPM service + * Call register usage: + * w3: Not used (MBZ) + * w4: TPM service function ID, CRB_FFA_START + * w5: Start function qualifier + * Bits[31:8] (MBZ) + * Bits[7:0] + * 0: Notifies TPM that a command is ready to be processed + * 1: Notifies TPM that a locality request is ready to be processed + * w6: TPM locality, one of 0..4 + * -If the start function qualifier is 0, identifies the locality + * from where the command originated. + * -If the start function qualifier is 1, identifies the locality + * of the locality request + * w6-w7: Reserved (MBZ) + * + * Return register usage: + * w3: Not used (MBZ) + * w4: TPM service function status + * w5-w7: Reserved (MBZ) + * + * Possible function status codes in register w4: + * CRB_FFA_OK: the TPM service has been notified successfully + * CRB_FFA_INVARG: one or more arguments are not valid + * CRB_FFA_INV_CRB_CTRL_DATA: CRB control data or locality control + * data at the given TPM locality is not valid + * CRB_FFA_DENIED: the TPM has previously disabled locality requests and + * command processing at the given locality + */ +#define CRB_FFA_START 0x0f000201 + +struct tpm_crb_ffa { + struct ffa_device *ffa_dev; + u16 major_version; + u16 minor_version; + /* lock to protect sending of FF-A messages: */ + struct mutex msg_data_lock; + struct ffa_send_direct_data direct_msg_data; +}; + +static struct tpm_crb_ffa *tpm_crb_ffa; + +static int tpm_crb_ffa_to_linux_errno(int errno) +{ + int rc; + + switch (errno) { + case CRB_FFA_OK: + rc = 0; + break; + case CRB_FFA_OK_RESULTS_RETURNED: + rc = 0; + break; + case CRB_FFA_NOFUNC: + rc = -ENOENT; + break; + case CRB_FFA_NOTSUP: + rc = -EPERM; + break; + case CRB_FFA_INVARG: + rc = -EINVAL; + break; + case CRB_FFA_INV_CRB_CTRL_DATA: + rc = -ENOEXEC; + break; + case CRB_FFA_ALREADY: + rc = -EEXIST; + break; + case CRB_FFA_DENIED: + rc = -EACCES; + break; + case CRB_FFA_NOMEM: + rc = -ENOMEM; + break; + default: + rc = -EINVAL; + } + + return rc; +} + +/** + * tpm_crb_ffa_init - called by the CRB driver to do any needed initialization + * + * This function is called by the tpm_crb driver during the tpm_crb + * driver's initialization. If the tpm_crb_ffa has not been probed + * yet, returns -ENOENT in order to force a retry. If th ffa_crb + * driver had been probed but failed with an error, returns -ENODEV + * in order to prevent further retries. + * + * Return: 0 on success, negative error code on failure. + */ +int tpm_crb_ffa_init(void) +{ + if (!tpm_crb_ffa) + return -ENOENT; + + if (IS_ERR_VALUE(tpm_crb_ffa)) + return -ENODEV; + + return 0; +} +EXPORT_SYMBOL_GPL(tpm_crb_ffa_init); + +static int __tpm_crb_ffa_send_recieve(unsigned long func_id, + unsigned long a0, + unsigned long a1, + unsigned long a2) +{ + const struct ffa_msg_ops *msg_ops; + int ret; + + if (!tpm_crb_ffa) + return -ENOENT; + + msg_ops = tpm_crb_ffa->ffa_dev->ops->msg_ops; + + memset(&tpm_crb_ffa->direct_msg_data, 0x00, + sizeof(struct ffa_send_direct_data)); + + tpm_crb_ffa->direct_msg_data.data1 = func_id; + tpm_crb_ffa->direct_msg_data.data2 = a0; + tpm_crb_ffa->direct_msg_data.data3 = a1; + tpm_crb_ffa->direct_msg_data.data4 = a2; + + ret = msg_ops->sync_send_receive(tpm_crb_ffa->ffa_dev, + &tpm_crb_ffa->direct_msg_data); + if (!ret) + ret = tpm_crb_ffa_to_linux_errno(tpm_crb_ffa->direct_msg_data.data1); + + return ret; +} + +/** + * tpm_crb_ffa_get_interface_version() - gets the ABI version of the TPM service + * @major: Pointer to caller-allocated buffer to hold the major version + * number the ABI + * @minor: Pointer to caller-allocated buffer to hold the minor version + * number the ABI + * + * Returns the major and minor version of the ABI of the FF-A based TPM. + * Allows the caller to evaluate its compatibility with the version of + * the ABI. + * + * Return: 0 on success, negative error code on failure. + */ +int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor) +{ + int rc; + + if (!tpm_crb_ffa) + return -ENOENT; + + if (IS_ERR_VALUE(tpm_crb_ffa)) + return -ENODEV; + + if (!major || !minor) + return -EINVAL; + + guard(mutex)(&tpm_crb_ffa->msg_data_lock); + + rc = __tpm_crb_ffa_send_recieve(CRB_FFA_GET_INTERFACE_VERSION, 0x00, 0x00, 0x00); + if (!rc) { + *major = CRB_FFA_MAJOR_VERSION(tpm_crb_ffa->direct_msg_data.data2); + *minor = CRB_FFA_MINOR_VERSION(tpm_crb_ffa->direct_msg_data.data2); + } + + return rc; +} +EXPORT_SYMBOL_GPL(tpm_crb_ffa_get_interface_version); + +/** + * tpm_crb_ffa_start() - signals the TPM that a field has changed in the CRB + * @request_type: Identifies whether the change to the CRB is in the command + * fields or locality fields. + * @locality: Specifies the locality number. + * + * Used by the CRB driver + * that might be useful to those using or modifying it. Begins with + * empty comment line, and may include additional embedded empty + * comment lines. + * + * Return: 0 on success, negative error code on failure. + */ +int tpm_crb_ffa_start(int request_type, int locality) +{ + if (!tpm_crb_ffa) + return -ENOENT; + + if (IS_ERR_VALUE(tpm_crb_ffa)) + return -ENODEV; + + guard(mutex)(&tpm_crb_ffa->msg_data_lock); + + return __tpm_crb_ffa_send_recieve(CRB_FFA_START, request_type, locality, 0x00); +} +EXPORT_SYMBOL_GPL(tpm_crb_ffa_start); + +static int tpm_crb_ffa_probe(struct ffa_device *ffa_dev) +{ + struct tpm_crb_ffa *p; + int rc; + + /* only one instance of a TPM partition is supported */ + if (tpm_crb_ffa && !IS_ERR_VALUE(tpm_crb_ffa)) + return -EEXIST; + + tpm_crb_ffa = ERR_PTR(-ENODEV); // set tpm_crb_ffa so we can detect probe failure + + if (!ffa_partition_supports_direct_recv(ffa_dev)) { + pr_err("TPM partition doesn't support direct message receive.\n"); + return -EINVAL; + } + + p = kzalloc(sizeof(*tpm_crb_ffa), GFP_KERNEL); + if (!p) + return -ENOMEM; + tpm_crb_ffa = p; + + mutex_init(&tpm_crb_ffa->msg_data_lock); + tpm_crb_ffa->ffa_dev = ffa_dev; + ffa_dev_set_drvdata(ffa_dev, tpm_crb_ffa); + + /* if TPM is aarch32 use 32-bit SMCs */ + if (!ffa_partition_check_property(ffa_dev, FFA_PARTITION_AARCH64_EXEC)) + ffa_dev->ops->msg_ops->mode_32bit_set(ffa_dev); + + /* verify compatibility of TPM service version number */ + rc = tpm_crb_ffa_get_interface_version(&tpm_crb_ffa->major_version, + &tpm_crb_ffa->minor_version); + if (rc) { + pr_err("failed to get crb interface version. rc:%d", rc); + goto out; + } + + pr_info("ABI version %u.%u", tpm_crb_ffa->major_version, + tpm_crb_ffa->minor_version); + + if (tpm_crb_ffa->major_version != CRB_FFA_VERSION_MAJOR || + (tpm_crb_ffa->minor_version > 0 && + tpm_crb_ffa->minor_version < CRB_FFA_VERSION_MINOR)) { + pr_err("Incompatible ABI version"); + goto out; + } + + return 0; + +out: + kfree(tpm_crb_ffa); + tpm_crb_ffa = ERR_PTR(-ENODEV); + return -EINVAL; +} + +static void tpm_crb_ffa_remove(struct ffa_device *ffa_dev) +{ + kfree(tpm_crb_ffa); + tpm_crb_ffa = NULL; +} + +static const struct ffa_device_id tpm_crb_ffa_device_id[] = { + /* 17b862a4-1806-4faf-86b3-089a58353861 */ + { UUID_INIT(0x17b862a4, 0x1806, 0x4faf, + 0x86, 0xb3, 0x08, 0x9a, 0x58, 0x35, 0x38, 0x61) }, + {} +}; + +static struct ffa_driver tpm_crb_ffa_driver = { + .name = "ffa-crb", + .probe = tpm_crb_ffa_probe, + .remove = tpm_crb_ffa_remove, + .id_table = tpm_crb_ffa_device_id, +}; + +module_ffa_driver(tpm_crb_ffa_driver); + +MODULE_AUTHOR("Arm"); +MODULE_DESCRIPTION("TPM CRB FFA driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/tpm_crb_ffa.h b/drivers/char/tpm/tpm_crb_ffa.h new file mode 100644 index 000000000000..645c41ede10e --- /dev/null +++ b/drivers/char/tpm/tpm_crb_ffa.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2024 Arm Ltd. + * + * This device driver implements the TPM CRB start method + * as defined in the TPM Service Command Response Buffer + * Interface Over FF-A (DEN0138). + */ +#ifndef _TPM_CRB_FFA_H +#define _TPM_CRB_FFA_H + +#if IS_REACHABLE(CONFIG_TCG_ARM_CRB_FFA) +int tpm_crb_ffa_init(void); +int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor); +int tpm_crb_ffa_start(int request_type, int locality); +#else +static inline int tpm_crb_ffa_init(void) { return 0; } +static inline int tpm_crb_ffa_get_interface_version(u16 *major, u16 *minor) { return 0; } +static inline int tpm_crb_ffa_start(int request_type, int locality) { return 0; } +#endif + +#define CRB_FFA_START_TYPE_COMMAND 0 +#define CRB_FFA_START_TYPE_LOCALITY_REQUEST 1 + +#endif diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c index 139556b21cc6..53ba28ccd5d3 100644 --- a/drivers/char/tpm/tpm_ftpm_tee.c +++ b/drivers/char/tpm/tpm_ftpm_tee.c @@ -164,30 +164,10 @@ static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len) return 0; } -static void ftpm_tee_tpm_op_cancel(struct tpm_chip *chip) -{ - /* not supported */ -} - -static u8 ftpm_tee_tpm_op_status(struct tpm_chip *chip) -{ - return 0; -} - -static bool ftpm_tee_tpm_req_canceled(struct tpm_chip *chip, u8 status) -{ - return false; -} - static const struct tpm_class_ops ftpm_tee_tpm_ops = { .flags = TPM_OPS_AUTO_STARTUP, .recv = ftpm_tee_tpm_op_recv, .send = ftpm_tee_tpm_op_send, - .cancel = ftpm_tee_tpm_op_cancel, - .status = ftpm_tee_tpm_op_status, - .req_complete_mask = 0, - .req_complete_val = 0, - .req_canceled = ftpm_tee_tpm_req_canceled, }; /* @@ -362,7 +342,7 @@ MODULE_DEVICE_TABLE(of, of_ftpm_tee_ids); static struct platform_driver ftpm_tee_plat_driver = { .driver = { .name = "ftpm-tee", - .of_match_table = of_match_ptr(of_ftpm_tee_ids), + .of_match_table = of_ftpm_tee_ids, }, .shutdown = ftpm_plat_tee_shutdown, .probe = ftpm_plat_tee_probe, diff --git a/drivers/char/tpm/tpm_ftpm_tee.h b/drivers/char/tpm/tpm_ftpm_tee.h index f98daa7bf68c..e39903b7ea07 100644 --- a/drivers/char/tpm/tpm_ftpm_tee.h +++ b/drivers/char/tpm/tpm_ftpm_tee.h @@ -21,7 +21,6 @@ /** * struct ftpm_tee_private - fTPM's private data * @chip: struct tpm_chip instance registered with tpm framework. - * @state: internal state * @session: fTPM TA session identifier. * @resp_len: cached response buffer length. * @resp_buf: cached response buffer. diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index fdef214b9f6b..ed0d3d8449b3 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -114,11 +114,10 @@ again: return 0; /* process status changes without irq support */ do { + usleep_range(priv->timeout_min, priv->timeout_max); status = chip->ops->status(chip); if ((status & mask) == mask) return 0; - usleep_range(priv->timeout_min, - priv->timeout_max); } while (time_before(jiffies, stop)); return -ETIME; } @@ -464,7 +463,10 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len) if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, &priv->int_queue, false) < 0) { - rc = -ETIME; + if (test_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags)) + rc = -EAGAIN; + else + rc = -ETIME; goto out_err; } status = tpm_tis_status(chip); @@ -481,7 +483,10 @@ static int tpm_tis_send_data(struct tpm_chip *chip, const u8 *buf, size_t len) if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, &priv->int_queue, false) < 0) { - rc = -ETIME; + if (test_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags)) + rc = -EAGAIN; + else + rc = -ETIME; goto out_err; } status = tpm_tis_status(chip); @@ -546,9 +551,11 @@ static int tpm_tis_send_main(struct tpm_chip *chip, const u8 *buf, size_t len) if (rc >= 0) /* Data transfer done successfully */ break; - else if (rc != -EIO) + else if (rc != -EAGAIN && rc != -EIO) /* Data transfer failed, not recoverable */ return rc; + + usleep_range(priv->timeout_min, priv->timeout_max); } /* go and do it */ @@ -1144,6 +1151,9 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, priv->timeout_max = TIS_TIMEOUT_MAX_ATML; } + if (priv->manufacturer_id == TPM_VID_IFX) + set_bit(TPM_TIS_STATUS_VALID_RETRY, &priv->flags); + if (is_bsw()) { priv->ilb_base_addr = ioremap(INTEL_LEGACY_BLK_BASE_ADDR, ILB_REMAP_SIZE); diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index 690ad8e9b731..970d02c337c7 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -89,6 +89,7 @@ enum tpm_tis_flags { TPM_TIS_INVALID_STATUS = 1, TPM_TIS_DEFAULT_CANCELLATION = 2, TPM_TIS_IRQ_TESTED = 3, + TPM_TIS_STATUS_VALID_RETRY = 4, }; struct tpm_tis_data { |