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