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