xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/ipmi/ipmi_enum.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
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 /*
23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <assert.h>
27 #include <fm/libtopo.h>
28 #include <fm/topo_mod.h>
29 #include <sys/fm/protocol.h>
30 #include <string.h>
31 
32 #define	TOPO_PGROUP_IPMI 		"ipmi"
33 #define	TOPO_PROP_IPMI_ENTITY_REF	"entity_ref"
34 #define	TOPO_PROP_IPMI_ENTITY_PRESENT	"entity_present"
35 
36 typedef struct ipmi_enum_data {
37 	topo_mod_t	*ed_mod;
38 	tnode_t		*ed_pnode;
39 	const char	*ed_name;
40 	char		*ed_label;
41 	uint8_t		ed_entity;
42 	topo_instance_t	ed_instance;
43 	boolean_t	ed_hasfru;
44 } ipmi_enum_data_t;
45 
46 static int ipmi_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
47     nvlist_t **);
48 static int ipmi_enum(topo_mod_t *, tnode_t *, const char *,
49     topo_instance_t, topo_instance_t, void *, void *);
50 static int ipmi_post_process(topo_mod_t *, tnode_t *);
51 
52 extern int ipmi_fru_label(topo_mod_t *mod, tnode_t *node,
53     topo_version_t vers, nvlist_t *in, nvlist_t **out);
54 
55 extern int ipmi_fru_fmri(topo_mod_t *mod, tnode_t *node,
56     topo_version_t vers, nvlist_t *in, nvlist_t **out);
57 
58 static const topo_method_t ipmi_methods[] = {
59 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
60 	    TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ipmi_present },
61 	{ "ipmi_fru_label", "Property method", 0,
62 	    TOPO_STABILITY_INTERNAL, ipmi_fru_label},
63 	{ "ipmi_fru_fmri", "Property method", 0,
64 	    TOPO_STABILITY_INTERNAL, ipmi_fru_fmri},
65 	{ TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC,
66 	    TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL,
67 	    topo_method_sensor_failure },
68 	{ NULL }
69 };
70 
71 const topo_modops_t ipmi_ops = { ipmi_enum, NULL };
72 
73 const topo_modinfo_t ipmi_info =
74 	{ "ipmi", FM_FMRI_SCHEME_HC, TOPO_VERSION, &ipmi_ops };
75 
76 /*
77  * Determine if the entity is present.
78  */
79 /*ARGSUSED*/
80 static int
81 ipmi_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
82     nvlist_t *in, nvlist_t **out)
83 {
84 	ipmi_handle_t *ihp;
85 	ipmi_entity_t *ep;
86 	boolean_t present;
87 	nvlist_t *nvl;
88 	int err, i;
89 	char *name, **names;
90 	ipmi_sdr_t *sdrp;
91 	uint_t nelems;
92 
93 	if ((ihp = topo_mod_ipmi_hold(mod)) == NULL)
94 		return (topo_mod_seterrno(mod, ETOPO_METHOD_UNKNOWN));
95 
96 	ep = topo_node_getspecific(tn);
97 	if (ep == NULL) {
98 		if (topo_prop_get_string(tn, TOPO_PGROUP_IPMI,
99 		    TOPO_PROP_IPMI_ENTITY_PRESENT, &name, &err) == 0) {
100 			/*
101 			 * Some broken IPMI implementations don't export correct
102 			 * entities, so referring to an entity isn't sufficient.
103 			 * For these platforms, we allow the XML to specify a
104 			 * single SDR record that represents the current present
105 			 * state.
106 			 */
107 			if ((sdrp = ipmi_sdr_lookup(ihp, name)) == NULL ||
108 			    ipmi_entity_present_sdr(ihp, sdrp, &present) != 0) {
109 				topo_mod_dprintf(mod,
110 				    "Failed to get present state of %s (%s)\n",
111 				    name, ipmi_errmsg(ihp));
112 				topo_mod_strfree(mod, name);
113 				topo_mod_ipmi_rele(mod);
114 				return (-1);
115 			}
116 
117 			topo_mod_dprintf(mod,
118 			    "ipmi_entity_present_sdr(%s) = %d\n", name,
119 			    present);
120 			topo_mod_strfree(mod, name);
121 		} else {
122 			if (topo_prop_get_string_array(tn, TOPO_PGROUP_IPMI,
123 			    TOPO_PROP_IPMI_ENTITY_REF, &names, &nelems, &err)
124 			    != 0) {
125 				/*
126 				 * Not all nodes have an entity_ref attribute.
127 				 * For these cases, return ENOTSUP so that we
128 				 * fall back to the default hc presence
129 				 * detection.
130 				 */
131 				topo_mod_ipmi_rele(mod);
132 				return (topo_mod_seterrno(mod,
133 				    ETOPO_METHOD_NOTSUP));
134 			}
135 
136 			for (i = 0; i < nelems; i++)
137 				if ((ep = ipmi_entity_lookup_sdr(ihp, names[i]))
138 				    != NULL)
139 					break;
140 
141 			for (i = 0; i < nelems; i++)
142 				topo_mod_strfree(mod, names[i]);
143 			topo_mod_free(mod, names, (nelems * sizeof (char *)));
144 
145 			if (ep == NULL) {
146 				topo_mod_dprintf(mod,
147 				    "Failed to get present state of %s=%d\n",
148 				    topo_node_name(tn), topo_node_instance(tn));
149 				topo_mod_ipmi_rele(mod);
150 				return (-1);
151 			}
152 			topo_node_setspecific(tn, ep);
153 		}
154 	}
155 
156 	if (ep != NULL) {
157 		if (ipmi_entity_present(ihp, ep, &present) != 0) {
158 			topo_mod_dprintf(mod,
159 			    "ipmi_entity_present() failed: %s",
160 			    ipmi_errmsg(ihp));
161 			topo_mod_ipmi_rele(mod);
162 			return (-1);
163 		}
164 
165 		topo_mod_dprintf(mod,
166 		    "ipmi_entity_present(%d, %d) = %d\n", ep->ie_type,
167 		    ep->ie_instance, present);
168 	}
169 
170 	topo_mod_ipmi_rele(mod);
171 
172 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
173 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
174 
175 	if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET, present) != 0) {
176 		nvlist_free(nvl);
177 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
178 	}
179 
180 	*out = nvl;
181 
182 	return (0);
183 }
184 
185 /*
186  * This determines if the entity has a FRU locator record set, in which case we
187  * treat this as a FRU, even if it's part of an association.
188  */
189 /*ARGSUSED*/
190 static int
191 ipmi_check_sdr(ipmi_handle_t *ihp, ipmi_entity_t *ep, const char *name,
192     ipmi_sdr_t *sdrp, void *data)
193 {
194 	ipmi_enum_data_t *edp = data;
195 
196 	if (sdrp->is_type == IPMI_SDR_TYPE_FRU_LOCATOR)
197 		edp->ed_hasfru = B_TRUE;
198 
199 	return (0);
200 }
201 
202 /*
203  * Main entity enumerator.  If we find a matching entity type, then instantiate
204  * a topo node.
205  */
206 static int
207 ipmi_check_entity(ipmi_handle_t *ihp, ipmi_entity_t *ep, void *data)
208 {
209 	ipmi_enum_data_t *edp = data;
210 	ipmi_enum_data_t cdata;
211 	tnode_t *pnode = edp->ed_pnode;
212 	topo_mod_t *mod = edp->ed_mod;
213 	nvlist_t *auth, *fmri;
214 	tnode_t *tn;
215 	topo_pgroup_info_t pgi;
216 	int err;
217 	const char *labelname;
218 	char label[64];
219 	size_t len;
220 
221 	if (ep->ie_type != edp->ed_entity)
222 		return (0);
223 
224 	/*
225 	 * The purpose of power and cooling domains is to group psus and fans
226 	 * together.  Unfortunately, some broken IPMI implementations declare
227 	 * domains that don't contain other elements.  Since the end goal is to
228 	 * only enumerate psus and fans, we'll just ignore such elements.
229 	 */
230 	if ((ep->ie_type == IPMI_ET_POWER_DOMAIN ||
231 	    ep->ie_type == IPMI_ET_COOLING_DOMAIN) &&
232 	    ep->ie_children == 0)
233 		return (0);
234 
235 	if ((auth = topo_mod_auth(mod, pnode)) == NULL) {
236 		topo_mod_dprintf(mod, "topo_mod_auth() failed: %s",
237 		    topo_mod_errmsg(mod));
238 		return (1);
239 	}
240 
241 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
242 	    edp->ed_name, edp->ed_instance, NULL, auth, NULL, NULL,
243 	    NULL)) == NULL) {
244 		nvlist_free(auth);
245 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
246 		    topo_mod_errmsg(mod));
247 		return (1);
248 	}
249 
250 	nvlist_free(auth);
251 
252 	if ((tn = topo_node_bind(mod, pnode, edp->ed_name,
253 	    edp->ed_instance, fmri)) == NULL) {
254 		nvlist_free(fmri);
255 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
256 		    topo_mod_errmsg(mod));
257 		return (1);
258 	}
259 
260 	/*
261 	 * We inherit our label from our parent, appending our label in the
262 	 * process.  This results in defaults labels of the form "FM 1 FAN 0"
263 	 * by default when given a hierarchy.
264 	 */
265 	if (edp->ed_label != NULL)
266 		(void) snprintf(label, sizeof (label), "%s ", edp->ed_label);
267 	else
268 		label[0] = '\0';
269 
270 	switch (edp->ed_entity) {
271 	case IPMI_ET_POWER_DOMAIN:
272 		labelname = "PM";
273 		break;
274 
275 	case IPMI_ET_PSU:
276 		labelname = "PSU";
277 		break;
278 
279 	case IPMI_ET_COOLING_DOMAIN:
280 		labelname = "FM";
281 		break;
282 
283 	case IPMI_ET_FAN:
284 		labelname = "FAN";
285 		break;
286 	}
287 
288 	len = strlen(label);
289 	(void) snprintf(label + len, sizeof (label) - len, "%s %d",
290 	    labelname, edp->ed_instance);
291 
292 	nvlist_free(fmri);
293 	edp->ed_instance++;
294 
295 	if (topo_node_label_set(tn, label, &err) != 0) {
296 		topo_mod_dprintf(mod, "failed to set label: %s\n",
297 		    topo_strerror(err));
298 		return (1);
299 	}
300 
301 	/*
302 	 * Store IPMI entity details as properties on the node
303 	 */
304 	pgi.tpi_name = TOPO_PGROUP_IPMI;
305 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
306 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
307 	pgi.tpi_version = TOPO_VERSION;
308 	if (topo_pgroup_create(tn, &pgi, &err) != 0) {
309 		if (err != ETOPO_PROP_DEFD) {
310 			topo_mod_dprintf(mod, "failed to create propgroup "
311 			    "%s: %s\n", TOPO_PGROUP_IPMI, topo_strerror(err));
312 			return (1);
313 		}
314 	}
315 
316 	if (topo_method_register(mod, tn, ipmi_methods) != 0) {
317 		topo_mod_dprintf(mod, "topo_method_register() failed: %s",
318 		    topo_mod_errmsg(mod));
319 		return (1);
320 	}
321 
322 	/*
323 	 * If we are a child of a non-chassis node, and there isn't an explicit
324 	 * FRU locator record, then propagate the parent's FRU.  Otherwise, set
325 	 * the FRU to be the same as the resource.
326 	 */
327 	edp->ed_hasfru = B_FALSE;
328 	(void) ipmi_entity_iter_sdr(ihp, ep, ipmi_check_sdr, edp);
329 
330 	if (strcmp(topo_node_name(pnode), CHASSIS) == 0 ||
331 	    edp->ed_hasfru) {
332 		if (topo_node_resource(tn, &fmri, &err) != 0) {
333 			topo_mod_dprintf(mod, "topo_node_resource() failed: %s",
334 			    topo_strerror(err));
335 			(void) topo_mod_seterrno(mod, err);
336 			return (1);
337 		}
338 	} else {
339 		if (topo_node_fru(pnode, &fmri, NULL, &err) != 0) {
340 			topo_mod_dprintf(mod, "topo_node_fru() failed: %s",
341 			    topo_strerror(err));
342 			(void) topo_mod_seterrno(mod, err);
343 			return (1);
344 		}
345 	}
346 
347 	if (topo_node_fru_set(tn, fmri, 0, &err) != 0) {
348 		nvlist_free(fmri);
349 		topo_mod_dprintf(mod, "topo_node_fru_set() failed: %s",
350 		    topo_strerror(err));
351 		(void) topo_mod_seterrno(mod, err);
352 		return (1);
353 	}
354 
355 	topo_node_setspecific(tn, ep);
356 
357 	nvlist_free(fmri);
358 
359 	/*
360 	 * Iterate over children, once for recursive domains and once for
361 	 * psu/fans.
362 	 */
363 	if (ep->ie_children != 0 &&
364 	    (ep->ie_type == IPMI_ET_POWER_DOMAIN ||
365 	    ep->ie_type == IPMI_ET_COOLING_DOMAIN)) {
366 		cdata.ed_mod = edp->ed_mod;
367 		cdata.ed_pnode = tn;
368 		cdata.ed_instance = 0;
369 		cdata.ed_name = edp->ed_name;
370 		cdata.ed_entity = edp->ed_entity;
371 		cdata.ed_label = label;
372 
373 		if (ipmi_entity_iter_children(ihp, ep,
374 		    ipmi_check_entity, &cdata) != 0)
375 			return (1);
376 
377 		switch (cdata.ed_entity) {
378 		case IPMI_ET_POWER_DOMAIN:
379 			cdata.ed_entity = IPMI_ET_PSU;
380 			cdata.ed_name = PSU;
381 			break;
382 
383 		case IPMI_ET_COOLING_DOMAIN:
384 			cdata.ed_entity = IPMI_ET_FAN;
385 			cdata.ed_name = FAN;
386 			break;
387 		}
388 
389 		if (ipmi_entity_iter_children(ihp, ep,
390 		    ipmi_check_entity, &cdata) != 0)
391 			return (1);
392 	}
393 
394 	return (0);
395 }
396 
397 /*
398  * libtopo enumeration point.  This simply iterates over entities looking for
399  * the appropriate type.
400  */
401 /*ARGSUSED*/
402 static int
403 ipmi_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
404     topo_instance_t min, topo_instance_t max, void *arg, void *unused)
405 {
406 	ipmi_handle_t *ihp;
407 	ipmi_enum_data_t data;
408 	int ret;
409 
410 	/*
411 	 * If the node being passed in ISN'T the chassis node, then we're being
412 	 * asked to post-process a statically defined node.
413 	 */
414 	if (strcmp(topo_node_name(rnode), CHASSIS) != 0) {
415 		if (ipmi_post_process(mod, rnode) != 0) {
416 			topo_mod_dprintf(mod, "post processing of node %s=%d "
417 			    "failed!", topo_node_name(rnode),
418 			    topo_node_instance(rnode));
419 			return (-1);
420 		}
421 		return (0);
422 	}
423 
424 	if (strcmp(name, POWERMODULE) == 0) {
425 		data.ed_entity = IPMI_ET_POWER_DOMAIN;
426 	} else if (strcmp(name, PSU) == 0) {
427 		data.ed_entity = IPMI_ET_PSU;
428 	} else if (strcmp(name, FANMODULE) == 0) {
429 		data.ed_entity = IPMI_ET_COOLING_DOMAIN;
430 	} else if (strcmp(name, FAN) == 0) {
431 		data.ed_entity = IPMI_ET_FAN;
432 	} else {
433 		topo_mod_dprintf(mod, "unknown enumeration type '%s'",
434 		    name);
435 		return (-1);
436 	}
437 
438 	if ((ihp = topo_mod_ipmi_hold(mod)) == NULL)
439 		return (0);
440 
441 	data.ed_mod = mod;
442 	data.ed_pnode = rnode;
443 	data.ed_name = name;
444 	data.ed_instance = 0;
445 	data.ed_label = NULL;
446 
447 	if ((ret = ipmi_entity_iter(ihp, ipmi_check_entity, &data)) != 0) {
448 		/*
449 		 * We don't return failure if IPMI enumeration fails.  This may
450 		 * be due to the SP being unavailable or an otherwise transient
451 		 * event.
452 		 */
453 		if (ret < 0) {
454 			topo_mod_dprintf(mod,
455 			    "failed to enumerate entities: %s",
456 			    ipmi_errmsg(ihp));
457 		} else {
458 			topo_mod_ipmi_rele(mod);
459 			return (-1);
460 		}
461 	}
462 
463 	topo_mod_ipmi_rele(mod);
464 	return (0);
465 }
466 
467 static int
468 ipmi_post_process(topo_mod_t *mod, tnode_t *tn)
469 {
470 	if (topo_method_register(mod, tn, ipmi_methods) != 0) {
471 		topo_mod_dprintf(mod, "ipmi_post_process() failed: %s",
472 		    topo_mod_errmsg(mod));
473 		return (1);
474 	}
475 	return (0);
476 }
477 
478 /*ARGSUSED*/
479 int
480 _topo_init(topo_mod_t *mod, topo_version_t version)
481 {
482 	if (getenv("TOPOIPMIDEBUG") != NULL)
483 		topo_mod_setdebug(mod);
484 
485 	if (topo_mod_register(mod, &ipmi_info, TOPO_VERSION) != 0) {
486 		topo_mod_dprintf(mod, "%s registration failed: %s\n",
487 		    DISK, topo_mod_errmsg(mod));
488 		return (-1); /* mod errno already set */
489 	}
490 
491 	topo_mod_dprintf(mod, "IPMI enumerator initialized\n");
492 	return (0);
493 }
494 
495 void
496 _topo_fini(topo_mod_t *mod)
497 {
498 	topo_mod_unregister(mod);
499 }
500