xref: /illumos-gate/usr/src/uts/common/crypto/io/viorand.c (revision b91bbe38)
1*b91bbe38SToomas Soome /*
2*b91bbe38SToomas Soome  * This file and its contents are supplied under the terms of the
3*b91bbe38SToomas Soome  * Common Development and Distribution License ("CDDL"), version 1.0.
4*b91bbe38SToomas Soome  * You may only use this file in accordance with the terms of version
5*b91bbe38SToomas Soome  * 1.0 of the CDDL.
6*b91bbe38SToomas Soome  *
7*b91bbe38SToomas Soome  * A full copy of the text of the CDDL should have accompanied this
8*b91bbe38SToomas Soome  * source.  A copy of the CDDL is also available via the Internet at
9*b91bbe38SToomas Soome  * http://www.illumos.org/license/CDDL.
10*b91bbe38SToomas Soome  */
11*b91bbe38SToomas Soome 
12*b91bbe38SToomas Soome /*
13*b91bbe38SToomas Soome  * Copyright 2023 Toomas Soome <tsoome@me.com>
14*b91bbe38SToomas Soome  */
15*b91bbe38SToomas Soome 
16*b91bbe38SToomas Soome /*
17*b91bbe38SToomas Soome  * Virtio random data device.
18*b91bbe38SToomas Soome  */
19*b91bbe38SToomas Soome 
20*b91bbe38SToomas Soome #include <sys/types.h>
21*b91bbe38SToomas Soome #include <sys/modctl.h>
22*b91bbe38SToomas Soome #include <sys/conf.h>
23*b91bbe38SToomas Soome #include <sys/sunddi.h>
24*b91bbe38SToomas Soome #include <sys/sysmacros.h>
25*b91bbe38SToomas Soome #include <sys/crypto/spi.h>
26*b91bbe38SToomas Soome #include <sys/time.h>
27*b91bbe38SToomas Soome #include <virtio.h>
28*b91bbe38SToomas Soome 
29*b91bbe38SToomas Soome #define	VIORAND_FEATURES	0
30*b91bbe38SToomas Soome #define	VIORAND_RQ		0
31*b91bbe38SToomas Soome 
32*b91bbe38SToomas Soome typedef struct viorand_state viorand_state_t;
33*b91bbe38SToomas Soome 
34*b91bbe38SToomas Soome typedef struct viorand_rdbuf {
35*b91bbe38SToomas Soome 	viorand_state_t		*rb_viornd;
36*b91bbe38SToomas Soome 	virtio_dma_t		*rb_dma;
37*b91bbe38SToomas Soome 	virtio_chain_t		*rb_chain;
38*b91bbe38SToomas Soome 	size_t			rb_recv_len;
39*b91bbe38SToomas Soome 	uchar_t			*rb_req_buf;
40*b91bbe38SToomas Soome 	size_t			rb_req_len;
41*b91bbe38SToomas Soome 	crypto_req_handle_t	rb_req_handle;
42*b91bbe38SToomas Soome 	list_node_t		rb_link;
43*b91bbe38SToomas Soome } viorand_rdbuf_t;
44*b91bbe38SToomas Soome 
45*b91bbe38SToomas Soome struct viorand_state {
46*b91bbe38SToomas Soome 	dev_info_t	*vio_dip;
47*b91bbe38SToomas Soome 	kmutex_t	vio_mutex;
48*b91bbe38SToomas Soome 	kcondvar_t	vio_cv;
49*b91bbe38SToomas Soome 	crypto_kcf_provider_handle_t vio_handle;
50*b91bbe38SToomas Soome 	taskq_t		*vio_taskq;
51*b91bbe38SToomas Soome 	virtio_t	*vio_virtio;
52*b91bbe38SToomas Soome 	virtio_queue_t	*vio_rq;
53*b91bbe38SToomas Soome 	uint64_t	vio_features;
54*b91bbe38SToomas Soome 
55*b91bbe38SToomas Soome 	uint_t		vio_rdbufs_capacity;
56*b91bbe38SToomas Soome 	uint_t		vio_rdbufs_alloc;
57*b91bbe38SToomas Soome 	list_t		vio_rdbufs_free;	/* Free list */
58*b91bbe38SToomas Soome 	viorand_rdbuf_t	*vio_rdbuf_mem;
59*b91bbe38SToomas Soome };
60*b91bbe38SToomas Soome 
61*b91bbe38SToomas Soome static const ddi_dma_attr_t viorand_dma_attr = {
62*b91bbe38SToomas Soome 	.dma_attr_version =	DMA_ATTR_V0,
63*b91bbe38SToomas Soome 	.dma_attr_addr_lo =	0x0000000000000000,
64*b91bbe38SToomas Soome 	.dma_attr_addr_hi =	0xFFFFFFFFFFFFFFFF,
65*b91bbe38SToomas Soome 	.dma_attr_count_max =	0x00000000FFFFFFFF,
66*b91bbe38SToomas Soome 	.dma_attr_align =	1,
67*b91bbe38SToomas Soome 	.dma_attr_burstsizes =	1,
68*b91bbe38SToomas Soome 	.dma_attr_minxfer =	1,
69*b91bbe38SToomas Soome 	.dma_attr_maxxfer =	0x00000000FFFFFFFF,
70*b91bbe38SToomas Soome 	.dma_attr_seg =		0x00000000FFFFFFFF,
71*b91bbe38SToomas Soome 	.dma_attr_sgllen =	64,
72*b91bbe38SToomas Soome 	.dma_attr_granular =	1,
73*b91bbe38SToomas Soome 	.dma_attr_flags =	0
74*b91bbe38SToomas Soome };
75*b91bbe38SToomas Soome 
76*b91bbe38SToomas Soome /* If set, we do not allow to detach ourselves. */
77*b91bbe38SToomas Soome boolean_t virtio_registered = B_TRUE;
78*b91bbe38SToomas Soome static void *viorand_statep;
79*b91bbe38SToomas Soome 
80*b91bbe38SToomas Soome static int viorand_attach(dev_info_t *, ddi_attach_cmd_t);
81*b91bbe38SToomas Soome static int viorand_detach(dev_info_t *, ddi_detach_cmd_t);
82*b91bbe38SToomas Soome static int viorand_quiesce(dev_info_t *);
83*b91bbe38SToomas Soome 
84*b91bbe38SToomas Soome static void viorand_provider_status(crypto_provider_handle_t, uint_t *);
85*b91bbe38SToomas Soome static int viorand_generate_random(crypto_provider_handle_t,
86*b91bbe38SToomas Soome     crypto_session_id_t, uchar_t *, size_t, crypto_req_handle_t);
87*b91bbe38SToomas Soome 
88*b91bbe38SToomas Soome static uint_t viorand_interrupt(caddr_t, caddr_t);
89*b91bbe38SToomas Soome 
90*b91bbe38SToomas Soome /*
91*b91bbe38SToomas Soome  * Module linkage information for the kernel.
92*b91bbe38SToomas Soome  */
93*b91bbe38SToomas Soome 
94*b91bbe38SToomas Soome static struct dev_ops devops = {
95*b91bbe38SToomas Soome 	.devo_rev = DEVO_REV,
96*b91bbe38SToomas Soome 	.devo_refcnt = 0,
97*b91bbe38SToomas Soome 	.devo_getinfo = ddi_no_info,
98*b91bbe38SToomas Soome 	.devo_identify = nulldev,
99*b91bbe38SToomas Soome 	.devo_probe = nulldev,
100*b91bbe38SToomas Soome 	.devo_attach = viorand_attach,
101*b91bbe38SToomas Soome 	.devo_detach = viorand_detach,
102*b91bbe38SToomas Soome 	.devo_reset = nodev,
103*b91bbe38SToomas Soome 	.devo_cb_ops = NULL,
104*b91bbe38SToomas Soome 	.devo_bus_ops = NULL,
105*b91bbe38SToomas Soome 	.devo_power = NULL,
106*b91bbe38SToomas Soome 	.devo_quiesce = viorand_quiesce
107*b91bbe38SToomas Soome };
108*b91bbe38SToomas Soome 
109*b91bbe38SToomas Soome static struct modldrv modldrv = {
110*b91bbe38SToomas Soome 	.drv_modops = &mod_driverops,
111*b91bbe38SToomas Soome 	.drv_linkinfo = "VirtIO Random Number Driver",
112*b91bbe38SToomas Soome 	.drv_dev_ops = &devops
113*b91bbe38SToomas Soome };
114*b91bbe38SToomas Soome 
115*b91bbe38SToomas Soome static struct modlcrypto modlcrypto = {
116*b91bbe38SToomas Soome 	.crypto_modops = &mod_cryptoops,
117*b91bbe38SToomas Soome 	.crypto_linkinfo = "VirtIO Random Number Provider"
118*b91bbe38SToomas Soome };
119*b91bbe38SToomas Soome 
120*b91bbe38SToomas Soome static struct modlinkage modlinkage = {
121*b91bbe38SToomas Soome 	.ml_rev = MODREV_1,
122*b91bbe38SToomas Soome 	.ml_linkage = { &modldrv, &modlcrypto, NULL }
123*b91bbe38SToomas Soome };
124*b91bbe38SToomas Soome 
125*b91bbe38SToomas Soome /*
126*b91bbe38SToomas Soome  * CSPI information (entry points, provider info, etc.)
127*b91bbe38SToomas Soome  */
128*b91bbe38SToomas Soome static void viorand_provider_status(crypto_provider_handle_t, uint_t *);
129*b91bbe38SToomas Soome 
130*b91bbe38SToomas Soome static crypto_control_ops_t viorand_control_ops = {
131*b91bbe38SToomas Soome 	.provider_status = viorand_provider_status
132*b91bbe38SToomas Soome };
133*b91bbe38SToomas Soome 
134*b91bbe38SToomas Soome static int viorand_generate_random(crypto_provider_handle_t,
135*b91bbe38SToomas Soome     crypto_session_id_t, uchar_t *, size_t, crypto_req_handle_t);
136*b91bbe38SToomas Soome 
137*b91bbe38SToomas Soome static crypto_random_number_ops_t viorand_random_number_ops = {
138*b91bbe38SToomas Soome 	.generate_random = viorand_generate_random
139*b91bbe38SToomas Soome };
140*b91bbe38SToomas Soome 
141*b91bbe38SToomas Soome static crypto_ops_t viorand_crypto_ops = {
142*b91bbe38SToomas Soome 	.co_control_ops = &viorand_control_ops,
143*b91bbe38SToomas Soome 	.co_random_ops = &viorand_random_number_ops
144*b91bbe38SToomas Soome };
145*b91bbe38SToomas Soome 
146*b91bbe38SToomas Soome static crypto_provider_info_t viorand_prov_info = {
147*b91bbe38SToomas Soome 	.pi_interface_version = CRYPTO_SPI_VERSION_1,
148*b91bbe38SToomas Soome 	.pi_provider_description = "VirtIO Random Number Provider",
149*b91bbe38SToomas Soome 	.pi_provider_type = CRYPTO_HW_PROVIDER,
150*b91bbe38SToomas Soome 	.pi_ops_vector = &viorand_crypto_ops,
151*b91bbe38SToomas Soome };
152*b91bbe38SToomas Soome 
153*b91bbe38SToomas Soome /*
154*b91bbe38SToomas Soome  * DDI entry points.
155*b91bbe38SToomas Soome  */
156*b91bbe38SToomas Soome int
_init(void)157*b91bbe38SToomas Soome _init(void)
158*b91bbe38SToomas Soome {
159*b91bbe38SToomas Soome 	int error;
160*b91bbe38SToomas Soome 
161*b91bbe38SToomas Soome 	error = ddi_soft_state_init(&viorand_statep,
162*b91bbe38SToomas Soome 	    sizeof (viorand_state_t), 0);
163*b91bbe38SToomas Soome 	if (error != 0)
164*b91bbe38SToomas Soome 		return (error);
165*b91bbe38SToomas Soome 
166*b91bbe38SToomas Soome 	return (mod_install(&modlinkage));
167*b91bbe38SToomas Soome }
168*b91bbe38SToomas Soome 
169*b91bbe38SToomas Soome int
_fini(void)170*b91bbe38SToomas Soome _fini(void)
171*b91bbe38SToomas Soome {
172*b91bbe38SToomas Soome 	int error;
173*b91bbe38SToomas Soome 
174*b91bbe38SToomas Soome 	error = mod_remove(&modlinkage);
175*b91bbe38SToomas Soome 	if (error == 0)
176*b91bbe38SToomas Soome 		ddi_soft_state_fini(&viorand_statep);
177*b91bbe38SToomas Soome 
178*b91bbe38SToomas Soome 	return (error);
179*b91bbe38SToomas Soome }
180*b91bbe38SToomas Soome 
181*b91bbe38SToomas Soome int
_info(struct modinfo * modinfop)182*b91bbe38SToomas Soome _info(struct modinfo *modinfop)
183*b91bbe38SToomas Soome {
184*b91bbe38SToomas Soome 	return (mod_info(&modlinkage, modinfop));
185*b91bbe38SToomas Soome }
186*b91bbe38SToomas Soome 
187*b91bbe38SToomas Soome /*
188*b91bbe38SToomas Soome  * return buffer from free list.
189*b91bbe38SToomas Soome  */
190*b91bbe38SToomas Soome static viorand_rdbuf_t *
viorand_rbuf_alloc(viorand_state_t * state)191*b91bbe38SToomas Soome viorand_rbuf_alloc(viorand_state_t *state)
192*b91bbe38SToomas Soome {
193*b91bbe38SToomas Soome 	viorand_rdbuf_t *rb;
194*b91bbe38SToomas Soome 
195*b91bbe38SToomas Soome 	VERIFY(MUTEX_HELD(&state->vio_mutex));
196*b91bbe38SToomas Soome 
197*b91bbe38SToomas Soome 	while ((rb = list_remove_head(&state->vio_rdbufs_free)) == NULL)
198*b91bbe38SToomas Soome 		cv_wait(&state->vio_cv, &state->vio_mutex);
199*b91bbe38SToomas Soome 
200*b91bbe38SToomas Soome 	state->vio_rdbufs_alloc++;
201*b91bbe38SToomas Soome 	return (rb);
202*b91bbe38SToomas Soome }
203*b91bbe38SToomas Soome 
204*b91bbe38SToomas Soome /*
205*b91bbe38SToomas Soome  * return buffer to free list
206*b91bbe38SToomas Soome  */
207*b91bbe38SToomas Soome static void
viorand_rbuf_free(viorand_state_t * state,viorand_rdbuf_t * rb)208*b91bbe38SToomas Soome viorand_rbuf_free(viorand_state_t *state, viorand_rdbuf_t *rb)
209*b91bbe38SToomas Soome {
210*b91bbe38SToomas Soome 	VERIFY(MUTEX_HELD(&state->vio_mutex));
211*b91bbe38SToomas Soome 	VERIFY3U(state->vio_rdbufs_alloc, >, 0);
212*b91bbe38SToomas Soome 
213*b91bbe38SToomas Soome 	state->vio_rdbufs_alloc--;
214*b91bbe38SToomas Soome 	virtio_chain_clear(rb->rb_chain);
215*b91bbe38SToomas Soome 	if (rb->rb_dma != NULL) {
216*b91bbe38SToomas Soome 		virtio_dma_free(rb->rb_dma);
217*b91bbe38SToomas Soome 		rb->rb_dma = NULL;
218*b91bbe38SToomas Soome 	}
219*b91bbe38SToomas Soome 	list_insert_head(&state->vio_rdbufs_free, rb);
220*b91bbe38SToomas Soome }
221*b91bbe38SToomas Soome 
222*b91bbe38SToomas Soome /*
223*b91bbe38SToomas Soome  * Free all allocated buffers. This is called to clean everything up,
224*b91bbe38SToomas Soome  * so we do not want to leave anything around.
225*b91bbe38SToomas Soome  */
226*b91bbe38SToomas Soome static void
viorand_free_bufs(viorand_state_t * state)227*b91bbe38SToomas Soome viorand_free_bufs(viorand_state_t *state)
228*b91bbe38SToomas Soome {
229*b91bbe38SToomas Soome 	VERIFY(MUTEX_HELD(&state->vio_mutex));
230*b91bbe38SToomas Soome 
231*b91bbe38SToomas Soome 	for (uint_t i = 0; i < state->vio_rdbufs_capacity; i++) {
232*b91bbe38SToomas Soome 		viorand_rdbuf_t *rb = &state->vio_rdbuf_mem[i];
233*b91bbe38SToomas Soome 
234*b91bbe38SToomas Soome 		if (rb->rb_dma != NULL) {
235*b91bbe38SToomas Soome 			virtio_dma_free(rb->rb_dma);
236*b91bbe38SToomas Soome 			rb->rb_dma = NULL;
237*b91bbe38SToomas Soome 		}
238*b91bbe38SToomas Soome 
239*b91bbe38SToomas Soome 		if (rb->rb_chain != NULL) {
240*b91bbe38SToomas Soome 			virtio_chain_free(rb->rb_chain);
241*b91bbe38SToomas Soome 			rb->rb_chain = NULL;
242*b91bbe38SToomas Soome 		}
243*b91bbe38SToomas Soome 	}
244*b91bbe38SToomas Soome 
245*b91bbe38SToomas Soome 	if (state->vio_rdbuf_mem != NULL) {
246*b91bbe38SToomas Soome 		kmem_free(state->vio_rdbuf_mem,
247*b91bbe38SToomas Soome 		    sizeof (viorand_rdbuf_t) * state->vio_rdbufs_capacity);
248*b91bbe38SToomas Soome 		state->vio_rdbuf_mem = NULL;
249*b91bbe38SToomas Soome 		state->vio_rdbufs_capacity = 0;
250*b91bbe38SToomas Soome 		state->vio_rdbufs_alloc = 0;
251*b91bbe38SToomas Soome 	}
252*b91bbe38SToomas Soome }
253*b91bbe38SToomas Soome 
254*b91bbe38SToomas Soome static int
viorand_alloc_bufs(viorand_state_t * state)255*b91bbe38SToomas Soome viorand_alloc_bufs(viorand_state_t *state)
256*b91bbe38SToomas Soome {
257*b91bbe38SToomas Soome 	VERIFY(MUTEX_HELD(&state->vio_mutex));
258*b91bbe38SToomas Soome 
259*b91bbe38SToomas Soome 	state->vio_rdbufs_capacity = virtio_queue_size(state->vio_rq);
260*b91bbe38SToomas Soome 	state->vio_rdbuf_mem = kmem_zalloc(sizeof (viorand_rdbuf_t) *
261*b91bbe38SToomas Soome 	    state->vio_rdbufs_capacity, KM_SLEEP);
262*b91bbe38SToomas Soome 	list_create(&state->vio_rdbufs_free, sizeof (viorand_rdbuf_t),
263*b91bbe38SToomas Soome 	    offsetof(viorand_rdbuf_t, rb_link));
264*b91bbe38SToomas Soome 
265*b91bbe38SToomas Soome 	/* Put everything in free list. */
266*b91bbe38SToomas Soome 	for (uint_t i = 0; i < state->vio_rdbufs_capacity; i++)
267*b91bbe38SToomas Soome 		list_insert_tail(&state->vio_rdbufs_free,
268*b91bbe38SToomas Soome 		    &state->vio_rdbuf_mem[i]);
269*b91bbe38SToomas Soome 
270*b91bbe38SToomas Soome 	for (viorand_rdbuf_t *rb = list_head(&state->vio_rdbufs_free);
271*b91bbe38SToomas Soome 	    rb != NULL; rb = list_next(&state->vio_rdbufs_free, rb)) {
272*b91bbe38SToomas Soome 		rb->rb_viornd = state;
273*b91bbe38SToomas Soome 		rb->rb_chain = virtio_chain_alloc(state->vio_rq, KM_SLEEP);
274*b91bbe38SToomas Soome 		if (rb->rb_chain == NULL)
275*b91bbe38SToomas Soome 			goto fail;
276*b91bbe38SToomas Soome 
277*b91bbe38SToomas Soome 		virtio_chain_data_set(rb->rb_chain, rb);
278*b91bbe38SToomas Soome 	}
279*b91bbe38SToomas Soome 	return (0);
280*b91bbe38SToomas Soome 
281*b91bbe38SToomas Soome fail:
282*b91bbe38SToomas Soome 	viorand_free_bufs(state);
283*b91bbe38SToomas Soome 	return (ENOMEM);
284*b91bbe38SToomas Soome }
285*b91bbe38SToomas Soome 
286*b91bbe38SToomas Soome static int
viorand_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)287*b91bbe38SToomas Soome viorand_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
288*b91bbe38SToomas Soome {
289*b91bbe38SToomas Soome 	int instance = ddi_get_instance(dip);
290*b91bbe38SToomas Soome 	int rv = 0;
291*b91bbe38SToomas Soome 	viorand_state_t *state;
292*b91bbe38SToomas Soome 	virtio_t *vio;
293*b91bbe38SToomas Soome 
294*b91bbe38SToomas Soome 	switch (cmd) {
295*b91bbe38SToomas Soome 	case DDI_ATTACH:
296*b91bbe38SToomas Soome 		break;
297*b91bbe38SToomas Soome 
298*b91bbe38SToomas Soome 	case DDI_RESUME:
299*b91bbe38SToomas Soome 	default:
300*b91bbe38SToomas Soome 		return (DDI_FAILURE);
301*b91bbe38SToomas Soome 	}
302*b91bbe38SToomas Soome 
303*b91bbe38SToomas Soome 	if (ddi_soft_state_zalloc(viorand_statep, instance) != DDI_SUCCESS)
304*b91bbe38SToomas Soome 		return (DDI_FAILURE);
305*b91bbe38SToomas Soome 
306*b91bbe38SToomas Soome 	vio = virtio_init(dip, VIORAND_FEATURES, B_TRUE);
307*b91bbe38SToomas Soome 	if (vio == NULL) {
308*b91bbe38SToomas Soome 		ddi_soft_state_free(viorand_statep, instance);
309*b91bbe38SToomas Soome 		return (DDI_FAILURE);
310*b91bbe38SToomas Soome 	}
311*b91bbe38SToomas Soome 
312*b91bbe38SToomas Soome 	state = ddi_get_soft_state(viorand_statep, instance);
313*b91bbe38SToomas Soome 	state->vio_dip = dip;
314*b91bbe38SToomas Soome 	state->vio_virtio = vio;
315*b91bbe38SToomas Soome 	state->vio_rq = virtio_queue_alloc(vio, VIORAND_RQ, "requestq",
316*b91bbe38SToomas Soome 	    viorand_interrupt, state, B_FALSE, 1);
317*b91bbe38SToomas Soome 	if (state->vio_rq == NULL) {
318*b91bbe38SToomas Soome 		virtio_fini(state->vio_virtio, B_TRUE);
319*b91bbe38SToomas Soome 		ddi_soft_state_free(viorand_statep, instance);
320*b91bbe38SToomas Soome 		return (DDI_FAILURE);
321*b91bbe38SToomas Soome 	}
322*b91bbe38SToomas Soome 
323*b91bbe38SToomas Soome 	if (virtio_init_complete(state->vio_virtio, VIRTIO_ANY_INTR_TYPE) !=
324*b91bbe38SToomas Soome 	    DDI_SUCCESS) {
325*b91bbe38SToomas Soome 		virtio_fini(state->vio_virtio, B_TRUE);
326*b91bbe38SToomas Soome 		ddi_soft_state_free(viorand_statep, instance);
327*b91bbe38SToomas Soome 		return (DDI_FAILURE);
328*b91bbe38SToomas Soome 	}
329*b91bbe38SToomas Soome 
330*b91bbe38SToomas Soome 	cv_init(&state->vio_cv, NULL, CV_DRIVER, NULL);
331*b91bbe38SToomas Soome 	mutex_init(&state->vio_mutex, NULL, MUTEX_DRIVER, virtio_intr_pri(vio));
332*b91bbe38SToomas Soome 	mutex_enter(&state->vio_mutex);
333*b91bbe38SToomas Soome 
334*b91bbe38SToomas Soome 	if (viorand_alloc_bufs(state) != 0) {
335*b91bbe38SToomas Soome 		mutex_exit(&state->vio_mutex);
336*b91bbe38SToomas Soome 		dev_err(dip, CE_WARN, "failed to allocate memory");
337*b91bbe38SToomas Soome 		goto fail;
338*b91bbe38SToomas Soome 	}
339*b91bbe38SToomas Soome 	mutex_exit(&state->vio_mutex);
340*b91bbe38SToomas Soome 
341*b91bbe38SToomas Soome 	viorand_prov_info.pi_provider_dev.pd_hw = dip;
342*b91bbe38SToomas Soome 	viorand_prov_info.pi_provider_handle = state;
343*b91bbe38SToomas Soome 
344*b91bbe38SToomas Soome 	if (virtio_interrupts_enable(state->vio_virtio) != DDI_SUCCESS)
345*b91bbe38SToomas Soome 		goto fail;
346*b91bbe38SToomas Soome 
347*b91bbe38SToomas Soome 	rv = crypto_register_provider(&viorand_prov_info, &state->vio_handle);
348*b91bbe38SToomas Soome 	if (rv == CRYPTO_SUCCESS) {
349*b91bbe38SToomas Soome 		return (DDI_SUCCESS);
350*b91bbe38SToomas Soome 	}
351*b91bbe38SToomas Soome 
352*b91bbe38SToomas Soome fail:
353*b91bbe38SToomas Soome 	virtio_interrupts_disable(state->vio_virtio);
354*b91bbe38SToomas Soome 	mutex_enter(&state->vio_mutex);
355*b91bbe38SToomas Soome 	viorand_free_bufs(state);
356*b91bbe38SToomas Soome 	mutex_exit(&state->vio_mutex);
357*b91bbe38SToomas Soome 	cv_destroy(&state->vio_cv);
358*b91bbe38SToomas Soome 	mutex_destroy(&state->vio_mutex);
359*b91bbe38SToomas Soome 	virtio_fini(state->vio_virtio, B_TRUE);
360*b91bbe38SToomas Soome 	ddi_soft_state_free(viorand_statep, instance);
361*b91bbe38SToomas Soome 	return (DDI_FAILURE);
362*b91bbe38SToomas Soome }
363*b91bbe38SToomas Soome 
364*b91bbe38SToomas Soome static int
viorand_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)365*b91bbe38SToomas Soome viorand_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
366*b91bbe38SToomas Soome {
367*b91bbe38SToomas Soome 	int instance = ddi_get_instance(dip);
368*b91bbe38SToomas Soome 	viorand_state_t *state = ddi_get_soft_state(viorand_statep, instance);
369*b91bbe38SToomas Soome 
370*b91bbe38SToomas Soome 	switch (cmd) {
371*b91bbe38SToomas Soome 	case DDI_DETACH:
372*b91bbe38SToomas Soome 		if (!virtio_registered)
373*b91bbe38SToomas Soome 			break;
374*b91bbe38SToomas Soome 
375*b91bbe38SToomas Soome 		/* FALLTHROUGH */
376*b91bbe38SToomas Soome 	case DDI_SUSPEND:
377*b91bbe38SToomas Soome 	default:
378*b91bbe38SToomas Soome 		return (DDI_FAILURE);
379*b91bbe38SToomas Soome 	}
380*b91bbe38SToomas Soome 
381*b91bbe38SToomas Soome 	if (crypto_unregister_provider(state->vio_handle) != CRYPTO_SUCCESS)
382*b91bbe38SToomas Soome 		return (DDI_FAILURE);
383*b91bbe38SToomas Soome 
384*b91bbe38SToomas Soome 	virtio_interrupts_disable(state->vio_virtio);
385*b91bbe38SToomas Soome 	virtio_shutdown(state->vio_virtio);
386*b91bbe38SToomas Soome 
387*b91bbe38SToomas Soome 	mutex_enter(&state->vio_mutex);
388*b91bbe38SToomas Soome 	for (;;) {
389*b91bbe38SToomas Soome 		virtio_chain_t *vic;
390*b91bbe38SToomas Soome 
391*b91bbe38SToomas Soome 		vic = virtio_queue_evacuate(state->vio_rq);
392*b91bbe38SToomas Soome 		if (vic == NULL)
393*b91bbe38SToomas Soome 			break;
394*b91bbe38SToomas Soome 
395*b91bbe38SToomas Soome 		viorand_rbuf_free(state, virtio_chain_data(vic));
396*b91bbe38SToomas Soome 	}
397*b91bbe38SToomas Soome 
398*b91bbe38SToomas Soome 	viorand_free_bufs(state);
399*b91bbe38SToomas Soome 	mutex_exit(&state->vio_mutex);
400*b91bbe38SToomas Soome 	cv_destroy(&state->vio_cv);
401*b91bbe38SToomas Soome 	mutex_destroy(&state->vio_mutex);
402*b91bbe38SToomas Soome 	(void) virtio_fini(state->vio_virtio, B_FALSE);
403*b91bbe38SToomas Soome 	ddi_soft_state_free(viorand_statep, instance);
404*b91bbe38SToomas Soome 	return (DDI_SUCCESS);
405*b91bbe38SToomas Soome }
406*b91bbe38SToomas Soome 
407*b91bbe38SToomas Soome static int
viorand_quiesce(dev_info_t * dip)408*b91bbe38SToomas Soome viorand_quiesce(dev_info_t *dip)
409*b91bbe38SToomas Soome {
410*b91bbe38SToomas Soome 	int instance = ddi_get_instance(dip);
411*b91bbe38SToomas Soome 	viorand_state_t *state = ddi_get_soft_state(viorand_statep, instance);
412*b91bbe38SToomas Soome 
413*b91bbe38SToomas Soome 	if (state == NULL)
414*b91bbe38SToomas Soome 		return (DDI_FAILURE);
415*b91bbe38SToomas Soome 
416*b91bbe38SToomas Soome 	return (virtio_quiesce(state->vio_virtio));
417*b91bbe38SToomas Soome }
418*b91bbe38SToomas Soome 
419*b91bbe38SToomas Soome /*
420*b91bbe38SToomas Soome  * Control entry points.
421*b91bbe38SToomas Soome  */
422*b91bbe38SToomas Soome static void
viorand_provider_status(crypto_provider_handle_t provider __unused,uint_t * status)423*b91bbe38SToomas Soome viorand_provider_status(crypto_provider_handle_t provider __unused,
424*b91bbe38SToomas Soome     uint_t *status)
425*b91bbe38SToomas Soome {
426*b91bbe38SToomas Soome 	*status = CRYPTO_PROVIDER_READY;
427*b91bbe38SToomas Soome }
428*b91bbe38SToomas Soome 
429*b91bbe38SToomas Soome static boolean_t
viorand_submit_request(viorand_rdbuf_t * rb)430*b91bbe38SToomas Soome viorand_submit_request(viorand_rdbuf_t *rb)
431*b91bbe38SToomas Soome {
432*b91bbe38SToomas Soome 	if (virtio_chain_append(rb->rb_chain,
433*b91bbe38SToomas Soome 	    virtio_dma_cookie_pa(rb->rb_dma, 0),
434*b91bbe38SToomas Soome 	    rb->rb_req_len,
435*b91bbe38SToomas Soome 	    VIRTIO_DIR_DEVICE_WRITES) != DDI_SUCCESS) {
436*b91bbe38SToomas Soome 		return (B_FALSE);
437*b91bbe38SToomas Soome 	}
438*b91bbe38SToomas Soome 
439*b91bbe38SToomas Soome 	virtio_dma_sync(rb->rb_dma, DDI_DMA_SYNC_FORDEV);
440*b91bbe38SToomas Soome 	virtio_chain_submit(rb->rb_chain, B_TRUE);
441*b91bbe38SToomas Soome 	return (B_TRUE);
442*b91bbe38SToomas Soome }
443*b91bbe38SToomas Soome 
444*b91bbe38SToomas Soome /* We got portion of data, process it */
445*b91bbe38SToomas Soome static void
viorand_process_data(viorand_rdbuf_t * rb)446*b91bbe38SToomas Soome viorand_process_data(viorand_rdbuf_t *rb)
447*b91bbe38SToomas Soome {
448*b91bbe38SToomas Soome 	size_t len;
449*b91bbe38SToomas Soome 	int error = CRYPTO_SUCCESS;
450*b91bbe38SToomas Soome 
451*b91bbe38SToomas Soome 	len = MIN(rb->rb_req_len, rb->rb_recv_len);
452*b91bbe38SToomas Soome 	bcopy(virtio_dma_va(rb->rb_dma, 0), rb->rb_req_buf, len);
453*b91bbe38SToomas Soome 	bzero(virtio_dma_va(rb->rb_dma, 0), len);
454*b91bbe38SToomas Soome 	if (len < rb->rb_req_len) {
455*b91bbe38SToomas Soome 		rb->rb_req_len -= len;
456*b91bbe38SToomas Soome 		rb->rb_req_buf += len;
457*b91bbe38SToomas Soome 		/* Try to get reminder */
458*b91bbe38SToomas Soome 		if (viorand_submit_request(rb))
459*b91bbe38SToomas Soome 			return;
460*b91bbe38SToomas Soome 
461*b91bbe38SToomas Soome 		/* Release our buffer and return error */
462*b91bbe38SToomas Soome 		viorand_rbuf_free(rb->rb_viornd, rb);
463*b91bbe38SToomas Soome 		error = CRYPTO_HOST_MEMORY;
464*b91bbe38SToomas Soome 	} else {
465*b91bbe38SToomas Soome 		/* Got all the data, free our buffer */
466*b91bbe38SToomas Soome 		viorand_rbuf_free(rb->rb_viornd, rb);
467*b91bbe38SToomas Soome 	}
468*b91bbe38SToomas Soome 	crypto_op_notification(rb->rb_req_handle, error);
469*b91bbe38SToomas Soome }
470*b91bbe38SToomas Soome 
471*b91bbe38SToomas Soome static uint_t
viorand_interrupt(caddr_t a,caddr_t b __unused)472*b91bbe38SToomas Soome viorand_interrupt(caddr_t a, caddr_t b __unused)
473*b91bbe38SToomas Soome {
474*b91bbe38SToomas Soome 	viorand_state_t *state = (viorand_state_t *)a;
475*b91bbe38SToomas Soome 	virtio_chain_t *vic;
476*b91bbe38SToomas Soome 	boolean_t notify = B_FALSE;
477*b91bbe38SToomas Soome 
478*b91bbe38SToomas Soome 	mutex_enter(&state->vio_mutex);
479*b91bbe38SToomas Soome 	while ((vic = virtio_queue_poll(state->vio_rq)) != NULL) {
480*b91bbe38SToomas Soome 		/* Actual received len and our read buffer */
481*b91bbe38SToomas Soome 		size_t len = virtio_chain_received_length(vic);
482*b91bbe38SToomas Soome 		viorand_rdbuf_t *rb = virtio_chain_data(vic);
483*b91bbe38SToomas Soome 
484*b91bbe38SToomas Soome 		virtio_dma_sync(rb->rb_dma, DDI_DMA_SYNC_FORCPU);
485*b91bbe38SToomas Soome 		rb->rb_recv_len = len;
486*b91bbe38SToomas Soome 		viorand_process_data(rb);
487*b91bbe38SToomas Soome 		notify = B_TRUE;
488*b91bbe38SToomas Soome 	}
489*b91bbe38SToomas Soome 	if (notify)
490*b91bbe38SToomas Soome 		cv_broadcast(&state->vio_cv);
491*b91bbe38SToomas Soome 	mutex_exit(&state->vio_mutex);
492*b91bbe38SToomas Soome 	if (notify)
493*b91bbe38SToomas Soome 		return (DDI_INTR_CLAIMED);
494*b91bbe38SToomas Soome 	return (DDI_INTR_UNCLAIMED);
495*b91bbe38SToomas Soome }
496*b91bbe38SToomas Soome 
497*b91bbe38SToomas Soome /*
498*b91bbe38SToomas Soome  * Random number entry point.
499*b91bbe38SToomas Soome  */
500*b91bbe38SToomas Soome static int
viorand_generate_random(crypto_provider_handle_t provider,crypto_session_id_t sid __unused,uchar_t * buf,size_t len,crypto_req_handle_t req)501*b91bbe38SToomas Soome viorand_generate_random(crypto_provider_handle_t provider,
502*b91bbe38SToomas Soome     crypto_session_id_t sid __unused, uchar_t *buf, size_t len,
503*b91bbe38SToomas Soome     crypto_req_handle_t req)
504*b91bbe38SToomas Soome {
505*b91bbe38SToomas Soome 	viorand_state_t *state = provider;
506*b91bbe38SToomas Soome 	viorand_rdbuf_t *rb;
507*b91bbe38SToomas Soome 
508*b91bbe38SToomas Soome 	mutex_enter(&state->vio_mutex);
509*b91bbe38SToomas Soome 	rb = viorand_rbuf_alloc(state);
510*b91bbe38SToomas Soome 	mutex_exit(&state->vio_mutex);
511*b91bbe38SToomas Soome 
512*b91bbe38SToomas Soome 	rb->rb_req_buf = buf;
513*b91bbe38SToomas Soome 	rb->rb_req_len = len;
514*b91bbe38SToomas Soome 	rb->rb_req_handle = req;
515*b91bbe38SToomas Soome 
516*b91bbe38SToomas Soome 	rb->rb_dma = virtio_dma_alloc(state->vio_virtio, len,
517*b91bbe38SToomas Soome 	    &viorand_dma_attr, DDI_DMA_READ | DDI_DMA_STREAMING, KM_SLEEP);
518*b91bbe38SToomas Soome 	if (rb->rb_dma == NULL) {
519*b91bbe38SToomas Soome 		goto error;
520*b91bbe38SToomas Soome 	}
521*b91bbe38SToomas Soome 
522*b91bbe38SToomas Soome 	if (viorand_submit_request(rb))
523*b91bbe38SToomas Soome 		return (CRYPTO_QUEUED);
524*b91bbe38SToomas Soome 
525*b91bbe38SToomas Soome error:
526*b91bbe38SToomas Soome 	mutex_enter(&state->vio_mutex);
527*b91bbe38SToomas Soome 	viorand_rbuf_free(state, rb);
528*b91bbe38SToomas Soome 	mutex_exit(&state->vio_mutex);
529*b91bbe38SToomas Soome 	return (CRYPTO_HOST_MEMORY);
530*b91bbe38SToomas Soome }
531