xref: /illumos-gate/usr/src/uts/sun4v/io/n2rng/n2rng.c (revision 03b8b03e)
1fec509a0Sgm /*
2fec509a0Sgm  * CDDL HEADER START
3fec509a0Sgm  *
4fec509a0Sgm  * The contents of this file are subject to the terms of the
5fec509a0Sgm  * Common Development and Distribution License (the "License").
6fec509a0Sgm  * You may not use this file except in compliance with the License.
7fec509a0Sgm  *
8fec509a0Sgm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fec509a0Sgm  * or http://www.opensolaris.org/os/licensing.
10fec509a0Sgm  * See the License for the specific language governing permissions
11fec509a0Sgm  * and limitations under the License.
12fec509a0Sgm  *
13fec509a0Sgm  * When distributing Covered Code, include this CDDL HEADER in each
14fec509a0Sgm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fec509a0Sgm  * If applicable, add the following below this CDDL HEADER, with the
16fec509a0Sgm  * fields enclosed by brackets "[]" replaced with your own identifying
17fec509a0Sgm  * information: Portions Copyright [yyyy] [name of copyright owner]
18fec509a0Sgm  *
19fec509a0Sgm  * CDDL HEADER END
20fec509a0Sgm  */
21fec509a0Sgm /*
2232e0ab73SMisaki Miyashita  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23fec509a0Sgm  */
24fec509a0Sgm 
25fec509a0Sgm 
26fec509a0Sgm /*
27fec509a0Sgm  * Niagara 2 Random Number Generator (RNG) driver
28fec509a0Sgm  */
29fec509a0Sgm 
30fec509a0Sgm #include <sys/types.h>
31fec509a0Sgm #include <sys/sysmacros.h>
32fec509a0Sgm #include <sys/modctl.h>
33fec509a0Sgm #include <sys/conf.h>
34fec509a0Sgm #include <sys/devops.h>
35fec509a0Sgm #include <sys/cmn_err.h>
36fec509a0Sgm #include <sys/ksynch.h>
37fec509a0Sgm #include <sys/kmem.h>
38fec509a0Sgm #include <sys/stat.h>
39fec509a0Sgm #include <sys/open.h>
40fec509a0Sgm #include <sys/file.h>
41fec509a0Sgm #include <sys/ddi.h>
42fec509a0Sgm #include <sys/sunddi.h>
43fec509a0Sgm #include <sys/param.h>
44fec509a0Sgm #include <sys/cpuvar.h>
45fec509a0Sgm #include <sys/disp.h>
46fec509a0Sgm #include <sys/hsvc.h>
47fec509a0Sgm #include <sys/machsystm.h>
48fec509a0Sgm #include <sys/hypervisor_api.h>
49fec509a0Sgm #include <sys/n2rng.h>
50fec509a0Sgm 
51fec509a0Sgm static int	n2rng_attach(dev_info_t *, ddi_attach_cmd_t);
52fec509a0Sgm static int	n2rng_detach(dev_info_t *, ddi_detach_cmd_t);
53fec509a0Sgm static int	n2rng_suspend(n2rng_t *);
54fec509a0Sgm static int	n2rng_resume(n2rng_t *);
55fec509a0Sgm static uint64_t sticks_per_usec(void);
56741c280dStwelke u_longlong_t	gettick(void);
57741c280dStwelke static int	n2rng_init_ctl(n2rng_t *);
58741c280dStwelke static void	n2rng_uninit_ctl(n2rng_t *);
59741c280dStwelke static int	n2rng_config(n2rng_t *);
60741c280dStwelke static void	n2rng_config_task(void * targ);
6159ac0c16Sdavemq 
62fec509a0Sgm /*
63fec509a0Sgm  * Device operations.
64fec509a0Sgm  */
65fec509a0Sgm 
66fec509a0Sgm static struct dev_ops devops = {
67fec509a0Sgm 	DEVO_REV,		/* devo_rev */
68fec509a0Sgm 	0,			/* devo_refcnt */
69fec509a0Sgm 	nodev,			/* devo_getinfo */
70fec509a0Sgm 	nulldev,		/* devo_identify */
71fec509a0Sgm 	nulldev,		/* devo_probe */
72fec509a0Sgm 	n2rng_attach,		/* devo_attach */
73fec509a0Sgm 	n2rng_detach,		/* devo_detach */
74fec509a0Sgm 	nodev,			/* devo_reset */
75fec509a0Sgm 	NULL,			/* devo_cb_ops */
76fec509a0Sgm 	NULL,			/* devo_bus_ops */
7719397407SSherry Moore 	ddi_power,		/* devo_power */
7819397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce */
79fec509a0Sgm };
80fec509a0Sgm 
81fec509a0Sgm /*
82fec509a0Sgm  * Module linkage.
83fec509a0Sgm  */
84fec509a0Sgm static struct modldrv modldrv = {
85fec509a0Sgm 	&mod_driverops,			/* drv_modops */
86fd80436bSgm 	"N2 RNG Driver",		/* drv_linkinfo */
87fec509a0Sgm 	&devops,			/* drv_dev_ops */
88fec509a0Sgm };
89fec509a0Sgm 
90fec509a0Sgm static struct modlinkage modlinkage = {
91fec509a0Sgm 	MODREV_1,			/* ml_rev */
92fec509a0Sgm 	&modldrv,			/* ml_linkage */
93fec509a0Sgm 	NULL
94fec509a0Sgm };
95fec509a0Sgm 
96fec509a0Sgm /*
97fec509a0Sgm  * Driver globals Soft state.
98fec509a0Sgm  */
99fec509a0Sgm static void	*n2rng_softstate = NULL;
100fec509a0Sgm 
101fec509a0Sgm /*
102741c280dStwelke  * Hypervisor NCS services information.
103fec509a0Sgm  */
104741c280dStwelke static boolean_t ncs_hsvc_available = B_FALSE;
105741c280dStwelke 
106741c280dStwelke #define	NVERSIONS	2
107fec509a0Sgm 
108741c280dStwelke /*
109741c280dStwelke  * HV API versions supported by this driver.
110741c280dStwelke  */
111741c280dStwelke static hsvc_info_t ncs_hsvc[NVERSIONS] = {
112741c280dStwelke 	{ HSVC_REV_1, NULL, HSVC_GROUP_RNG, 2, 0, DRIVER },	/* v2.0 */
113741c280dStwelke 	{ HSVC_REV_1, NULL, HSVC_GROUP_RNG, 1, 0, DRIVER },	/* v1.0 */
114fec509a0Sgm };
115741c280dStwelke int	ncs_version_index;	/* index into ncs_hsvc[] */
116fec509a0Sgm 
117fec509a0Sgm /*
118fec509a0Sgm  * DDI entry points.
119fec509a0Sgm  */
120fec509a0Sgm int
_init(void)121fec509a0Sgm _init(void)
122fec509a0Sgm {
123fec509a0Sgm 	int	rv;
124fec509a0Sgm 
125fec509a0Sgm 	rv = ddi_soft_state_init(&n2rng_softstate, sizeof (n2rng_t), 1);
126fec509a0Sgm 	if (rv != 0) {
127fec509a0Sgm 		/* this should *never* happen! */
128fec509a0Sgm 		return (rv);
129fec509a0Sgm 	}
130fec509a0Sgm 
131fec509a0Sgm 	if ((rv = mod_install(&modlinkage)) != 0) {
132fec509a0Sgm 		/* cleanup here */
133fec509a0Sgm 		ddi_soft_state_fini(&n2rng_softstate);
134fec509a0Sgm 		return (rv);
135fec509a0Sgm 	}
136fec509a0Sgm 
137fec509a0Sgm 	return (0);
138fec509a0Sgm }
139fec509a0Sgm 
140fec509a0Sgm int
_fini(void)141fec509a0Sgm _fini(void)
142fec509a0Sgm {
143fec509a0Sgm 	int	rv;
144fec509a0Sgm 
145fec509a0Sgm 	rv = mod_remove(&modlinkage);
146fec509a0Sgm 	if (rv == 0) {
147fec509a0Sgm 		/* cleanup here */
148fec509a0Sgm 		ddi_soft_state_fini(&n2rng_softstate);
149fec509a0Sgm 	}
150fec509a0Sgm 
151fec509a0Sgm 	return (rv);
152fec509a0Sgm }
153fec509a0Sgm 
154fec509a0Sgm int
_info(struct modinfo * modinfop)155fec509a0Sgm _info(struct modinfo *modinfop)
156fec509a0Sgm {
157fec509a0Sgm 	return (mod_info(&modlinkage, modinfop));
158fec509a0Sgm }
159fec509a0Sgm 
160fec509a0Sgm static int
n2rng_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)161fec509a0Sgm n2rng_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
162fec509a0Sgm {
163fec509a0Sgm 	n2rng_t		*n2rng = NULL;
164fec509a0Sgm 	int		instance;
165fec509a0Sgm 	int		rv;
166741c280dStwelke 	int		version;
167741c280dStwelke 	uint64_t	ncs_minor_ver;
168fec509a0Sgm 
169fec509a0Sgm 	instance = ddi_get_instance(dip);
170741c280dStwelke 	DBG1(NULL, DENTRY, "n2rng_attach called, instance %d", instance);
171fec509a0Sgm 	/*
172fec509a0Sgm 	 * Only instance 0 of n2rng driver is allowed.
173fec509a0Sgm 	 */
174fec509a0Sgm 	if (instance != 0) {
175fec509a0Sgm 		n2rng_diperror(dip, "only one instance (0) allowed");
176fec509a0Sgm 		return (DDI_FAILURE);
177fec509a0Sgm 	}
178fec509a0Sgm 
179fec509a0Sgm 	switch (cmd) {
180fec509a0Sgm 	case DDI_RESUME:
181fec509a0Sgm 		n2rng = (n2rng_t *)ddi_get_soft_state(n2rng_softstate,
182fec509a0Sgm 		    instance);
183fec509a0Sgm 		if (n2rng == NULL) {
184fec509a0Sgm 			n2rng_diperror(dip, "no soft state in attach");
185fec509a0Sgm 			return (DDI_FAILURE);
186fec509a0Sgm 		}
187fec509a0Sgm 		return (n2rng_resume(n2rng));
188fec509a0Sgm 
189fec509a0Sgm 	case DDI_ATTACH:
190fec509a0Sgm 		break;
191fec509a0Sgm 	default:
192fec509a0Sgm 		return (DDI_FAILURE);
193fec509a0Sgm 	}
194fec509a0Sgm 
195fec509a0Sgm 	rv = ddi_soft_state_zalloc(n2rng_softstate, instance);
196fec509a0Sgm 	if (rv != DDI_SUCCESS) {
197fec509a0Sgm 		n2rng_diperror(dip, "unable to allocate soft state");
198fec509a0Sgm 		return (DDI_FAILURE);
199fec509a0Sgm 	}
200fec509a0Sgm 	n2rng = (n2rng_t *)ddi_get_soft_state(n2rng_softstate, instance);
201fec509a0Sgm 	ASSERT(n2rng != NULL);
202fec509a0Sgm 	n2rng->n_dip = dip;
203fec509a0Sgm 
204741c280dStwelke 	mutex_init(&n2rng->n_lock, NULL, MUTEX_DRIVER, NULL);
205741c280dStwelke 	n2rng->n_flags = 0;
206741c280dStwelke 	n2rng->n_timeout_id = 0;
207741c280dStwelke 	n2rng->n_sticks_per_usec = sticks_per_usec();
208fec509a0Sgm 
209741c280dStwelke 	/* Determine binding type */
210741c280dStwelke 	n2rng->n_binding_name = ddi_binding_name(dip);
211741c280dStwelke 	if (strncmp(n2rng->n_binding_name, N2RNG_BINDNAME_N2,
212741c280dStwelke 	    strlen(N2RNG_BINDNAME_N2)) == 0) {
213741c280dStwelke 		/*
214741c280dStwelke 		 * Niagara 2
215741c280dStwelke 		 */
216741c280dStwelke 		n2rng->n_binding = N2RNG_CPU_N2;
217741c280dStwelke 	} else if (strncmp(n2rng->n_binding_name, N2RNG_BINDNAME_VF,
218741c280dStwelke 	    strlen(N2RNG_BINDNAME_VF)) == 0) {
219741c280dStwelke 		/*
220741c280dStwelke 		 * Victoria Falls
221741c280dStwelke 		 */
222741c280dStwelke 		n2rng->n_binding = N2RNG_CPU_VF;
2234df55fdeSJanie Lu 	} else if (strncmp(n2rng->n_binding_name, N2RNG_BINDNAME_KT,
2244df55fdeSJanie Lu 	    strlen(N2RNG_BINDNAME_KT)) == 0) {
2254df55fdeSJanie Lu 		/*
2264df55fdeSJanie Lu 		 * Rainbow Falls
2274df55fdeSJanie Lu 		 */
2284df55fdeSJanie Lu 		n2rng->n_binding = N2RNG_CPU_KT;
229741c280dStwelke 	} else {
230741c280dStwelke 		n2rng_diperror(dip,
231741c280dStwelke 		    "unable to determine n2rng (cpu) binding (%s)",
232741c280dStwelke 		    n2rng->n_binding_name);
233741c280dStwelke 		goto errorexit;
234741c280dStwelke 	}
235741c280dStwelke 	DBG1(n2rng, DCHATTY, "n2rng_attach: n2rng->n_binding_name = %s",
236741c280dStwelke 	    n2rng->n_binding_name);
237741c280dStwelke 
238741c280dStwelke 	/* Negotiate HV api version number */
239741c280dStwelke 	for (version = 0; version < NVERSIONS; version++) {
240741c280dStwelke 		rv = hsvc_register(&ncs_hsvc[version], &ncs_minor_ver);
241741c280dStwelke 		if (rv == 0)
242741c280dStwelke 			break;
243741c280dStwelke 
244741c280dStwelke 		DBG4(n2rng, DCHATTY, "n2rng_attach: grp: 0x%lx, maj: %ld, "
245741c280dStwelke 		    "min: %ld, errno: %d", ncs_hsvc[version].hsvc_group,
246741c280dStwelke 		    ncs_hsvc[version].hsvc_major,
247741c280dStwelke 		    ncs_hsvc[version].hsvc_minor, rv);
248741c280dStwelke 	}
249741c280dStwelke 	if (version == NVERSIONS) {
250741c280dStwelke 		for (version = 0; version < NVERSIONS; version++) {
251741c280dStwelke 			cmn_err(CE_WARN,
252741c280dStwelke 			    "%s: cannot negotiate hypervisor services "
253741c280dStwelke 			    "group: 0x%lx major: %ld minor: %ld errno: %d",
254741c280dStwelke 			    ncs_hsvc[version].hsvc_modname,
255741c280dStwelke 			    ncs_hsvc[version].hsvc_group,
256741c280dStwelke 			    ncs_hsvc[version].hsvc_major,
257741c280dStwelke 			    ncs_hsvc[version].hsvc_minor, rv);
258741c280dStwelke 		}
259741c280dStwelke 		goto errorexit;
260741c280dStwelke 	}
261741c280dStwelke 	ncs_version_index = version;
262741c280dStwelke 	ncs_hsvc_available = B_TRUE;
263741c280dStwelke 	DBG2(n2rng, DATTACH, "n2rng_attach: ncs api version (%ld.%ld)",
264741c280dStwelke 	    ncs_hsvc[ncs_version_index].hsvc_major, ncs_minor_ver);
265741c280dStwelke 	n2rng->n_hvapi_major_version = ncs_hsvc[ncs_version_index].hsvc_major;
266741c280dStwelke 	n2rng->n_hvapi_minor_version = (uint_t)ncs_minor_ver;
267741c280dStwelke 
268741c280dStwelke 	/*
269741c280dStwelke 	 * Verify that we are running version 2.0 or later api on multiple
270741c280dStwelke 	 * rng systems.
271741c280dStwelke 	 */
272741c280dStwelke 	if ((n2rng->n_binding != N2RNG_CPU_N2) &&
273741c280dStwelke 	    (n2rng->n_hvapi_major_version < 2)) {
274741c280dStwelke 		cmn_err(CE_NOTE, "n2rng: Incompatible hyperviser api "
275741c280dStwelke 		    "version %d.%d detected", n2rng->n_hvapi_major_version,
276741c280dStwelke 		    n2rng->n_hvapi_minor_version);
277741c280dStwelke 	}
278741c280dStwelke 
279741c280dStwelke 	/* Initialize ctl structure if runnning in the control domain */
280741c280dStwelke 	if (n2rng_init_ctl(n2rng) != DDI_SUCCESS) {
281741c280dStwelke 		cmn_err(CE_WARN, "n2rng: unable to initialize rng "
282741c280dStwelke 		    "control structures");
283741c280dStwelke 		goto errorexit;
284fec509a0Sgm 	}
28559ac0c16Sdavemq 
28659ac0c16Sdavemq 	/* Allocate single thread task queue for rng diags and registration */
28759ac0c16Sdavemq 	n2rng->n_taskq = ddi_taskq_create(dip, "n2rng_taskq", 1,
28859ac0c16Sdavemq 	    TASKQ_DEFAULTPRI, 0);
28959ac0c16Sdavemq 
29059ac0c16Sdavemq 	if (n2rng->n_taskq == NULL) {
29159ac0c16Sdavemq 		n2rng_diperror(dip, "ddi_taskq_create() failed");
29259ac0c16Sdavemq 		goto errorexit;
29359ac0c16Sdavemq 	}
29459ac0c16Sdavemq 
29559ac0c16Sdavemq 	/* Dispatch task to configure the RNG and register with KCF */
29659ac0c16Sdavemq 	if (ddi_taskq_dispatch(n2rng->n_taskq, n2rng_config_task,
29759ac0c16Sdavemq 	    (void *)n2rng, DDI_SLEEP) != DDI_SUCCESS) {
29859ac0c16Sdavemq 		n2rng_diperror(dip, "ddi_taskq_dispatch() failed");
299fec509a0Sgm 		goto errorexit;
300fec509a0Sgm 	}
301fec509a0Sgm 
302fec509a0Sgm 	return (DDI_SUCCESS);
303fec509a0Sgm 
304fec509a0Sgm errorexit:
305741c280dStwelke 	/* Wait for pending config tasks to complete and delete the taskq */
30659ac0c16Sdavemq 	if (n2rng->n_taskq != NULL) {
30759ac0c16Sdavemq 		ddi_taskq_destroy(n2rng->n_taskq);
30859ac0c16Sdavemq 		n2rng->n_taskq = NULL;
30959ac0c16Sdavemq 	}
31059ac0c16Sdavemq 
311741c280dStwelke 	n2rng_uninit_ctl(n2rng);
312741c280dStwelke 
313741c280dStwelke 	(void) n2rng_uninit(n2rng);
314741c280dStwelke 
315741c280dStwelke 	if (ncs_hsvc_available == B_TRUE) {
316741c280dStwelke 		(void) hsvc_unregister(&ncs_hsvc[ncs_version_index]);
317741c280dStwelke 		ncs_hsvc_available = B_FALSE;
318741c280dStwelke 	}
319741c280dStwelke 
320741c280dStwelke 	mutex_destroy(&n2rng->n_lock);
321fec509a0Sgm 	ddi_soft_state_free(n2rng_softstate, instance);
322fec509a0Sgm 
323fec509a0Sgm 	return (DDI_FAILURE);
324fec509a0Sgm }
325fec509a0Sgm 
326fec509a0Sgm static int
n2rng_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)327fec509a0Sgm n2rng_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
328fec509a0Sgm {
329fec509a0Sgm 	int		instance;
330fec509a0Sgm 	int		rv;
331fec509a0Sgm 	n2rng_t		*n2rng;
332741c280dStwelke 	timeout_id_t	tid;
333fec509a0Sgm 
334fec509a0Sgm 	instance = ddi_get_instance(dip);
335fec509a0Sgm 	n2rng = (n2rng_t *)ddi_get_soft_state(n2rng_softstate, instance);
336fec509a0Sgm 	if (n2rng == NULL) {
337fec509a0Sgm 		n2rng_diperror(dip, "no soft state in detach");
338fec509a0Sgm 		return (DDI_FAILURE);
339fec509a0Sgm 	}
340fec509a0Sgm 
341fec509a0Sgm 	switch (cmd) {
342fec509a0Sgm 	case DDI_SUSPEND:
343fec509a0Sgm 		return (n2rng_suspend(n2rng));
344fec509a0Sgm 	case DDI_DETACH:
345fec509a0Sgm 		break;
346fec509a0Sgm 	default:
347fec509a0Sgm 		return (DDI_FAILURE);
348fec509a0Sgm 	}
349fec509a0Sgm 
350802b83c4Stwelke 	/* Destroy task queue first to insure configuration has completed */
35159ac0c16Sdavemq 	if (n2rng->n_taskq != NULL) {
35259ac0c16Sdavemq 		ddi_taskq_destroy(n2rng->n_taskq);
35359ac0c16Sdavemq 		n2rng->n_taskq = NULL;
35459ac0c16Sdavemq 	}
35559ac0c16Sdavemq 
356741c280dStwelke 	/* Untimeout pending config retry operations */
357741c280dStwelke 	mutex_enter(&n2rng->n_lock);
358741c280dStwelke 	tid = n2rng->n_timeout_id;
359741c280dStwelke 	n2rng->n_timeout_id = 0;
360741c280dStwelke 	mutex_exit(&n2rng->n_lock);
361741c280dStwelke 	if (tid) {
362741c280dStwelke 		DBG1(n2rng, DCHATTY, "n2rng_detach: untimeout pending retry "
363741c280dStwelke 		    "id = %x", tid);
364741c280dStwelke 		(void) untimeout(tid);
365741c280dStwelke 	}
366741c280dStwelke 
367741c280dStwelke 	n2rng_uninit_ctl(n2rng);
368741c280dStwelke 
369802b83c4Stwelke 	/* unregister with KCF---also tears down FIPS state */
370802b83c4Stwelke 	rv = n2rng_uninit(n2rng) ? DDI_FAILURE : DDI_SUCCESS;
371802b83c4Stwelke 
372741c280dStwelke 	if (ncs_hsvc_available == B_TRUE) {
373741c280dStwelke 		(void) hsvc_unregister(&ncs_hsvc[ncs_version_index]);
374741c280dStwelke 		ncs_hsvc_available = B_FALSE;
375fec509a0Sgm 	}
376fec509a0Sgm 
377741c280dStwelke 	mutex_destroy(&n2rng->n_lock);
378fec509a0Sgm 	ddi_soft_state_free(n2rng_softstate, instance);
379fec509a0Sgm 
380fec509a0Sgm 	return (rv);
381fec509a0Sgm }
382fec509a0Sgm 
383fec509a0Sgm /*ARGSUSED*/
384fec509a0Sgm static int
n2rng_suspend(n2rng_t * n2rng)385fec509a0Sgm n2rng_suspend(n2rng_t *n2rng)
386fec509a0Sgm {
387741c280dStwelke 	/* unregister with KCF---also tears down FIPS state */
388741c280dStwelke 	if (n2rng_uninit(n2rng) != DDI_SUCCESS) {
389741c280dStwelke 		cmn_err(CE_WARN, "n2rng: unable to unregister from KCF");
390741c280dStwelke 		return (DDI_FAILURE);
391741c280dStwelke 	}
392741c280dStwelke 
393fec509a0Sgm 	return (DDI_SUCCESS);
394fec509a0Sgm }
395fec509a0Sgm 
396fec509a0Sgm /*ARGSUSED*/
397fec509a0Sgm static int
n2rng_resume(n2rng_t * n2rng)398fec509a0Sgm n2rng_resume(n2rng_t *n2rng)
399fec509a0Sgm {
400741c280dStwelke 	/* Assume clock is same speed and all data structures are intact */
401fec509a0Sgm 
402741c280dStwelke 	/* Re-configure the RNG hardware and register with KCF */
403741c280dStwelke 	return (n2rng_config(n2rng));
404fec509a0Sgm }
405fec509a0Sgm 
406fec509a0Sgm /*
407fec509a0Sgm  * Map hypervisor error code to solaris. Only
408fec509a0Sgm  * H_ENORADDR, H_EBADALIGN, H_EWOULDBLOCK, and EIO
409fec509a0Sgm  * are meaningful to this device. Any other error
410fec509a0Sgm  * codes are mapped EINVAL.
411fec509a0Sgm  */
412fec509a0Sgm int
n2rng_herr2kerr(uint64_t hv_errcode)413fec509a0Sgm n2rng_herr2kerr(uint64_t hv_errcode)
414fec509a0Sgm {
415fec509a0Sgm 	int	s_errcode;
416fec509a0Sgm 
417fec509a0Sgm 	switch (hv_errcode) {
418fec509a0Sgm 	case H_EWOULDBLOCK:
419fec509a0Sgm 		s_errcode = EWOULDBLOCK;
420fec509a0Sgm 		break;
421fec509a0Sgm 	case H_EIO:
422fec509a0Sgm 		s_errcode = EIO;
423fec509a0Sgm 		break;
424741c280dStwelke 	case H_EBUSY:
425741c280dStwelke 		s_errcode = EBUSY;
426741c280dStwelke 		break;
427fec509a0Sgm 	case H_EOK:
428fec509a0Sgm 		s_errcode = 0;
429fec509a0Sgm 		break;
430fec509a0Sgm 	case H_ENOACCESS:
431fec509a0Sgm 		s_errcode = EPERM;
432fec509a0Sgm 		break;
433741c280dStwelke 	case H_ENORADDR:
434741c280dStwelke 	case H_EBADALIGN:
435fec509a0Sgm 	default:
436fec509a0Sgm 		s_errcode = EINVAL;
437fec509a0Sgm 		break;
438fec509a0Sgm 	}
439fec509a0Sgm 	return (s_errcode);
440fec509a0Sgm }
441fec509a0Sgm 
442fec509a0Sgm /*
443fec509a0Sgm  * Waits approximately delay_sticks counts of the stick register.
444fec509a0Sgm  * Times shorter than one sys clock tick (10ms on most systems) are
445fec509a0Sgm  * done by busy waiting.
446fec509a0Sgm  */
447fec509a0Sgm void
cyclesleep(n2rng_t * n2rng,uint64_t delay_sticks)448fec509a0Sgm cyclesleep(n2rng_t *n2rng, uint64_t delay_sticks)
449fec509a0Sgm {
450fec509a0Sgm 	uint64_t	end_stick = gettick() + delay_sticks;
451fec509a0Sgm 	int64_t		sticks_to_wait;
452fec509a0Sgm 	clock_t		sys_ticks_to_wait;
453fec509a0Sgm 	clock_t		usecs_to_wait;
454fec509a0Sgm 
455fec509a0Sgm 	/*CONSTCOND*/
456fec509a0Sgm 	while (1) {
457fec509a0Sgm 		sticks_to_wait = end_stick - gettick();
458fec509a0Sgm 		if (sticks_to_wait <= 0) {
459fec509a0Sgm 			return;
460fec509a0Sgm 		}
461fec509a0Sgm 
462fec509a0Sgm 		usecs_to_wait = sticks_to_wait / n2rng->n_sticks_per_usec;
463fec509a0Sgm 		sys_ticks_to_wait = drv_usectohz(usecs_to_wait);
464fec509a0Sgm 
465fec509a0Sgm 		if (sys_ticks_to_wait > 0) {
466fec509a0Sgm 			/* sleep */
467fec509a0Sgm 			delay(sys_ticks_to_wait);
468fec509a0Sgm 		} else if (usecs_to_wait > 0) {
469fec509a0Sgm 			/* busy wait */
470fec509a0Sgm 			drv_usecwait(usecs_to_wait);
471fec509a0Sgm 		}
472fec509a0Sgm 	}
473fec509a0Sgm }
474fec509a0Sgm 
475fec509a0Sgm static void
log_internal_errors(uint64_t hverr,char * fname)476fec509a0Sgm log_internal_errors(uint64_t hverr, char *fname)
477fec509a0Sgm {
478fec509a0Sgm 	switch (hverr) {
479fec509a0Sgm 	case H_EBADALIGN:
480fec509a0Sgm 		cmn_err(CE_WARN,
481fec509a0Sgm 		    "n2rng: internal alignment "
482fec509a0Sgm 		    "problem");
483fec509a0Sgm 		break;
484fec509a0Sgm 	case H_ENORADDR:
485fec509a0Sgm 		cmn_err(CE_WARN, "n2rng: internal "
486fec509a0Sgm 		    "invalid address");
487fec509a0Sgm 		break;
488741c280dStwelke 	case H_ENOACCESS:
489741c280dStwelke 		cmn_err(CE_WARN, "n2rng: access failure");
490741c280dStwelke 		break;
491741c280dStwelke 	case H_EWOULDBLOCK:
492741c280dStwelke 		cmn_err(CE_WARN, "n2rng: hardware busy");
493741c280dStwelke 		break;
494fec509a0Sgm 	default:
495fec509a0Sgm 		cmn_err(CE_NOTE,
496fec509a0Sgm 		    "n2rng: %s "
497fec509a0Sgm 		    "unexpectedly "
498fec509a0Sgm 		    "returned hverr %ld", fname, hverr);
499fec509a0Sgm 		break;
500fec509a0Sgm 	}
501fec509a0Sgm }
502fec509a0Sgm 
503fec509a0Sgm /*
504fec509a0Sgm  * Collects a buffer full of bits, using the specified setup. numbytes
505fec509a0Sgm  * must be a multiple of 8. If a sub-operation fails with EIO (handle
506fec509a0Sgm  * mismatch), returns EIO.  If collect_setupp is NULL, the current
507fec509a0Sgm  * setup is used.  If exit_setupp is NULL, the control configuratin
508fec509a0Sgm  * and state are not set at exit.  WARNING: the buffer must be 8-byte
509fec509a0Sgm  * aligned and in contiguous physical addresses.  Contiguousness is
510fec509a0Sgm  * not checked!
511fec509a0Sgm  */
512fec509a0Sgm int
n2rng_collect_diag_bits(n2rng_t * n2rng,int rngid,n2rng_setup_t * collect_setupp,void * buffer,int numbytes,n2rng_setup_t * exit_setupp,uint64_t exitstate)513741c280dStwelke n2rng_collect_diag_bits(n2rng_t *n2rng, int rngid,
514741c280dStwelke     n2rng_setup_t *collect_setupp, void *buffer, int numbytes,
515741c280dStwelke     n2rng_setup_t *exit_setupp, uint64_t exitstate)
516fec509a0Sgm {
517fec509a0Sgm 	int		rv;
518fec509a0Sgm 	int		override_rv = 0;
519fec509a0Sgm 	uint64_t	hverr;
520fec509a0Sgm 	int		i;
521fec509a0Sgm 	uint64_t	tdelta;
522fec509a0Sgm 	n2rng_setup_t	setupbuffer[2];
523fec509a0Sgm 	n2rng_setup_t	*setupcontigp;
524fec509a0Sgm 	uint64_t	setupphys;
525fec509a0Sgm 	int		numchunks;
526fec509a0Sgm 	boolean_t	rnglooping;
527741c280dStwelke 	int		busycount = 0;
528741c280dStwelke 	int		blockcount = 0;
529fec509a0Sgm 
530fec509a0Sgm 	if (numbytes % sizeof (uint64_t)) {
531fec509a0Sgm 		return (EINVAL);
532fec509a0Sgm 	}
533fec509a0Sgm 
534fec509a0Sgm 	if ((uint64_t)buffer % sizeof (uint64_t) != 0) {
535fec509a0Sgm 		return (EINVAL);
536fec509a0Sgm 	}
537fec509a0Sgm 
538fec509a0Sgm 	numchunks = ((numbytes / sizeof (uint64_t)) + RNG_DIAG_CHUNK_SIZE - 1)
539fec509a0Sgm 	    / RNG_DIAG_CHUNK_SIZE;
540fec509a0Sgm 	/*
541fec509a0Sgm 	 * Use setupbuffer[0] if it is contiguous, otherwise
542fec509a0Sgm 	 * setupbuffer[1].
543fec509a0Sgm 	 */
544fec509a0Sgm 	setupcontigp = &setupbuffer[
545fec509a0Sgm 	    CONTIGUOUS(&setupbuffer[0], n2rng_setup_t) ? 0 : 1];
546fec509a0Sgm 	setupphys = va_to_pa(setupcontigp);
547fec509a0Sgm 
548fec509a0Sgm 	/*
549fec509a0Sgm 	 * If a non-null collect_setupp pointer has been provided,
550fec509a0Sgm 	 * push the specified setup into the hardware.
551fec509a0Sgm 	 */
552fec509a0Sgm 	if (collect_setupp != NULL) {
553fec509a0Sgm 		/* copy the specified state to the aligned buffer */
554fec509a0Sgm 		*setupcontigp = *collect_setupp;
555fec509a0Sgm 		rnglooping = B_TRUE;
556fec509a0Sgm 		while (rnglooping) {
557741c280dStwelke 			hverr = n2rng_ctl_write(n2rng, rngid, setupphys,
558fec509a0Sgm 			    CTL_STATE_HEALTHCHECK,
559741c280dStwelke 			    n2rng->n_ctl_data->n_watchdog_cycles, &tdelta);
560fec509a0Sgm 			rv = n2rng_herr2kerr(hverr);
561fec509a0Sgm 			switch (hverr) {
562741c280dStwelke 			case H_EOK:
563fec509a0Sgm 				rnglooping = B_FALSE;
564fec509a0Sgm 				break;
565fec509a0Sgm 			case H_EIO: /* control yanked from us */
566fec509a0Sgm 			case H_ENOACCESS: /* We are not control domain */
567fec509a0Sgm 				return (rv);
568fec509a0Sgm 			case H_EWOULDBLOCK:
569741c280dStwelke 				/* Data currently not available, try again */
570741c280dStwelke 				if (++blockcount > RNG_MAX_BLOCK_ATTEMPTS) {
571741c280dStwelke 					DBG1(n2rng, DHEALTH,
572741c280dStwelke 					    "n2rng_collect_diag_bits(1) : "
573741c280dStwelke 					    "exceeded block count of %d",
574741c280dStwelke 					    RNG_MAX_BLOCK_ATTEMPTS);
575741c280dStwelke 					return (rv);
576741c280dStwelke 				} else {
577741c280dStwelke 					cyclesleep(n2rng, tdelta);
578741c280dStwelke 				}
579741c280dStwelke 				break;
580741c280dStwelke 			case H_EBUSY:
581741c280dStwelke 				/*
582741c280dStwelke 				 * A control write is already in progress.
583741c280dStwelke 				 * Note: This shouldn't happen since
584741c280dStwelke 				 * n2rng_ctl_write() waits for the
585741c280dStwelke 				 * write to complete.
586741c280dStwelke 				 */
587741c280dStwelke 				if (++busycount > RNG_MAX_BUSY_ATTEMPTS) {
588741c280dStwelke 					DBG1(n2rng, DHEALTH,
589741c280dStwelke 					    "n2rng_collect_diag_bits(1): "
590741c280dStwelke 					    "exceeded busy count of %d",
591741c280dStwelke 					    RNG_MAX_BUSY_ATTEMPTS);
592741c280dStwelke 					return (rv);
593741c280dStwelke 				} else {
594741c280dStwelke 					delay(RNG_RETRY_BUSY_DELAY);
595741c280dStwelke 				}
596fec509a0Sgm 				break;
597fec509a0Sgm 			default:
598fec509a0Sgm 				log_internal_errors(hverr, "hv_rng_ctl_write");
599fec509a0Sgm 				override_rv = rv;
600fec509a0Sgm 				goto restore_state;
601fec509a0Sgm 			}
602fec509a0Sgm 		} /* while (rnglooping) */
603fec509a0Sgm 	} /* if (collect_setupp != NULL) */
604fec509a0Sgm 
605fec509a0Sgm 	/* If the caller asks for some bytes, collect the data */
606fec509a0Sgm 	if (numbytes > 0) {
607fec509a0Sgm 		for (i = 0; i < numchunks; i++) {
608fec509a0Sgm 			size_t thisnumbytes = (i == numchunks - 1) ?
609fec509a0Sgm 			    numbytes - i * (RNG_DIAG_CHUNK_SIZE *
610fec509a0Sgm 			    sizeof (uint64_t)) :
611fec509a0Sgm 			    RNG_DIAG_CHUNK_SIZE * sizeof (uint64_t);
612741c280dStwelke 
613fec509a0Sgm 			/* try until we successfully read a word of data */
614fec509a0Sgm 			rnglooping = B_TRUE;
615741c280dStwelke 			busycount = 0;
616741c280dStwelke 			blockcount = 0;
617fec509a0Sgm 			while (rnglooping) {
618741c280dStwelke 				hverr = n2rng_data_read_diag(n2rng, rngid,
619fec509a0Sgm 				    va_to_pa((uint64_t *)buffer +
620fec509a0Sgm 				    RNG_DIAG_CHUNK_SIZE * i),
621fec509a0Sgm 				    thisnumbytes, &tdelta);
622fec509a0Sgm 				rv = n2rng_herr2kerr(hverr);
623fec509a0Sgm 				switch (hverr) {
624741c280dStwelke 				case H_EOK:
625fec509a0Sgm 					rnglooping = B_FALSE;
626fec509a0Sgm 					break;
627fec509a0Sgm 				case H_EIO:
628fec509a0Sgm 				case H_ENOACCESS:
629fec509a0Sgm 					return (rv);
630fec509a0Sgm 				case H_EWOULDBLOCK:
631741c280dStwelke 					/* Data not available, try again */
632741c280dStwelke 					if (++blockcount >
633741c280dStwelke 					    RNG_MAX_BLOCK_ATTEMPTS) {
634741c280dStwelke 						DBG1(n2rng, DHEALTH,
635741c280dStwelke 						    "n2rng_collect_diag_bits"
636741c280dStwelke 						    "(2): exceeded block count"
637741c280dStwelke 						    " of %d",
638741c280dStwelke 						    RNG_MAX_BLOCK_ATTEMPTS);
639741c280dStwelke 						return (rv);
640741c280dStwelke 					} else {
641741c280dStwelke 						cyclesleep(n2rng, tdelta);
642741c280dStwelke 					}
643fec509a0Sgm 					break;
644fec509a0Sgm 				default:
645fec509a0Sgm 					log_internal_errors(hverr,
646fec509a0Sgm 					    "hv_rng_data_read_diag");
647fec509a0Sgm 					override_rv = rv;
648fec509a0Sgm 					goto restore_state;
649fec509a0Sgm 				}
650fec509a0Sgm 			} /* while (!rnglooping) */
651fec509a0Sgm 		} /* for */
652741c280dStwelke 	}
653fec509a0Sgm 
654fec509a0Sgm restore_state:
655fec509a0Sgm 
656fec509a0Sgm 	/* restore the preferred configuration and set exit state */
657fec509a0Sgm 	if (exit_setupp != NULL) {
658fec509a0Sgm 
659fec509a0Sgm 		*setupcontigp = *exit_setupp;
660fec509a0Sgm 		rnglooping = B_TRUE;
661741c280dStwelke 		busycount = 0;
662741c280dStwelke 		blockcount = 0;
663fec509a0Sgm 		while (rnglooping) {
664741c280dStwelke 			hverr = n2rng_ctl_write(n2rng, rngid, setupphys,
665741c280dStwelke 			    exitstate, n2rng->n_ctl_data->n_watchdog_cycles,
666741c280dStwelke 			    &tdelta);
667fec509a0Sgm 			rv = n2rng_herr2kerr(hverr);
668fec509a0Sgm 			switch (hverr) {
669741c280dStwelke 			case H_EOK:
670fec509a0Sgm 			case H_EIO: /* control yanked from us */
671fec509a0Sgm 			case H_EINVAL: /* some external error, probably */
672fec509a0Sgm 			case H_ENOACCESS: /* We are not control domain */
673fec509a0Sgm 				rnglooping = B_FALSE;
674fec509a0Sgm 				break;
675fec509a0Sgm 			case H_EWOULDBLOCK:
676741c280dStwelke 				/* Data currently not available, try again */
677741c280dStwelke 				if (++blockcount > RNG_MAX_BLOCK_ATTEMPTS) {
678741c280dStwelke 					DBG1(n2rng, DHEALTH,
679741c280dStwelke 					    "n2rng_collect_diag_bits(3): "
680741c280dStwelke 					    "exceeded block count of %d",
681741c280dStwelke 					    RNG_MAX_BLOCK_ATTEMPTS);
682741c280dStwelke 					return (rv);
683741c280dStwelke 				} else {
684741c280dStwelke 					cyclesleep(n2rng, tdelta);
685741c280dStwelke 				}
686741c280dStwelke 				break;
687741c280dStwelke 			case H_EBUSY:
688741c280dStwelke 				/*
689741c280dStwelke 				 * A control write is already in progress.
690741c280dStwelke 				 * Note: This shouldn't happen since
691741c280dStwelke 				 * n2rng_ctl_write() waits for the
692741c280dStwelke 				 * write to complete.
693741c280dStwelke 				 */
694741c280dStwelke 				if (++busycount > RNG_MAX_BUSY_ATTEMPTS) {
695741c280dStwelke 					DBG1(n2rng, DHEALTH,
696741c280dStwelke 					    "n2rng_collect_diag_bits(3): "
697741c280dStwelke 					    "exceeded busy count of %d",
698741c280dStwelke 					    RNG_MAX_BUSY_ATTEMPTS);
699741c280dStwelke 					return (rv);
700741c280dStwelke 				} else {
701741c280dStwelke 					delay(RNG_RETRY_BUSY_DELAY);
702741c280dStwelke 				}
703fec509a0Sgm 				break;
704fec509a0Sgm 			default:
705fec509a0Sgm 				rnglooping = B_FALSE;
706fec509a0Sgm 				log_internal_errors(hverr, "hv_rng_ctl_write");
707fec509a0Sgm 				break;
708fec509a0Sgm 			}
709fec509a0Sgm 		} /* while */
710fec509a0Sgm 	} /* if */
711fec509a0Sgm 
712fec509a0Sgm 	/*
713fec509a0Sgm 	 * override_rv takes care of the case where we abort becuase
714fec509a0Sgm 	 * of some error, but still want to restore the peferred state
715fec509a0Sgm 	 * and return the first error, even if other error occur.
716fec509a0Sgm 	 */
717fec509a0Sgm 	return (override_rv ? override_rv : rv);
718fec509a0Sgm }
719fec509a0Sgm 
720fec509a0Sgm int
n2rng_getentropy(n2rng_t * n2rng,void * buffer,size_t size)721fec509a0Sgm n2rng_getentropy(n2rng_t *n2rng, void *buffer, size_t size)
722fec509a0Sgm {
723fec509a0Sgm 	int		i, rv = 0;  /* so it works if size is zero */
724fec509a0Sgm 	uint64_t	hverr;
725fec509a0Sgm 	uint64_t	*buffer_w = (uint64_t *)buffer;
726fec509a0Sgm 	int		num_w = size / sizeof (uint64_t);
727fec509a0Sgm 	uint64_t	randval;
728fec509a0Sgm 	uint64_t	randvalphys = va_to_pa(&randval);
729fec509a0Sgm 	uint64_t	tdelta;
730fec509a0Sgm 	int		failcount = 0;
731741c280dStwelke 	int		blockcount = 0;
732fec509a0Sgm 	boolean_t	rnglooping;
733fec509a0Sgm 
734fec509a0Sgm 	for (i = 0; i < num_w; i++) {
735fec509a0Sgm 		rnglooping = B_TRUE;
736fec509a0Sgm 		while (rnglooping) {
737fec509a0Sgm 			hverr = hv_rng_data_read(randvalphys, &tdelta);
738fec509a0Sgm 			rv = n2rng_herr2kerr(hverr);
739fec509a0Sgm 			switch (hverr) {
740fec509a0Sgm 			case H_EOK:
741fec509a0Sgm 				buffer_w[i] = randval;
742fec509a0Sgm 				failcount = 0;
743fec509a0Sgm 				rnglooping = B_FALSE;
744fec509a0Sgm 				break;
745fec509a0Sgm 			case H_EIO:
746fec509a0Sgm 				/*
747741c280dStwelke 				 * Either a health check is in progress, or
748741c280dStwelke 				 * the watchdog timer has expired while running
749741c280dStwelke 				 * hv api version 2.0 or higher with health
750741c280dStwelke 				 * checks enabled.
751fec509a0Sgm 				 */
752741c280dStwelke 				if (n2rng->n_hvapi_major_version < 2) {
753741c280dStwelke 					/*
754741c280dStwelke 					 * A health check is in progress.
755741c280dStwelke 					 * Wait RNG_RETRY_HLCHK_USECS and fail
756741c280dStwelke 					 * after RNG_MAX_DATA_READ_ATTEMPTS
757741c280dStwelke 					 * failures.
758741c280dStwelke 					 */
759741c280dStwelke 					if (++failcount >
760741c280dStwelke 					    RNG_MAX_DATA_READ_ATTEMPTS) {
761741c280dStwelke 						DBG2(n2rng, DHEALTH,
762741c280dStwelke 						    "n2rng_getentropy: exceeded"
763741c280dStwelke 						    "EIO count of %d on cpu %d",
764741c280dStwelke 						    RNG_MAX_DATA_READ_ATTEMPTS,
765741c280dStwelke 						    CPU->cpu_id);
766741c280dStwelke 						goto exitpoint;
767741c280dStwelke 					} else {
768741c280dStwelke 						delay(drv_usectohz
769741c280dStwelke 						    (RNG_RETRY_HLCHK_USECS));
770741c280dStwelke 					}
771fec509a0Sgm 				} else {
772741c280dStwelke 					/*
773741c280dStwelke 					 * Just return the error. If a flurry of
774741c280dStwelke 					 * random data requests happen to occur
775741c280dStwelke 					 * during a health check, there are
776741c280dStwelke 					 * multiple levels of defense:
777741c280dStwelke 					 * - 2.0 HV provides random data pool
778741c280dStwelke 					 * - FIPS algorithm tolerates failures
779741c280dStwelke 					 * - Software failover
780741c280dStwelke 					 * - Automatic configuration retries
781741c280dStwelke 					 * - Hardware failover on some systems
782741c280dStwelke 					 */
783741c280dStwelke 					goto exitpoint;
784fec509a0Sgm 				}
785fec509a0Sgm 				break;
786fec509a0Sgm 			case H_EWOULDBLOCK:
787741c280dStwelke 				/* Data currently not available, try again */
788741c280dStwelke 				if (++blockcount > RNG_MAX_BLOCK_ATTEMPTS) {
789741c280dStwelke 					DBG1(n2rng, DHEALTH,
790741c280dStwelke 					    "n2rng_getentropy: "
791741c280dStwelke 					    "exceeded block count of %d",
792741c280dStwelke 					    RNG_MAX_BLOCK_ATTEMPTS);
793741c280dStwelke 					goto exitpoint;
794741c280dStwelke 				} else {
795741c280dStwelke 					cyclesleep(n2rng, tdelta);
796741c280dStwelke 				}
797fec509a0Sgm 				break;
798fec509a0Sgm 			default:
799fec509a0Sgm 				log_internal_errors(hverr, "hv_rng_data_read");
800fec509a0Sgm 				goto exitpoint;
801fec509a0Sgm 			}
802fec509a0Sgm 		} /* while */
803fec509a0Sgm 	} /* for */
804fec509a0Sgm 
805fec509a0Sgm exitpoint:
806741c280dStwelke 	return (rv);
807741c280dStwelke }
808741c280dStwelke 
809741c280dStwelke uint64_t
n2rng_ctl_read(n2rng_t * n2rng,int rngid,uint64_t ctlregs_pa,uint64_t * state,uint64_t * tdelta,uint64_t * wdelta)810741c280dStwelke n2rng_ctl_read(n2rng_t *n2rng, int rngid, uint64_t ctlregs_pa, uint64_t *state,
811741c280dStwelke     uint64_t *tdelta, uint64_t *wdelta)
812741c280dStwelke {
813741c280dStwelke 	uint64_t	rv;
814741c280dStwelke 	uint64_t	wstatus;
815741c280dStwelke 
816741c280dStwelke 	/* Call correct hv function based on api version */
817741c280dStwelke 	if (n2rng->n_hvapi_major_version == 2) {
818741c280dStwelke 		rv = hv_rng_ctl_read_v2(ctlregs_pa, (uint64_t)rngid, state,
819741c280dStwelke 		    tdelta, wdelta, &wstatus);
820741c280dStwelke 		if (rv == 0) {
821741c280dStwelke 			rv = wstatus;
822741c280dStwelke 		}
823741c280dStwelke 	} else {
824741c280dStwelke 		rv = hv_rng_ctl_read(ctlregs_pa, state, tdelta);
825741c280dStwelke 		*wdelta = 0;
826741c280dStwelke 	}
827741c280dStwelke 
828741c280dStwelke 	return (rv);
829741c280dStwelke }
830741c280dStwelke 
831741c280dStwelke uint64_t
n2rng_ctl_wait(n2rng_t * n2rng,int rngid)832741c280dStwelke n2rng_ctl_wait(n2rng_t *n2rng, int rngid)
833741c280dStwelke {
834741c280dStwelke 	uint64_t	state;
835741c280dStwelke 	uint64_t	tdelta;
836741c280dStwelke 	uint64_t	wdelta;
837741c280dStwelke 	uint64_t	wstatus;
838741c280dStwelke 	boolean_t	rnglooping = B_TRUE;
839741c280dStwelke 	uint64_t	rv;
840741c280dStwelke 	n2rng_setup_t	setupbuffer[2];
841741c280dStwelke 	n2rng_setup_t	*setupcontigp;
842741c280dStwelke 	uint64_t	setupphys;
843741c280dStwelke 	int		busycount = 0;
844741c280dStwelke 	int		blockcount = 0;
845741c280dStwelke 
846741c280dStwelke 	/*
847741c280dStwelke 	 * Use setupbuffer[0] if it is contiguous, otherwise
848741c280dStwelke 	 * setupbuffer[1].
849741c280dStwelke 	 */
850741c280dStwelke 	setupcontigp = &setupbuffer[
851741c280dStwelke 	    CONTIGUOUS(&setupbuffer[0], n2rng_setup_t) ? 0 : 1];
852741c280dStwelke 	setupphys = va_to_pa(setupcontigp);
853741c280dStwelke 
854741c280dStwelke 	while (rnglooping) {
855741c280dStwelke 		rv = hv_rng_ctl_read_v2(setupphys, (uint64_t)rngid, &state,
856741c280dStwelke 		    &tdelta, &wdelta, &wstatus);
857741c280dStwelke 		switch (rv) {
858741c280dStwelke 		case H_EOK:
859741c280dStwelke 			rv = wstatus;
860741c280dStwelke 			rnglooping = B_FALSE;
861741c280dStwelke 			break;
862741c280dStwelke 		case H_EWOULDBLOCK:
863741c280dStwelke 			/* Data currently not available, try again */
864741c280dStwelke 			if (++blockcount > RNG_MAX_BLOCK_ATTEMPTS) {
865741c280dStwelke 				DBG1(n2rng, DHEALTH, "n2rng_ctl_wait: "
866741c280dStwelke 				    "exceeded block count of %d",
867741c280dStwelke 				    RNG_MAX_BLOCK_ATTEMPTS);
868741c280dStwelke 				return (rv);
869741c280dStwelke 			} else {
870741c280dStwelke 				cyclesleep(n2rng, tdelta);
871741c280dStwelke 			}
872741c280dStwelke 			break;
873741c280dStwelke 		case H_EBUSY:
874741c280dStwelke 			/* Control write still pending, try again */
875741c280dStwelke 			if (++busycount > RNG_MAX_BUSY_ATTEMPTS) {
876741c280dStwelke 				DBG1(n2rng, DHEALTH, "n2rng_ctl_wait: "
877741c280dStwelke 				    "exceeded busy count of %d",
878741c280dStwelke 				    RNG_MAX_BUSY_ATTEMPTS);
879741c280dStwelke 				return (rv);
880741c280dStwelke 			} else {
881741c280dStwelke 				delay(RNG_RETRY_BUSY_DELAY);
882741c280dStwelke 			}
883741c280dStwelke 			break;
884741c280dStwelke 		default:
885741c280dStwelke 			log_internal_errors(rv, "n2rng_ctl_wait");
886741c280dStwelke 			rnglooping = B_FALSE;
887741c280dStwelke 		}
888741c280dStwelke 	} /* while (rnglooping) */
889741c280dStwelke 
890741c280dStwelke 	return (rv);
891741c280dStwelke }
892741c280dStwelke 
893741c280dStwelke uint64_t
n2rng_ctl_write(n2rng_t * n2rng,int rngid,uint64_t ctlregs_pa,uint64_t newstate,uint64_t wtimeout,uint64_t * tdelta)894741c280dStwelke n2rng_ctl_write(n2rng_t *n2rng, int rngid, uint64_t ctlregs_pa,
895741c280dStwelke     uint64_t newstate, uint64_t wtimeout, uint64_t *tdelta)
896741c280dStwelke {
897741c280dStwelke 	uint64_t	rv;
898741c280dStwelke 
899741c280dStwelke 	/* Call correct hv function based on api version */
900741c280dStwelke 	if (n2rng->n_hvapi_major_version == 2) {
901741c280dStwelke 		rv = hv_rng_ctl_write_v2(ctlregs_pa, newstate, wtimeout,
902741c280dStwelke 		    (uint64_t)rngid);
903741c280dStwelke 		if (rv == H_EOK) {
904741c280dStwelke 			/* Wait for control registers to be written */
905741c280dStwelke 			rv = n2rng_ctl_wait(n2rng, rngid);
906741c280dStwelke 		}
907741c280dStwelke 		*tdelta = RNG_DEFAULT_ACCUMULATE_CYCLES;
908741c280dStwelke 	} else {
909741c280dStwelke 		rv = hv_rng_ctl_write(ctlregs_pa, newstate, wtimeout, tdelta);
910741c280dStwelke 	}
911741c280dStwelke 
912741c280dStwelke 	return (rv);
913741c280dStwelke }
914741c280dStwelke 
915741c280dStwelke uint64_t
n2rng_data_read_diag(n2rng_t * n2rng,int rngid,uint64_t data_pa,size_t datalen,uint64_t * tdelta)916741c280dStwelke n2rng_data_read_diag(n2rng_t *n2rng, int rngid, uint64_t data_pa,
917741c280dStwelke     size_t  datalen, uint64_t *tdelta)
918741c280dStwelke {
919741c280dStwelke 	uint64_t	rv;
920741c280dStwelke 
921741c280dStwelke 	/* Call correct hv function based on api version */
922741c280dStwelke 	if (n2rng->n_hvapi_major_version == 2) {
923741c280dStwelke 		rv = hv_rng_data_read_diag_v2(data_pa, datalen,
924741c280dStwelke 		    (uint64_t)rngid, tdelta);
925741c280dStwelke 		if (*tdelta == 0) {
926741c280dStwelke 			*tdelta = RNG_DEFAULT_ACCUMULATE_CYCLES;
927741c280dStwelke 		}
928741c280dStwelke 	} else {
929741c280dStwelke 		rv = hv_rng_data_read_diag(data_pa, datalen, tdelta);
930741c280dStwelke 	}
931fec509a0Sgm 
932fec509a0Sgm 	return (rv);
933fec509a0Sgm }
934fec509a0Sgm 
935741c280dStwelke uint64_t
n2rng_check_ctl_access(n2rng_t * n2rng)936741c280dStwelke n2rng_check_ctl_access(n2rng_t *n2rng)
937741c280dStwelke {
938741c280dStwelke 	uint64_t	rv;
939741c280dStwelke 	uint64_t	unused_64;
940741c280dStwelke 
941741c280dStwelke 	/* Call correct hv function based on api version */
942741c280dStwelke 	if (n2rng->n_hvapi_major_version == 2) {
943741c280dStwelke 		/*
944741c280dStwelke 		 * Attempt to read control registers with invalid ID and data
945741c280dStwelke 		 * just to see if we get an access error
946741c280dStwelke 		 */
947*03b8b03eSToomas Soome 		rv = hv_rng_ctl_read_v2(0, N2RNG_INVALID_ID,
948741c280dStwelke 		    &unused_64, &unused_64, &unused_64, &unused_64);
949741c280dStwelke 	} else {
950741c280dStwelke 		rv = hv_rng_get_diag_control();
951741c280dStwelke 	}
952741c280dStwelke 
953741c280dStwelke 	return (rv);
954741c280dStwelke }
955741c280dStwelke 
956741c280dStwelke /*
957741c280dStwelke  * n2rng_config_retry()
958741c280dStwelke  *
959741c280dStwelke  * Schedule a timed call to n2rng_config() if one is not already pending
960741c280dStwelke  */
961741c280dStwelke void
n2rng_config_retry(n2rng_t * n2rng,clock_t seconds)962741c280dStwelke n2rng_config_retry(n2rng_t *n2rng, clock_t seconds)
963741c280dStwelke {
964741c280dStwelke 	mutex_enter(&n2rng->n_lock);
965741c280dStwelke 	/* Check if a config retry is already pending */
966741c280dStwelke 	if (n2rng->n_timeout_id) {
967741c280dStwelke 		DBG1(n2rng, DCFG, "n2rng_config_retry: retry pending "
968741c280dStwelke 		    "id = %x", n2rng->n_timeout_id);
969741c280dStwelke 	} else {
970741c280dStwelke 		n2rng->n_timeout_id = timeout(n2rng_config_task,
971741c280dStwelke 		    (void *)n2rng, drv_usectohz(seconds * SECOND));
972741c280dStwelke 		DBG2(n2rng, DCFG, "n2rng_config_retry: retry scheduled in "
973741c280dStwelke 		    "%d seconds, id = %x", seconds, n2rng->n_timeout_id);
974741c280dStwelke 	}
975741c280dStwelke 	mutex_exit(&n2rng->n_lock);
976741c280dStwelke }
977741c280dStwelke 
978fec509a0Sgm static uint64_t
sticks_per_usec(void)979fec509a0Sgm sticks_per_usec(void)
980fec509a0Sgm {
981fec509a0Sgm 	uint64_t starttick = gettick();
982fec509a0Sgm 	hrtime_t starttime = gethrtime();
983fec509a0Sgm 	uint64_t endtick;
984fec509a0Sgm 	hrtime_t endtime;
985fec509a0Sgm 
986fec509a0Sgm 	delay(2);
987fec509a0Sgm 
988fec509a0Sgm 	endtick = gettick();
989fec509a0Sgm 	endtime = gethrtime();
990fec509a0Sgm 
991fec509a0Sgm 	return ((1000 * (endtick - starttick)) / (endtime - starttime));
992fec509a0Sgm }
99359ac0c16Sdavemq 
994741c280dStwelke static int
n2rng_init_ctl(n2rng_t * n2rng)995741c280dStwelke n2rng_init_ctl(n2rng_t *n2rng)
996741c280dStwelke {
997741c280dStwelke 	int		rv;
998741c280dStwelke 	int		hverr;
999741c280dStwelke 	rng_entry_t	*rng;
1000741c280dStwelke 	int		rngid;
1001741c280dStwelke 	int		blockcount = 0;
1002741c280dStwelke 
1003741c280dStwelke 	n2rng->n_ctl_data = NULL;
1004741c280dStwelke 
1005741c280dStwelke 	/* Attempt to gain diagnostic control */
1006741c280dStwelke 	do {
1007741c280dStwelke 		hverr = n2rng_check_ctl_access(n2rng);
1008741c280dStwelke 		rv = n2rng_herr2kerr(hverr);
1009741c280dStwelke 		if ((hverr == H_EWOULDBLOCK) &&
1010741c280dStwelke 		    (++blockcount > RNG_MAX_BUSY_ATTEMPTS)) {
1011741c280dStwelke 			DBG1(n2rng, DHEALTH, "n2rng_int_ctl: exceeded busy "
1012741c280dStwelke 			    "count of %d", RNG_MAX_BUSY_ATTEMPTS);
1013741c280dStwelke 			return (rv);
1014741c280dStwelke 		} else {
1015741c280dStwelke 			delay(RNG_RETRY_BUSY_DELAY);
1016741c280dStwelke 		}
1017741c280dStwelke 	} while (hverr == H_EWOULDBLOCK);
1018741c280dStwelke 
1019741c280dStwelke 	/*
1020741c280dStwelke 	 * If attempt fails with EPERM, the driver is not running in the
1021741c280dStwelke 	 * control domain
1022741c280dStwelke 	 */
1023741c280dStwelke 	if (rv == EPERM) {
1024741c280dStwelke 		DBG0(n2rng, DATTACH,
1025741c280dStwelke 		    "n2rng_init_ctl: Running in guest domain");
1026741c280dStwelke 		return (DDI_SUCCESS);
1027741c280dStwelke 	}
1028741c280dStwelke 
1029741c280dStwelke 	/* Allocate control stucture only used in control domain */
1030741c280dStwelke 	n2rng->n_ctl_data = kmem_alloc(sizeof (rng_ctl_data_t), KM_SLEEP);
1031741c280dStwelke 	n2rng->n_ctl_data->n_num_rngs_online = 0;
1032741c280dStwelke 
1033741c280dStwelke 	/*
1034741c280dStwelke 	 * If running with an API version less than 2.0 default to one rng.
1035741c280dStwelke 	 * Otherwise get number of rngs from device properties.
1036741c280dStwelke 	 */
1037741c280dStwelke 	if (n2rng->n_hvapi_major_version < 2) {
1038741c280dStwelke 		n2rng->n_ctl_data->n_num_rngs = 1;
1039741c280dStwelke 	} else {
1040741c280dStwelke 		n2rng->n_ctl_data->n_num_rngs =
1041741c280dStwelke 		    ddi_getprop(DDI_DEV_T_ANY, n2rng->n_dip,
1042741c280dStwelke 		    DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS,
1043741c280dStwelke 		    N2RNG_PROP_NUM_UNITS, 0);
1044741c280dStwelke 		if (n2rng->n_ctl_data->n_num_rngs == 0) {
1045741c280dStwelke 			cmn_err(CE_WARN, "n2rng: %s property not found",
1046741c280dStwelke 			    N2RNG_PROP_NUM_UNITS);
1047741c280dStwelke 			return (DDI_FAILURE);
1048741c280dStwelke 		}
1049741c280dStwelke 	}
1050741c280dStwelke 
1051741c280dStwelke 	/* Allocate space for all rng entries */
1052741c280dStwelke 	n2rng->n_ctl_data->n_rngs =
1053741c280dStwelke 	    kmem_zalloc(n2rng->n_ctl_data->n_num_rngs *
1054741c280dStwelke 	    sizeof (rng_entry_t), KM_SLEEP);
1055741c280dStwelke 
1056741c280dStwelke 	/* Get accumulate cycles from .conf file. */
1057741c280dStwelke 	n2rng->n_ctl_data->n_accumulate_cycles =
1058741c280dStwelke 	    ddi_getprop(DDI_DEV_T_ANY, n2rng->n_dip,
1059741c280dStwelke 	    DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "acc_cycles",
1060741c280dStwelke 	    RNG_DEFAULT_ACCUMULATE_CYCLES);
1061741c280dStwelke 
1062741c280dStwelke 	/* Get health check frequency from .conf file */
1063741c280dStwelke 	n2rng->n_ctl_data->n_hc_secs = ddi_getprop(DDI_DEV_T_ANY, n2rng->n_dip,
1064741c280dStwelke 	    DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "hc_seconds",
1065741c280dStwelke 	    RNG_DEFAULT_HC_SECS);
1066741c280dStwelke 
1067741c280dStwelke 	/* API versions prior to 2.0 do not support health checks */
1068741c280dStwelke 	if ((n2rng->n_hvapi_major_version < 2) &&
1069741c280dStwelke 	    (n2rng->n_ctl_data->n_hc_secs > 0)) {
1070741c280dStwelke 		cmn_err(CE_WARN, "n2rng: Hyperviser api "
1071741c280dStwelke 		    "version %d.%d does not support health checks",
1072741c280dStwelke 		    n2rng->n_hvapi_major_version,
1073741c280dStwelke 		    n2rng->n_hvapi_minor_version);
1074741c280dStwelke 		n2rng->n_ctl_data->n_hc_secs = 0;
1075741c280dStwelke 	}
1076741c280dStwelke 
107732e0ab73SMisaki Miyashita 
1078741c280dStwelke 	/* Calculate watchdog timeout value */
1079741c280dStwelke 	if (n2rng->n_ctl_data->n_hc_secs <= 0) {
1080741c280dStwelke 		n2rng->n_ctl_data->n_watchdog_cycles = 0;
1081741c280dStwelke 	} else {
1082741c280dStwelke 		n2rng->n_ctl_data->n_watchdog_cycles =
1083741c280dStwelke 		    ((uint64_t)(RNG_EXTRA_WATCHDOG_SECS) +
1084741c280dStwelke 		    n2rng->n_ctl_data->n_hc_secs) *
1085741c280dStwelke 		    n2rng->n_sticks_per_usec * 1000000;
1086741c280dStwelke 	}
1087741c280dStwelke 
1088741c280dStwelke 	/*
1089741c280dStwelke 	 * Set some plausible state into the preferred configuration.
1090741c280dStwelke 	 * The intent is that the health check will immediately overwrite it.
1091741c280dStwelke 	 */
1092741c280dStwelke 	for (rngid = 0; rngid < n2rng->n_ctl_data->n_num_rngs; rngid++) {
1093741c280dStwelke 
1094741c280dStwelke 		rng = &n2rng->n_ctl_data->n_rngs[rngid];
1095741c280dStwelke 
1096741c280dStwelke 		rng->n_preferred_config.ctlwds[0].word = 0;
1097741c280dStwelke 		rng->n_preferred_config.ctlwds[0].fields.rnc_anlg_sel =
1098741c280dStwelke 		    N2RNG_NOANALOGOUT;
1099741c280dStwelke 		rng->n_preferred_config.ctlwds[0].fields.rnc_cnt =
1100741c280dStwelke 		    RNG_DEFAULT_ACCUMULATE_CYCLES;
1101741c280dStwelke 		rng->n_preferred_config.ctlwds[0].fields.rnc_mode =
1102741c280dStwelke 		    RNG_MODE_NORMAL;
1103741c280dStwelke 		rng->n_preferred_config.ctlwds[1].word =
1104741c280dStwelke 		    rng->n_preferred_config.ctlwds[0].word;
1105741c280dStwelke 		rng->n_preferred_config.ctlwds[2].word =
1106741c280dStwelke 		    rng->n_preferred_config.ctlwds[0].word;
1107741c280dStwelke 		rng->n_preferred_config.ctlwds[3].word =
1108741c280dStwelke 		    rng->n_preferred_config.ctlwds[0].word;
1109741c280dStwelke 		rng->n_preferred_config.ctlwds[0].fields.rnc_vcoctl = 1;
1110741c280dStwelke 		rng->n_preferred_config.ctlwds[0].fields.rnc_selbits = 1;
1111741c280dStwelke 		rng->n_preferred_config.ctlwds[1].fields.rnc_vcoctl = 2;
1112741c280dStwelke 		rng->n_preferred_config.ctlwds[1].fields.rnc_selbits = 2;
1113741c280dStwelke 		rng->n_preferred_config.ctlwds[2].fields.rnc_vcoctl = 3;
1114741c280dStwelke 		rng->n_preferred_config.ctlwds[2].fields.rnc_selbits = 4;
1115741c280dStwelke 		rng->n_preferred_config.ctlwds[3].fields.rnc_vcoctl = 0;
1116741c280dStwelke 		rng->n_preferred_config.ctlwds[3].fields.rnc_selbits = 7;
1117741c280dStwelke 	}
1118741c280dStwelke 
1119741c280dStwelke 	n2rng_setcontrol(n2rng);
1120741c280dStwelke 	DBG2(n2rng, DATTACH,
1121741c280dStwelke 	    "n2rng_init_ctl: Running in control domain with %d rng device%s",
1122741c280dStwelke 	    n2rng->n_ctl_data->n_num_rngs,
1123741c280dStwelke 	    (n2rng->n_ctl_data->n_num_rngs == 1) ? "" : "s");
1124741c280dStwelke 	DBG2(n2rng, DCFG,
1125741c280dStwelke 	    "n2rng_init_ctl: n_sticks_per_usec = %ld, n_hc_secs = %d",
1126741c280dStwelke 	    n2rng->n_sticks_per_usec,
1127741c280dStwelke 	    n2rng->n_ctl_data->n_hc_secs);
1128741c280dStwelke 	DBG2(n2rng, DCFG,
1129741c280dStwelke 	    "n2rng_init_ctl: n_watchdog_cycles = %ld, "
1130741c280dStwelke 	    "n_accumulate_cycles = %ld", n2rng->n_ctl_data->n_watchdog_cycles,
1131741c280dStwelke 	    n2rng->n_ctl_data->n_accumulate_cycles);
1132741c280dStwelke 
1133741c280dStwelke 	return (DDI_SUCCESS);
1134741c280dStwelke }
1135741c280dStwelke 
1136741c280dStwelke static void
n2rng_uninit_ctl(n2rng_t * n2rng)1137741c280dStwelke n2rng_uninit_ctl(n2rng_t *n2rng)
1138741c280dStwelke {
1139741c280dStwelke 	if (n2rng->n_ctl_data) {
1140741c280dStwelke 		if (n2rng->n_ctl_data->n_num_rngs) {
1141741c280dStwelke 			kmem_free(n2rng->n_ctl_data->n_rngs,
1142741c280dStwelke 			    n2rng->n_ctl_data->n_num_rngs *
1143741c280dStwelke 			    sizeof (rng_entry_t));
1144741c280dStwelke 			n2rng->n_ctl_data->n_rngs = NULL;
1145741c280dStwelke 			n2rng->n_ctl_data->n_num_rngs = 0;
1146741c280dStwelke 		}
1147741c280dStwelke 		kmem_free(n2rng->n_ctl_data, sizeof (rng_ctl_data_t));
1148741c280dStwelke 		n2rng->n_ctl_data = NULL;
1149741c280dStwelke 	}
1150741c280dStwelke }
1151741c280dStwelke 
1152741c280dStwelke 
115359ac0c16Sdavemq /*
1154741c280dStwelke  * n2rng_config_test()
115559ac0c16Sdavemq  *
1156741c280dStwelke  * Attempt read random data to see if the rng is configured.
115759ac0c16Sdavemq  */
1158741c280dStwelke int
n2rng_config_test(n2rng_t * n2rng)1159741c280dStwelke n2rng_config_test(n2rng_t *n2rng)
1160741c280dStwelke {
1161741c280dStwelke 	int		rv = 0;
1162741c280dStwelke 	uint64_t	hverr;
1163741c280dStwelke 	uint64_t	randval = 0;
1164741c280dStwelke 	uint64_t	randvalphys = va_to_pa(&randval);
1165741c280dStwelke 	uint64_t	tdelta;
1166741c280dStwelke 	int		failcount = 0;
1167741c280dStwelke 	int		blockcount = 0;
1168741c280dStwelke 	boolean_t	rnglooping = B_TRUE;
1169741c280dStwelke 
1170741c280dStwelke 	while (rnglooping) {
1171741c280dStwelke 		hverr = hv_rng_data_read(randvalphys, &tdelta);
1172741c280dStwelke 		rv = n2rng_herr2kerr(hverr);
1173741c280dStwelke 		switch (hverr) {
1174741c280dStwelke 		case H_EOK:
1175741c280dStwelke 			failcount = 0;
1176741c280dStwelke 			rnglooping = B_FALSE;
1177741c280dStwelke 			break;
1178741c280dStwelke 		case H_EIO:
1179741c280dStwelke 			/*
1180741c280dStwelke 			 * A health check is in progress.
1181741c280dStwelke 			 * Wait RNG_RETRY_HLCHK_USECS and fail
1182741c280dStwelke 			 * after RNG_MAX_DATA_READ_ATTEMPTS
1183741c280dStwelke 			 * failures.
1184741c280dStwelke 			 */
1185741c280dStwelke 			if (++failcount > RNG_MAX_DATA_READ_ATTEMPTS) {
1186741c280dStwelke 				goto exitpoint;
1187741c280dStwelke 			} else {
1188741c280dStwelke 				delay(drv_usectohz(RNG_RETRY_HLCHK_USECS));
1189741c280dStwelke 			}
1190741c280dStwelke 			break;
1191741c280dStwelke 		case H_EWOULDBLOCK:
1192741c280dStwelke 			/* Data currently not available, try again */
1193741c280dStwelke 			if (++blockcount > RNG_MAX_BLOCK_ATTEMPTS) {
1194741c280dStwelke 				DBG1(n2rng, DHEALTH, "n2rng_config_test: "
1195741c280dStwelke 				    "exceeded block count of %d",
1196741c280dStwelke 				    RNG_MAX_BLOCK_ATTEMPTS);
1197741c280dStwelke 				goto exitpoint;
1198741c280dStwelke 			} else {
1199741c280dStwelke 				cyclesleep(n2rng, tdelta);
1200741c280dStwelke 			}
1201741c280dStwelke 			break;
1202741c280dStwelke 		case H_ENOACCESS:
1203741c280dStwelke 			/* An rng error has occured during health check */
1204741c280dStwelke 			goto exitpoint;
1205741c280dStwelke 		default:
1206741c280dStwelke 			log_internal_errors(hverr, "hv_rng_data_read");
1207741c280dStwelke 			goto exitpoint;
1208741c280dStwelke 		}
1209741c280dStwelke 	} /* while */
1210741c280dStwelke 
1211741c280dStwelke exitpoint:
1212741c280dStwelke 	return (rv);
1213741c280dStwelke }
1214741c280dStwelke 
1215741c280dStwelke /*
1216741c280dStwelke  * n2rng_config()
1217741c280dStwelke  *
1218741c280dStwelke  * Run health check on the RNG hardware
1219741c280dStwelke  * Configure the RNG hardware
1220741c280dStwelke  * Register with crypto framework
1221741c280dStwelke  */
1222741c280dStwelke static int
n2rng_config(n2rng_t * n2rng)1223741c280dStwelke n2rng_config(n2rng_t *n2rng)
122459ac0c16Sdavemq {
122559ac0c16Sdavemq 	int		rv;
1226741c280dStwelke 	rng_entry_t	*rng;
1227741c280dStwelke 	int		rngid;
122859ac0c16Sdavemq 
1229741c280dStwelke 	/*
1230741c280dStwelke 	 * Run health checks and configure rngs if running in control domain,
1231741c280dStwelke 	 * otherwise just check if at least one rng is available.
1232741c280dStwelke 	 */
1233741c280dStwelke 	if (n2rng_iscontrol(n2rng)) {
123459ac0c16Sdavemq 
1235741c280dStwelke 		for (rngid = 0; rngid < n2rng->n_ctl_data->n_num_rngs;
1236741c280dStwelke 		    rngid++) {
123759ac0c16Sdavemq 
1238741c280dStwelke 			rng = &n2rng->n_ctl_data->n_rngs[rngid];
1239741c280dStwelke 
1240741c280dStwelke 			/* Only test rngs that have not already failed */
1241741c280dStwelke 			if (rng->n_rng_state == CTL_STATE_ERROR) {
1242741c280dStwelke 				continue;
1243741c280dStwelke 			}
1244741c280dStwelke 
1245741c280dStwelke 			if ((n2rng->n_binding == N2RNG_CPU_VF) &&
1246741c280dStwelke 			    (n2rng->n_hvapi_major_version < 2)) {
1247741c280dStwelke 				/*
1248741c280dStwelke 				 * Since api versions prior to 2.0 do not
1249741c280dStwelke 				 * support multiple rngs, bind to the current
1250741c280dStwelke 				 * processor for the entire health check
1251741c280dStwelke 				 * process.
1252741c280dStwelke 				 */
1253741c280dStwelke 				thread_affinity_set(curthread, CPU_CURRENT);
1254741c280dStwelke 				DBG1(n2rng, DCFG, "n2rng_config: "
1255741c280dStwelke 				    "Configuring single rng from cpu %d",
1256741c280dStwelke 				    CPU->cpu_id);
1257741c280dStwelke 				rv = n2rng_do_health_check(n2rng, rngid);
1258741c280dStwelke 				thread_affinity_clear(curthread);
1259741c280dStwelke 			} else {
1260741c280dStwelke 				rv = n2rng_do_health_check(n2rng, rngid);
1261741c280dStwelke 			}
1262741c280dStwelke 
1263741c280dStwelke 			switch (rv) {
1264741c280dStwelke 			case 0:
1265741c280dStwelke 				/*
1266741c280dStwelke 				 * Successful, increment online count if
1267741c280dStwelke 				 * necessary
1268741c280dStwelke 				 */
1269741c280dStwelke 				DBG1(n2rng, DCFG, "n2rng_config: rng(%d) "
1270741c280dStwelke 				    "passed health checks", rngid);
1271741c280dStwelke 				if (rng->n_rng_state != CTL_STATE_CONFIGURED) {
1272741c280dStwelke 					rng->n_rng_state =
1273741c280dStwelke 					    CTL_STATE_CONFIGURED;
1274741c280dStwelke 					n2rng->n_ctl_data->n_num_rngs_online++;
1275741c280dStwelke 				}
1276741c280dStwelke 				break;
1277741c280dStwelke 			default:
1278741c280dStwelke 				/*
1279741c280dStwelke 				 * Health checks failed, decrement online
1280741c280dStwelke 				 * count if necessary
1281741c280dStwelke 				 */
1282741c280dStwelke 				cmn_err(CE_WARN, "n2rng: rng(%d) "
1283741c280dStwelke 				    "failed health checks", rngid);
1284741c280dStwelke 				if (rng->n_rng_state == CTL_STATE_CONFIGURED) {
1285741c280dStwelke 					n2rng->n_ctl_data->n_num_rngs_online--;
1286741c280dStwelke 				}
1287741c280dStwelke 				rng->n_rng_state = CTL_STATE_ERROR;
1288741c280dStwelke 				break;
1289741c280dStwelke 			}
1290741c280dStwelke 		}
1291741c280dStwelke 		DBG2(n2rng, DCFG, "n2rng_config: %d rng%s online",
1292741c280dStwelke 		    n2rng->n_ctl_data->n_num_rngs_online,
1293741c280dStwelke 		    (n2rng->n_ctl_data->n_num_rngs_online == 1) ? "" : "s");
1294741c280dStwelke 
1295741c280dStwelke 		/* Check if all rngs have failed */
1296741c280dStwelke 		if (n2rng->n_ctl_data->n_num_rngs_online == 0) {
1297741c280dStwelke 			cmn_err(CE_WARN, "n2rng: %d RNG device%s failed",
1298741c280dStwelke 			    n2rng->n_ctl_data->n_num_rngs,
1299741c280dStwelke 			    (n2rng->n_ctl_data->n_num_rngs == 1) ? "" : "s");
1300741c280dStwelke 			goto errorexit;
1301741c280dStwelke 		} else {
1302741c280dStwelke 			n2rng_setconfigured(n2rng);
1303741c280dStwelke 		}
1304741c280dStwelke 	} else {
1305741c280dStwelke 		/* Running in guest domain, just check if rng is configured */
1306741c280dStwelke 		rv = n2rng_config_test(n2rng);
1307741c280dStwelke 		switch (rv) {
1308741c280dStwelke 		case 0:
1309741c280dStwelke 			n2rng_setconfigured(n2rng);
1310741c280dStwelke 			break;
1311741c280dStwelke 		case EIO:
1312741c280dStwelke 			/* Don't set configured to force a retry */
1313741c280dStwelke 			break;
1314741c280dStwelke 		default:
1315741c280dStwelke 			goto errorexit;
1316741c280dStwelke 		}
131759ac0c16Sdavemq 	}
131859ac0c16Sdavemq 
1319741c280dStwelke 	/*
1320741c280dStwelke 	 * Initialize FIPS state and register with KCF if we have at least one
1321741c280dStwelke 	 * RNG configured.  Otherwise schedule a retry if all rngs have not
1322741c280dStwelke 	 * failed.
1323741c280dStwelke 	 */
1324741c280dStwelke 	if (n2rng_isconfigured(n2rng)) {
1325741c280dStwelke 
1326741c280dStwelke 		if (n2rng_init(n2rng) != DDI_SUCCESS) {
1327741c280dStwelke 			cmn_err(CE_WARN, "n2rng: unable to register with KCF");
1328741c280dStwelke 			goto errorexit;
1329741c280dStwelke 		}
1330741c280dStwelke 
1331741c280dStwelke 		/*
1332741c280dStwelke 		 * Schedule a retry if running in the control domain and a
1333741c280dStwelke 		 * health check time has been specified.
1334741c280dStwelke 		 */
1335741c280dStwelke 		if (n2rng_iscontrol(n2rng) &&
1336741c280dStwelke 		    (n2rng->n_ctl_data->n_hc_secs > 0)) {
1337741c280dStwelke 			n2rng_config_retry(n2rng,
1338741c280dStwelke 			    n2rng->n_ctl_data->n_hc_secs);
1339741c280dStwelke 		}
1340741c280dStwelke 	} else if (!n2rng_isfailed(n2rng)) {
1341741c280dStwelke 		/* Schedule a retry if one is not already pending */
1342741c280dStwelke 		n2rng_config_retry(n2rng, RNG_CFG_RETRY_SECS);
1343741c280dStwelke 	}
1344741c280dStwelke 	return (DDI_SUCCESS);
134559ac0c16Sdavemq 
134659ac0c16Sdavemq errorexit:
1347741c280dStwelke 	/* Unregister from kCF if we are registered */
1348741c280dStwelke 	(void) n2rng_unregister_provider(n2rng);
1349741c280dStwelke 	n2rng_setfailed(n2rng);
1350741c280dStwelke 	cmn_err(CE_WARN, "n2rng: hardware failure detected");
1351741c280dStwelke 	return (DDI_FAILURE);
1352741c280dStwelke }
1353741c280dStwelke 
1354741c280dStwelke /*
1355741c280dStwelke  * n2rng_config_task()
1356741c280dStwelke  *
1357741c280dStwelke  * Call n2rng_config() from the task queue or after a timeout, ignore result.
1358741c280dStwelke  */
1359741c280dStwelke static void
n2rng_config_task(void * targ)1360741c280dStwelke n2rng_config_task(void *targ)
1361741c280dStwelke {
1362741c280dStwelke 	n2rng_t *n2rng = (n2rng_t *)targ;
1363741c280dStwelke 
1364741c280dStwelke 	mutex_enter(&n2rng->n_lock);
1365741c280dStwelke 	n2rng->n_timeout_id = 0;
1366741c280dStwelke 	mutex_exit(&n2rng->n_lock);
1367741c280dStwelke 	(void) n2rng_config(n2rng);
136859ac0c16Sdavemq }
1369