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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /*
26  * Copyright (c) 2018, Joyent, Inc.
27  */
28 
29 #include <pthread.h>
30 #include <assert.h>
31 #include <errno.h>
32 #include <dirent.h>
33 #include <fnmatch.h>
34 #include <limits.h>
35 #include <alloca.h>
36 #include <unistd.h>
37 #include <stdio.h>
38 #include <strings.h>
39 
40 #include <topo_mod.h>
41 
42 #include <topo_error.h>
43 #include <topo_module.h>
44 #include <topo_subr.h>
45 #include <topo_tree.h>
46 
47 topo_imethod_t *
topo_method_lookup(tnode_t * node,const char * name)48 topo_method_lookup(tnode_t *node, const char *name)
49 {
50 	topo_imethod_t *mp;
51 
52 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
53 	    mp = topo_list_next(mp)) {
54 		if (strcmp(name, mp->tim_name) == 0) {
55 			topo_node_unlock(node);
56 			return (mp);
57 		}
58 	}
59 
60 	return (NULL);
61 }
62 
63 /*
64  * Simple API to determine if the specified node supports a given topo method
65  * (specified by the method name and version).  Returns true if supported, false
66  * otherwise.
67  */
68 boolean_t
topo_method_supported(tnode_t * node,const char * name,topo_version_t vers)69 topo_method_supported(tnode_t *node, const char *name, topo_version_t vers)
70 {
71 	topo_imethod_t *mp;
72 
73 	topo_node_lock(node);
74 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
75 	    mp = topo_list_next(mp)) {
76 		if ((strcmp(name, mp->tim_name) == 0) &&
77 		    (vers == mp->tim_version)) {
78 			topo_node_unlock(node);
79 			return (B_TRUE);
80 		}
81 	}
82 	topo_node_unlock(node);
83 	return (B_FALSE);
84 }
85 
86 static void
topo_method_enter(topo_imethod_t * mp)87 topo_method_enter(topo_imethod_t *mp)
88 {
89 	(void) pthread_mutex_lock(&mp->tim_lock);
90 
91 	while (mp->tim_busy != 0)
92 		(void) pthread_cond_wait(&mp->tim_cv, &mp->tim_lock);
93 
94 	++mp->tim_busy;
95 
96 	(void) pthread_mutex_unlock(&mp->tim_lock);
97 }
98 
99 static void
topo_method_exit(topo_imethod_t * mp)100 topo_method_exit(topo_imethod_t *mp)
101 {
102 	(void) pthread_mutex_lock(&mp->tim_lock);
103 	--mp->tim_busy;
104 
105 	assert(mp->tim_busy == 0);
106 
107 	(void) pthread_cond_broadcast(&mp->tim_cv);
108 	(void) pthread_mutex_unlock(&mp->tim_lock);
109 }
110 
111 static int
set_methregister_error(topo_mod_t * mod,tnode_t * node,topo_imethod_t * mp,int err)112 set_methregister_error(topo_mod_t *mod, tnode_t *node, topo_imethod_t *mp,
113     int err)
114 {
115 	if (mp != NULL) {
116 		topo_list_delete(&node->tn_methods, mp);
117 		if (mp->tim_name != NULL)
118 			topo_mod_strfree(mod, mp->tim_name);
119 		if (mp->tim_desc != NULL)
120 			topo_mod_strfree(mod, mp->tim_desc);
121 
122 		topo_mod_free(mod, mp, sizeof (topo_imethod_t));
123 	}
124 
125 	topo_node_unlock(node);
126 
127 	topo_dprintf(mod->tm_hdl, TOPO_DBG_ERR,
128 	    "method registration failed for %s: %s\n",
129 	    mod->tm_name, topo_strerror(err));
130 
131 	return (topo_mod_seterrno(mod, err));
132 }
133 
134 int
topo_method_register(topo_mod_t * mod,tnode_t * node,const topo_method_t * mp)135 topo_method_register(topo_mod_t *mod, tnode_t *node, const topo_method_t *mp)
136 {
137 	topo_imethod_t *imp;
138 	const topo_method_t *meth;
139 
140 	/*
141 	 * Initialize module methods
142 	 */
143 	for (meth = &mp[0]; meth->tm_name != NULL; meth++) {
144 
145 		topo_node_lock(node);
146 		if (topo_method_lookup(node, meth->tm_name) != NULL) {
147 			topo_node_unlock(node);
148 			continue;
149 		}
150 
151 		if (meth->tm_stability < TOPO_STABILITY_INTERNAL ||
152 		    meth->tm_stability > TOPO_STABILITY_MAX ||
153 		    meth->tm_func == NULL)
154 			return (set_methregister_error(mod, node, NULL,
155 			    ETOPO_METHOD_INVAL));
156 
157 		imp = topo_mod_zalloc(mod, sizeof (topo_imethod_t));
158 		if (imp == NULL)
159 			return (set_methregister_error(mod, node, imp,
160 			    ETOPO_METHOD_NOMEM));
161 
162 		if ((imp->tim_name = topo_mod_strdup(mod, meth->tm_name))
163 		    == NULL)
164 			return (set_methregister_error(mod, node, imp,
165 			    ETOPO_METHOD_NOMEM));
166 
167 		if ((imp->tim_desc = topo_mod_strdup(mod, meth->tm_desc))
168 		    == NULL)
169 			return (set_methregister_error(mod, node, imp,
170 			    ETOPO_METHOD_NOMEM));
171 
172 
173 		imp->tim_stability = meth->tm_stability;
174 		imp->tim_version = meth->tm_version;
175 		imp->tim_func = meth->tm_func;
176 		imp->tim_mod = mod;
177 
178 		topo_list_append(&node->tn_methods, imp);
179 		topo_node_unlock(node);
180 
181 		topo_dprintf(mod->tm_hdl, TOPO_DBG_MODSVC,
182 		    "registered module %s method "
183 		    "%s for %s=%d\n", mod->tm_name, imp->tim_name,
184 		    topo_node_name(node), topo_node_instance(node));
185 
186 	}
187 
188 	return (0);
189 }
190 
191 void
topo_method_unregister(topo_mod_t * mod,tnode_t * node,const char * name)192 topo_method_unregister(topo_mod_t *mod, tnode_t *node, const char *name)
193 {
194 	topo_imethod_t *mp;
195 
196 	topo_node_lock(node);
197 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
198 	    mp = topo_list_next(mp)) {
199 		if (strcmp(name, mp->tim_name) == 0)
200 			break;
201 	}
202 
203 	if (mp == NULL) {
204 		topo_node_unlock(node);
205 		return;
206 	}
207 
208 	topo_list_delete(&node->tn_methods, mp);
209 	topo_node_unlock(node);
210 
211 	if (mp->tim_name != NULL)
212 		topo_mod_strfree(mod, mp->tim_name);
213 	if (mp->tim_desc != NULL)
214 		topo_mod_strfree(mod, mp->tim_desc);
215 
216 	topo_mod_free(mod, mp, sizeof (topo_imethod_t));
217 }
218 
219 void
topo_method_unregister_all(topo_mod_t * mod,tnode_t * node)220 topo_method_unregister_all(topo_mod_t *mod, tnode_t *node)
221 {
222 	topo_imethod_t *mp;
223 
224 	topo_node_lock(node);
225 	while ((mp = topo_list_next(&node->tn_methods)) != NULL) {
226 		topo_list_delete(&node->tn_methods, mp);
227 		if (mp->tim_name != NULL)
228 			topo_mod_strfree(mod, mp->tim_name);
229 		if (mp->tim_desc != NULL)
230 			topo_mod_strfree(mod, mp->tim_desc);
231 		topo_mod_free(mod, mp, sizeof (topo_imethod_t));
232 	}
233 	topo_node_unlock(node);
234 }
235 
236 
237 int
topo_method_call(tnode_t * node,const char * method,topo_version_t version,nvlist_t * in,nvlist_t ** out,int * err)238 topo_method_call(tnode_t *node, const char *method,
239     topo_version_t version, nvlist_t *in, nvlist_t **out, int *err)
240 {
241 	int rc, save;
242 	topo_imethod_t *mp;
243 
244 	for (mp = topo_list_next(&node->tn_methods); mp != NULL;
245 	    mp = topo_list_next(mp)) {
246 		if (strcmp(method, mp->tim_name) != 0)
247 			continue;
248 
249 		if (version < mp->tim_version) {
250 			*err = ETOPO_METHOD_VEROLD;
251 			return (-1);
252 		} else if (version > mp->tim_version) {
253 			*err = ETOPO_METHOD_VERNEW;
254 			return (-1);
255 		}
256 
257 		topo_method_enter(mp);
258 		save = mp->tim_mod->tm_errno;
259 		mp->tim_mod->tm_errno = 0;
260 		if ((rc = mp->tim_func(mp->tim_mod, node, version, in, out))
261 		    < 0) {
262 			if (mp->tim_mod->tm_errno == 0)
263 				*err = ETOPO_METHOD_FAIL;
264 			else
265 				*err = mp->tim_mod->tm_errno;
266 		}
267 		mp->tim_mod->tm_errno = save;
268 		topo_method_exit(mp);
269 
270 		return (rc);
271 
272 	}
273 
274 	*err = ETOPO_METHOD_NOTSUP;
275 	return (-1);
276 }
277 
278 int
topo_method_invoke(tnode_t * node,const char * method,topo_version_t version,nvlist_t * in,nvlist_t ** out,int * err)279 topo_method_invoke(tnode_t *node, const char *method,
280     topo_version_t version, nvlist_t *in, nvlist_t **out, int *err)
281 {
282 	int rc;
283 
284 	topo_node_hold(node);
285 	rc = topo_method_call(node, method, version, in, out, err);
286 	topo_node_rele(node);
287 
288 	return (rc);
289 }
290 
291 struct sensor_errinfo
292 {
293 	boolean_t se_predictive;
294 	boolean_t se_nonrecov;
295 	uint32_t se_src;
296 };
297 
298 static boolean_t
topo_sensor_failed(int32_t type,uint32_t state,struct sensor_errinfo * seinfo)299 topo_sensor_failed(int32_t type, uint32_t state, struct sensor_errinfo *seinfo)
300 {
301 	boolean_t failed;
302 
303 	failed = B_FALSE;
304 	/*
305 	 * Unless the sensor explicitely says otherwise, all failures are
306 	 * non-recoverable, hard failures, coming from an unknown source.
307 	 */
308 	seinfo->se_predictive = B_FALSE;
309 	seinfo->se_nonrecov = B_TRUE;
310 	seinfo->se_src = TOPO_SENSOR_ERRSRC_UNKNOWN;
311 
312 	switch (type) {
313 	case TOPO_SENSOR_TYPE_THRESHOLD_STATE:
314 		if (state & (TOPO_SENSOR_STATE_THRESH_LOWER_NONREC |
315 		    TOPO_SENSOR_STATE_THRESH_UPPER_NONREC)) {
316 			failed = B_TRUE;
317 		} else if (state & (TOPO_SENSOR_STATE_THRESH_LOWER_CRIT |
318 		    TOPO_SENSOR_STATE_THRESH_UPPER_CRIT)) {
319 			failed = B_TRUE;
320 			seinfo->se_nonrecov = B_FALSE;
321 		}
322 		break;
323 
324 	case TOPO_SENSOR_TYPE_POWER_SUPPLY:
325 		if (state & TOPO_SENSOR_STATE_POWER_SUPPLY_PREDFAIL) {
326 			failed = B_TRUE;
327 			seinfo->se_predictive = B_TRUE;
328 			seinfo->se_src = TOPO_SENSOR_ERRSRC_INTERNAL;
329 		} else if (state & TOPO_SENSOR_STATE_POWER_SUPPLY_FAILURE) {
330 			failed = B_TRUE;
331 			seinfo->se_src = TOPO_SENSOR_ERRSRC_INTERNAL;
332 		} else if (state &
333 		    (TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_LOST |
334 		    TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE |
335 		    TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE_PRES)) {
336 			seinfo->se_src = TOPO_SENSOR_ERRSRC_EXTERNAL;
337 			failed = B_TRUE;
338 		}
339 		break;
340 
341 	case TOPO_SENSOR_TYPE_GENERIC_FAILURE:
342 		if (state & TOPO_SENSOR_STATE_GENERIC_FAIL_NONRECOV) {
343 			failed = B_TRUE;
344 		} else if (state & TOPO_SENSOR_STATE_GENERIC_FAIL_CRITICAL) {
345 			failed = B_TRUE;
346 			seinfo->se_nonrecov = B_FALSE;
347 		}
348 		break;
349 
350 	case TOPO_SENSOR_TYPE_GENERIC_OK:
351 		if (state & TOPO_SENSOR_STATE_GENERIC_OK_DEASSERTED)
352 			failed = B_TRUE;
353 		break;
354 	case TOPO_SENSOR_TYPE_GENERIC_PREDFAIL:
355 		if (state & TOPO_SENSOR_STATE_GENERIC_PREDFAIL_ASSERTED) {
356 			failed = B_TRUE;
357 			seinfo->se_predictive = B_TRUE;
358 		}
359 		break;
360 	}
361 
362 	return (failed);
363 }
364 
365 static boolean_t
topo_spoof_apply(topo_hdl_t * thp,tnode_t * node,tnode_t * facnode,nvlist_t * in,uint32_t * state)366 topo_spoof_apply(topo_hdl_t *thp, tnode_t *node, tnode_t *facnode,
367     nvlist_t *in, uint32_t *state)
368 {
369 	nvpair_t *elem = NULL;
370 	nvlist_t *spoof, *rsrc = NULL;
371 	char *fmrimatch, *fmri, *facmatch;
372 	uint32_t spoof_state;
373 	int err;
374 
375 	while ((elem = nvlist_next_nvpair(in, elem)) != NULL) {
376 		if (nvpair_value_nvlist(elem, &spoof) != 0)
377 			return (B_FALSE);
378 
379 		if (nvlist_lookup_string(spoof, ST_SPOOF_FMRI, &fmrimatch) !=
380 		    0 || nvlist_lookup_string(spoof, ST_SPOOF_SENSOR,
381 		    &facmatch) != 0 || nvlist_lookup_uint32(spoof,
382 		    ST_SPOOF_STATE, &spoof_state) != 0)
383 			continue;
384 
385 		if (topo_node_resource(node, &rsrc, &err) != 0 ||
386 		    topo_fmri_nvl2str(thp, rsrc, &fmri, &err) != 0) {
387 			nvlist_free(rsrc);
388 			continue;
389 		}
390 		nvlist_free(rsrc);
391 
392 		if (fnmatch(fmrimatch, fmri, 0) == 0 &&
393 		    strcmp(facmatch, topo_node_name(facnode)) == 0) {
394 			*state = spoof_state;
395 			topo_hdl_strfree(thp, fmri);
396 			return (B_TRUE);
397 		}
398 		topo_hdl_strfree(thp, fmri);
399 	}
400 	return (B_FALSE);
401 }
402 
403 /*
404  * Determine whether there are any sensors indicating failure.  This function
405  * is used internally to determine whether a given component is usable, as well
406  * by external monitoring software that wants additional information such as
407  * which sensors indicated failure.  The return value is an nvlist of nvlists
408  * indexed by sensor name, each entry with the following contents:
409  *
410  *	type, state, units, reading
411  *
412  *	Identical to sensor node.
413  *
414  *	nonrecov
415  *
416  *		Boolean value that is set to indicate that the error is
417  *		non-recoverable (the unit is out of service).  The default is
418  *		critical failure, which indicates a fault but the unit is still
419  *		operating.
420  *
421  *	injected
422  *
423  *		Boolean value indicating that the sensor state was injected.
424  */
425 /*ARGSUSED*/
426 int
topo_method_sensor_failure(topo_mod_t * mod,tnode_t * node,topo_version_t version,nvlist_t * in,nvlist_t ** out)427 topo_method_sensor_failure(topo_mod_t *mod, tnode_t *node,
428     topo_version_t version, nvlist_t *in, nvlist_t **out)
429 {
430 	topo_faclist_t faclist, *fp;
431 	int err;
432 	nvlist_t *nvl, *props, *propval, *tmp;
433 	int ret = -1;
434 	uint32_t type, state, units;
435 	nvpair_t *elem;
436 	double reading;
437 	char *propname;
438 	boolean_t has_reading, is_spoofed = B_FALSE;
439 	struct sensor_errinfo seinfo;
440 
441 	if (topo_node_facility(mod->tm_hdl, node, TOPO_FAC_TYPE_SENSOR,
442 	    TOPO_FAC_TYPE_ANY, &faclist, &err) != 0)
443 		return (topo_mod_seterrno(mod, ETOPO_METHOD_NOTSUP));
444 
445 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
446 		goto error;
447 
448 	for (fp = topo_list_next(&faclist.tf_list); fp != NULL;
449 	    fp = topo_list_next(fp)) {
450 		if (topo_prop_getpgrp(fp->tf_node, TOPO_PGROUP_FACILITY,
451 		    &props, &err) != 0) {
452 			nvlist_free(nvl);
453 			goto error;
454 		}
455 		type = state = units = 0;
456 		reading = 0;
457 		has_reading = B_FALSE;
458 
459 		elem = NULL;
460 		while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
461 			if (strcmp(nvpair_name(elem), TOPO_PROP_VAL) != 0 ||
462 			    nvpair_type(elem) != DATA_TYPE_NVLIST)
463 				continue;
464 
465 			(void) nvpair_value_nvlist(elem, &propval);
466 			if (nvlist_lookup_string(propval,
467 			    TOPO_PROP_VAL_NAME, &propname) != 0)
468 				continue;
469 
470 			if (strcmp(propname, TOPO_FACILITY_TYPE) == 0) {
471 				(void) nvlist_lookup_uint32(propval,
472 				    TOPO_PROP_VAL_VAL, &type);
473 			} else if (strcmp(propname, TOPO_SENSOR_STATE) == 0) {
474 				(void) nvlist_lookup_uint32(propval,
475 				    TOPO_PROP_VAL_VAL, &state);
476 			} else if (strcmp(propname, TOPO_SENSOR_UNITS) == 0) {
477 				(void) nvlist_lookup_uint32(propval,
478 				    TOPO_PROP_VAL_VAL, &units);
479 			} else if (strcmp(propname, TOPO_SENSOR_READING) == 0) {
480 				has_reading = B_TRUE;
481 				(void) nvlist_lookup_double(propval,
482 				    TOPO_PROP_VAL_VAL, &reading);
483 			}
484 		}
485 
486 		if (in != NULL)
487 			is_spoofed = topo_spoof_apply(mod->tm_hdl, node,
488 			    fp->tf_node, in, &state);
489 
490 		if (topo_sensor_failed(type, state, &seinfo)) {
491 			tmp = NULL;
492 			if (topo_mod_nvalloc(mod, &tmp, NV_UNIQUE_NAME) != 0 ||
493 			    nvlist_add_uint32(tmp, TOPO_FACILITY_TYPE,
494 			    type) != 0 ||
495 			    nvlist_add_uint32(tmp, TOPO_SENSOR_STATE,
496 			    state) != 0 ||
497 			    nvlist_add_uint32(tmp, TOPO_SENSOR_UNITS,
498 			    units) != 0 ||
499 			    nvlist_add_boolean_value(tmp,
500 			    "nonrecov", seinfo.se_nonrecov) != 0 ||
501 			    nvlist_add_boolean_value(tmp,
502 			    "predictive", seinfo.se_predictive) != 0 ||
503 			    nvlist_add_uint32(tmp, "source",
504 			    seinfo.se_src) != 0 ||
505 			    nvlist_add_boolean_value(nvl, "injected",
506 			    is_spoofed) != 0 ||
507 			    (has_reading && nvlist_add_double(tmp,
508 			    TOPO_SENSOR_READING, reading) != 0) ||
509 			    nvlist_add_nvlist(nvl, topo_node_name(fp->tf_node),
510 			    tmp) != 0) {
511 				nvlist_free(props);
512 				nvlist_free(tmp);
513 				nvlist_free(nvl);
514 				ret = topo_mod_seterrno(mod,
515 				    ETOPO_METHOD_NOMEM);
516 				goto error;
517 			}
518 
519 			nvlist_free(tmp);
520 		}
521 
522 		nvlist_free(props);
523 		is_spoofed = B_FALSE;
524 	}
525 
526 	*out = nvl;
527 	ret = 0;
528 error:
529 	while ((fp = topo_list_next(&faclist.tf_list)) != NULL) {
530 		topo_list_delete(&faclist.tf_list, fp);
531 		topo_mod_free(mod, fp, sizeof (topo_faclist_t));
532 	}
533 	return (ret);
534 }
535