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#include <sys/types.h>
29#include <sys/ddi.h>
30#include <sys/sunddi.h>
31#include <sys/systm.h>
32#include <sys/kstat.h>
33#include <sys/crypto/common.h>
34#include <sys/crypto/spi.h>
35#include <sys/n2rng.h>
36
37/*
38 * Kernel statistics.
39 */
40static int n2rng_ksupdate(kstat_t *, int);
41
42/*
43 * Initialize Kstats.
44 */
45void
46n2rng_ksinit(n2rng_t *n2rng)
47{
48	int	instance;
49	int	i;
50	int	j;
51	char	buf[64];
52
53	if (ddi_getprop(DDI_DEV_T_ANY, n2rng->n_dip,
54	    DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "nostats", 0) != 0) {
55		/*
56		 * sysadmin has explicity disabled stats to prevent
57		 * covert channel.
58		 */
59		return;
60	}
61
62	instance = ddi_get_instance(n2rng->n_dip);
63
64	/*
65	 * Named kstats.
66	 */
67	n2rng->n_ksp = kstat_create(DRIVER, instance, NULL, "misc",
68	    KSTAT_TYPE_NAMED,
69	    sizeof (n2rng_stat_t) / sizeof (kstat_named_t),
70	    KSTAT_FLAG_WRITABLE);
71	if (n2rng->n_ksp == NULL) {
72		n2rng_error(n2rng, "unable to create kstats");
73	} else {
74		n2rng_stat_t *dkp = (n2rng_stat_t *)n2rng->n_ksp->ks_data;
75
76		kstat_named_init(&dkp->ns_status, "status", KSTAT_DATA_CHAR);
77
78		kstat_named_init(&dkp->ns_algs[DS_RNGJOBS], "rngjobs",
79		    KSTAT_DATA_ULONGLONG);
80		kstat_named_init(&dkp->ns_algs[DS_RNGBYTES], "rngbytes",
81		    KSTAT_DATA_ULONGLONG);
82
83		if (n2rng_iscontrol(n2rng)) {
84
85			for (i = 0; i < n2rng->n_ctl_data->n_num_rngs; i++) {
86				(void) sprintf(buf, "rng%d-state", i);
87				kstat_named_init(&dkp->ns_rngstate[i],
88				    buf, KSTAT_DATA_CHAR);
89				for (j = 0; j < N2RNG_NOSC; j++) {
90					(void) sprintf(buf,
91					    "rng%d-cell%d-bias", i, j);
92					kstat_named_init
93					    (&dkp->ns_rngbias[i][j],
94					    buf, KSTAT_DATA_ULONGLONG);
95					(void) sprintf(buf,
96					    "rng%d-cell%d-entropy", i, j);
97					kstat_named_init
98					    (&dkp->ns_rngentropy[i][j],
99					    buf, KSTAT_DATA_ULONGLONG);
100				}
101			}
102		}
103		n2rng->n_ksp->ks_update = n2rng_ksupdate;
104		n2rng->n_ksp->ks_private = n2rng;
105
106		kstat_install(n2rng->n_ksp);
107	}
108}
109
110/*
111 * Deinitialize Kstats.
112 */
113void
114n2rng_ksdeinit(n2rng_t *n2rng)
115{
116
117	if (n2rng->n_ksp != NULL) {
118		kstat_delete(n2rng->n_ksp);
119		n2rng->n_ksp = NULL;
120	}
121}
122
123/*
124 * Update Kstats.
125 */
126int
127n2rng_ksupdate(kstat_t *ksp, int rw)
128{
129	n2rng_t		*n2rng;
130	n2rng_stat_t	*dkp;
131	int		i;
132	int		j;
133
134	n2rng = (n2rng_t *)ksp->ks_private;
135	dkp = (n2rng_stat_t *)ksp->ks_data;
136
137	if (rw == KSTAT_WRITE) {
138		for (i = 0; i < DS_MAX; i++) {
139			n2rng->n_stats[i] = dkp->ns_algs[i].value.ull;
140		}
141	} else {
142		/* handy status value */
143		if (n2rng_isfailed(n2rng)) {
144			/* device has failed */
145			(void) strcpy(dkp->ns_status.value.c, "failed");
146		} else if (!n2rng_isconfigured(n2rng)) {
147			/* device is not configured */
148			(void) strcpy(dkp->ns_status.value.c, "offline");
149		} else {
150			/* everything looks good */
151			(void) strcpy(dkp->ns_status.value.c, "online");
152		}
153
154		for (i = 0; i < DS_MAX; i++) {
155			dkp->ns_algs[i].value.ull = n2rng->n_stats[i];
156		}
157
158		if (n2rng_iscontrol(n2rng)) {
159			rng_entry_t *rng;
160
161			for (i = 0; i < n2rng->n_ctl_data->n_num_rngs; i++) {
162
163				rng = &n2rng->n_ctl_data->n_rngs[i];
164
165				switch (rng->n_rng_state) {
166				case CTL_STATE_ERROR:
167					(void) strcpy(
168					    dkp->ns_rngstate[i].value.c,
169					    "error");
170					break;
171				case CTL_STATE_HEALTHCHECK:
172					(void) strcpy(
173					    dkp->ns_rngstate[i].value.c,
174					    "healthcheck");
175					break;
176				case CTL_STATE_CONFIGURED:
177					(void) strcpy(
178					    dkp->ns_rngstate[i].value.c,
179					    "online");
180					break;
181				case CTL_STATE_UNCONFIGURED:
182					(void) strcpy(
183					    dkp->ns_rngstate[i].value.c,
184					    "offline");
185					break;
186				default:
187					(void) strcpy(
188					    dkp->ns_rngstate[i].value.c,
189					    "unknown");
190					break;
191				}
192				for (j = 0; j < N2RNG_NOSC; j++) {
193					dkp->ns_rngbias[i][j].value.ull =
194					    rng->n_bias_info[j].bias;
195					dkp->ns_rngentropy[i][j].value.ull =
196					    rng->n_bias_info[j].entropy;
197				}
198			}
199		}
200	}
201
202	return (0);
203}
204