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