xref: /openbsd/sys/arch/sparc64/dev/vrng.c (revision 73471bf0)
1 /*	$OpenBSD: vrng.c,v 1.7 2021/10/24 17:05:04 mpi Exp $	*/
2 /*
3  * Copyright (c) 2008 Mark Kettenis
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/device.h>
20 #include <sys/malloc.h>
21 #include <sys/systm.h>
22 #include <sys/timeout.h>
23 
24 #include <uvm/uvm_extern.h>
25 
26 #include <machine/autoconf.h>
27 #include <machine/hypervisor.h>
28 #include <machine/openfirm.h>
29 #include <machine/sparc64.h>
30 
31 #define HSVC_GROUP_RNG	0x104
32 
33 #include <sparc64/dev/vbusvar.h>
34 
35 struct rng_ctl {
36 	uint64_t rng_res : 39;
37 	uint64_t rng_wait_cnt : 16;
38 	uint64_t rng_bypass : 1;
39 	uint64_t rng_vcoctl_sel : 2;
40 	uint64_t rng_anlg_sel : 2;
41 	uint64_t rng_ctl4 : 1;
42 	uint64_t rng_ctl3 : 1;
43 	uint64_t rng_ctl2 : 1;
44 	uint64_t rng_ctl1 : 1;
45 };
46 
47 struct vrng_softc {
48 	struct device sc_dv;
49 	struct timeout sc_to;
50 	int sc_count;
51 };
52 
53 int	vrng_match(struct device *, void *, void *);
54 void	vrng_attach(struct device *, struct device *, void *);
55 
56 const struct cfattach vrng_ca = {
57 	sizeof(struct vrng_softc), vrng_match, vrng_attach
58 };
59 
60 struct cfdriver vrng_cd = {
61 	NULL, "vrng", DV_DULL
62 };
63 
64 void	vrng_rnd(void *);
65 
66 int
67 vrng_match(struct device *parent, void *match, void *aux)
68 {
69 	struct vbus_attach_args *va = aux;
70 
71 	if (strcmp(va->va_name, "random-number-generator") == 0)
72 		return (1);
73 
74 	return (0);
75 }
76 
77 void
78 vrng_attach(struct device *parent, struct device *self, void *aux)
79 {
80 	struct vrng_softc *sc = (void *)self;
81 	uint64_t supported_minor;
82 	struct rng_ctl ctl[4];
83 	uint64_t delta;
84 	paddr_t addr;
85 	int err;
86 
87 	if (prom_set_sun4v_api_version(HSVC_GROUP_RNG, 1, 0, &supported_minor))
88 		printf(": unsupported hypervisor\n");
89 
90 	err = hv_rng_get_diag_control();
91 	if (err != H_EOK && err != H_ENOACCESS)
92 		printf(": hv_rng_get_diag_control %d\n", err);
93 
94 	/*
95 	 * If we're not the Trusted Domain, the hypervisor call above
96 	 * will fails with H_ENOACCESS.  In that case we hope that the
97 	 * RNG has been properly initialized.
98 	 */
99 	if (err == H_EOK) {
100 		bzero(ctl, sizeof(ctl));
101 
102 		ctl[0].rng_ctl1 = 1;
103 		ctl[0].rng_vcoctl_sel = 0;
104 		ctl[0].rng_wait_cnt = 0x3e;
105 
106 		ctl[1].rng_ctl2 = 1;
107 		ctl[1].rng_vcoctl_sel = 1;
108 		ctl[1].rng_wait_cnt = 0x3e;
109 
110 		ctl[2].rng_ctl3 = 1;
111 		ctl[2].rng_vcoctl_sel = 2;
112 		ctl[2].rng_wait_cnt = 0x3e;
113 
114 		ctl[3].rng_ctl1 = 1;
115 		ctl[3].rng_ctl2 = 1;
116 		ctl[3].rng_ctl3 = 1;
117 		ctl[3].rng_ctl4 = 1;
118 		ctl[3].rng_wait_cnt = 0x3e;
119 
120 		if (!pmap_extract(pmap_kernel(), (vaddr_t)&ctl, &addr))
121 			panic("vrng_attach: pmap_extract failed");
122 
123 		err = hv_rng_ctl_write(addr, RNG_STATE_CONFIGURED, 0, &delta);
124 		if (err != H_EOK)
125 			printf(": hv_rng_ctl_write %d\n", err);
126 	}
127 
128 	printf("\n");
129 
130 	timeout_set(&sc->sc_to, vrng_rnd, sc);
131 	vrng_rnd(sc);
132 }
133 
134 void
135 vrng_rnd(void *v)
136 {
137 	struct vrng_softc *sc = v;
138 	uint64_t rnd;
139 	uint64_t delta;
140 	paddr_t addr;
141 	int err;
142 
143 	if (!pmap_extract(pmap_kernel(), (vaddr_t)&rnd, &addr))
144 		panic("vrng_rnd: pmap_extract failed");
145 	err = hv_rng_data_read(addr, &delta);
146 	if (err == H_EOK) {
147 #if 0
148 		if ((sc->sc_count++ % 100) == 0)
149 			printf("vrng: %lx\n", rnd);
150 #endif
151 		enqueue_randomness(rnd);
152 		enqueue_randomness(rnd >> 32);
153 	}
154 	if (err != H_EOK && err != H_EWOULDBLOCK)
155 		printf("vrng_rnd: err = %d\n", err);
156 	else
157 		timeout_add(&sc->sc_to, 1);
158 }
159