aboutsummaryrefslogtreecommitdiff
path: root/drivers/rng/riscv_zkr_rng.c
blob: 48a5251988faee74ac1451b3be961c4434573122 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * The RISC-V Zkr extension provides CSR seed which provides access to a
 * random number generator.
 */

#define LOG_CATEGORY UCLASS_RNG

#include <dm.h>
#include <interrupt.h>
#include <log.h>
#include <rng.h>

#define DRIVER_NAME "riscv_zkr"

enum opst {
	/** @BIST: built in self test running */
	BIST = 0b00,
	/** @WAIT: sufficient amount of entropy is not yet available */
	WAIT = 0b01,
	/** @ES16: 16bits of entropy available */
	ES16 = 0b10,
	/** @DEAD: unrecoverable self-test error */
	DEAD = 0b11,
};

static unsigned long read_seed(void)
{
	unsigned long ret;

	__asm__ __volatile__("csrrw %0, seed, x0" : "=r" (ret) : : "memory");

	return ret;
}

static int riscv_zkr_read(struct udevice *dev, void *data, size_t len)
{
	u8 *ptr = data;

	while (len) {
		u32 val;

		val = read_seed();

		switch (val >> 30) {
		case BIST:
			continue;
		case WAIT:
			continue;
		case ES16:
			*ptr++ = val & 0xff;
			if (--len) {
				*ptr++ = val >> 8;
				--len;
			}
			break;
		case DEAD:
			return -ENOENT;
		}
	}

	return 0;
}

/**
 * riscv_zkr_bind() - check if the seed register is available
 *
 * If the SBI software has not set mseccfg.sseed=1 or the Zkr extension is not
 * available, reading the seed register will result in an exception from which
 * this function safely resumes.
 *
 * @dev:	RNG device
 * Return:	0 if successfully probed
 */
static int riscv_zkr_bind(struct udevice *dev)
{
	struct resume_data resume;
	int ret;
	u32 val;

	/* Check if reading seed leads to interrupt */
	set_resume(&resume);
	ret = setjmp(resume.jump);
	if (ret)
		log_debug("Exception %ld reading seed CSR\n", resume.code);
	else
		val = read_seed();
	set_resume(NULL);
	if (ret)
		return -ENOENT;

	return 0;
}

/**
 * riscv_zkr_probe() - check if entropy is available
 *
 * The bind method already checked that the seed register can be read without
 * excpetiong. Here we wait for the self test to finish and entropy becoming
 * available.
 *
 * @dev:	RNG device
 * Return:	0 if successfully probed
 */
static int riscv_zkr_probe(struct udevice *dev)
{
	u32 val;

	do {
		val = read_seed();
		val >>= 30;
	} while (val == BIST || val == WAIT);

	if (val == DEAD)
		return -ENOENT;

	return 0;
}

static const struct dm_rng_ops riscv_zkr_ops = {
	.read = riscv_zkr_read,
};

U_BOOT_DRIVER(riscv_zkr) = {
	.name = DRIVER_NAME,
	.id = UCLASS_RNG,
	.ops = &riscv_zkr_ops,
	.bind = riscv_zkr_bind,
	.probe = riscv_zkr_probe,
};

U_BOOT_DRVINFO(cpu_riscv_zkr) = {
	.name = DRIVER_NAME,
};