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