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 | |
75 | switch ((int)res.a0) { |
76 | case SMCCC_RET_SUCCESS: |
77 | copied += copy_from_registers(buf: buf + copied, res: &res, |
78 | bytes: bits / BITS_PER_BYTE); |
79 | tries = 0; |
80 | break; |
81 | case SMCCC_RET_TRNG_NO_ENTROPY: |
82 | if (!wait) |
83 | return copied; |
84 | tries++; |
85 | if (tries >= SMCCC_TRNG_MAX_TRIES) |
86 | return copied; |
87 | cond_resched(); |
88 | break; |
89 | default: |
90 | return -EIO; |
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(dev: &pdev->dev, size: sizeof(*trng), GFP_KERNEL); |
102 | if (!trng) |
103 | return -ENOMEM; |
104 | |
105 | trng->name = "smccc_trng" ; |
106 | trng->read = smccc_trng_read; |
107 | |
108 | return devm_hwrng_register(dev: &pdev->dev, rng: trng); |
109 | } |
110 | |
111 | static struct platform_driver smccc_trng_driver = { |
112 | .driver = { |
113 | .name = "smccc_trng" , |
114 | }, |
115 | .probe = smccc_trng_probe, |
116 | }; |
117 | module_platform_driver(smccc_trng_driver); |
118 | |
119 | MODULE_ALIAS("platform:smccc_trng" ); |
120 | MODULE_AUTHOR("Andre Przywara" ); |
121 | MODULE_LICENSE("GPL" ); |
122 | |