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 }