xref: /titanic_51/usr/src/uts/sun4v/io/n2rng/n2rng.c (revision 002c70ff32f5df6f93c15f88d351ce26443e6ee7)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Niagara 2 Random Number Generator (RNG) driver
30  */
31 
32 #include <sys/types.h>
33 #include <sys/sysmacros.h>
34 #include <sys/modctl.h>
35 #include <sys/conf.h>
36 #include <sys/devops.h>
37 #include <sys/cmn_err.h>
38 #include <sys/ksynch.h>
39 #include <sys/kmem.h>
40 #include <sys/stat.h>
41 #include <sys/open.h>
42 #include <sys/file.h>
43 #include <sys/ddi.h>
44 #include <sys/sunddi.h>
45 #include <sys/param.h>
46 #include <sys/cpuvar.h>
47 #include <sys/disp.h>
48 #include <sys/hsvc.h>
49 #include <sys/machsystm.h>
50 #include <sys/hypervisor_api.h>
51 #include <sys/n2rng.h>
52 
53 static int	n2rng_attach(dev_info_t *, ddi_attach_cmd_t);
54 static int	n2rng_detach(dev_info_t *, ddi_detach_cmd_t);
55 static int	n2rng_suspend(n2rng_t *);
56 static int	n2rng_resume(n2rng_t *);
57 int	n2rng_herr2kerr(uint64_t);
58 int	n2rng_logic_test(n2rng_t *);
59 int	n2rng_noise_gen_test_set(void);
60 int	n2rng_init(n2rng_t *n2rng);
61 int	n2rng_uninit(n2rng_t *n2rng);
62 
63 static uint64_t sticks_per_usec(void);
64 u_longlong_t gettick(void);
65 
66 static void n2rng_config_task(void * targ);
67 
68 /*
69  * Device operations.
70  */
71 
72 static struct dev_ops devops = {
73 	DEVO_REV,		/* devo_rev */
74 	0,			/* devo_refcnt */
75 	nodev,			/* devo_getinfo */
76 	nulldev,		/* devo_identify */
77 	nulldev,		/* devo_probe */
78 	n2rng_attach,		/* devo_attach */
79 	n2rng_detach,		/* devo_detach */
80 	nodev,			/* devo_reset */
81 	NULL,			/* devo_cb_ops */
82 	NULL,			/* devo_bus_ops */
83 	ddi_power		/* devo_power */
84 };
85 
86 /*
87  * Module linkage.
88  */
89 static struct modldrv modldrv = {
90 	&mod_driverops,			/* drv_modops */
91 	"N2 RNG Driver v%I%",		/* drv_linkinfo */
92 	&devops,			/* drv_dev_ops */
93 };
94 
95 static struct modlinkage modlinkage = {
96 	MODREV_1,			/* ml_rev */
97 	&modldrv,			/* ml_linkage */
98 	NULL
99 };
100 
101 /*
102  * Driver globals Soft state.
103  */
104 static void	*n2rng_softstate = NULL;
105 
106 /*
107  * Hypervisor RNG information.
108  */
109 static uint64_t	rng_min_ver;	/* negotiated RNG API minor version */
110 static boolean_t rng_hsvc_available = B_FALSE;
111 
112 static hsvc_info_t rng_hsvc = {
113 	HSVC_REV_1, NULL, HSVC_GROUP_RNG, RNG_MAJOR_VER,
114 	RNG_MINOR_VER, "n2rng"
115 };
116 
117 /*
118  * DDI entry points.
119  */
120 int
121 _init(void)
122 {
123 	int	rv;
124 
125 	rv = ddi_soft_state_init(&n2rng_softstate, sizeof (n2rng_t), 1);
126 	if (rv != 0) {
127 		/* this should *never* happen! */
128 		return (rv);
129 	}
130 
131 	if ((rv = mod_install(&modlinkage)) != 0) {
132 		/* cleanup here */
133 		ddi_soft_state_fini(&n2rng_softstate);
134 		return (rv);
135 	}
136 
137 	return (0);
138 }
139 
140 int
141 _fini(void)
142 {
143 	int	rv;
144 
145 	rv = mod_remove(&modlinkage);
146 	if (rv == 0) {
147 		/* cleanup here */
148 		ddi_soft_state_fini(&n2rng_softstate);
149 	}
150 
151 	return (rv);
152 }
153 
154 int
155 _info(struct modinfo *modinfop)
156 {
157 	return (mod_info(&modlinkage, modinfop));
158 }
159 
160 static int
161 n2rng_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
162 {
163 	n2rng_t		*n2rng = NULL;
164 	int		instance;
165 	int		rv;
166 
167 	instance = ddi_get_instance(dip);
168 	DBG1(NULL, DATTACH, "n2rng_attach called, instance %d", instance);
169 	/*
170 	 * Only instance 0 of n2rng driver is allowed.
171 	 */
172 	if (instance != 0) {
173 		n2rng_diperror(dip, "only one instance (0) allowed");
174 		return (DDI_FAILURE);
175 	}
176 
177 	switch (cmd) {
178 	case DDI_RESUME:
179 		n2rng = (n2rng_t *)ddi_get_soft_state(n2rng_softstate,
180 		    instance);
181 		if (n2rng == NULL) {
182 			n2rng_diperror(dip, "no soft state in attach");
183 			return (DDI_FAILURE);
184 		}
185 		return (n2rng_resume(n2rng));
186 
187 	case DDI_ATTACH:
188 		break;
189 	default:
190 		return (DDI_FAILURE);
191 	}
192 
193 	rv = ddi_soft_state_zalloc(n2rng_softstate, instance);
194 	if (rv != DDI_SUCCESS) {
195 		n2rng_diperror(dip, "unable to allocate soft state");
196 		return (DDI_FAILURE);
197 	}
198 	n2rng = (n2rng_t *)ddi_get_soft_state(n2rng_softstate, instance);
199 	ASSERT(n2rng != NULL);
200 	n2rng->n_dip = dip;
201 
202 	mutex_init(&n2rng->n_health_check_mutex, NULL, MUTEX_DRIVER, NULL);
203 
204 	if ((rv = hsvc_register(&rng_hsvc, &rng_min_ver)) != 0) {
205 		cmn_err(CE_WARN, "%s: cannot negotiate hypervisor services "
206 		    "group: 0x%lx major: 0x%lx minor: 0x%lx errno: %d",
207 		    rng_hsvc.hsvc_modname, rng_hsvc.hsvc_group,
208 		    rng_hsvc.hsvc_major, rng_hsvc.hsvc_minor, rv);
209 		ddi_soft_state_free(n2rng_softstate, instance);
210 		mutex_destroy(&n2rng->n_health_check_mutex);
211 		return (DDI_FAILURE);
212 	}
213 	rng_hsvc_available = B_TRUE;
214 
215 	/* Allocate single thread task queue for rng diags and registration */
216 	n2rng->n_taskq = ddi_taskq_create(dip, "n2rng_taskq", 1,
217 	    TASKQ_DEFAULTPRI, 0);
218 
219 	if (n2rng->n_taskq == NULL) {
220 		n2rng_diperror(dip, "ddi_taskq_create() failed");
221 		goto errorexit;
222 	}
223 
224 	/* No locking, but it is okay */
225 	n2rng->n_sticks_per_usec = sticks_per_usec();
226 	/*
227 	 * The first product will likely be around 4 billion, so we
228 	 * use uint64_t to avoid integer overflow.
229 	 */
230 	n2rng->n_anlg_settle_cycles = (uint64_t)RNG_CTL_SETTLE_NS *
231 	    n2rng->n_sticks_per_usec / 1000;
232 
233 	/*
234 	 * Set some plausible state into the preferred
235 	 * configuration. The intent is that the health check, below,
236 	 * will immediately overwrite it.  If we are not in a control
237 	 * domain, this stuff will have no effect.
238 	 */
239 	n2rng->n_preferred_config.ctlwds[0].word = 0;
240 	n2rng->n_preferred_config.ctlwds[0].fields.rnc_anlg_sel =
241 	    N2RNG_NOANALOGOUT;
242 	n2rng->n_preferred_config.ctlwds[0].fields.rnc_cnt =
243 	    RNG_DEFAULT_ACCUMULATE_CYCLES;
244 	n2rng->n_preferred_config.ctlwds[0].fields.rnc_mode =
245 	    RNG_MODE_NORMAL;
246 	n2rng->n_preferred_config.ctlwds[1].word =
247 	    n2rng->n_preferred_config.ctlwds[0].word;
248 	n2rng->n_preferred_config.ctlwds[2].word =
249 	    n2rng->n_preferred_config.ctlwds[0].word;
250 	n2rng->n_preferred_config.ctlwds[3].word =
251 	    n2rng->n_preferred_config.ctlwds[0].word;
252 	n2rng->n_preferred_config.ctlwds[0].fields.rnc_vcoctl = 1;
253 	n2rng->n_preferred_config.ctlwds[0].fields.rnc_selbits = 1;
254 	n2rng->n_preferred_config.ctlwds[1].fields.rnc_vcoctl = 2;
255 	n2rng->n_preferred_config.ctlwds[1].fields.rnc_selbits = 2;
256 	n2rng->n_preferred_config.ctlwds[2].fields.rnc_vcoctl = 3;
257 	n2rng->n_preferred_config.ctlwds[2].fields.rnc_selbits = 4;
258 	n2rng->n_preferred_config.ctlwds[3].fields.rnc_vcoctl = 0;
259 	n2rng->n_preferred_config.ctlwds[3].fields.rnc_selbits = 7;
260 
261 	/* Dispatch task to configure the RNG and register with KCF */
262 	if (ddi_taskq_dispatch(n2rng->n_taskq, n2rng_config_task,
263 	    (void *)n2rng, DDI_SLEEP) != DDI_SUCCESS) {
264 		n2rng_diperror(dip, "ddi_taskq_dispatch() failed");
265 		goto errorexit;
266 	}
267 
268 	return (DDI_SUCCESS);
269 
270 errorexit:
271 	if (rng_hsvc_available == B_TRUE) {
272 		(void) hsvc_unregister(&rng_hsvc);
273 		rng_hsvc_available = B_FALSE;
274 	}
275 
276 	if (n2rng->n_taskq != NULL) {
277 		ddi_taskq_destroy(n2rng->n_taskq);
278 		n2rng->n_taskq = NULL;
279 	}
280 
281 	mutex_destroy(&n2rng->n_health_check_mutex);
282 	ddi_soft_state_free(n2rng_softstate, instance);
283 
284 	return (DDI_FAILURE);
285 }
286 
287 static int
288 n2rng_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
289 {
290 	int		instance;
291 	int		rv;
292 	n2rng_t		*n2rng;
293 
294 	instance = ddi_get_instance(dip);
295 	n2rng = (n2rng_t *)ddi_get_soft_state(n2rng_softstate, instance);
296 	if (n2rng == NULL) {
297 		n2rng_diperror(dip, "no soft state in detach");
298 		return (DDI_FAILURE);
299 	}
300 
301 	switch (cmd) {
302 	case DDI_SUSPEND:
303 		return (n2rng_suspend(n2rng));
304 	case DDI_DETACH:
305 		break;
306 	default:
307 		return (DDI_FAILURE);
308 	}
309 
310 	/* Destroy task queue first to insure configuration has completed */
311 	if (n2rng->n_taskq != NULL) {
312 		ddi_taskq_destroy(n2rng->n_taskq);
313 		n2rng->n_taskq = NULL;
314 	}
315 
316 	/* unregister with KCF---also tears down FIPS state */
317 	rv = n2rng_uninit(n2rng) ? DDI_FAILURE : DDI_SUCCESS;
318 
319 	if (rng_hsvc_available == B_TRUE) {
320 		(void) hsvc_unregister(&rng_hsvc);
321 		rng_hsvc_available = B_FALSE;
322 	}
323 
324 	mutex_destroy(&n2rng->n_health_check_mutex);
325 
326 	ddi_soft_state_free(n2rng_softstate, instance);
327 
328 	return (rv);
329 }
330 
331 /*ARGSUSED*/
332 static int
333 n2rng_suspend(n2rng_t *n2rng)
334 {
335 	return (DDI_SUCCESS);
336 }
337 
338 /*ARGSUSED*/
339 static int
340 n2rng_resume(n2rng_t *n2rng)
341 {
342 	int		rv;
343 
344 	/* assume clock is same speed, all data structures intact.  */
345 	rv = n2rng_do_health_check(n2rng);
346 	switch (rv) {
347 	case 0:
348 	case EPERM:
349 		break;
350 	default:
351 		cmn_err(CE_WARN, "n2rng: n2rng_resume: health check failed. "
352 		    "Unregistering from encryption framework");
353 		n2rng->n_flags |= N2RNG_FAILED;
354 		(void) n2rng_uninit(n2rng);
355 		break;
356 	}
357 
358 	return (DDI_SUCCESS);
359 }
360 
361 /*
362  * Map hypervisor error code to solaris. Only
363  * H_ENORADDR, H_EBADALIGN, H_EWOULDBLOCK, and EIO
364  * are meaningful to this device. Any other error
365  * codes are mapped EINVAL.
366  */
367 int
368 n2rng_herr2kerr(uint64_t hv_errcode)
369 {
370 	int	s_errcode;
371 
372 	switch (hv_errcode) {
373 	case H_EWOULDBLOCK:
374 		s_errcode = EWOULDBLOCK;
375 		break;
376 	case H_ENORADDR:
377 	case H_EBADALIGN:
378 	case H_EIO:
379 		s_errcode = EIO;
380 		break;
381 	case H_EOK:
382 		s_errcode = 0;
383 		break;
384 	case H_ENOACCESS:
385 		s_errcode = EPERM;
386 		break;
387 	default:
388 		s_errcode = EINVAL;
389 		break;
390 	}
391 	return (s_errcode);
392 }
393 
394 /*
395  * Waits approximately delay_sticks counts of the stick register.
396  * Times shorter than one sys clock tick (10ms on most systems) are
397  * done by busy waiting.
398  */
399 void
400 cyclesleep(n2rng_t *n2rng, uint64_t delay_sticks)
401 {
402 	uint64_t	end_stick = gettick() + delay_sticks;
403 	int64_t		sticks_to_wait;
404 	clock_t		sys_ticks_to_wait;
405 	clock_t		usecs_to_wait;
406 
407 	/*CONSTCOND*/
408 	while (1) {
409 		sticks_to_wait = end_stick - gettick();
410 		if (sticks_to_wait <= 0) {
411 			return;
412 		}
413 
414 		usecs_to_wait = sticks_to_wait / n2rng->n_sticks_per_usec;
415 		sys_ticks_to_wait = drv_usectohz(usecs_to_wait);
416 
417 		if (sys_ticks_to_wait > 0) {
418 			/* sleep */
419 			delay(sys_ticks_to_wait);
420 		} else if (usecs_to_wait > 0) {
421 			/* busy wait */
422 			drv_usecwait(usecs_to_wait);
423 		}
424 	}
425 }
426 
427 static void
428 log_internal_errors(uint64_t hverr, char *fname)
429 {
430 	switch (hverr) {
431 	case H_EBADALIGN:
432 		cmn_err(CE_WARN,
433 		    "n2rng: internal alignment "
434 		    "problem");
435 		break;
436 	case H_ENORADDR:
437 		cmn_err(CE_WARN, "n2rng: internal "
438 		    "invalid address");
439 		break;
440 	default:
441 		cmn_err(CE_NOTE,
442 		    "n2rng: %s "
443 		    "unexpectedly "
444 		    "returned hverr %ld", fname, hverr);
445 		break;
446 	}
447 }
448 
449 /*
450  * Collects a buffer full of bits, using the specified setup. numbytes
451  * must be a multiple of 8. If a sub-operation fails with EIO (handle
452  * mismatch), returns EIO.  If collect_setupp is NULL, the current
453  * setup is used.  If exit_setupp is NULL, the control configuratin
454  * and state are not set at exit.  WARNING: the buffer must be 8-byte
455  * aligned and in contiguous physical addresses.  Contiguousness is
456  * not checked!
457  */
458 int
459 n2rng_collect_diag_bits(n2rng_t *n2rng, n2rng_setup_t *collect_setupp,
460     void *buffer, int numbytes, n2rng_setup_t *exit_setupp,
461     uint64_t exitstate)
462 {
463 	int		rv;
464 	int		override_rv = 0;
465 	uint64_t	hverr;
466 	int		i;
467 	uint64_t	tdelta;
468 	n2rng_setup_t	setupbuffer[2];
469 	n2rng_setup_t	*setupcontigp;
470 	uint64_t	setupphys;
471 	int		numchunks;
472 	boolean_t	rnglooping;
473 
474 	if (numbytes % sizeof (uint64_t)) {
475 		return (EINVAL);
476 	}
477 
478 	if ((uint64_t)buffer % sizeof (uint64_t) != 0) {
479 		return (EINVAL);
480 	}
481 
482 	numchunks = ((numbytes / sizeof (uint64_t)) + RNG_DIAG_CHUNK_SIZE - 1)
483 	    / RNG_DIAG_CHUNK_SIZE;
484 	/*
485 	 * Use setupbuffer[0] if it is contiguous, otherwise
486 	 * setupbuffer[1].
487 	 */
488 	setupcontigp = &setupbuffer[
489 	    CONTIGUOUS(&setupbuffer[0], n2rng_setup_t) ? 0 : 1];
490 	setupphys = va_to_pa(setupcontigp);
491 
492 	/*
493 	 * If a non-null collect_setupp pointer has been provided,
494 	 * push the specified setup into the hardware.
495 	 */
496 	if (collect_setupp != NULL) {
497 		/* copy the specified state to the aligned buffer */
498 		*setupcontigp = *collect_setupp;
499 		rnglooping = B_TRUE;
500 		while (rnglooping) {
501 			hverr = hv_rng_ctl_write(setupphys,
502 			    CTL_STATE_HEALTHCHECK,
503 			    n2rng->n_anlg_settle_cycles, &tdelta);
504 			rv = n2rng_herr2kerr(hverr);
505 			switch (hverr) {
506 			case 0:
507 				rnglooping = B_FALSE;
508 				break;
509 			case H_EIO: /* control yanked from us */
510 			case H_ENOACCESS: /* We are not control domain */
511 				return (rv);
512 			case H_EWOULDBLOCK:
513 				cyclesleep(n2rng, tdelta);
514 				break;
515 			default:
516 				log_internal_errors(hverr, "hv_rng_ctl_write");
517 				override_rv = rv;
518 				goto restore_state;
519 			}
520 		} /* while (rnglooping) */
521 	} /* if (collect_setupp != NULL) */
522 
523 	/* If the caller asks for some bytes, collect the data */
524 	if (numbytes > 0) {
525 		for (i = 0; i < numchunks; i++) {
526 			size_t thisnumbytes = (i == numchunks - 1) ?
527 			    numbytes - i * (RNG_DIAG_CHUNK_SIZE *
528 			    sizeof (uint64_t)) :
529 			    RNG_DIAG_CHUNK_SIZE * sizeof (uint64_t);
530 			/* try until we successfully read a word of data */
531 			rnglooping = B_TRUE;
532 			while (rnglooping) {
533 				hverr = hv_rng_data_read_diag(
534 				    va_to_pa((uint64_t *)buffer +
535 				    RNG_DIAG_CHUNK_SIZE * i),
536 				    thisnumbytes, &tdelta);
537 				rv = n2rng_herr2kerr(hverr);
538 				switch (hverr) {
539 				case 0:
540 					rnglooping = B_FALSE;
541 					break;
542 				case H_EIO:
543 				case H_ENOACCESS:
544 					return (rv);
545 				case H_EWOULDBLOCK:
546 					cyclesleep(n2rng, tdelta);
547 					break;
548 				default:
549 					log_internal_errors(hverr,
550 					    "hv_rng_data_read_diag");
551 					override_rv = rv;
552 					goto restore_state;
553 				}
554 			} /* while (!rnglooping) */
555 		} /* for */
556 	} /* if */
557 
558 restore_state:
559 
560 	/* restore the preferred configuration and set exit state */
561 	if (exit_setupp != NULL) {
562 
563 		*setupcontigp = *exit_setupp;
564 		rnglooping = B_TRUE;
565 		while (rnglooping) {
566 			hverr = hv_rng_ctl_write(setupphys, exitstate,
567 			    n2rng->n_anlg_settle_cycles, &tdelta);
568 			rv = n2rng_herr2kerr(hverr);
569 			switch (hverr) {
570 			case 0:
571 			case H_EIO: /* control yanked from us */
572 			case H_EINVAL: /* some external error, probably */
573 			case H_ENOACCESS: /* We are not control domain */
574 				rnglooping = B_FALSE;
575 				break;
576 			case H_EWOULDBLOCK:
577 				cyclesleep(n2rng, tdelta);
578 				break;
579 
580 			default:
581 				rnglooping = B_FALSE;
582 				log_internal_errors(hverr, "hv_rng_ctl_write");
583 				break;
584 			}
585 		} /* while */
586 	} /* if */
587 
588 	/*
589 	 * override_rv takes care of the case where we abort becuase
590 	 * of some error, but still want to restore the peferred state
591 	 * and return the first error, even if other error occur.
592 	 */
593 	return (override_rv ? override_rv : rv);
594 }
595 
596 int
597 n2rng_getentropy(n2rng_t *n2rng, void *buffer, size_t size)
598 {
599 	int		i, rv = 0;  /* so it works if size is zero */
600 	uint64_t	hverr;
601 	uint64_t	*buffer_w = (uint64_t *)buffer;
602 	int		num_w = size / sizeof (uint64_t);
603 	uint64_t	randval;
604 	uint64_t	randvalphys = va_to_pa(&randval);
605 	uint64_t	tdelta;
606 	int		failcount = 0;
607 	boolean_t	rnglooping;
608 
609 	for (i = 0; i < num_w; i++) {
610 		rnglooping = B_TRUE;
611 		while (rnglooping) {
612 			hverr = hv_rng_data_read(randvalphys, &tdelta);
613 			rv = n2rng_herr2kerr(hverr);
614 			switch (hverr) {
615 			case H_EOK:
616 				buffer_w[i] = randval;
617 				failcount = 0;
618 				rnglooping = B_FALSE;
619 				break;
620 			case H_EIO:
621 				/*
622 				 * A health check is in progress.
623 				 * Wait RNG_RETRY_HLCHK_USECS and fail
624 				 * after RNG_MAX_DATA_READ_ATTEMPTS
625 				 * failures.
626 				 */
627 				if (++failcount > RNG_MAX_DATA_READ_ATTEMPTS) {
628 					goto exitpoint;
629 				} else {
630 					delay(drv_usectohz(
631 					    RNG_RETRY_HLCHK_USECS));
632 				}
633 				break;
634 			case H_EWOULDBLOCK:
635 				cyclesleep(n2rng, tdelta);
636 				break;
637 			default:
638 				log_internal_errors(hverr, "hv_rng_data_read");
639 				goto exitpoint;
640 			}
641 		} /* while */
642 	} /* for */
643 
644 exitpoint:
645 
646 	return (rv);
647 }
648 
649 static uint64_t
650 sticks_per_usec(void)
651 {
652 	uint64_t starttick = gettick();
653 	hrtime_t starttime = gethrtime();
654 	uint64_t endtick;
655 	hrtime_t endtime;
656 
657 	delay(2);
658 
659 	endtick = gettick();
660 	endtime = gethrtime();
661 
662 	return ((1000 * (endtick - starttick)) / (endtime - starttime));
663 }
664 
665 /*
666  * n2rng_config_task()
667  *
668  * Runs health checks on the RNG hardware
669  * Configures the RNG hardware
670  * Registers with crypto framework if successful.
671  */
672 static void
673 n2rng_config_task(void * targ)
674 {
675 	int		rv;
676 	n2rng_t		*n2rng = (n2rng_t *)targ;
677 
678 	thread_affinity_set(curthread, CPU_CURRENT);
679 	rv = n2rng_do_health_check(n2rng);
680 	thread_affinity_clear(curthread);
681 
682 	switch (rv) {
683 	case 0:
684 		/* We are a control domain.  Success. */
685 		break;
686 	case EPERM:
687 		/* We must not be a control domain, declare success. */
688 		rv = 0;
689 		break;
690 	default:
691 		goto errorexit;
692 	}
693 
694 	/* Register with KCF and initialize FIPS state */
695 	rv = n2rng_init(n2rng);
696 	if (rv != DDI_SUCCESS) {
697 		goto errorexit;
698 	}
699 
700 	n2rng->n_flags &= ~N2RNG_FAILED;
701 	return;
702 
703 errorexit:
704 	cmn_err(CE_WARN, "n2rng_config_task: RNG configuration failed");
705 	n2rng->n_flags |= N2RNG_FAILED;
706 }
707