Skip to content

Commit 10bb6ac

Browse files
Sam Protsenkoherbertx
authored andcommitted
hwrng: exynos - Add SMC based TRNG operation
On some Exynos chips like Exynos850 the access to Security Sub System (SSS) registers is protected with TrustZone, and therefore only possible from EL3 monitor software. The Linux kernel is running in EL1, so the only way for the driver to obtain TRNG data is via SMC calls to EL3 monitor. Implement such SMC operation and use it when EXYNOS_SMC flag is set in the corresponding chip driver data. Signed-off-by: Sam Protsenko <[email protected]> Reviewed-by: Krzysztof Kozlowski <[email protected]> Signed-off-by: Herbert Xu <[email protected]>
1 parent e003d67 commit 10bb6ac

File tree

1 file changed

+130
-10
lines changed

1 file changed

+130
-10
lines changed

drivers/char/hw_random/exynos-trng.c

Lines changed: 130 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
* Krzysztof Kozłowski <[email protected]>
1111
*/
1212

13+
#include <linux/arm-smccc.h>
1314
#include <linux/clk.h>
1415
#include <linux/crypto.h>
1516
#include <linux/delay.h>
@@ -22,6 +23,7 @@
2223
#include <linux/mod_devicetable.h>
2324
#include <linux/platform_device.h>
2425
#include <linux/pm_runtime.h>
26+
#include <linux/property.h>
2527

2628
#define EXYNOS_TRNG_CLKDIV 0x0
2729

@@ -44,16 +46,41 @@
4446
#define EXYNOS_TRNG_FIFO_LEN 8
4547
#define EXYNOS_TRNG_CLOCK_RATE 500000
4648

49+
/* Driver feature flags */
50+
#define EXYNOS_SMC BIT(0)
51+
52+
#define EXYNOS_SMC_CALL_VAL(func_num) \
53+
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
54+
ARM_SMCCC_SMC_32, \
55+
ARM_SMCCC_OWNER_SIP, \
56+
func_num)
57+
58+
/* SMC command for DTRNG access */
59+
#define SMC_CMD_RANDOM EXYNOS_SMC_CALL_VAL(0x1012)
60+
61+
/* SMC_CMD_RANDOM: arguments */
62+
#define HWRNG_INIT 0x0
63+
#define HWRNG_EXIT 0x1
64+
#define HWRNG_GET_DATA 0x2
65+
#define HWRNG_RESUME 0x3
66+
67+
/* SMC_CMD_RANDOM: return values */
68+
#define HWRNG_RET_OK 0x0
69+
#define HWRNG_RET_RETRY_ERROR 0x2
70+
71+
#define HWRNG_MAX_TRIES 100
72+
4773
struct exynos_trng_dev {
4874
struct device *dev;
4975
void __iomem *mem;
5076
struct clk *clk; /* operating clock */
5177
struct clk *pclk; /* bus clock */
5278
struct hwrng rng;
79+
unsigned long flags;
5380
};
5481

55-
static int exynos_trng_do_read(struct hwrng *rng, void *data, size_t max,
56-
bool wait)
82+
static int exynos_trng_do_read_reg(struct hwrng *rng, void *data, size_t max,
83+
bool wait)
5784
{
5885
struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
5986
int val;
@@ -70,7 +97,40 @@ static int exynos_trng_do_read(struct hwrng *rng, void *data, size_t max,
7097
return max;
7198
}
7299

73-
static int exynos_trng_init(struct hwrng *rng)
100+
static int exynos_trng_do_read_smc(struct hwrng *rng, void *data, size_t max,
101+
bool wait)
102+
{
103+
struct arm_smccc_res res;
104+
unsigned int copied = 0;
105+
u32 *buf = data;
106+
int tries = 0;
107+
108+
while (copied < max) {
109+
arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_GET_DATA, 0, 0, 0, 0, 0, 0,
110+
&res);
111+
switch (res.a0) {
112+
case HWRNG_RET_OK:
113+
*buf++ = res.a2;
114+
*buf++ = res.a3;
115+
copied += 8;
116+
tries = 0;
117+
break;
118+
case HWRNG_RET_RETRY_ERROR:
119+
if (!wait)
120+
return copied;
121+
if (++tries >= HWRNG_MAX_TRIES)
122+
return copied;
123+
cond_resched();
124+
break;
125+
default:
126+
return -EIO;
127+
}
128+
}
129+
130+
return copied;
131+
}
132+
133+
static int exynos_trng_init_reg(struct hwrng *rng)
74134
{
75135
struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
76136
unsigned long sss_rate;
@@ -103,6 +163,24 @@ static int exynos_trng_init(struct hwrng *rng)
103163
return 0;
104164
}
105165

166+
static int exynos_trng_init_smc(struct hwrng *rng)
167+
{
168+
struct exynos_trng_dev *trng = (struct exynos_trng_dev *)rng->priv;
169+
struct arm_smccc_res res;
170+
int ret = 0;
171+
172+
arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_INIT, 0, 0, 0, 0, 0, 0, &res);
173+
if (res.a0 != HWRNG_RET_OK) {
174+
dev_err(trng->dev, "SMC command for TRNG init failed (%d)\n",
175+
(int)res.a0);
176+
ret = -EIO;
177+
}
178+
if ((int)res.a0 == -1)
179+
dev_info(trng->dev, "Make sure LDFW is loaded by your BL\n");
180+
181+
return ret;
182+
}
183+
106184
static int exynos_trng_probe(struct platform_device *pdev)
107185
{
108186
struct exynos_trng_dev *trng;
@@ -112,21 +190,29 @@ static int exynos_trng_probe(struct platform_device *pdev)
112190
if (!trng)
113191
return ret;
114192

193+
platform_set_drvdata(pdev, trng);
194+
trng->dev = &pdev->dev;
195+
196+
trng->flags = (unsigned long)device_get_match_data(&pdev->dev);
197+
115198
trng->rng.name = devm_kstrdup(&pdev->dev, dev_name(&pdev->dev),
116199
GFP_KERNEL);
117200
if (!trng->rng.name)
118201
return ret;
119202

120-
trng->rng.init = exynos_trng_init;
121-
trng->rng.read = exynos_trng_do_read;
122203
trng->rng.priv = (unsigned long)trng;
123204

124-
platform_set_drvdata(pdev, trng);
125-
trng->dev = &pdev->dev;
205+
if (trng->flags & EXYNOS_SMC) {
206+
trng->rng.init = exynos_trng_init_smc;
207+
trng->rng.read = exynos_trng_do_read_smc;
208+
} else {
209+
trng->rng.init = exynos_trng_init_reg;
210+
trng->rng.read = exynos_trng_do_read_reg;
126211

127-
trng->mem = devm_platform_ioremap_resource(pdev, 0);
128-
if (IS_ERR(trng->mem))
129-
return PTR_ERR(trng->mem);
212+
trng->mem = devm_platform_ioremap_resource(pdev, 0);
213+
if (IS_ERR(trng->mem))
214+
return PTR_ERR(trng->mem);
215+
}
130216

131217
pm_runtime_enable(&pdev->dev);
132218
ret = pm_runtime_resume_and_get(&pdev->dev);
@@ -170,19 +256,39 @@ static int exynos_trng_probe(struct platform_device *pdev)
170256

171257
static void exynos_trng_remove(struct platform_device *pdev)
172258
{
259+
struct exynos_trng_dev *trng = platform_get_drvdata(pdev);
260+
261+
if (trng->flags & EXYNOS_SMC) {
262+
struct arm_smccc_res res;
263+
264+
arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_EXIT, 0, 0, 0, 0, 0, 0,
265+
&res);
266+
}
267+
173268
pm_runtime_put_sync(&pdev->dev);
174269
pm_runtime_disable(&pdev->dev);
175270
}
176271

177272
static int exynos_trng_suspend(struct device *dev)
178273
{
274+
struct exynos_trng_dev *trng = dev_get_drvdata(dev);
275+
struct arm_smccc_res res;
276+
277+
if (trng->flags & EXYNOS_SMC) {
278+
arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_EXIT, 0, 0, 0, 0, 0, 0,
279+
&res);
280+
if (res.a0 != HWRNG_RET_OK)
281+
return -EIO;
282+
}
283+
179284
pm_runtime_put_sync(dev);
180285

181286
return 0;
182287
}
183288

184289
static int exynos_trng_resume(struct device *dev)
185290
{
291+
struct exynos_trng_dev *trng = dev_get_drvdata(dev);
186292
int ret;
187293

188294
ret = pm_runtime_resume_and_get(dev);
@@ -191,6 +297,20 @@ static int exynos_trng_resume(struct device *dev)
191297
return ret;
192298
}
193299

300+
if (trng->flags & EXYNOS_SMC) {
301+
struct arm_smccc_res res;
302+
303+
arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_RESUME, 0, 0, 0, 0, 0, 0,
304+
&res);
305+
if (res.a0 != HWRNG_RET_OK)
306+
return -EIO;
307+
308+
arm_smccc_smc(SMC_CMD_RANDOM, HWRNG_INIT, 0, 0, 0, 0, 0, 0,
309+
&res);
310+
if (res.a0 != HWRNG_RET_OK)
311+
return -EIO;
312+
}
313+
194314
return 0;
195315
}
196316

0 commit comments

Comments
 (0)