Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Randomness driver for the ARM SMCCC TRNG Firmware Interface
0004  * https://developer.arm.com/documentation/den0098/latest/
0005  *
0006  *  Copyright (C) 2020 Arm Ltd.
0007  *
0008  * The ARM TRNG firmware interface specifies a protocol to read entropy
0009  * from a higher exception level, to abstract from any machine specific
0010  * implemenations and allow easier use in hypervisors.
0011  *
0012  * The firmware interface is realised using the SMCCC specification.
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 /* We don't want to allow the firmware to stall us forever. */
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");