xref: /titanic_50/usr/src/lib/libipmi/common/ipmi_sdr.c (revision fa60c371cd00bdca17de2ff18fe3e64d051ae61b)
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_16(sip->isi_record_count);
75 	sip->isi_free_space = LE_16(sip->isi_free_space);
76 	sip->isi_add_ts = LE_32(sip->isi_add_ts);
77 	sip->isi_erase_ts = LE_32(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_16(csp->is_cs_assert_mask);
191 				csp->is_cs_deassert_mask =
192 				    LE_16(csp->is_cs_deassert_mask);
193 				csp->is_cs_reading_mask =
194 				    LE_16(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_16(csp->is_fs_assert_mask);
209 				csp->is_fs_deassert_mask =
210 				    LE_16(csp->is_fs_deassert_mask);
211 				csp->is_fs_reading_mask =
212 				    LE_16(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 = LE_16(mcp->is_mc_product);
245 				break;
246 			}
247 
248 		default:
249 			name = NULL;
250 		}
251 
252 		if ((ent = ipmi_zalloc(ihp,
253 		    sizeof (ipmi_sdr_cache_ent_t))) == NULL)
254 			return (-1);
255 
256 		len = sdr->is_length + offsetof(ipmi_sdr_t, is_record);
257 		if ((ent->isc_sdr = ipmi_alloc(ihp, len)) == NULL) {
258 			ipmi_free(ihp, ent);
259 			return (-1);
260 		}
261 		bcopy(sdr, ent->isc_sdr, len);
262 
263 		if (name != NULL) {
264 			if ((ent->isc_name = ipmi_alloc(ihp, namelen + 1)) ==
265 			    NULL) {
266 				ipmi_free(ihp, ent->isc_sdr);
267 				ipmi_free(ihp, ent);
268 				return (-1);
269 			}
270 
271 			ipmi_decode_string(type, namelen, name, ent->isc_name);
272 		}
273 
274 		/*
275 		 * This should never happen.  It means that the SP has returned
276 		 * a SDR record twice, with the same name and ID.  This has
277 		 * been observed on service processors that don't correctly
278 		 * return SDR_LAST during iteration, so assume we've looped in
279 		 * the SDR and return gracefully.
280 		 */
281 		if (ipmi_hash_lookup(ihp->ih_sdr_cache, ent) != NULL) {
282 			ipmi_free(ihp, ent->isc_sdr);
283 			ipmi_free(ihp, ent->isc_name);
284 			ipmi_free(ihp, ent);
285 			break;
286 		}
287 
288 		ipmi_hash_insert(ihp->ih_sdr_cache, ent);
289 	}
290 
291 	return (0);
292 }
293 
294 /*
295  * Hash routines.  We allow lookup by name, but since not all entries have
296  * names, we fall back to the entry pointer, which is guaranteed to be unique.
297  * The end result is that entities without names cannot be looked up, but will
298  * show up during iteration.
299  */
300 static const void *
301 ipmi_sdr_hash_convert(const void *p)
302 {
303 	return (p);
304 }
305 
306 static ulong_t
307 ipmi_sdr_hash_compute(const void *p)
308 {
309 	const ipmi_sdr_cache_ent_t *ep = p;
310 
311 	if (ep->isc_name)
312 		return (ipmi_hash_strhash(ep->isc_name));
313 	else
314 		return (ipmi_hash_ptrhash(ep));
315 }
316 
317 static int
318 ipmi_sdr_hash_compare(const void *a, const void *b)
319 {
320 	const ipmi_sdr_cache_ent_t *ap = a;
321 	const ipmi_sdr_cache_ent_t *bp = b;
322 
323 	if (ap->isc_name == NULL || bp->isc_name == NULL)
324 		return (-1);
325 
326 	if (strcmp(ap->isc_name, bp->isc_name) != 0)
327 		return (-1);
328 
329 	/*
330 	 * While it is strange for a service processor to report multiple
331 	 * entries with the same name, we allow it by treating the (name, id)
332 	 * as the unique identifier.  When looking up by name, the SDR pointer
333 	 * is NULL, and we return the first matching name.
334 	 */
335 	if (ap->isc_sdr == NULL || bp->isc_sdr == NULL)
336 		return (0);
337 
338 	if (ap->isc_sdr->is_id == bp->isc_sdr->is_id)
339 		return (0);
340 	else
341 		return (-1);
342 }
343 
344 int
345 ipmi_sdr_init(ipmi_handle_t *ihp)
346 {
347 	if ((ihp->ih_sdr_cache = ipmi_hash_create(ihp,
348 	    offsetof(ipmi_sdr_cache_ent_t, isc_link),
349 	    ipmi_sdr_hash_convert, ipmi_sdr_hash_compute,
350 	    ipmi_sdr_hash_compare)) == NULL)
351 		return (-1);
352 
353 	return (0);
354 }
355 
356 void
357 ipmi_sdr_clear(ipmi_handle_t *ihp)
358 {
359 	ipmi_sdr_cache_ent_t *ent;
360 
361 	while ((ent = ipmi_hash_first(ihp->ih_sdr_cache)) != NULL) {
362 		ipmi_hash_remove(ihp->ih_sdr_cache, ent);
363 		ipmi_free(ihp, ent->isc_sdr);
364 		ipmi_free(ihp, ent->isc_name);
365 		ipmi_free(ihp, ent);
366 	}
367 }
368 
369 void
370 ipmi_sdr_fini(ipmi_handle_t *ihp)
371 {
372 	if (ihp->ih_sdr_cache != NULL) {
373 		ipmi_sdr_clear(ihp);
374 		ipmi_hash_destroy(ihp->ih_sdr_cache);
375 	}
376 }
377 
378 ipmi_sdr_t *
379 ipmi_sdr_get(ipmi_handle_t *ihp, uint16_t id, uint16_t *next)
380 {
381 	ipmi_cmd_t cmd, *rsp;
382 	ipmi_cmd_get_sdr_t req;
383 	ipmi_rsp_get_sdr_t *sdr;
384 	int i;
385 
386 	req.ic_gs_resid = ihp->ih_reservation;
387 	req.ic_gs_recid = id;
388 	req.ic_gs_offset = 0;
389 	req.ic_gs_len = 0xFF;
390 
391 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
392 	cmd.ic_lun = 0;
393 	cmd.ic_cmd = IPMI_CMD_GET_SDR;
394 	cmd.ic_dlen = sizeof (req);
395 	cmd.ic_data = &req;
396 
397 	for (i = 0; i < ihp->ih_retries; i++) {
398 		if ((rsp = ipmi_send(ihp, &cmd)) != NULL)
399 			break;
400 
401 		if (ipmi_errno(ihp) != EIPMI_INVALID_RESERVATION)
402 			return (NULL);
403 
404 		if (ipmi_sdr_reserve_repository(ihp) != 0)
405 			return (NULL);
406 		req.ic_gs_resid = ihp->ih_reservation;
407 	}
408 
409 	if (rsp == NULL)
410 		return (NULL);
411 
412 	if (rsp->ic_dlen < sizeof (uint16_t) + sizeof (ipmi_sdr_t)) {
413 		(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
414 		return (NULL);
415 	}
416 
417 	sdr = rsp->ic_data;
418 	*next = sdr->ir_gs_next;
419 
420 	return ((ipmi_sdr_t *)sdr->ir_gs_record);
421 }
422 
423 int
424 ipmi_sdr_iter(ipmi_handle_t *ihp, int (*func)(ipmi_handle_t *,
425     const char *, ipmi_sdr_t *, void *), void *data)
426 {
427 	ipmi_sdr_cache_ent_t *ent;
428 	int ret;
429 
430 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
431 	    ipmi_sdr_refresh(ihp) != 0)
432 		return (-1);
433 
434 	for (ent = ipmi_hash_first(ihp->ih_sdr_cache); ent != NULL;
435 	    ent = ipmi_hash_next(ihp->ih_sdr_cache, ent)) {
436 		if ((ret = func(ihp, ent->isc_name, ent->isc_sdr, data)) != 0)
437 			return (ret);
438 	}
439 
440 	return (0);
441 }
442 
443 ipmi_sdr_t *
444 ipmi_sdr_lookup(ipmi_handle_t *ihp, const char *idstr)
445 {
446 	ipmi_sdr_cache_ent_t *ent, search;
447 
448 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
449 	    ipmi_sdr_refresh(ihp) != 0)
450 		return (NULL);
451 
452 	search.isc_name = (char *)idstr;
453 	search.isc_sdr = NULL;
454 	if ((ent = ipmi_hash_lookup(ihp->ih_sdr_cache, &search)) == NULL) {
455 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
456 		return (NULL);
457 	}
458 
459 	return (ent->isc_sdr);
460 }
461 
462 static void *
463 ipmi_sdr_lookup_common(ipmi_handle_t *ihp, const char *idstr,
464     uint8_t type)
465 {
466 	ipmi_sdr_t *sdrp;
467 
468 	if ((sdrp = ipmi_sdr_lookup(ihp, idstr)) == NULL)
469 		return (NULL);
470 
471 	if (sdrp->is_type != type) {
472 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
473 		return (NULL);
474 	}
475 
476 	return (sdrp->is_record);
477 }
478 
479 ipmi_sdr_fru_locator_t *
480 ipmi_sdr_lookup_fru(ipmi_handle_t *ihp, const char *idstr)
481 {
482 	return (ipmi_sdr_lookup_common(ihp, idstr,
483 	    IPMI_SDR_TYPE_FRU_LOCATOR));
484 }
485 
486 ipmi_sdr_generic_locator_t *
487 ipmi_sdr_lookup_generic(ipmi_handle_t *ihp, const char *idstr)
488 {
489 	return (ipmi_sdr_lookup_common(ihp, idstr,
490 	    IPMI_SDR_TYPE_GENERIC_LOCATOR));
491 }
492 
493 ipmi_sdr_compact_sensor_t *
494 ipmi_sdr_lookup_compact_sensor(ipmi_handle_t *ihp, const char *idstr)
495 {
496 	return (ipmi_sdr_lookup_common(ihp, idstr,
497 	    IPMI_SDR_TYPE_COMPACT_SENSOR));
498 }
499 
500 ipmi_sdr_full_sensor_t *
501 ipmi_sdr_lookup_full_sensor(ipmi_handle_t *ihp, const char *idstr)
502 {
503 	return (ipmi_sdr_lookup_common(ihp, idstr,
504 	    IPMI_SDR_TYPE_FULL_SENSOR));
505 }
506