xref: /openbsd-src/sys/arch/sparc64/dev/vrng.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: vrng.c,v 1.4 2010/08/07 03:50:01 krw 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 <dev/rndvar.h>
34 #include <sparc64/dev/vbusvar.h>
35 
36 struct rng_ctl {
37 	uint64_t rng_res : 39;
38 	uint64_t rng_wait_cnt : 16;
39 	uint64_t rng_bypass : 1;
40 	uint64_t rng_vcoctl_sel : 2;
41 	uint64_t rng_anlg_sel : 2;
42 	uint64_t rng_ctl4 : 1;
43 	uint64_t rng_ctl3 : 1;
44 	uint64_t rng_ctl2 : 1;
45 	uint64_t rng_ctl1 : 1;
46 };
47 
48 struct vrng_softc {
49 	struct device sc_dv;
50 	struct timeout sc_to;
51 	int sc_count;
52 };
53 
54 int	vrng_match(struct device *, void *, void *);
55 void	vrng_attach(struct device *, struct device *, void *);
56 
57 struct cfattach vrng_ca = {
58 	sizeof(struct vrng_softc), vrng_match, vrng_attach
59 };
60 
61 struct cfdriver vrng_cd = {
62 	NULL, "vrng", DV_DULL
63 };
64 
65 void	vrng_rnd(void *);
66 
67 int
68 vrng_match(struct device *parent, void *match, void *aux)
69 {
70 	struct vbus_attach_args *va = aux;
71 
72 	if (strcmp(va->va_name, "random-number-generator") == 0)
73 		return (1);
74 
75 	return (0);
76 }
77 
78 void
79 vrng_attach(struct device *parent, struct device *self, void *aux)
80 {
81 	struct vrng_softc *sc = (void *)self;
82 	uint64_t supported_minor;
83 	struct rng_ctl ctl[4];
84 	uint64_t delta;
85 	paddr_t addr;
86 	int err;
87 
88 	if (prom_set_sun4v_api_version(HSVC_GROUP_RNG, 1, 0, &supported_minor))
89 		printf(": unsupported hypervisor\n");
90 
91 	err = hv_rng_get_diag_control();
92 	if (err != H_EOK && err != H_ENOACCESS)
93 		printf(": hv_rng_get_diag_control %d\n", err);
94 
95 	/*
96 	 * If we're not the Trusted Domain, the hypervisor call above
97 	 * will fails with H_ENOACCESS.  In that case we hope that the
98 	 * RNG has been properly initialized.
99 	 */
100 	if (err == H_EOK) {
101 		bzero(ctl, sizeof(ctl));
102 
103 		ctl[0].rng_ctl1 = 1;
104 		ctl[0].rng_vcoctl_sel = 0;
105 		ctl[0].rng_wait_cnt = 0x3e;
106 
107 		ctl[1].rng_ctl2 = 1;
108 		ctl[1].rng_vcoctl_sel = 1;
109 		ctl[1].rng_wait_cnt = 0x3e;
110 
111 		ctl[2].rng_ctl3 = 1;
112 		ctl[2].rng_vcoctl_sel = 2;
113 		ctl[2].rng_wait_cnt = 0x3e;
114 
115 		ctl[3].rng_ctl1 = 1;
116 		ctl[3].rng_ctl2 = 1;
117 		ctl[3].rng_ctl3 = 1;
118 		ctl[3].rng_ctl4 = 1;
119 		ctl[3].rng_wait_cnt = 0x3e;
120 
121 		if (!pmap_extract(pmap_kernel(), (vaddr_t)&ctl, &addr))
122 			panic("vrng_attach: pmap_extract failed");
123 
124 		err = hv_rng_ctl_write(addr, RNG_STATE_CONFIGURED, 0, &delta);
125 		if (err != H_EOK)
126 			printf(": hv_rng_ctl_write %d\n", err);
127 	}
128 
129 	printf("\n");
130 
131 	timeout_set(&sc->sc_to, vrng_rnd, sc);
132 	vrng_rnd(sc);
133 }
134 
135 void
136 vrng_rnd(void *v)
137 {
138 	struct vrng_softc *sc = v;
139 	uint64_t rnd;
140 	uint64_t delta;
141 	paddr_t addr;
142 	int err;
143 
144 	if (!pmap_extract(pmap_kernel(), (vaddr_t)&rnd, &addr))
145 		panic("vrng_rnd: pmap_extract failed");
146 	err = hv_rng_data_read(addr, &delta);
147 	if (err == H_EOK) {
148 #if 0
149 		if ((sc->sc_count++ % 100) == 0)
150 			printf("vrng: %lx\n", rnd);
151 #endif
152 		add_true_randomness(rnd);
153 		add_true_randomness(rnd >> 32);
154 	}
155 	if (err != H_EOK && err != H_EWOULDBLOCK)
156 		printf("vrng_rnd: err = %d\n", err);
157 	else
158 		timeout_add(&sc->sc_to, 1);
159 }
160