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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 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 /* 30 * These routines in this file are used to interact with SMC driver to 31 * read and write FRUID data 32 */ 33 34 #include <stdio.h> 35 #include <unistd.h> 36 #include <string.h> 37 #include <strings.h> 38 #include <stdarg.h> 39 #include <synch.h> 40 #include <thread.h> 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <fcntl.h> 44 #include <syslog.h> 45 #include <stropts.h> 46 #include <poll.h> 47 #include <smclib.h> 48 #include "fru_access_impl.h" 49 50 #define POLL_TIMEOUT 10000 51 #define FRUID_CHECK_POLL_TIMEOUT 5000 52 #define SIZE_TO_READ_WRITE 20 53 54 /* IPMI fru spec Storage definition version 1.0, rev 1.1 */ 55 #define IPMI_COMMON_HEADER_SIZE 8 56 #define IPMI_VERSION 1 57 #define CMN_HDR_VERSION_MASK 0x0 58 #define CMN_HDR_OFFSET 0x0 59 #define BD_MFR_OFFSET 6 60 #define BD_FIELDS_SIZE 6 61 #define AREA_TERMINATION_INDICATOR 0xc1 62 63 /* type encoding */ 64 #define BINARY_TYPE 0x0 65 #define BCDPLUS_TYPE 0x1 66 #define SIX_BITASCII_TYPE 0x2 67 #define UNICODE_TYPE 0x3 68 69 /* for ascii conversion */ 70 #define ASCII_MAP 0x20 71 #define BIT_MASK1 0x3f 72 #define BIT_MASK2 0x0f 73 #define BIT_MASK3 0x03 74 75 #define SUN_NAME "SUN MICROSYSTEMS, INC." 76 #define SUN_JEDEC_CODE 0x3e 77 #define MANR_MAX_LENGTH 80 78 #define FRU_DATA_MAX_SIZE 100 79 80 /* IPMI commands */ 81 #define IPMI_GET_DEVICE_ID 0x1 82 #define FRU_DEVICE_ID 0x0 83 #define READ_FRU_INVENTORY_INFO 0x10 84 #define READ_FRU_INVENTORY_DATA 0x11 85 #define WRITE_FRU_INVENTORY_DATA 0x12 86 87 #define TMP_BUFFER_SIZE 10 88 #define BYTE_TO_READ_SUN_CHK 5 89 90 typedef struct { 91 uint8_t internal; /* internal use area */ 92 uint8_t chassis; /* chassis info area */ 93 uint8_t board; /* board area */ 94 uint8_t product; /* product info area */ 95 uint8_t records; /* multirecord area */ 96 } fruid_offset_t; 97 98 extern void get_fru_data_info(int, int, format_t *); 99 static void convert_to_ascii(uint8_t [], uint8_t [], int, int); 100 static void bcdplus_to_ascii(uint8_t [], uint8_t [], int); 101 static time_t get_utc_time(uint8_t []); 102 static uint8_t cpu_no = 0; 103 104 /* 105 * Routine to read FRUID information from BMC 106 */ 107 static int 108 get_alarm_fru_data(int offset, int size, void *buffer, format_t *format) 109 { 110 uint8_t datap[5]; 111 sc_reqmsg_t req_pkt; 112 sc_rspmsg_t res_pkt; 113 114 if (buffer == NULL) { 115 return (-1); 116 } 117 bzero(buffer, size); 118 119 datap[0] = 0x7; /* bus id */ 120 datap[1] = 0xa0; /* slave address */ 121 datap[2] = size; /* count */ 122 datap[3] = offset >> 8; /* MSB */ 123 datap[4] = (uint8_t)offset; /* LSB */ 124 125 (void) smc_init_ipmi_msg(&req_pkt, SMC_MASTER_WR_RD_I2C, 126 FRUACCESS_MSG_ID, 5, datap, DEFAULT_SEQN, format->dest, 127 SMC_NETFN_APP_REQ, SMC_BMC_LUN); 128 129 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt, 130 POLL_TIMEOUT) != SMC_SUCCESS) { 131 return (-1); 132 } 133 /* check the completion code */ 134 if (res_pkt.data[7] != 0) { 135 return (-1); 136 } 137 138 (void) memcpy(buffer, &(res_pkt.data[8]), size); 139 return (0); 140 } 141 142 /* 143 * Routine to read FRUID information from other boards 144 */ 145 static int 146 get_fru_data(int offset, int size, void *buffer, format_t *format) 147 { 148 sc_reqmsg_t req_pkt; 149 sc_rspmsg_t res_pkt; 150 uint8_t datap[4]; 151 int ipmi = 0; 152 153 if (buffer == NULL) { 154 return (-1); 155 } 156 157 /* figure out if onboard access or ipmb access */ 158 if (format->src == format->dest) { 159 ipmi = 0; 160 } else { 161 ipmi = 1; 162 } 163 164 switch (ipmi) { 165 166 case 0: /* on board info (local i2c) */ 167 168 SC_MSG_CMD(&req_pkt) = SMC_EEPROM_READ; 169 SC_MSG_LEN(&req_pkt) = 4; 170 SC_MSG_ID(&req_pkt) = FRUACCESS_MSG_ID; 171 172 /* data field for request */ 173 req_pkt.data[0] = format->sun_device_id; /* device id */ 174 req_pkt.data[1] = (uint8_t)offset; /* (LSB) */ 175 req_pkt.data[3] = size; 176 177 if (format->format == SUN_FORMAT) { 178 req_pkt.data[2] = offset >> 8; 179 } else { 180 req_pkt.data[2] = 0x0; /* (MSB) always 0x0 for IPMI */ 181 } 182 183 /* make a call to smc library to send cmd */ 184 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt, 185 POLL_TIMEOUT) != SMC_SUCCESS) { 186 return (-1); 187 } 188 189 if (SC_MSG_LEN(&res_pkt) != size) { 190 return (-1); 191 } 192 (void) memcpy(buffer, res_pkt.data, size); 193 return (0); 194 195 default: 196 197 /* data for request packet */ 198 datap[0] = format->sun_device_id; /* device id */ 199 datap[1] = (uint8_t)offset; /* LSB */ 200 datap[3] = size; /* bytes to read */ 201 if (format->format == SUN_FORMAT) { 202 datap[2] = offset >> 8; 203 } else { 204 datap[2] = 0x0; /* (MSB) always 0x0 for IPMI */ 205 } 206 207 (void) smc_init_ipmi_msg(&req_pkt, READ_FRU_INVENTORY_DATA, 208 FRUACCESS_MSG_ID, 4, datap, DEFAULT_SEQN, 209 format->dest, SMC_NETFN_STORAGE_REQ, format->sun_lun); 210 211 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt, 212 POLL_TIMEOUT) != SMC_SUCCESS) { 213 return (-1); 214 } 215 /* check the completion code */ 216 if (res_pkt.data[7] != 0) { 217 return (-1); 218 } 219 220 /* check the size */ 221 if (res_pkt.data[8] != size) { 222 return (-1); 223 } 224 225 (void) memcpy(buffer, &(res_pkt.data[9]), size); 226 return (0); 227 } 228 } 229 230 /* 231 * routine to read the IPMI common header field 232 */ 233 static int 234 read_common_header(fruid_offset_t *offset, format_t *format) 235 { 236 int ret = 0; 237 uint8_t data[FRU_DATA_MAX_SIZE]; 238 239 ret = get_fru_data(CMN_HDR_OFFSET, IPMI_COMMON_HEADER_SIZE, data, 240 format); 241 if (ret < 0) { 242 return (-1); 243 } 244 245 /* version check */ 246 if ((data[0] | CMN_HDR_VERSION_MASK) != 1) { 247 return (-1); 248 } 249 250 offset->internal = data[1] * 8; 251 offset->chassis = data[2] * 8; 252 offset->board = data[3] * 8; 253 offset->product = data[4] * 8; 254 offset->records = data[5] * 8; 255 256 return (0); 257 } 258 259 /* 260 * Read the values of each field based on FORMAT 261 */ 262 /* ARGSUSED */ 263 static int 264 read_bd_fields(uint8_t *field, int offset, format_t *format) 265 { 266 267 int ret, encode_type = 0x0, len, length, extra_bytes, alloc_size; 268 uint8_t *store; 269 uint8_t data[FRU_DATA_MAX_SIZE]; 270 271 bzero(field, MANR_MAX_LENGTH); 272 273 ret = get_fru_data(offset, BD_FIELDS_SIZE, data, format); 274 if (ret < 0) { 275 return (-1); 276 } 277 278 if (data[0] == AREA_TERMINATION_INDICATOR) { 279 return (0); 280 } 281 282 encode_type = data[0] >> 6; 283 len = data[0] & 0x3f; 284 if (len <= 0) { 285 return (0); 286 } 287 288 ret = get_fru_data(offset+1, len, data, format); 289 if (ret < 0) { 290 return (-1); 291 } 292 293 switch (encode_type) { 294 295 case SIX_BITASCII_TYPE: 296 297 length = len - (len % 3); 298 extra_bytes = len % 3; 299 alloc_size = ((length/3) * 4) + extra_bytes; 300 store = (uint8_t *)malloc(sizeof (uint8_t) * alloc_size); 301 if (store == NULL) { 302 return (-1); 303 } 304 convert_to_ascii(data, store, len, extra_bytes); 305 break; 306 307 case BCDPLUS_TYPE: 308 309 alloc_size = len * 2; 310 store = (uint8_t *)malloc(sizeof (uint8_t) * alloc_size); 311 if (store == NULL) { 312 return (-1); 313 } 314 315 bcdplus_to_ascii(data, store, len); 316 break; 317 318 case BINARY_TYPE: 319 case UNICODE_TYPE: 320 default: 321 return (-1); 322 } 323 324 (void) memcpy(field, store, alloc_size); 325 free(store); 326 return (len); 327 } 328 329 static int 330 read_board_info(uint8_t board_offset, payload_t *manr, format_t *format) 331 { 332 time_t time; 333 uint8_t *buffer; 334 uint8_t mfg_time[4]; 335 uint8_t data[FRU_DATA_MAX_SIZE]; 336 int ret = 0, current_offset = 0x0; 337 int bd_area_len = 0; 338 339 /* read version, length, lang code, mfg. time */ 340 ret = get_fru_data(board_offset, BD_FIELDS_SIZE, data, format); 341 342 if (ret < 0) { 343 return (-1); 344 } 345 346 /* version check */ 347 if ((data[0] | CMN_HDR_VERSION_MASK) != 1) { 348 return (-1); 349 } 350 351 /* byte 2 is lang code */ 352 bd_area_len = data[1] * 8; 353 mfg_time[3] = data[3]; 354 mfg_time[2] = data[4]; 355 mfg_time[1] = data[5]; 356 mfg_time[0] = 0x0; 357 time = get_utc_time(mfg_time); 358 359 /* fill the timestamp into manr */ 360 (void) memcpy(manr->timestamp, &time, MANR_TIME_LEN); 361 362 if (bd_area_len < BD_MFR_OFFSET) { 363 return (-1); 364 } 365 buffer = (uint8_t *)malloc(sizeof (uint8_t) * MANR_MAX_LENGTH); 366 if (buffer == NULL) { 367 return (-1); 368 } 369 370 /* read the board info */ 371 current_offset += board_offset + BD_MFR_OFFSET; 372 current_offset += read_bd_fields(buffer, current_offset, format); 373 374 if (strncmp(SUN_NAME, (char *)buffer, sizeof (SUN_NAME)) == 0) { 375 manr->vendor_name[0] = 0x00; 376 manr->vendor_name[1] = 0x3e; 377 } else { 378 manr->vendor_name[0] = 0x00; 379 manr->vendor_name[1] = 0x00; 380 } 381 382 current_offset += 1; /* for length/type field */ 383 384 current_offset += read_bd_fields(buffer, current_offset, format); 385 current_offset += 1; /* for length/type field */ 386 (void) memcpy(manr->fru_short_name, buffer, MANR_FRUNAME_LEN); 387 388 current_offset += read_bd_fields(buffer, current_offset, format); 389 current_offset += 1; /* for length/type field */ 390 (void) memcpy(manr->sun_serial_no, buffer, MANR_SERIALNUM_LEN); 391 392 current_offset += read_bd_fields(buffer, current_offset, format); 393 current_offset += 1; /* for length/type field */ 394 (void) memcpy(manr->sun_part_no, buffer, MANR_PARTNUM_LEN); 395 396 /* 397 * We dont need the FRU FILE ID, so just skip the field 398 * and get the offset to read the custom MFG. info fields 399 */ 400 current_offset += read_bd_fields(buffer, current_offset, format); 401 current_offset += 1; /* for length/type field */ 402 403 current_offset += read_bd_fields(buffer, current_offset, format); 404 current_offset += 1; /* for length/type field */ 405 406 /* read the custom mfg. info fields */ 407 current_offset += read_bd_fields(buffer, current_offset, format); 408 current_offset += 1; /* for length/type field */ 409 (void) memcpy(manr->manufacture_loc, buffer, MANR_MFRLOC_LEN); 410 411 current_offset += read_bd_fields(buffer, current_offset, format); 412 (void) memcpy(manr->fru_descr, buffer, MANR_FRUDESCR_LEN); 413 414 free(buffer); 415 return (0); 416 } 417 418 /* 419 * Read the IPMI information from hardware and translate it into 420 * MANR(SUN format) 421 */ 422 int 423 get_manr(format_t *format, payload_t *manr) 424 { 425 int ret = 0; 426 fruid_offset_t *offset = NULL; 427 428 offset = (fruid_offset_t *)malloc(sizeof (fruid_offset_t)); 429 if (offset == NULL) { 430 return (-1); 431 } 432 433 ret = read_common_header(offset, format); 434 if (ret != 0) { 435 free(offset); 436 return (-1); 437 } 438 439 if (offset->board != 0) { 440 ret = read_board_info(offset->board, manr, format); 441 } 442 443 free(offset); 444 return (ret); 445 } 446 447 static void 448 convert_to_ascii(uint8_t data [], uint8_t store[], 449 int length, int extra_bytes) 450 { 451 uint8_t x, y; 452 int index = 0; 453 int i, idx = length - (length % 3); 454 455 for (i = 0; ; i += 3) { 456 457 x = 0x0; 458 y = 0x0; 459 460 if (i == idx && extra_bytes == 0) { 461 break; 462 } 463 464 /* get the first six bits */ 465 x = (data[i] & BIT_MASK1); 466 x += ASCII_MAP; 467 store[index] = x; 468 469 if (i == idx && extra_bytes == 1) { 470 break; 471 } 472 473 /* 474 * get last 2 bits of first byte and first 475 * 4 bits of second byte 476 */ 477 478 x = (data[i] >> 6); 479 y = (data[i + 1] & BIT_MASK2) << 2; 480 x |= y + ASCII_MAP; 481 store[index+1] = x; 482 483 if (i == idx) { 484 break; 485 } 486 487 /* get last 4 bits of second byte and 2 bits of last byte */ 488 x = data[i + 1] >> 4; 489 y = (data[i + 2] & BIT_MASK3) << 4; 490 x |= y + ASCII_MAP; 491 store[index+2] = x; 492 493 /* get last six bits of third byte */ 494 store[index + 3] = (data[i + 2] >> 2) + ASCII_MAP; 495 index += 4; 496 } 497 } 498 499 static void 500 bcdplus_to_ascii(uint8_t data[], uint8_t store[], int len) 501 { 502 int i, j, index = 0; 503 uint8_t tmp = 0; 504 505 struct { 506 int a:4; 507 int b:4; 508 } val; 509 510 for (i = 0; i < len; i++) { 511 (void) memcpy(&val, &data[i], 1); 512 for (j = 0; j < 2; j++) { 513 if (j == 0) { 514 tmp = val.a; 515 } else 516 tmp = val.b; 517 518 if (tmp <= 9) { 519 /* ascii conversion */ 520 store[index++] = tmp + 48; 521 continue; 522 } 523 524 switch (tmp) { 525 526 case 0xa: 527 store[index++] = ' '; 528 break; 529 case 0xb: 530 store[index++] = '-'; 531 break; 532 case 0xc: 533 store[index++] = '.'; 534 break; 535 default: 536 store[index++] = ' '; 537 } 538 } 539 } 540 } 541 542 /* converts ipmi format time to UTC time (unix 32 bit timestamp) */ 543 static time_t 544 get_utc_time(uint8_t data []) 545 { 546 time_t time; 547 struct tm tm1; 548 uint32_t ipmi_time; 549 550 (void) memcpy(&ipmi_time, data, 4); 551 552 ipmi_time *= 60; /* convert into seconds */ 553 554 /* get UTC time for 0:00 1/1/96 (ipmi epoch) */ 555 tm1.tm_sec = 0; 556 tm1.tm_min = 0; 557 tm1.tm_hour = 0; 558 tm1.tm_mday = 1; 559 tm1.tm_mon = 0; 560 tm1.tm_year = 96; 561 562 time = mktime(&tm1); 563 time += ipmi_time; 564 565 return (time); 566 } 567 568 /* 569 * routine to write information to BMC 570 */ 571 static int 572 write_alarm_fru_data(const void *buffer, size_t size, 573 off_t offset, format_t *format) 574 { 575 sc_reqmsg_t req_pkt; 576 sc_rspmsg_t res_pkt; 577 uint8_t *datap = NULL; 578 579 if (buffer == NULL) { 580 return (-1); 581 } 582 datap = (uint8_t *)malloc(sizeof (uint8_t) * (size + 5)); 583 if (datap == NULL) { 584 return (-1); 585 } 586 587 datap[0] = 0x7; /* bus id */ 588 datap[1] = 0xa0; /* slave address */ 589 datap[2] = 0; /* count */ 590 datap[3] = offset >> 8; /* MSB */ 591 datap[4] = (uint8_t)offset; /* LSB */ 592 (void) memcpy((void *)&(datap[5]), buffer, size); 593 594 /* initialize ipmi request packet */ 595 (void) smc_init_ipmi_msg(&req_pkt, SMC_MASTER_WR_RD_I2C, 596 FRUACCESS_MSG_ID, (5 + size), datap, DEFAULT_SEQN, 597 format->dest, SMC_NETFN_APP_REQ, SMC_BMC_LUN); 598 free(datap); 599 600 /* send ipmi request packet */ 601 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt, 602 POLL_TIMEOUT) != SMC_SUCCESS) { 603 return (-1); 604 } 605 /* check the completion code */ 606 if (res_pkt.data[7] != 0) { 607 return (-1); 608 } 609 return (0); 610 } 611 612 static int 613 write_fru_data(const void *buffer, size_t size, 614 off_t offset, format_t *format) 615 { 616 int ipmi = 0; 617 sc_reqmsg_t req_pkt; 618 sc_rspmsg_t res_pkt; 619 uint8_t *datap = NULL; 620 621 if (buffer == NULL) { 622 return (-1); 623 } 624 625 if (format->src == format->dest) { 626 ipmi = 0; 627 } else { 628 ipmi = 1; 629 } 630 631 switch (ipmi) { 632 633 case 0: /* on board info (local i2c) */ 634 635 SC_MSG_CMD(&req_pkt) = SMC_EEPROM_WRITE; 636 SC_MSG_LEN(&req_pkt) = 4 + size; 637 SC_MSG_ID(&req_pkt) = FRUACCESS_MSG_ID; 638 639 /* data field for request */ 640 req_pkt.data[0] = format->sun_device_id; /* device id */ 641 req_pkt.data[1] = offset; /* (LSB) */ 642 req_pkt.data[3] = size; 643 if (format->format == SUN_FORMAT) { 644 req_pkt.data[2] = offset >> 8; 645 } else { 646 req_pkt.data[2] = 0x0; /* (MSB) always 0x0 for IPMI */ 647 } 648 (void) memcpy((void *)&(req_pkt.data[4]), buffer, size); 649 650 /* make a call to smc library to send cmd */ 651 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt, 652 POLL_TIMEOUT) != SMC_SUCCESS) { 653 return (-1); 654 } 655 break; 656 657 default: /* read data from remote device (ipmi) */ 658 datap = (uint8_t *)malloc(sizeof (uint8_t) * (size + 4)); 659 if (datap == NULL) { 660 return (-1); 661 } 662 663 datap[0] = format->sun_device_id; /* device id */ 664 datap[1] = offset; /* LSB */ 665 datap[3] = size; /* nbytes */ 666 if (format->format == SUN_FORMAT) { 667 datap[2] = offset >> 8; 668 } else { 669 datap[2] = 0x0; /* (MSB) always 0x0 for IPMI */ 670 } 671 (void) memcpy((void *)&(datap[4]), buffer, size); 672 673 (void) smc_init_ipmi_msg(&req_pkt, WRITE_FRU_INVENTORY_DATA, 674 FRUACCESS_MSG_ID, (4 + size), datap, DEFAULT_SEQN, 675 format->dest, SMC_NETFN_STORAGE_REQ, format->sun_lun); 676 free(datap); 677 678 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt, 679 POLL_TIMEOUT) != SMC_SUCCESS) { 680 return (-1); 681 } 682 /* check the completion code */ 683 if (res_pkt.data[7] != 0) { 684 return (-1); 685 } 686 break; 687 } /* end of switch */ 688 return (0); 689 } 690 691 /* 692 * This routine splits the data to write into smaller chunks and 693 * write it to FRUID chip using SMC drv APIs 694 */ 695 696 /* ARGSUSED */ 697 ssize_t 698 pwrite_new(int fd, const void *buffer, size_t size, 699 off_t offset, format_t *format) 700 { 701 int ret; 702 int index = 0; 703 size_t bytes = 0; 704 off_t next_offset = 0x0; 705 off_t curr_offset = offset; 706 size_t bytes_to_write = size; 707 uint8_t *data; 708 int retry = 3; 709 int (* func_ptr)(const void *, size_t, off_t, format_t *); 710 711 if (format->dest == 0x20) { 712 func_ptr = write_alarm_fru_data; 713 } else { 714 func_ptr = write_fru_data; 715 } 716 717 data = (uint8_t *)buffer; 718 while (bytes_to_write != 0) { 719 720 retry = 3; 721 ret = 1; 722 723 if (bytes_to_write > SIZE_TO_READ_WRITE) { 724 bytes = SIZE_TO_READ_WRITE; 725 next_offset = curr_offset + SIZE_TO_READ_WRITE; 726 } else { 727 bytes = bytes_to_write; 728 } 729 730 bytes_to_write = bytes_to_write - bytes; 731 while ((ret != 0) && (retry != 0)) { 732 ret = (*func_ptr)((void *)&data[index], 733 bytes, curr_offset, format); 734 retry--; 735 } 736 if (ret != 0) { 737 return (ret); 738 } 739 index = index + bytes; 740 curr_offset = next_offset; 741 } 742 return (size); 743 } 744 745 /* 746 * This routine reads the data in smaller chunks and 747 * sends it to upper layer(frudata plugin) in the sw stack 748 */ 749 /* ARGSUSED */ 750 ssize_t 751 pread_new(int fd, void *buffer, size_t size, 752 off_t offset, format_t *format) 753 { 754 int ret; 755 int index = 0; 756 size_t bytes = 0; 757 off_t next_offset = 0x0; 758 off_t curr_offset = offset; 759 size_t bytes_to_read = size; 760 uint8_t *data; 761 int retry = 3; 762 int (* func_ptr)(int, int, void *, format_t *); 763 764 if (format->dest == 0x20) { 765 func_ptr = get_alarm_fru_data; 766 } else { 767 func_ptr = get_fru_data; 768 } 769 770 data = (uint8_t *)buffer; 771 772 while (bytes_to_read != 0) { 773 774 retry = 3; 775 ret = 1; 776 777 if (bytes_to_read > SIZE_TO_READ_WRITE) { 778 bytes = SIZE_TO_READ_WRITE; 779 next_offset = curr_offset + SIZE_TO_READ_WRITE; 780 } else { 781 bytes = bytes_to_read; 782 } 783 784 bytes_to_read = bytes_to_read - bytes; 785 786 while ((ret != 0) && (retry != 0)) { 787 ret = (* func_ptr)(curr_offset, bytes, 788 (void *) &data[index], format); 789 retry--; 790 } 791 if (ret != 0) { 792 return (ret); 793 } 794 index = index + bytes; 795 curr_offset = next_offset; 796 } 797 return (size); 798 } 799 800 /* 801 * routine to check if IPMI fruid info is available, 802 * return 0: IPMI fruid not present 803 * return 1: IPMI fruid present 804 */ 805 static int 806 is_ipmi_fru_data_available(int src, int dest) 807 { 808 sc_reqmsg_t req_pkt; 809 sc_rspmsg_t res_pkt; 810 uint8_t datap[5]; 811 812 /* on board access */ 813 if (src == dest) { 814 815 SC_MSG_CMD(&req_pkt) = SMC_EEPROM_READ; 816 SC_MSG_LEN(&req_pkt) = 4; 817 SC_MSG_ID(&req_pkt) = FRUACCESS_MSG_ID; 818 819 /* data field for request */ 820 req_pkt.data[0] = 0x0; /* eeprom number (ipmi format) */ 821 req_pkt.data[1] = CMN_HDR_OFFSET; /* (LSB) */ 822 req_pkt.data[2] = 0x0; /* (MSB) always 0x0 for IPMI */ 823 req_pkt.data[3] = IPMI_COMMON_HEADER_SIZE; 824 825 /* make a call to smc library to send cmd */ 826 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt, 827 POLL_TIMEOUT) != SMC_SUCCESS) { 828 return (0); 829 } 830 831 /* version check */ 832 if (res_pkt.data[0] != IPMI_VERSION) { 833 return (0); 834 } else { 835 return (1); 836 } 837 } 838 839 /* ipmi access */ 840 datap[0] = FRU_DEVICE_ID; /* fru device id - always */ 841 datap[1] = 0x0; /* LSB */ 842 datap[2] = 0x0; /* MSB */ 843 datap[3] = 8; /* bytes to read */ 844 845 (void) smc_init_ipmi_msg(&req_pkt, READ_FRU_INVENTORY_DATA, 846 FRUACCESS_MSG_ID, 4, datap, DEFAULT_SEQN, 847 IPMB_ADDR(dest), SMC_NETFN_STORAGE_REQ, SMC_BMC_LUN); 848 849 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt, 850 FRUID_CHECK_POLL_TIMEOUT) != SMC_SUCCESS) { 851 return (0); 852 } 853 854 if (res_pkt.data[9] == IPMI_VERSION) { 855 return (1); 856 } else { 857 return (0); 858 } 859 } 860 861 /* 862 * routine to check if fruid info is available on BMC, 863 * return 0: fruid not present 864 * return 1: fruid present 865 */ 866 static int 867 is_alarm_frudata_available(format_t *fru_format) 868 { 869 int ret; 870 char buffer[TMP_BUFFER_SIZE]; 871 int fd = -1; 872 format_t format; 873 874 bzero(buffer, sizeof (buffer)); 875 format.src = fru_format->src; 876 format.dest = fru_format->dest; 877 format.sun_device_id = 0x0; 878 format.sun_lun = 0x0; 879 format.format |= SUN_FORMAT; 880 881 /* passing dummy fd */ 882 /* for now read the first 3 bytes and check the info */ 883 ret = pread_new(fd, (void *) buffer, 3, STATIC_OFFSET, &format); 884 if (ret < 0) { 885 return (0); 886 } 887 888 if (buffer[0] != SECTION_HDR_TAG) { 889 fru_format->format = NO_FRUDATA; 890 return (0); 891 } 892 893 fru_format->format = SUN_FORMAT; 894 fru_format->sun_device_id = 0x0; 895 fru_format->sun_lun = 0x0; 896 return (1); 897 } 898 899 /* 900 * checks if the remote device intelligent device (IPMI capable) or not 901 * return 0: not ipmi capable 902 * return 1: ipmi capable 903 */ 904 static int 905 is_ipmi_capable(int src, int dest) 906 { 907 sc_reqmsg_t req_pkt; 908 sc_rspmsg_t res_pkt; 909 910 if (src == dest) { 911 return (1); 912 } 913 914 (void) smc_init_ipmi_msg(&req_pkt, IPMI_GET_DEVICE_ID, 915 FRUACCESS_MSG_ID, 0, NULL, DEFAULT_SEQN, 916 IPMB_ADDR(dest), SMC_NETFN_APP_REQ, SMC_BMC_LUN); 917 918 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt, 919 FRUID_CHECK_POLL_TIMEOUT) != SMC_SUCCESS) { 920 return (0); 921 } 922 return (1); /* got response */ 923 } 924 925 int 926 is_fru_data_available(int precedence, int slot_no, format_t *fru_format) 927 { 928 int ret, fd = 0; 929 uint8_t data[TMP_BUFFER_SIZE]; 930 931 fru_format->format = NO_FRUDATA; 932 if (fru_format->dest == 0x20) { /* alarm card */ 933 ret = is_alarm_frudata_available(fru_format); 934 return (ret); 935 } 936 937 if (cpu_no == 0) { /* get the geo_addr */ 938 sc_reqmsg_t req_pkt; 939 sc_rspmsg_t rsp_pkt; 940 uint8_t size = 0; 941 942 /* initialize the request packet */ 943 (void) smc_init_smc_msg(&req_pkt, 944 SMC_GET_GEOGRAPHICAL_ADDRESS, DEFAULT_SEQN, size); 945 /* make a call to smc library to send cmd */ 946 if (smc_send_msg(DEFAULT_FD, &req_pkt, &rsp_pkt, 947 POLL_TIMEOUT) != SMC_SUCCESS) { 948 return (0); 949 } 950 if (SC_MSG_LEN(&rsp_pkt) == 0) { 951 return (0); 952 } 953 cpu_no = rsp_pkt.data[0]; 954 } 955 956 /* check if it is IPMI intelligent or not */ 957 if (slot_no != cpu_no) { 958 ret = is_ipmi_capable(cpu_no, slot_no); 959 if (ret == 0) { /* dumb I/O card */ 960 return (0); 961 } 962 } 963 964 /* check if ipmi frudata is present or not */ 965 ret = is_ipmi_fru_data_available(cpu_no, slot_no); 966 if (ret == 1) { 967 fru_format->format |= IPMI_FORMAT; 968 fru_format->sun_device_id = 0x0; 969 fru_format->sun_lun = 0x0; 970 971 /* no need to look for sun format */ 972 if (precedence == IPMI_FORMAT) { 973 return (fru_format->format); 974 } 975 } 976 977 /* check if sun fruid is present */ 978 get_fru_data_info(cpu_no, slot_no, fru_format); 979 /* check the hdr version */ 980 if (fru_format->format & SUN_FORMAT) { 981 ret = pread_new(fd, &data, BYTE_TO_READ_SUN_CHK, 982 STATIC_OFFSET, fru_format); 983 if (ret != BYTE_TO_READ_SUN_CHK) { 984 fru_format->format = fru_format->format & 985 (~ (SUN_FORMAT)); 986 fru_format->sun_device_id = 0x0; 987 fru_format->sun_lun = 0x0; 988 } 989 if (data[0] != SECTION_HDR_TAG) { 990 fru_format->format = fru_format->format & 991 (~ (SUN_FORMAT)); 992 fru_format->sun_device_id = 0x0; 993 fru_format->sun_lun = 0x0; 994 } 995 } 996 return (fru_format->format); 997 }