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