Skip to content

Commit 0888d04

Browse files
Andre-ARMherbertx
authored andcommitted
hwrng: Add Arm SMCCC TRNG based driver
The "Arm True Random Number Generator Firmware Interface"[1] provides an SMCCC based interface to a true hardware random number generator. So far we are using that in arch_get_random_seed(), but it might be useful to expose the entropy through the /dev/hwrng device as well. This allows to assess the quality of the implementation, by using "rngtest" from the rng-tools package, for example. Add a simple platform driver implementing the hw_random interface. The corresponding platform device is created by the SMCCC core code, we just match it here by name and provide a module alias. Since the firmware takes care about serialisation, this can happily coexist with the arch_get_random_seed() bits. [1] https://developer.arm.com/documentation/den0098/latest/ Signed-off-by: Andre Przywara <[email protected]> Reviewed-by: Ard Biesheuvel <[email protected]> Reviewed-by: Mark Brown <[email protected]> Signed-off-by: Herbert Xu <[email protected]>
1 parent b83c2d9 commit 0888d04

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-0
lines changed

drivers/char/hw_random/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,20 @@ config HW_RANDOM_XIPHERA
524524
To compile this driver as a module, choose M here: the
525525
module will be called xiphera-trng.
526526

527+
config HW_RANDOM_ARM_SMCCC_TRNG
528+
tristate "Arm SMCCC TRNG firmware interface support"
529+
depends on HAVE_ARM_SMCCC_DISCOVERY
530+
default HW_RANDOM
531+
help
532+
Say 'Y' to enable the True Random Number Generator driver using
533+
the Arm SMCCC TRNG firmware interface. This reads entropy from
534+
higher exception levels (firmware, hypervisor). Uses SMCCC for
535+
communicating with the firmware:
536+
https://developer.arm.com/documentation/den0098/latest/
537+
538+
To compile this driver as a module, choose M here: the
539+
module will be called arm_smccc_trng.
540+
527541
endif # HW_RANDOM
528542

529543
config UML_RANDOM

drivers/char/hw_random/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,4 @@ obj-$(CONFIG_HW_RANDOM_OPTEE) += optee-rng.o
4545
obj-$(CONFIG_HW_RANDOM_NPCM) += npcm-rng.o
4646
obj-$(CONFIG_HW_RANDOM_CCTRNG) += cctrng.o
4747
obj-$(CONFIG_HW_RANDOM_XIPHERA) += xiphera-trng.o
48+
obj-$(CONFIG_HW_RANDOM_ARM_SMCCC_TRNG) += arm_smccc_trng.o
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Randomness driver for the ARM SMCCC TRNG Firmware Interface
4+
* https://developer.arm.com/documentation/den0098/latest/
5+
*
6+
* Copyright (C) 2020 Arm Ltd.
7+
*
8+
* The ARM TRNG firmware interface specifies a protocol to read entropy
9+
* from a higher exception level, to abstract from any machine specific
10+
* implemenations and allow easier use in hypervisors.
11+
*
12+
* The firmware interface is realised using the SMCCC specification.
13+
*/
14+
15+
#include <linux/bits.h>
16+
#include <linux/device.h>
17+
#include <linux/hw_random.h>
18+
#include <linux/module.h>
19+
#include <linux/platform_device.h>
20+
#include <linux/arm-smccc.h>
21+
22+
#ifdef CONFIG_ARM64
23+
#define ARM_SMCCC_TRNG_RND ARM_SMCCC_TRNG_RND64
24+
#define MAX_BITS_PER_CALL (3 * 64UL)
25+
#else
26+
#define ARM_SMCCC_TRNG_RND ARM_SMCCC_TRNG_RND32
27+
#define MAX_BITS_PER_CALL (3 * 32UL)
28+
#endif
29+
30+
/* We don't want to allow the firmware to stall us forever. */
31+
#define SMCCC_TRNG_MAX_TRIES 20
32+
33+
#define SMCCC_RET_TRNG_INVALID_PARAMETER -2
34+
#define SMCCC_RET_TRNG_NO_ENTROPY -3
35+
36+
static int copy_from_registers(char *buf, struct arm_smccc_res *res,
37+
size_t bytes)
38+
{
39+
unsigned int chunk, copied;
40+
41+
if (bytes == 0)
42+
return 0;
43+
44+
chunk = min(bytes, sizeof(long));
45+
memcpy(buf, &res->a3, chunk);
46+
copied = chunk;
47+
if (copied >= bytes)
48+
return copied;
49+
50+
chunk = min((bytes - copied), sizeof(long));
51+
memcpy(&buf[copied], &res->a2, chunk);
52+
copied += chunk;
53+
if (copied >= bytes)
54+
return copied;
55+
56+
chunk = min((bytes - copied), sizeof(long));
57+
memcpy(&buf[copied], &res->a1, chunk);
58+
59+
return copied + chunk;
60+
}
61+
62+
static int smccc_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
63+
{
64+
struct arm_smccc_res res;
65+
u8 *buf = data;
66+
unsigned int copied = 0;
67+
int tries = 0;
68+
69+
while (copied < max) {
70+
size_t bits = min_t(size_t, (max - copied) * BITS_PER_BYTE,
71+
MAX_BITS_PER_CALL);
72+
73+
arm_smccc_1_1_invoke(ARM_SMCCC_TRNG_RND, bits, &res);
74+
if ((int)res.a0 < 0)
75+
return (int)res.a0;
76+
77+
switch ((int)res.a0) {
78+
case SMCCC_RET_SUCCESS:
79+
copied += copy_from_registers(buf + copied, &res,
80+
bits / BITS_PER_BYTE);
81+
tries = 0;
82+
break;
83+
case SMCCC_RET_TRNG_NO_ENTROPY:
84+
if (!wait)
85+
return copied;
86+
tries++;
87+
if (tries >= SMCCC_TRNG_MAX_TRIES)
88+
return copied;
89+
cond_resched();
90+
break;
91+
}
92+
}
93+
94+
return copied;
95+
}
96+
97+
static int smccc_trng_probe(struct platform_device *pdev)
98+
{
99+
struct hwrng *trng;
100+
101+
trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
102+
if (!trng)
103+
return -ENOMEM;
104+
105+
trng->name = "smccc_trng";
106+
trng->read = smccc_trng_read;
107+
108+
platform_set_drvdata(pdev, trng);
109+
110+
return devm_hwrng_register(&pdev->dev, trng);
111+
}
112+
113+
static struct platform_driver smccc_trng_driver = {
114+
.driver = {
115+
.name = "smccc_trng",
116+
},
117+
.probe = smccc_trng_probe,
118+
};
119+
module_platform_driver(smccc_trng_driver);
120+
121+
MODULE_ALIAS("platform:smccc_trng");
122+
MODULE_AUTHOR("Andre Przywara");
123+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)