xref: /illumos-gate/usr/src/lib/libipmi/common/ipmi_sdr.c (revision 99dda20867d903eec23291ba1ecb18a82d70096b)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <libipmi.h>
30 #include <stddef.h>
31 #include <string.h>
32 #include <strings.h>
33 
34 #include "ipmi_impl.h"
35 
36 typedef struct ipmi_sdr_cache_ent {
37 	char				*isc_name;
38 	struct ipmi_sdr			*isc_sdr;
39 	ipmi_hash_link_t		isc_link;
40 } ipmi_sdr_cache_ent_t;
41 
42 typedef struct ipmi_cmd_get_sdr {
43 	uint16_t	ic_gs_resid;
44 	uint16_t	ic_gs_recid;
45 	uint8_t		ic_gs_offset;
46 	uint8_t		ic_gs_len;
47 } ipmi_cmd_get_sdr_t;
48 
49 typedef struct ipmi_rsp_get_sdr {
50 	uint16_t	ir_gs_next;
51 	uint8_t		ir_gs_record[1];
52 } ipmi_rsp_get_sdr_t;
53 
54 /*
55  * "Get SDR Repostiory Info" command.
56  */
57 ipmi_sdr_info_t *
58 ipmi_sdr_get_info(ipmi_handle_t *ihp)
59 {
60 	ipmi_cmd_t cmd, *rsp;
61 	ipmi_sdr_info_t *sip;
62 
63 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
64 	cmd.ic_lun = 0;
65 	cmd.ic_cmd = IPMI_CMD_GET_SDR_INFO;
66 	cmd.ic_dlen = 0;
67 	cmd.ic_data = NULL;
68 
69 	if ((rsp = ipmi_send(ihp, &cmd)) == NULL)
70 		return (NULL);
71 
72 	sip = rsp->ic_data;
73 
74 	sip->isi_record_count = LE_IN16(&sip->isi_record_count);
75 	sip->isi_free_space = LE_IN16(&sip->isi_free_space);
76 	sip->isi_add_ts = LE_IN32(&sip->isi_add_ts);
77 	sip->isi_erase_ts = LE_IN32(&sip->isi_erase_ts);
78 
79 	return (sip);
80 }
81 
82 /*
83  * Issue the "Reserve SDR Repository" command.
84  */
85 static int
86 ipmi_sdr_reserve_repository(ipmi_handle_t *ihp)
87 {
88 	ipmi_cmd_t cmd, *rsp;
89 
90 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
91 	cmd.ic_lun = 0;
92 	cmd.ic_cmd = IPMI_CMD_RESERVE_SDR_REPOSITORY;
93 	cmd.ic_dlen = 0;
94 	cmd.ic_data = NULL;
95 
96 	if ((rsp = ipmi_send(ihp, &cmd)) == NULL)
97 		return (-1);
98 
99 	ihp->ih_reservation = *((uint16_t *)rsp->ic_data);
100 	return (0);
101 }
102 
103 /*
104  * Returns B_TRUE if the repository has changed since the cached copy was last
105  * referenced.
106  */
107 boolean_t
108 ipmi_sdr_changed(ipmi_handle_t *ihp)
109 {
110 	ipmi_sdr_info_t *sip;
111 
112 	if ((sip = ipmi_sdr_get_info(ihp)) == NULL)
113 		return (B_TRUE);
114 
115 	return (sip->isi_add_ts > ihp->ih_sdr_ts ||
116 	    sip->isi_erase_ts > ihp->ih_sdr_ts ||
117 	    ipmi_hash_first(ihp->ih_sdr_cache) == NULL);
118 }
119 
120 /*
121  * Refresh the cache of sensor data records.
122  */
123 int
124 ipmi_sdr_refresh(ipmi_handle_t *ihp)
125 {
126 	uint16_t id;
127 	ipmi_sdr_t *sdr;
128 	ipmi_sdr_cache_ent_t *ent;
129 	size_t namelen, len;
130 	uint8_t type;
131 	char *name;
132 	ipmi_sdr_info_t *sip;
133 
134 	if ((sip = ipmi_sdr_get_info(ihp)) == NULL)
135 		return (-1);
136 
137 	if (sip->isi_add_ts <= ihp->ih_sdr_ts &&
138 	    sip->isi_erase_ts <= ihp->ih_sdr_ts &&
139 	    ipmi_hash_first(ihp->ih_sdr_cache) != NULL)
140 		return (0);
141 
142 	ipmi_sdr_clear(ihp);
143 	ipmi_entity_clear(ihp);
144 	ihp->ih_sdr_ts = MAX(sip->isi_add_ts, sip->isi_erase_ts);
145 
146 	/*
147 	 * Iterate over all existing SDRs and add them to the cache.
148 	 */
149 	id = IPMI_SDR_FIRST;
150 	while (id != IPMI_SDR_LAST) {
151 		if ((sdr = ipmi_sdr_get(ihp, id, &id)) == NULL)
152 			return (-1);
153 
154 		/*
155 		 * Extract the name from the record-specific data.
156 		 */
157 		switch (sdr->is_type) {
158 		case IPMI_SDR_TYPE_GENERIC_LOCATOR:
159 			{
160 				ipmi_sdr_generic_locator_t *glp =
161 				    (ipmi_sdr_generic_locator_t *)
162 				    sdr->is_record;
163 				namelen = glp->is_gl_idlen;
164 				type = glp->is_gl_idtype;
165 				name = glp->is_gl_idstring;
166 				break;
167 			}
168 
169 		case IPMI_SDR_TYPE_FRU_LOCATOR:
170 			{
171 				ipmi_sdr_fru_locator_t *flp =
172 				    (ipmi_sdr_fru_locator_t *)
173 				    sdr->is_record;
174 				namelen = flp->is_fl_idlen;
175 				name = flp->is_fl_idstring;
176 				type = flp->is_fl_idtype;
177 				break;
178 			}
179 
180 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
181 			{
182 				ipmi_sdr_compact_sensor_t *csp =
183 				    (ipmi_sdr_compact_sensor_t *)
184 				    sdr->is_record;
185 				namelen = csp->is_cs_idlen;
186 				type = csp->is_cs_idtype;
187 				name = csp->is_cs_idstring;
188 
189 				csp->is_cs_assert_mask =
190 				    LE_IN16(&csp->is_cs_assert_mask);
191 				csp->is_cs_deassert_mask =
192 				    LE_IN16(&csp->is_cs_deassert_mask);
193 				csp->is_cs_reading_mask =
194 				    LE_IN16(&csp->is_cs_reading_mask);
195 				break;
196 			}
197 
198 		case IPMI_SDR_TYPE_FULL_SENSOR:
199 			{
200 				ipmi_sdr_full_sensor_t *csp =
201 				    (ipmi_sdr_full_sensor_t *)
202 				    sdr->is_record;
203 				namelen = csp->is_fs_idlen;
204 				type = csp->is_fs_idtype;
205 				name = csp->is_fs_idstring;
206 
207 				csp->is_fs_assert_mask =
208 				    LE_IN16(&csp->is_fs_assert_mask);
209 				csp->is_fs_deassert_mask =
210 				    LE_IN16(&csp->is_fs_deassert_mask);
211 				csp->is_fs_reading_mask =
212 				    LE_IN16(&csp->is_fs_reading_mask);
213 				break;
214 			}
215 
216 		case IPMI_SDR_TYPE_EVENT_ONLY:
217 			{
218 				ipmi_sdr_event_only_t *esp =
219 				    (ipmi_sdr_event_only_t *)
220 				    sdr->is_record;
221 				namelen = esp->is_eo_idlen;
222 				type = esp->is_eo_idtype;
223 				name = esp->is_eo_idstring;
224 				break;
225 			}
226 
227 		case IPMI_SDR_TYPE_MANAGEMENT_LOCATOR:
228 			{
229 				ipmi_sdr_management_locator_t *msp =
230 				    (ipmi_sdr_management_locator_t *)
231 				    sdr->is_record;
232 				namelen = msp->is_ml_idlen;
233 				type = msp->is_ml_idtype;
234 				name = msp->is_ml_idstring;
235 				break;
236 			}
237 
238 		case IPMI_SDR_TYPE_MANAGEMENT_CONFIRMATION:
239 			{
240 				ipmi_sdr_management_confirmation_t *mcp =
241 				    (ipmi_sdr_management_confirmation_t *)
242 				    sdr->is_record;
243 				name = NULL;
244 				mcp->is_mc_product =
245 				    LE_IN16(&mcp->is_mc_product);
246 				break;
247 			}
248 
249 		default:
250 			name = NULL;
251 		}
252 
253 		if ((ent = ipmi_zalloc(ihp,
254 		    sizeof (ipmi_sdr_cache_ent_t))) == NULL)
255 			return (-1);
256 
257 		len = sdr->is_length + offsetof(ipmi_sdr_t, is_record);
258 		if ((ent->isc_sdr = ipmi_alloc(ihp, len)) == NULL) {
259 			ipmi_free(ihp, ent);
260 			return (-1);
261 		}
262 		bcopy(sdr, ent->isc_sdr, len);
263 
264 		if (name != NULL) {
265 			if ((ent->isc_name = ipmi_alloc(ihp, namelen + 1)) ==
266 			    NULL) {
267 				ipmi_free(ihp, ent->isc_sdr);
268 				ipmi_free(ihp, ent);
269 				return (-1);
270 			}
271 
272 			ipmi_decode_string(type, namelen, name, ent->isc_name);
273 		}
274 
275 		/*
276 		 * This should never happen.  It means that the SP has returned
277 		 * a SDR record twice, with the same name and ID.  This has
278 		 * been observed on service processors that don't correctly
279 		 * return SDR_LAST during iteration, so assume we've looped in
280 		 * the SDR and return gracefully.
281 		 */
282 		if (ipmi_hash_lookup(ihp->ih_sdr_cache, ent) != NULL) {
283 			ipmi_free(ihp, ent->isc_sdr);
284 			ipmi_free(ihp, ent->isc_name);
285 			ipmi_free(ihp, ent);
286 			break;
287 		}
288 
289 		ipmi_hash_insert(ihp->ih_sdr_cache, ent);
290 	}
291 
292 	return (0);
293 }
294 
295 /*
296  * Hash routines.  We allow lookup by name, but since not all entries have
297  * names, we fall back to the entry pointer, which is guaranteed to be unique.
298  * The end result is that entities without names cannot be looked up, but will
299  * show up during iteration.
300  */
301 static const void *
302 ipmi_sdr_hash_convert(const void *p)
303 {
304 	return (p);
305 }
306 
307 static ulong_t
308 ipmi_sdr_hash_compute(const void *p)
309 {
310 	const ipmi_sdr_cache_ent_t *ep = p;
311 
312 	if (ep->isc_name)
313 		return (ipmi_hash_strhash(ep->isc_name));
314 	else
315 		return (ipmi_hash_ptrhash(ep));
316 }
317 
318 static int
319 ipmi_sdr_hash_compare(const void *a, const void *b)
320 {
321 	const ipmi_sdr_cache_ent_t *ap = a;
322 	const ipmi_sdr_cache_ent_t *bp = b;
323 
324 	if (ap->isc_name == NULL || bp->isc_name == NULL)
325 		return (-1);
326 
327 	if (strcmp(ap->isc_name, bp->isc_name) != 0)
328 		return (-1);
329 
330 	/*
331 	 * While it is strange for a service processor to report multiple
332 	 * entries with the same name, we allow it by treating the (name, id)
333 	 * as the unique identifier.  When looking up by name, the SDR pointer
334 	 * is NULL, and we return the first matching name.
335 	 */
336 	if (ap->isc_sdr == NULL || bp->isc_sdr == NULL)
337 		return (0);
338 
339 	if (ap->isc_sdr->is_id == bp->isc_sdr->is_id)
340 		return (0);
341 	else
342 		return (-1);
343 }
344 
345 int
346 ipmi_sdr_init(ipmi_handle_t *ihp)
347 {
348 	if ((ihp->ih_sdr_cache = ipmi_hash_create(ihp,
349 	    offsetof(ipmi_sdr_cache_ent_t, isc_link),
350 	    ipmi_sdr_hash_convert, ipmi_sdr_hash_compute,
351 	    ipmi_sdr_hash_compare)) == NULL)
352 		return (-1);
353 
354 	return (0);
355 }
356 
357 void
358 ipmi_sdr_clear(ipmi_handle_t *ihp)
359 {
360 	ipmi_sdr_cache_ent_t *ent;
361 
362 	while ((ent = ipmi_hash_first(ihp->ih_sdr_cache)) != NULL) {
363 		ipmi_hash_remove(ihp->ih_sdr_cache, ent);
364 		ipmi_free(ihp, ent->isc_sdr);
365 		ipmi_free(ihp, ent->isc_name);
366 		ipmi_free(ihp, ent);
367 	}
368 }
369 
370 void
371 ipmi_sdr_fini(ipmi_handle_t *ihp)
372 {
373 	if (ihp->ih_sdr_cache != NULL) {
374 		ipmi_sdr_clear(ihp);
375 		ipmi_hash_destroy(ihp->ih_sdr_cache);
376 	}
377 }
378 
379 ipmi_sdr_t *
380 ipmi_sdr_get(ipmi_handle_t *ihp, uint16_t id, uint16_t *next)
381 {
382 	ipmi_cmd_t cmd, *rsp;
383 	ipmi_cmd_get_sdr_t req;
384 	ipmi_rsp_get_sdr_t *sdr;
385 	int i;
386 
387 	req.ic_gs_resid = ihp->ih_reservation;
388 	req.ic_gs_recid = id;
389 	req.ic_gs_offset = 0;
390 	req.ic_gs_len = 0xFF;
391 
392 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
393 	cmd.ic_lun = 0;
394 	cmd.ic_cmd = IPMI_CMD_GET_SDR;
395 	cmd.ic_dlen = sizeof (req);
396 	cmd.ic_data = &req;
397 
398 	for (i = 0; i < ihp->ih_retries; i++) {
399 		if ((rsp = ipmi_send(ihp, &cmd)) != NULL)
400 			break;
401 
402 		if (ipmi_errno(ihp) != EIPMI_INVALID_RESERVATION)
403 			return (NULL);
404 
405 		if (ipmi_sdr_reserve_repository(ihp) != 0)
406 			return (NULL);
407 		req.ic_gs_resid = ihp->ih_reservation;
408 	}
409 
410 	if (rsp == NULL)
411 		return (NULL);
412 
413 	if (rsp->ic_dlen < sizeof (uint16_t) + sizeof (ipmi_sdr_t)) {
414 		(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
415 		return (NULL);
416 	}
417 
418 	sdr = rsp->ic_data;
419 	*next = sdr->ir_gs_next;
420 
421 	return ((ipmi_sdr_t *)sdr->ir_gs_record);
422 }
423 
424 int
425 ipmi_sdr_iter(ipmi_handle_t *ihp, int (*func)(ipmi_handle_t *,
426     const char *, ipmi_sdr_t *, void *), void *data)
427 {
428 	ipmi_sdr_cache_ent_t *ent;
429 	int ret;
430 
431 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
432 	    ipmi_sdr_refresh(ihp) != 0)
433 		return (-1);
434 
435 	for (ent = ipmi_hash_first(ihp->ih_sdr_cache); ent != NULL;
436 	    ent = ipmi_hash_next(ihp->ih_sdr_cache, ent)) {
437 		if ((ret = func(ihp, ent->isc_name, ent->isc_sdr, data)) != 0)
438 			return (ret);
439 	}
440 
441 	return (0);
442 }
443 
444 ipmi_sdr_t *
445 ipmi_sdr_lookup(ipmi_handle_t *ihp, const char *idstr)
446 {
447 	ipmi_sdr_cache_ent_t *ent, search;
448 
449 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
450 	    ipmi_sdr_refresh(ihp) != 0)
451 		return (NULL);
452 
453 	search.isc_name = (char *)idstr;
454 	search.isc_sdr = NULL;
455 	if ((ent = ipmi_hash_lookup(ihp->ih_sdr_cache, &search)) == NULL) {
456 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
457 		return (NULL);
458 	}
459 
460 	return (ent->isc_sdr);
461 }
462 
463 static void *
464 ipmi_sdr_lookup_common(ipmi_handle_t *ihp, const char *idstr,
465     uint8_t type)
466 {
467 	ipmi_sdr_t *sdrp;
468 
469 	if ((sdrp = ipmi_sdr_lookup(ihp, idstr)) == NULL)
470 		return (NULL);
471 
472 	if (sdrp->is_type != type) {
473 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
474 		return (NULL);
475 	}
476 
477 	return (sdrp->is_record);
478 }
479 
480 ipmi_sdr_fru_locator_t *
481 ipmi_sdr_lookup_fru(ipmi_handle_t *ihp, const char *idstr)
482 {
483 	return (ipmi_sdr_lookup_common(ihp, idstr,
484 	    IPMI_SDR_TYPE_FRU_LOCATOR));
485 }
486 
487 ipmi_sdr_generic_locator_t *
488 ipmi_sdr_lookup_generic(ipmi_handle_t *ihp, const char *idstr)
489 {
490 	return (ipmi_sdr_lookup_common(ihp, idstr,
491 	    IPMI_SDR_TYPE_GENERIC_LOCATOR));
492 }
493 
494 ipmi_sdr_compact_sensor_t *
495 ipmi_sdr_lookup_compact_sensor(ipmi_handle_t *ihp, const char *idstr)
496 {
497 	return (ipmi_sdr_lookup_common(ihp, idstr,
498 	    IPMI_SDR_TYPE_COMPACT_SENSOR));
499 }
500 
501 ipmi_sdr_full_sensor_t *
502 ipmi_sdr_lookup_full_sensor(ipmi_handle_t *ihp, const char *idstr)
503 {
504 	return (ipmi_sdr_lookup_common(ihp, idstr,
505 	    IPMI_SDR_TYPE_FULL_SENSOR));
506 }
507