// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2022 MediaTek Inc. */ #include #include #include #include #include #include #define SMC_RET_NUM 4 #define SEC_RND_SIZE (sizeof(u32) * SMC_RET_NUM) #define MT67XX_RNG_MAGIC 0x74726e67 #define HWRNG_SMC_FAST_CALL_VAL(func_num) \ ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_32, \ ARM_SMCCC_OWNER_SIP, (func_num)) #define to_sec_rng(p) container_of(p, struct sec_rng_priv, rng) #define sec_rng_log(p, s, fmt, args...) \ (p += scnprintf(p, sizeof(s) - strlen(s), fmt, ##args)) struct sec_rng_priv { const char *method; uint16_t func_num; struct hwrng rng; }; static struct sec_rng_priv *priv; static bool __sec_get_rnd(struct sec_rng_priv *priv, uint32_t *val) { struct arm_smccc_res res; if (!strncmp("smc", priv->method, strlen("smc"))) arm_smccc_smc(HWRNG_SMC_FAST_CALL_VAL(priv->func_num), MT67XX_RNG_MAGIC, 0, 0, 0, 0, 0, 0, &res); else if (!strncmp("hvc", priv->method, strlen("hvc"))) arm_smccc_hvc(HWRNG_SMC_FAST_CALL_VAL(priv->func_num), MT67XX_RNG_MAGIC, 0, 0, 0, 0, 0, 0, &res); else return false; if (!res.a0 && !res.a1 && !res.a2 && !res.a3) return false; val[0] = res.a0; val[1] = res.a1; val[2] = res.a2; val[3] = res.a3; return true; } static int sec_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) { struct sec_rng_priv *rng_priv = to_sec_rng(rng); u32 val[4] = {0}; int retval = 0; int i; while (max >= SEC_RND_SIZE) { if (!__sec_get_rnd(rng_priv, val)) return retval; for (i = 0; i < SMC_RET_NUM; i++) { *(u32 *)buf = val[i]; buf += sizeof(u32); } retval += SEC_RND_SIZE; max -= SEC_RND_SIZE; } return retval; } ssize_t sec_rng_dbg_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct sec_rng_priv *rng_priv = priv; u32 val[4] = {0}; int i; int len; char buf[1024] = {0}; char *p = buf; if (!__sec_get_rnd(rng_priv, val)) { pr_info("failed to get rnd\n"); return -EFAULT; } for (i = 0; i < 4; i++) sec_rng_log(p, buf, "rnd[%d]: 0x%x\n", i, val[i]); len = p - buf; return simple_read_from_buffer(buffer, count, ppos, buf, len); } static const struct proc_ops sec_rng_dbg_fops = { .proc_read = sec_rng_dbg_read, }; static int sec_rng_probe(struct platform_device *pdev) { int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; if (of_property_read_string(pdev->dev.of_node, "methods", &priv->method)) return -ENXIO; if (of_property_read_u16(pdev->dev.of_node, "method-fid", &priv->func_num)) return -ENXIO; if (of_property_read_u16(pdev->dev.of_node, "quality", &priv->rng.quality)) return -ENXIO; priv->rng.name = pdev->name; priv->rng.read = sec_rng_read; priv->rng.priv = (unsigned long)&pdev->dev; ret = devm_hwrng_register(&pdev->dev, &priv->rng); if (ret) { dev_err(&pdev->dev, "failed to register rng device: %d\n", ret); return ret; } proc_create("sec_rng_dbg", 0660, NULL, &sec_rng_dbg_fops); return 0; } static const struct of_device_id sec_rng_match[] = { { .compatible = "arm,sec-rng", }, {} }; MODULE_DEVICE_TABLE(of, sec_rng_match); static struct platform_driver sec_rng_driver = { .probe = sec_rng_probe, .driver = { .name = KBUILD_MODNAME, .owner = THIS_MODULE, .of_match_table = sec_rng_match, }, }; module_platform_driver(sec_rng_driver); MODULE_DESCRIPTION("Security Random Number Generator Driver"); MODULE_AUTHOR("Jackson Chang "); MODULE_LICENSE("GPL");