0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #include <linux/bits.h>
0016 #include <linux/device.h>
0017 #include <linux/hw_random.h>
0018 #include <linux/module.h>
0019 #include <linux/platform_device.h>
0020 #include <linux/arm-smccc.h>
0021
0022 #ifdef CONFIG_ARM64
0023 #define ARM_SMCCC_TRNG_RND ARM_SMCCC_TRNG_RND64
0024 #define MAX_BITS_PER_CALL (3 * 64UL)
0025 #else
0026 #define ARM_SMCCC_TRNG_RND ARM_SMCCC_TRNG_RND32
0027 #define MAX_BITS_PER_CALL (3 * 32UL)
0028 #endif
0029
0030
0031 #define SMCCC_TRNG_MAX_TRIES 20
0032
0033 #define SMCCC_RET_TRNG_INVALID_PARAMETER -2
0034 #define SMCCC_RET_TRNG_NO_ENTROPY -3
0035
0036 static int copy_from_registers(char *buf, struct arm_smccc_res *res,
0037 size_t bytes)
0038 {
0039 unsigned int chunk, copied;
0040
0041 if (bytes == 0)
0042 return 0;
0043
0044 chunk = min(bytes, sizeof(long));
0045 memcpy(buf, &res->a3, chunk);
0046 copied = chunk;
0047 if (copied >= bytes)
0048 return copied;
0049
0050 chunk = min((bytes - copied), sizeof(long));
0051 memcpy(&buf[copied], &res->a2, chunk);
0052 copied += chunk;
0053 if (copied >= bytes)
0054 return copied;
0055
0056 chunk = min((bytes - copied), sizeof(long));
0057 memcpy(&buf[copied], &res->a1, chunk);
0058
0059 return copied + chunk;
0060 }
0061
0062 static int smccc_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
0063 {
0064 struct arm_smccc_res res;
0065 u8 *buf = data;
0066 unsigned int copied = 0;
0067 int tries = 0;
0068
0069 while (copied < max) {
0070 size_t bits = min_t(size_t, (max - copied) * BITS_PER_BYTE,
0071 MAX_BITS_PER_CALL);
0072
0073 arm_smccc_1_1_invoke(ARM_SMCCC_TRNG_RND, bits, &res);
0074 if ((int)res.a0 < 0)
0075 return (int)res.a0;
0076
0077 switch ((int)res.a0) {
0078 case SMCCC_RET_SUCCESS:
0079 copied += copy_from_registers(buf + copied, &res,
0080 bits / BITS_PER_BYTE);
0081 tries = 0;
0082 break;
0083 case SMCCC_RET_TRNG_NO_ENTROPY:
0084 if (!wait)
0085 return copied;
0086 tries++;
0087 if (tries >= SMCCC_TRNG_MAX_TRIES)
0088 return copied;
0089 cond_resched();
0090 break;
0091 }
0092 }
0093
0094 return copied;
0095 }
0096
0097 static int smccc_trng_probe(struct platform_device *pdev)
0098 {
0099 struct hwrng *trng;
0100
0101 trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
0102 if (!trng)
0103 return -ENOMEM;
0104
0105 trng->name = "smccc_trng";
0106 trng->read = smccc_trng_read;
0107
0108 platform_set_drvdata(pdev, trng);
0109
0110 return devm_hwrng_register(&pdev->dev, trng);
0111 }
0112
0113 static struct platform_driver smccc_trng_driver = {
0114 .driver = {
0115 .name = "smccc_trng",
0116 },
0117 .probe = smccc_trng_probe,
0118 };
0119 module_platform_driver(smccc_trng_driver);
0120
0121 MODULE_ALIAS("platform:smccc_trng");
0122 MODULE_AUTHOR("Andre Przywara");
0123 MODULE_LICENSE("GPL");