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