xref: /titanic_52/usr/src/lib/libipmi/common/ipmi_sdr.c (revision b6c3f7863936abeae522e48a13887dddeb691a45)
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 			}
269 
270 			ipmi_decode_string(type, namelen, name, ent->isc_name);
271 		}
272 
273 		ipmi_hash_insert(ihp->ih_sdr_cache, ent);
274 	}
275 
276 	return (0);
277 }
278 
279 /*
280  * Hash routines.  We allow lookup by name, but since not all entries have
281  * names, we fall back to the entry pointer, which is guaranteed to be unique.
282  * The end result is that entities without names cannot be looked up, but will
283  * show up during iteration.
284  */
285 static const void *
286 ipmi_sdr_hash_convert(const void *p)
287 {
288 	return (p);
289 }
290 
291 static ulong_t
292 ipmi_sdr_hash_compute(const void *p)
293 {
294 	const ipmi_sdr_cache_ent_t *ep = p;
295 
296 	if (ep->isc_name)
297 		return (ipmi_hash_strhash(ep->isc_name));
298 	else
299 		return (ipmi_hash_ptrhash(ep));
300 }
301 
302 static int
303 ipmi_sdr_hash_compare(const void *a, const void *b)
304 {
305 	const ipmi_sdr_cache_ent_t *ap = a;
306 	const ipmi_sdr_cache_ent_t *bp = b;
307 
308 	if (ap->isc_name == NULL || bp->isc_name == NULL)
309 		return (-1);
310 
311 	return (strcmp(ap->isc_name, bp->isc_name));
312 }
313 
314 int
315 ipmi_sdr_init(ipmi_handle_t *ihp)
316 {
317 	if ((ihp->ih_sdr_cache = ipmi_hash_create(ihp,
318 	    offsetof(ipmi_sdr_cache_ent_t, isc_link),
319 	    ipmi_sdr_hash_convert, ipmi_sdr_hash_compute,
320 	    ipmi_sdr_hash_compare)) == NULL)
321 		return (-1);
322 
323 	return (0);
324 }
325 
326 void
327 ipmi_sdr_clear(ipmi_handle_t *ihp)
328 {
329 	ipmi_sdr_cache_ent_t *ent;
330 
331 	while ((ent = ipmi_hash_first(ihp->ih_sdr_cache)) != NULL) {
332 		ipmi_hash_remove(ihp->ih_sdr_cache, ent);
333 		ipmi_free(ihp, ent->isc_sdr);
334 		ipmi_free(ihp, ent->isc_name);
335 		ipmi_free(ihp, ent);
336 	}
337 }
338 
339 void
340 ipmi_sdr_fini(ipmi_handle_t *ihp)
341 {
342 	if (ihp->ih_sdr_cache != NULL) {
343 		ipmi_sdr_clear(ihp);
344 		ipmi_hash_destroy(ihp->ih_sdr_cache);
345 	}
346 }
347 
348 ipmi_sdr_t *
349 ipmi_sdr_get(ipmi_handle_t *ihp, uint16_t id, uint16_t *next)
350 {
351 	ipmi_cmd_t cmd, *rsp;
352 	ipmi_cmd_get_sdr_t req;
353 	ipmi_rsp_get_sdr_t *sdr;
354 	int i;
355 
356 	req.ic_gs_resid = ihp->ih_reservation;
357 	req.ic_gs_recid = id;
358 	req.ic_gs_offset = 0;
359 	req.ic_gs_len = 0xFF;
360 
361 	cmd.ic_netfn = IPMI_NETFN_STORAGE;
362 	cmd.ic_lun = 0;
363 	cmd.ic_cmd = IPMI_CMD_GET_SDR;
364 	cmd.ic_dlen = sizeof (req);
365 	cmd.ic_data = &req;
366 
367 	for (i = 0; i < ihp->ih_retries; i++) {
368 		if ((rsp = ipmi_send(ihp, &cmd)) != NULL)
369 			break;
370 
371 		if (ipmi_errno(ihp) != EIPMI_INVALID_RESERVATION)
372 			return (NULL);
373 
374 		if (ipmi_sdr_reserve_repository(ihp) != 0)
375 			return (NULL);
376 		req.ic_gs_resid = ihp->ih_reservation;
377 	}
378 
379 	if (rsp == NULL)
380 		return (NULL);
381 
382 	if (rsp->ic_dlen < sizeof (uint16_t) + sizeof (ipmi_sdr_t)) {
383 		(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
384 		return (NULL);
385 	}
386 
387 	sdr = rsp->ic_data;
388 	*next = sdr->ir_gs_next;
389 
390 	return ((ipmi_sdr_t *)sdr->ir_gs_record);
391 }
392 
393 int
394 ipmi_sdr_iter(ipmi_handle_t *ihp, int (*func)(ipmi_handle_t *,
395     const char *, ipmi_sdr_t *, void *), void *data)
396 {
397 	ipmi_sdr_cache_ent_t *ent;
398 	int ret;
399 
400 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
401 	    ipmi_sdr_refresh(ihp) != 0)
402 		return (-1);
403 
404 	for (ent = ipmi_hash_first(ihp->ih_sdr_cache); ent != NULL;
405 	    ent = ipmi_hash_next(ihp->ih_sdr_cache, ent)) {
406 		if ((ret = func(ihp, ent->isc_name, ent->isc_sdr, data)) != 0)
407 			return (ret);
408 	}
409 
410 	return (0);
411 }
412 
413 ipmi_sdr_t *
414 ipmi_sdr_lookup(ipmi_handle_t *ihp, const char *idstr)
415 {
416 	ipmi_sdr_cache_ent_t *ent, search;
417 
418 	if (ipmi_hash_first(ihp->ih_sdr_cache) == NULL &&
419 	    ipmi_sdr_refresh(ihp) != 0)
420 		return (NULL);
421 
422 	search.isc_name = (char *)idstr;
423 	if ((ent = ipmi_hash_lookup(ihp->ih_sdr_cache, &search)) == NULL) {
424 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
425 		return (NULL);
426 	}
427 
428 	return (ent->isc_sdr);
429 }
430 
431 static void *
432 ipmi_sdr_lookup_common(ipmi_handle_t *ihp, const char *idstr,
433     uint8_t type)
434 {
435 	ipmi_sdr_t *sdrp;
436 
437 	if ((sdrp = ipmi_sdr_lookup(ihp, idstr)) == NULL)
438 		return (NULL);
439 
440 	if (sdrp->is_type != type) {
441 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT, NULL);
442 		return (NULL);
443 	}
444 
445 	return (sdrp->is_record);
446 }
447 
448 ipmi_sdr_fru_locator_t *
449 ipmi_sdr_lookup_fru(ipmi_handle_t *ihp, const char *idstr)
450 {
451 	return (ipmi_sdr_lookup_common(ihp, idstr,
452 	    IPMI_SDR_TYPE_FRU_LOCATOR));
453 }
454 
455 ipmi_sdr_generic_locator_t *
456 ipmi_sdr_lookup_generic(ipmi_handle_t *ihp, const char *idstr)
457 {
458 	return (ipmi_sdr_lookup_common(ihp, idstr,
459 	    IPMI_SDR_TYPE_GENERIC_LOCATOR));
460 }
461 
462 ipmi_sdr_compact_sensor_t *
463 ipmi_sdr_lookup_compact_sensor(ipmi_handle_t *ihp, const char *idstr)
464 {
465 	return (ipmi_sdr_lookup_common(ihp, idstr,
466 	    IPMI_SDR_TYPE_COMPACT_SENSOR));
467 }
468 
469 ipmi_sdr_full_sensor_t *
470 ipmi_sdr_lookup_full_sensor(ipmi_handle_t *ihp, const char *idstr)
471 {
472 	return (ipmi_sdr_lookup_common(ihp, idstr,
473 	    IPMI_SDR_TYPE_FULL_SENSOR));
474 }
475