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
vrng_match(struct device * parent,void * match,void * aux)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
vrng_attach(struct device * parent,struct device * self,void * aux)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
vrng_rnd(void * v)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