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
47topo_imethod_t *
48topo_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 */
68boolean_t
69topo_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
86static void
87topo_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
99static void
100topo_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
111static int
112set_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
134int
135topo_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
191void
192topo_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
219void
220topo_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
237int
238topo_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
278int
279topo_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
291struct sensor_errinfo
292{
293	boolean_t se_predictive;
294	boolean_t se_nonrecov;
295	uint32_t se_src;
296};
297
298static boolean_t
299topo_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
365static boolean_t
366topo_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*/
426int
427topo_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;
528error:
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