1fec509a0Sgm89044 /* 2fec509a0Sgm89044 * CDDL HEADER START 3fec509a0Sgm89044 * 4fec509a0Sgm89044 * The contents of this file are subject to the terms of the 5fec509a0Sgm89044 * Common Development and Distribution License (the "License"). 6fec509a0Sgm89044 * You may not use this file except in compliance with the License. 7fec509a0Sgm89044 * 8fec509a0Sgm89044 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fec509a0Sgm89044 * or http://www.opensolaris.org/os/licensing. 10fec509a0Sgm89044 * See the License for the specific language governing permissions 11fec509a0Sgm89044 * and limitations under the License. 12fec509a0Sgm89044 * 13fec509a0Sgm89044 * When distributing Covered Code, include this CDDL HEADER in each 14fec509a0Sgm89044 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fec509a0Sgm89044 * If applicable, add the following below this CDDL HEADER, with the 16fec509a0Sgm89044 * fields enclosed by brackets "[]" replaced with your own identifying 17fec509a0Sgm89044 * information: Portions Copyright [yyyy] [name of copyright owner] 18fec509a0Sgm89044 * 19fec509a0Sgm89044 * CDDL HEADER END 20fec509a0Sgm89044 */ 21fec509a0Sgm89044 /* 22*32e0ab73SMisaki Miyashita * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23fec509a0Sgm89044 */ 24fec509a0Sgm89044 25fec509a0Sgm89044 26fec509a0Sgm89044 /* 27fec509a0Sgm89044 * Niagara 2 Random Number Generator (RNG) driver 28fec509a0Sgm89044 */ 29fec509a0Sgm89044 30fec509a0Sgm89044 #include <sys/types.h> 31fec509a0Sgm89044 #include <sys/sysmacros.h> 32fec509a0Sgm89044 #include <sys/modctl.h> 33fec509a0Sgm89044 #include <sys/conf.h> 34fec509a0Sgm89044 #include <sys/devops.h> 35fec509a0Sgm89044 #include <sys/cmn_err.h> 36fec509a0Sgm89044 #include <sys/ksynch.h> 37fec509a0Sgm89044 #include <sys/kmem.h> 38fec509a0Sgm89044 #include <sys/stat.h> 39fec509a0Sgm89044 #include <sys/open.h> 40fec509a0Sgm89044 #include <sys/file.h> 41fec509a0Sgm89044 #include <sys/ddi.h> 42fec509a0Sgm89044 #include <sys/sunddi.h> 43fec509a0Sgm89044 #include <sys/param.h> 44fec509a0Sgm89044 #include <sys/cpuvar.h> 45fec509a0Sgm89044 #include <sys/disp.h> 46fec509a0Sgm89044 #include <sys/hsvc.h> 47fec509a0Sgm89044 #include <sys/machsystm.h> 48fec509a0Sgm89044 #include <sys/hypervisor_api.h> 49fec509a0Sgm89044 #include <sys/n2rng.h> 50fec509a0Sgm89044 51fec509a0Sgm89044 static int n2rng_attach(dev_info_t *, ddi_attach_cmd_t); 52fec509a0Sgm89044 static int n2rng_detach(dev_info_t *, ddi_detach_cmd_t); 53fec509a0Sgm89044 static int n2rng_suspend(n2rng_t *); 54fec509a0Sgm89044 static int n2rng_resume(n2rng_t *); 55fec509a0Sgm89044 static uint64_t sticks_per_usec(void); 56fec509a0Sgm89044 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 *); 6059ac0c16Sdavemq static void n2rng_config_task(void * targ); 6159ac0c16Sdavemq 62fec509a0Sgm89044 /* 63fec509a0Sgm89044 * Device operations. 64fec509a0Sgm89044 */ 65fec509a0Sgm89044 66fec509a0Sgm89044 static struct dev_ops devops = { 67fec509a0Sgm89044 DEVO_REV, /* devo_rev */ 68fec509a0Sgm89044 0, /* devo_refcnt */ 69fec509a0Sgm89044 nodev, /* devo_getinfo */ 70fec509a0Sgm89044 nulldev, /* devo_identify */ 71fec509a0Sgm89044 nulldev, /* devo_probe */ 72fec509a0Sgm89044 n2rng_attach, /* devo_attach */ 73fec509a0Sgm89044 n2rng_detach, /* devo_detach */ 74fec509a0Sgm89044 nodev, /* devo_reset */ 75fec509a0Sgm89044 NULL, /* devo_cb_ops */ 76fec509a0Sgm89044 NULL, /* devo_bus_ops */ 7719397407SSherry Moore ddi_power, /* devo_power */ 7819397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */ 79fec509a0Sgm89044 }; 80fec509a0Sgm89044 81fec509a0Sgm89044 /* 82fec509a0Sgm89044 * Module linkage. 83fec509a0Sgm89044 */ 84fec509a0Sgm89044 static struct modldrv modldrv = { 85fec509a0Sgm89044 &mod_driverops, /* drv_modops */ 86fd80436bSgm89044 "N2 RNG Driver", /* drv_linkinfo */ 87fec509a0Sgm89044 &devops, /* drv_dev_ops */ 88fec509a0Sgm89044 }; 89fec509a0Sgm89044 90fec509a0Sgm89044 static struct modlinkage modlinkage = { 91fec509a0Sgm89044 MODREV_1, /* ml_rev */ 92fec509a0Sgm89044 &modldrv, /* ml_linkage */ 93fec509a0Sgm89044 NULL 94fec509a0Sgm89044 }; 95fec509a0Sgm89044 96fec509a0Sgm89044 /* 97fec509a0Sgm89044 * Driver globals Soft state. 98fec509a0Sgm89044 */ 99fec509a0Sgm89044 static void *n2rng_softstate = NULL; 100fec509a0Sgm89044 101fec509a0Sgm89044 /* 102741c280dStwelke * Hypervisor NCS services information. 103fec509a0Sgm89044 */ 104741c280dStwelke static boolean_t ncs_hsvc_available = B_FALSE; 105fec509a0Sgm89044 106741c280dStwelke #define NVERSIONS 2 107741c280dStwelke 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 */ 114fec509a0Sgm89044 }; 115741c280dStwelke int ncs_version_index; /* index into ncs_hsvc[] */ 116fec509a0Sgm89044 117fec509a0Sgm89044 /* 118fec509a0Sgm89044 * DDI entry points. 119fec509a0Sgm89044 */ 120fec509a0Sgm89044 int 121fec509a0Sgm89044 _init(void) 122fec509a0Sgm89044 { 123fec509a0Sgm89044 int rv; 124fec509a0Sgm89044 125fec509a0Sgm89044 rv = ddi_soft_state_init(&n2rng_softstate, sizeof (n2rng_t), 1); 126fec509a0Sgm89044 if (rv != 0) { 127fec509a0Sgm89044 /* this should *never* happen! */ 128fec509a0Sgm89044 return (rv); 129fec509a0Sgm89044 } 130fec509a0Sgm89044 131fec509a0Sgm89044 if ((rv = mod_install(&modlinkage)) != 0) { 132fec509a0Sgm89044 /* cleanup here */ 133fec509a0Sgm89044 ddi_soft_state_fini(&n2rng_softstate); 134fec509a0Sgm89044 return (rv); 135fec509a0Sgm89044 } 136fec509a0Sgm89044 137fec509a0Sgm89044 return (0); 138fec509a0Sgm89044 } 139fec509a0Sgm89044 140fec509a0Sgm89044 int 141fec509a0Sgm89044 _fini(void) 142fec509a0Sgm89044 { 143fec509a0Sgm89044 int rv; 144fec509a0Sgm89044 145fec509a0Sgm89044 rv = mod_remove(&modlinkage); 146fec509a0Sgm89044 if (rv == 0) { 147fec509a0Sgm89044 /* cleanup here */ 148fec509a0Sgm89044 ddi_soft_state_fini(&n2rng_softstate); 149fec509a0Sgm89044 } 150fec509a0Sgm89044 151fec509a0Sgm89044 return (rv); 152fec509a0Sgm89044 } 153fec509a0Sgm89044 154fec509a0Sgm89044 int 155fec509a0Sgm89044 _info(struct modinfo *modinfop) 156fec509a0Sgm89044 { 157fec509a0Sgm89044 return (mod_info(&modlinkage, modinfop)); 158fec509a0Sgm89044 } 159fec509a0Sgm89044 160fec509a0Sgm89044 static int 161fec509a0Sgm89044 n2rng_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 162fec509a0Sgm89044 { 163fec509a0Sgm89044 n2rng_t *n2rng = NULL; 164fec509a0Sgm89044 int instance; 165fec509a0Sgm89044 int rv; 166741c280dStwelke int version; 167741c280dStwelke uint64_t ncs_minor_ver; 168fec509a0Sgm89044 169fec509a0Sgm89044 instance = ddi_get_instance(dip); 170741c280dStwelke DBG1(NULL, DENTRY, "n2rng_attach called, instance %d", instance); 171fec509a0Sgm89044 /* 172fec509a0Sgm89044 * Only instance 0 of n2rng driver is allowed. 173fec509a0Sgm89044 */ 174fec509a0Sgm89044 if (instance != 0) { 175fec509a0Sgm89044 n2rng_diperror(dip, "only one instance (0) allowed"); 176fec509a0Sgm89044 return (DDI_FAILURE); 177fec509a0Sgm89044 } 178fec509a0Sgm89044 179fec509a0Sgm89044 switch (cmd) { 180fec509a0Sgm89044 case DDI_RESUME: 181fec509a0Sgm89044 n2rng = (n2rng_t *)ddi_get_soft_state(n2rng_softstate, 182fec509a0Sgm89044 instance); 183fec509a0Sgm89044 if (n2rng == NULL) { 184fec509a0Sgm89044 n2rng_diperror(dip, "no soft state in attach"); 185fec509a0Sgm89044 return (DDI_FAILURE); 186fec509a0Sgm89044 } 187fec509a0Sgm89044 return (n2rng_resume(n2rng)); 188fec509a0Sgm89044 189fec509a0Sgm89044 case DDI_ATTACH: 190fec509a0Sgm89044 break; 191fec509a0Sgm89044 default: 192fec509a0Sgm89044 return (DDI_FAILURE); 193fec509a0Sgm89044 } 194fec509a0Sgm89044 195fec509a0Sgm89044 rv = ddi_soft_state_zalloc(n2rng_softstate, instance); 196fec509a0Sgm89044 if (rv != DDI_SUCCESS) { 197fec509a0Sgm89044 n2rng_diperror(dip, "unable to allocate soft state"); 198fec509a0Sgm89044 return (DDI_FAILURE); 199fec509a0Sgm89044 } 200fec509a0Sgm89044 n2rng = (n2rng_t *)ddi_get_soft_state(n2rng_softstate, instance); 201fec509a0Sgm89044 ASSERT(n2rng != NULL); 202fec509a0Sgm89044 n2rng->n_dip = dip; 203fec509a0Sgm89044 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(); 208fec509a0Sgm89044 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; 234fec509a0Sgm89044 } 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; 284741c280dStwelke } 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"); 299fec509a0Sgm89044 goto errorexit; 300fec509a0Sgm89044 } 301fec509a0Sgm89044 302fec509a0Sgm89044 return (DDI_SUCCESS); 303fec509a0Sgm89044 304fec509a0Sgm89044 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); 321fec509a0Sgm89044 ddi_soft_state_free(n2rng_softstate, instance); 322fec509a0Sgm89044 323fec509a0Sgm89044 return (DDI_FAILURE); 324fec509a0Sgm89044 } 325fec509a0Sgm89044 326fec509a0Sgm89044 static int 327fec509a0Sgm89044 n2rng_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 328fec509a0Sgm89044 { 329fec509a0Sgm89044 int instance; 330fec509a0Sgm89044 int rv; 331fec509a0Sgm89044 n2rng_t *n2rng; 332741c280dStwelke timeout_id_t tid; 333fec509a0Sgm89044 334fec509a0Sgm89044 instance = ddi_get_instance(dip); 335fec509a0Sgm89044 n2rng = (n2rng_t *)ddi_get_soft_state(n2rng_softstate, instance); 336fec509a0Sgm89044 if (n2rng == NULL) { 337fec509a0Sgm89044 n2rng_diperror(dip, "no soft state in detach"); 338fec509a0Sgm89044 return (DDI_FAILURE); 339fec509a0Sgm89044 } 340fec509a0Sgm89044 341fec509a0Sgm89044 switch (cmd) { 342fec509a0Sgm89044 case DDI_SUSPEND: 343fec509a0Sgm89044 return (n2rng_suspend(n2rng)); 344fec509a0Sgm89044 case DDI_DETACH: 345fec509a0Sgm89044 break; 346fec509a0Sgm89044 default: 347fec509a0Sgm89044 return (DDI_FAILURE); 348fec509a0Sgm89044 } 349fec509a0Sgm89044 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; 375fec509a0Sgm89044 } 376fec509a0Sgm89044 377741c280dStwelke mutex_destroy(&n2rng->n_lock); 378fec509a0Sgm89044 ddi_soft_state_free(n2rng_softstate, instance); 379fec509a0Sgm89044 380fec509a0Sgm89044 return (rv); 381fec509a0Sgm89044 } 382fec509a0Sgm89044 383fec509a0Sgm89044 /*ARGSUSED*/ 384fec509a0Sgm89044 static int 385fec509a0Sgm89044 n2rng_suspend(n2rng_t *n2rng) 386fec509a0Sgm89044 { 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 393fec509a0Sgm89044 return (DDI_SUCCESS); 394fec509a0Sgm89044 } 395fec509a0Sgm89044 396fec509a0Sgm89044 /*ARGSUSED*/ 397fec509a0Sgm89044 static int 398fec509a0Sgm89044 n2rng_resume(n2rng_t *n2rng) 399fec509a0Sgm89044 { 400741c280dStwelke /* Assume clock is same speed and all data structures are intact */ 401fec509a0Sgm89044 402741c280dStwelke /* Re-configure the RNG hardware and register with KCF */ 403741c280dStwelke return (n2rng_config(n2rng)); 404fec509a0Sgm89044 } 405fec509a0Sgm89044 406fec509a0Sgm89044 /* 407fec509a0Sgm89044 * Map hypervisor error code to solaris. Only 408fec509a0Sgm89044 * H_ENORADDR, H_EBADALIGN, H_EWOULDBLOCK, and EIO 409fec509a0Sgm89044 * are meaningful to this device. Any other error 410fec509a0Sgm89044 * codes are mapped EINVAL. 411fec509a0Sgm89044 */ 412fec509a0Sgm89044 int 413fec509a0Sgm89044 n2rng_herr2kerr(uint64_t hv_errcode) 414fec509a0Sgm89044 { 415fec509a0Sgm89044 int s_errcode; 416fec509a0Sgm89044 417fec509a0Sgm89044 switch (hv_errcode) { 418fec509a0Sgm89044 case H_EWOULDBLOCK: 419fec509a0Sgm89044 s_errcode = EWOULDBLOCK; 420fec509a0Sgm89044 break; 421fec509a0Sgm89044 case H_EIO: 422fec509a0Sgm89044 s_errcode = EIO; 423fec509a0Sgm89044 break; 424741c280dStwelke case H_EBUSY: 425741c280dStwelke s_errcode = EBUSY; 426741c280dStwelke break; 427fec509a0Sgm89044 case H_EOK: 428fec509a0Sgm89044 s_errcode = 0; 429fec509a0Sgm89044 break; 430fec509a0Sgm89044 case H_ENOACCESS: 431fec509a0Sgm89044 s_errcode = EPERM; 432fec509a0Sgm89044 break; 433741c280dStwelke case H_ENORADDR: 434741c280dStwelke case H_EBADALIGN: 435fec509a0Sgm89044 default: 436fec509a0Sgm89044 s_errcode = EINVAL; 437fec509a0Sgm89044 break; 438fec509a0Sgm89044 } 439fec509a0Sgm89044 return (s_errcode); 440fec509a0Sgm89044 } 441fec509a0Sgm89044 442fec509a0Sgm89044 /* 443fec509a0Sgm89044 * Waits approximately delay_sticks counts of the stick register. 444fec509a0Sgm89044 * Times shorter than one sys clock tick (10ms on most systems) are 445fec509a0Sgm89044 * done by busy waiting. 446fec509a0Sgm89044 */ 447fec509a0Sgm89044 void 448fec509a0Sgm89044 cyclesleep(n2rng_t *n2rng, uint64_t delay_sticks) 449fec509a0Sgm89044 { 450fec509a0Sgm89044 uint64_t end_stick = gettick() + delay_sticks; 451fec509a0Sgm89044 int64_t sticks_to_wait; 452fec509a0Sgm89044 clock_t sys_ticks_to_wait; 453fec509a0Sgm89044 clock_t usecs_to_wait; 454fec509a0Sgm89044 455fec509a0Sgm89044 /*CONSTCOND*/ 456fec509a0Sgm89044 while (1) { 457fec509a0Sgm89044 sticks_to_wait = end_stick - gettick(); 458fec509a0Sgm89044 if (sticks_to_wait <= 0) { 459fec509a0Sgm89044 return; 460fec509a0Sgm89044 } 461fec509a0Sgm89044 462fec509a0Sgm89044 usecs_to_wait = sticks_to_wait / n2rng->n_sticks_per_usec; 463fec509a0Sgm89044 sys_ticks_to_wait = drv_usectohz(usecs_to_wait); 464fec509a0Sgm89044 465fec509a0Sgm89044 if (sys_ticks_to_wait > 0) { 466fec509a0Sgm89044 /* sleep */ 467fec509a0Sgm89044 delay(sys_ticks_to_wait); 468fec509a0Sgm89044 } else if (usecs_to_wait > 0) { 469fec509a0Sgm89044 /* busy wait */ 470fec509a0Sgm89044 drv_usecwait(usecs_to_wait); 471fec509a0Sgm89044 } 472fec509a0Sgm89044 } 473fec509a0Sgm89044 } 474fec509a0Sgm89044 475fec509a0Sgm89044 static void 476fec509a0Sgm89044 log_internal_errors(uint64_t hverr, char *fname) 477fec509a0Sgm89044 { 478fec509a0Sgm89044 switch (hverr) { 479fec509a0Sgm89044 case H_EBADALIGN: 480fec509a0Sgm89044 cmn_err(CE_WARN, 481fec509a0Sgm89044 "n2rng: internal alignment " 482fec509a0Sgm89044 "problem"); 483fec509a0Sgm89044 break; 484fec509a0Sgm89044 case H_ENORADDR: 485fec509a0Sgm89044 cmn_err(CE_WARN, "n2rng: internal " 486fec509a0Sgm89044 "invalid address"); 487fec509a0Sgm89044 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; 494fec509a0Sgm89044 default: 495fec509a0Sgm89044 cmn_err(CE_NOTE, 496fec509a0Sgm89044 "n2rng: %s " 497fec509a0Sgm89044 "unexpectedly " 498fec509a0Sgm89044 "returned hverr %ld", fname, hverr); 499fec509a0Sgm89044 break; 500fec509a0Sgm89044 } 501fec509a0Sgm89044 } 502fec509a0Sgm89044 503fec509a0Sgm89044 /* 504fec509a0Sgm89044 * Collects a buffer full of bits, using the specified setup. numbytes 505fec509a0Sgm89044 * must be a multiple of 8. If a sub-operation fails with EIO (handle 506fec509a0Sgm89044 * mismatch), returns EIO. If collect_setupp is NULL, the current 507fec509a0Sgm89044 * setup is used. If exit_setupp is NULL, the control configuratin 508fec509a0Sgm89044 * and state are not set at exit. WARNING: the buffer must be 8-byte 509fec509a0Sgm89044 * aligned and in contiguous physical addresses. Contiguousness is 510fec509a0Sgm89044 * not checked! 511fec509a0Sgm89044 */ 512fec509a0Sgm89044 int 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) 516fec509a0Sgm89044 { 517fec509a0Sgm89044 int rv; 518fec509a0Sgm89044 int override_rv = 0; 519fec509a0Sgm89044 uint64_t hverr; 520fec509a0Sgm89044 int i; 521fec509a0Sgm89044 uint64_t tdelta; 522fec509a0Sgm89044 n2rng_setup_t setupbuffer[2]; 523fec509a0Sgm89044 n2rng_setup_t *setupcontigp; 524fec509a0Sgm89044 uint64_t setupphys; 525fec509a0Sgm89044 int numchunks; 526fec509a0Sgm89044 boolean_t rnglooping; 527741c280dStwelke int busycount = 0; 528741c280dStwelke int blockcount = 0; 529fec509a0Sgm89044 530fec509a0Sgm89044 if (numbytes % sizeof (uint64_t)) { 531fec509a0Sgm89044 return (EINVAL); 532fec509a0Sgm89044 } 533fec509a0Sgm89044 534fec509a0Sgm89044 if ((uint64_t)buffer % sizeof (uint64_t) != 0) { 535fec509a0Sgm89044 return (EINVAL); 536fec509a0Sgm89044 } 537fec509a0Sgm89044 538fec509a0Sgm89044 numchunks = ((numbytes / sizeof (uint64_t)) + RNG_DIAG_CHUNK_SIZE - 1) 539fec509a0Sgm89044 / RNG_DIAG_CHUNK_SIZE; 540fec509a0Sgm89044 /* 541fec509a0Sgm89044 * Use setupbuffer[0] if it is contiguous, otherwise 542fec509a0Sgm89044 * setupbuffer[1]. 543fec509a0Sgm89044 */ 544fec509a0Sgm89044 setupcontigp = &setupbuffer[ 545fec509a0Sgm89044 CONTIGUOUS(&setupbuffer[0], n2rng_setup_t) ? 0 : 1]; 546fec509a0Sgm89044 setupphys = va_to_pa(setupcontigp); 547fec509a0Sgm89044 548fec509a0Sgm89044 /* 549fec509a0Sgm89044 * If a non-null collect_setupp pointer has been provided, 550fec509a0Sgm89044 * push the specified setup into the hardware. 551fec509a0Sgm89044 */ 552fec509a0Sgm89044 if (collect_setupp != NULL) { 553fec509a0Sgm89044 /* copy the specified state to the aligned buffer */ 554fec509a0Sgm89044 *setupcontigp = *collect_setupp; 555fec509a0Sgm89044 rnglooping = B_TRUE; 556fec509a0Sgm89044 while (rnglooping) { 557741c280dStwelke hverr = n2rng_ctl_write(n2rng, rngid, setupphys, 558fec509a0Sgm89044 CTL_STATE_HEALTHCHECK, 559741c280dStwelke n2rng->n_ctl_data->n_watchdog_cycles, &tdelta); 560fec509a0Sgm89044 rv = n2rng_herr2kerr(hverr); 561fec509a0Sgm89044 switch (hverr) { 562741c280dStwelke case H_EOK: 563fec509a0Sgm89044 rnglooping = B_FALSE; 564fec509a0Sgm89044 break; 565fec509a0Sgm89044 case H_EIO: /* control yanked from us */ 566fec509a0Sgm89044 case H_ENOACCESS: /* We are not control domain */ 567fec509a0Sgm89044 return (rv); 568fec509a0Sgm89044 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 { 577fec509a0Sgm89044 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 } 596fec509a0Sgm89044 break; 597fec509a0Sgm89044 default: 598fec509a0Sgm89044 log_internal_errors(hverr, "hv_rng_ctl_write"); 599fec509a0Sgm89044 override_rv = rv; 600fec509a0Sgm89044 goto restore_state; 601fec509a0Sgm89044 } 602fec509a0Sgm89044 } /* while (rnglooping) */ 603fec509a0Sgm89044 } /* if (collect_setupp != NULL) */ 604fec509a0Sgm89044 605fec509a0Sgm89044 /* If the caller asks for some bytes, collect the data */ 606fec509a0Sgm89044 if (numbytes > 0) { 607fec509a0Sgm89044 for (i = 0; i < numchunks; i++) { 608fec509a0Sgm89044 size_t thisnumbytes = (i == numchunks - 1) ? 609fec509a0Sgm89044 numbytes - i * (RNG_DIAG_CHUNK_SIZE * 610fec509a0Sgm89044 sizeof (uint64_t)) : 611fec509a0Sgm89044 RNG_DIAG_CHUNK_SIZE * sizeof (uint64_t); 612741c280dStwelke 613fec509a0Sgm89044 /* try until we successfully read a word of data */ 614fec509a0Sgm89044 rnglooping = B_TRUE; 615741c280dStwelke busycount = 0; 616741c280dStwelke blockcount = 0; 617fec509a0Sgm89044 while (rnglooping) { 618741c280dStwelke hverr = n2rng_data_read_diag(n2rng, rngid, 619fec509a0Sgm89044 va_to_pa((uint64_t *)buffer + 620fec509a0Sgm89044 RNG_DIAG_CHUNK_SIZE * i), 621fec509a0Sgm89044 thisnumbytes, &tdelta); 622fec509a0Sgm89044 rv = n2rng_herr2kerr(hverr); 623fec509a0Sgm89044 switch (hverr) { 624741c280dStwelke case H_EOK: 625fec509a0Sgm89044 rnglooping = B_FALSE; 626fec509a0Sgm89044 break; 627fec509a0Sgm89044 case H_EIO: 628fec509a0Sgm89044 case H_ENOACCESS: 629fec509a0Sgm89044 return (rv); 630fec509a0Sgm89044 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 { 641fec509a0Sgm89044 cyclesleep(n2rng, tdelta); 642741c280dStwelke } 643fec509a0Sgm89044 break; 644fec509a0Sgm89044 default: 645fec509a0Sgm89044 log_internal_errors(hverr, 646fec509a0Sgm89044 "hv_rng_data_read_diag"); 647fec509a0Sgm89044 override_rv = rv; 648fec509a0Sgm89044 goto restore_state; 649fec509a0Sgm89044 } 650fec509a0Sgm89044 } /* while (!rnglooping) */ 651fec509a0Sgm89044 } /* for */ 652741c280dStwelke } 653fec509a0Sgm89044 654fec509a0Sgm89044 restore_state: 655fec509a0Sgm89044 656fec509a0Sgm89044 /* restore the preferred configuration and set exit state */ 657fec509a0Sgm89044 if (exit_setupp != NULL) { 658fec509a0Sgm89044 659fec509a0Sgm89044 *setupcontigp = *exit_setupp; 660fec509a0Sgm89044 rnglooping = B_TRUE; 661741c280dStwelke busycount = 0; 662741c280dStwelke blockcount = 0; 663fec509a0Sgm89044 while (rnglooping) { 664741c280dStwelke hverr = n2rng_ctl_write(n2rng, rngid, setupphys, 665741c280dStwelke exitstate, n2rng->n_ctl_data->n_watchdog_cycles, 666741c280dStwelke &tdelta); 667fec509a0Sgm89044 rv = n2rng_herr2kerr(hverr); 668fec509a0Sgm89044 switch (hverr) { 669741c280dStwelke case H_EOK: 670fec509a0Sgm89044 case H_EIO: /* control yanked from us */ 671fec509a0Sgm89044 case H_EINVAL: /* some external error, probably */ 672fec509a0Sgm89044 case H_ENOACCESS: /* We are not control domain */ 673fec509a0Sgm89044 rnglooping = B_FALSE; 674fec509a0Sgm89044 break; 675fec509a0Sgm89044 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 { 684fec509a0Sgm89044 cyclesleep(n2rng, tdelta); 685741c280dStwelke } 686fec509a0Sgm89044 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 } 703741c280dStwelke break; 704fec509a0Sgm89044 default: 705fec509a0Sgm89044 rnglooping = B_FALSE; 706fec509a0Sgm89044 log_internal_errors(hverr, "hv_rng_ctl_write"); 707fec509a0Sgm89044 break; 708fec509a0Sgm89044 } 709fec509a0Sgm89044 } /* while */ 710fec509a0Sgm89044 } /* if */ 711fec509a0Sgm89044 712fec509a0Sgm89044 /* 713fec509a0Sgm89044 * override_rv takes care of the case where we abort becuase 714fec509a0Sgm89044 * of some error, but still want to restore the peferred state 715fec509a0Sgm89044 * and return the first error, even if other error occur. 716fec509a0Sgm89044 */ 717fec509a0Sgm89044 return (override_rv ? override_rv : rv); 718fec509a0Sgm89044 } 719fec509a0Sgm89044 720fec509a0Sgm89044 int 721fec509a0Sgm89044 n2rng_getentropy(n2rng_t *n2rng, void *buffer, size_t size) 722fec509a0Sgm89044 { 723fec509a0Sgm89044 int i, rv = 0; /* so it works if size is zero */ 724fec509a0Sgm89044 uint64_t hverr; 725fec509a0Sgm89044 uint64_t *buffer_w = (uint64_t *)buffer; 726fec509a0Sgm89044 int num_w = size / sizeof (uint64_t); 727fec509a0Sgm89044 uint64_t randval; 728fec509a0Sgm89044 uint64_t randvalphys = va_to_pa(&randval); 729fec509a0Sgm89044 uint64_t tdelta; 730fec509a0Sgm89044 int failcount = 0; 731741c280dStwelke int blockcount = 0; 732fec509a0Sgm89044 boolean_t rnglooping; 733fec509a0Sgm89044 734fec509a0Sgm89044 for (i = 0; i < num_w; i++) { 735fec509a0Sgm89044 rnglooping = B_TRUE; 736fec509a0Sgm89044 while (rnglooping) { 737fec509a0Sgm89044 hverr = hv_rng_data_read(randvalphys, &tdelta); 738fec509a0Sgm89044 rv = n2rng_herr2kerr(hverr); 739fec509a0Sgm89044 switch (hverr) { 740fec509a0Sgm89044 case H_EOK: 741fec509a0Sgm89044 buffer_w[i] = randval; 742fec509a0Sgm89044 failcount = 0; 743fec509a0Sgm89044 rnglooping = B_FALSE; 744fec509a0Sgm89044 break; 745fec509a0Sgm89044 case H_EIO: 746fec509a0Sgm89044 /* 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. 751741c280dStwelke */ 752741c280dStwelke if (n2rng->n_hvapi_major_version < 2) { 753741c280dStwelke /* 754fec509a0Sgm89044 * A health check is in progress. 755fec509a0Sgm89044 * Wait RNG_RETRY_HLCHK_USECS and fail 756fec509a0Sgm89044 * after RNG_MAX_DATA_READ_ATTEMPTS 757fec509a0Sgm89044 * failures. 758fec509a0Sgm89044 */ 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); 766fec509a0Sgm89044 goto exitpoint; 767fec509a0Sgm89044 } else { 768741c280dStwelke delay(drv_usectohz 769741c280dStwelke (RNG_RETRY_HLCHK_USECS)); 770741c280dStwelke } 771741c280dStwelke } 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; 784fec509a0Sgm89044 } 785fec509a0Sgm89044 break; 786fec509a0Sgm89044 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 { 795fec509a0Sgm89044 cyclesleep(n2rng, tdelta); 796741c280dStwelke } 797fec509a0Sgm89044 break; 798fec509a0Sgm89044 default: 799fec509a0Sgm89044 log_internal_errors(hverr, "hv_rng_data_read"); 800fec509a0Sgm89044 goto exitpoint; 801fec509a0Sgm89044 } 802fec509a0Sgm89044 } /* while */ 803fec509a0Sgm89044 } /* for */ 804fec509a0Sgm89044 805fec509a0Sgm89044 exitpoint: 806741c280dStwelke return (rv); 807741c280dStwelke } 808741c280dStwelke 809741c280dStwelke uint64_t 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 } 827fec509a0Sgm89044 828fec509a0Sgm89044 return (rv); 829fec509a0Sgm89044 } 830fec509a0Sgm89044 831741c280dStwelke uint64_t 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 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 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 } 931741c280dStwelke 932741c280dStwelke return (rv); 933741c280dStwelke } 934741c280dStwelke 935741c280dStwelke uint64_t 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 */ 947741c280dStwelke rv = hv_rng_ctl_read_v2(NULL, 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 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 978fec509a0Sgm89044 static uint64_t 979fec509a0Sgm89044 sticks_per_usec(void) 980fec509a0Sgm89044 { 981fec509a0Sgm89044 uint64_t starttick = gettick(); 982fec509a0Sgm89044 hrtime_t starttime = gethrtime(); 983fec509a0Sgm89044 uint64_t endtick; 984fec509a0Sgm89044 hrtime_t endtime; 985fec509a0Sgm89044 986fec509a0Sgm89044 delay(2); 987fec509a0Sgm89044 988fec509a0Sgm89044 endtick = gettick(); 989fec509a0Sgm89044 endtime = gethrtime(); 990fec509a0Sgm89044 991fec509a0Sgm89044 return ((1000 * (endtick - starttick)) / (endtime - starttime)); 992fec509a0Sgm89044 } 99359ac0c16Sdavemq 994741c280dStwelke static int 995741c280dStwelke n2rng_init_ctl(n2rng_t *n2rng) 99659ac0c16Sdavemq { 99759ac0c16Sdavemq int rv; 998741c280dStwelke int hverr; 999741c280dStwelke rng_entry_t *rng; 1000741c280dStwelke int rngid; 1001741c280dStwelke int blockcount = 0; 100259ac0c16Sdavemq 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 1077*32e0ab73SMisaki 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 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 1153741c280dStwelke /* 1154741c280dStwelke * n2rng_config_test() 1155741c280dStwelke * 1156741c280dStwelke * Attempt read random data to see if the rng is configured. 1157741c280dStwelke */ 1158741c280dStwelke int 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 1223741c280dStwelke n2rng_config(n2rng_t *n2rng) 1224741c280dStwelke { 1225741c280dStwelke int rv; 1226741c280dStwelke rng_entry_t *rng; 1227741c280dStwelke int rngid; 1228741c280dStwelke 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)) { 1234741c280dStwelke 1235741c280dStwelke for (rngid = 0; rngid < n2rng->n_ctl_data->n_num_rngs; 1236741c280dStwelke rngid++) { 1237741c280dStwelke 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 */ 125359ac0c16Sdavemq 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); 125859ac0c16Sdavemq thread_affinity_clear(curthread); 1259741c280dStwelke } else { 1260741c280dStwelke rv = n2rng_do_health_check(n2rng, rngid); 1261741c280dStwelke } 126259ac0c16Sdavemq 126359ac0c16Sdavemq switch (rv) { 126459ac0c16Sdavemq 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 } 127659ac0c16Sdavemq 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 */ 131359ac0c16Sdavemq break; 131459ac0c16Sdavemq default: 131559ac0c16Sdavemq goto errorexit; 131659ac0c16Sdavemq } 1317741c280dStwelke } 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"); 132859ac0c16Sdavemq goto errorexit; 132959ac0c16Sdavemq } 133059ac0c16Sdavemq 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 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