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  * Read the SDR information on a board and get the device id to
  31  * read the FRUID information
  32  */
  33 
  34 #include <stdio.h>
  35 #include <sys/types.h>
  36 #include <sys/stat.h>
  37 #include <fcntl.h>
  38 #include <errno.h>
  39 #include <time.h>
  40 #include <poll.h>
  41 #include <stropts.h>
  42 #include <stdarg.h>
  43 #include <syslog.h>
  44 #include <smclib.h>
  45 #include "fru_access_impl.h"
  46 
  47 #define POLL_TIMEOUT    10000   /* 20 sec */
  48 #define SEQUENCE        10
  49 
  50 #define IPMI_GET_SDR_INFO       0x20
  51 #define IPMI_SENSOR_NETFN       0x4
  52 #define SDR_INFO_RESPONSE_SZ_MIN        10
  53 
  54 #define NUM_OF_LUN      4
  55 #define SUN_FRU "SUN FRU SDR"
  56 #define FRU_DEVICE_SDR_TYPE     0x11
  57 #define IPMI_SDR_VERSION        0x51
  58 #define IPMI_DATA_OFFSET        7
  59 #define IPMI_GET_SDR_INFO_CMD   0x20
  60 #define SDR_BUFFER_LEN_MAX      100
  61 
  62 typedef struct {
  63         uint8_t src;
  64         uint8_t dest;
  65         uint8_t lun;
  66         uint8_t record_id_lsb;
  67         uint8_t record_id_msb;
  68         int     offset;
  69         int     length;
  70         char    *buffer;
  71 } sdr_info_t;
  72 
  73 static int get_sdr_info(int, int, uint8_t lun);
  74 static int get_sdr(sdr_info_t *);
  75 static int get_sun_sdr(int, uint8_t, uint8_t, uint8_t);
  76 
  77 /*
  78  * bug in smc f/w
  79  *
  80  *  static int lun_mask[4] = { 0x01, 0x02, 0x04, 0x08 };
  81  */
  82 
  83 /*
  84  * routine to read the onboard/remote device SDR information
  85  */
  86 void
  87 get_fru_data_info(int src, int dest, format_t *fru_format)
  88 {
  89         int ret;
  90 
  91         src = IPMB_ADDR(src);
  92         dest = IPMB_ADDR(dest);
  93 
  94         if (src != dest) { /* ipmi */
  95                 int i = 0;
  96                 for (i = 0; i < NUM_OF_LUN; i++) { /* for each lun */
  97                         ret = get_sdr_info(src, dest, i);
  98                         if (ret > 0) {
  99                                 ret = get_sun_sdr(ret, src, dest, i);
 100                                 if (ret > 0) {
 101                                         fru_format->format |= SUN_FORMAT;
 102                                         fru_format->sun_device_id = ret;
 103                                         fru_format->sun_lun = i;
 104                                         break;
 105                                 }
 106                         }
 107                 }
 108         } else { /* on board */
 109                 ret = get_sdr_info(src, dest, 0);
 110                 if (ret > 0) {
 111                         ret = get_sun_sdr(ret, src, dest, 0);
 112                         if (ret > 0) {
 113                                 fru_format->format |= SUN_FORMAT;
 114                                 fru_format->sun_device_id = ret;
 115                                 fru_format->sun_lun = 0;
 116                         }
 117                 }
 118         }
 119 }
 120 
 121 /*
 122  * read the onboard sdr information
 123  */
 124 static int
 125 get_onboard_sdr(sdr_info_t *sdr)
 126 {
 127         sc_reqmsg_t req_pkt;
 128         sc_rspmsg_t res_pkt;
 129 
 130         SC_MSG_CMD(&req_pkt) =  SMC_DEVICE_SDR_GET;
 131         SC_MSG_LEN(&req_pkt) = 6;
 132         SC_MSG_ID(&req_pkt) = SEQUENCE;
 133 
 134         /* data  for request packet */
 135         req_pkt.data[0] = 0x0;
 136         req_pkt.data[1] = 0x0;
 137         req_pkt.data[2] = sdr->record_id_lsb;
 138         req_pkt.data[3] = sdr->record_id_msb;
 139         req_pkt.data[4] = sdr->offset;
 140         req_pkt.data[5] = sdr->length;
 141 
 142         if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
 143                 POLL_TIMEOUT) != SMC_SUCCESS) {
 144                 return (-1);
 145         }
 146 
 147         bzero(sdr->buffer, SDR_BUFFER_LEN_MAX);
 148         (void) memcpy(sdr->buffer, res_pkt.data, res_pkt.hdr.len);
 149         return (0);
 150 }
 151 
 152 /*
 153  * get the sdr information
 154  */
 155 static int
 156 get_sdr_info(int src, int dest, uint8_t lun)
 157 {
 158         sc_reqmsg_t req_pkt;
 159         sc_rspmsg_t res_pkt;
 160 
 161         if (lun >= NUM_OF_LUN) {
 162                 return (-1);
 163         }
 164 
 165         if (src == dest) {      /* onboard */
 166                 SC_MSG_CMD(&req_pkt) = SMC_DEVICE_SDR_INFO_GET;
 167                 SC_MSG_LEN(&req_pkt) = 0;
 168                 SC_MSG_ID(&req_pkt) = SEQUENCE;
 169                 if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
 170                         POLL_TIMEOUT) != SMC_SUCCESS) {
 171                         return (-1);
 172                 }
 173                 return (res_pkt.data[0]);
 174         }
 175 
 176         /* ipmb access */
 177         (void) smc_init_ipmi_msg(&req_pkt, IPMI_GET_SDR_INFO_CMD,
 178                 FRUACCESS_MSG_ID, 0, NULL, DEFAULT_SEQN, dest,
 179                 SMC_NETFN_SENSOR_REQ, lun);
 180 
 181         if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
 182                 POLL_TIMEOUT) != SMC_SUCCESS) {
 183                 return (-1);
 184         }
 185 
 186         /* completion code */
 187         if (res_pkt.data[IPMI_DATA_OFFSET] != 0) {
 188                 return (-1);
 189         }
 190 
 191         /*
 192          * Known bug in SMC f/w. take this out for next release
 193          * if ((res_pkt.data[IPMI_DATA_OFFSET + 2] & lun_mask[lun]) != 1) {
 194          *      return (0);
 195          * }
 196          */
 197         return (res_pkt.data[IPMI_DATA_OFFSET + 1]);
 198 }
 199 
 200 static int
 201 get_sun_sdr(int num_records, uint8_t src, uint8_t dest, uint8_t lun)
 202 {
 203         int i, ret;
 204         sdr_info_t sdr;
 205         char data[SDR_BUFFER_LEN_MAX];
 206         uint8_t next_record_lsb;
 207         uint8_t next_record_msb;
 208 
 209         sdr.src = src;
 210         sdr.dest = dest;
 211         sdr.lun = lun;
 212         sdr.buffer = data;
 213 
 214         /* get the first record info */
 215         next_record_lsb = 0x0;
 216         next_record_msb = 0x0;
 217         sdr.length = 4;
 218         sdr.offset = 0x0;
 219 
 220         if (src == dest) { /* onboard */
 221                 for (i = 0; i < num_records; i++) {
 222                         sdr.record_id_lsb = next_record_lsb;
 223                         sdr.record_id_msb = next_record_msb;
 224 
 225                         if ((ret = get_onboard_sdr(&sdr)) < 0) {
 226                                 return (ret);
 227                         }
 228 
 229                         next_record_lsb = data[0];
 230                         next_record_msb = data[1];
 231                         if (data[4] != IPMI_SDR_VERSION) {
 232                                 return (-1);
 233                         }
 234 
 235                         if (data[5] == FRU_DEVICE_SDR_TYPE) {
 236                                 sdr.offset = 0x10;
 237                                 sdr.length = strlen(SUN_FRU);
 238                                 if ((ret = get_onboard_sdr(&sdr)) < 0) {
 239                                         return (ret);
 240                                 }
 241 
 242                                 /* first two bytes of response is reserv. id */
 243                                 if (strncmp(SUN_FRU, &data[2],
 244                                         strlen(SUN_FRU)) == 0) {
 245                                         /* found sun sdr */
 246                                         sdr.offset = 0x0;
 247                                         sdr.length = 7;
 248                                         if ((ret = get_onboard_sdr(&sdr)) < 0) {
 249                                                 return (ret);
 250                                         }
 251                                         return (data[8]);
 252                                 }
 253                         }
 254                 }
 255                 return (-1);
 256         }
 257 
 258         /* ipmb access */
 259         /* traverse thru all the records until we find sun sdr */
 260         for (i = 0; i < num_records; i++) {
 261 
 262                 sdr.record_id_lsb = next_record_lsb;
 263                 sdr.record_id_msb = next_record_msb;
 264 
 265                 if ((ret = get_sdr(&sdr)) < 0) {
 266                         return (ret);
 267                 }
 268 
 269                 /* completion code */
 270                 if (data[IPMI_DATA_OFFSET] != 0) {
 271                         return (-1);
 272                 }
 273                 next_record_lsb = data[IPMI_DATA_OFFSET + 1];
 274                 next_record_msb = data[IPMI_DATA_OFFSET + 2];
 275 
 276                 if (data[IPMI_DATA_OFFSET + 5] != IPMI_SDR_VERSION) {
 277                         return (-1);
 278                 }
 279 
 280                 if (data[IPMI_DATA_OFFSET + 6] == FRU_DEVICE_SDR_TYPE) {
 281 
 282                         sdr.offset = 0x10;
 283                         sdr.length = strlen(SUN_FRU);
 284                         if ((ret = get_sdr(&sdr)) < 0) {
 285                                 return (ret);
 286                         }
 287 
 288                         /* completion code */
 289                         if (data[IPMI_DATA_OFFSET] != 0) {
 290                                 return (-1);
 291                         }
 292 
 293                         if (strncmp(&data[IPMI_DATA_OFFSET+ 3],
 294                                 SUN_FRU, strlen(SUN_FRU)) == 0) {
 295                                 /* found sun sdr */
 296                                 sdr.offset = 0x0;
 297                                 sdr.length = 7;
 298                                 if ((ret = get_sdr(&sdr)) < 0) {
 299                                         return (ret);
 300                                 }
 301 
 302                                 /* completion code */
 303                                 if (data[IPMI_DATA_OFFSET] != 0) {
 304                                         return (-1);
 305                                 }
 306                                 return (data[IPMI_DATA_OFFSET + 9]);
 307                         }
 308                 }
 309         }
 310         return (-1);
 311 }
 312 
 313 static int
 314 get_sdr(sdr_info_t *sdr)
 315 {
 316         sc_reqmsg_t req_pkt;
 317         sc_rspmsg_t res_pkt;
 318         uint8_t datap[6];
 319 
 320         if (sdr->lun > 3) {
 321                 return (-1);
 322         }
 323 
 324         /* data  for request packet */
 325         datap[0] = 0x0;         /* reserved */
 326         datap[1] = 0x0;         /* reserved */
 327         datap[2] = sdr->record_id_lsb;
 328         datap[3] = sdr->record_id_msb;
 329         datap[4] = sdr->offset;
 330         datap[5] = sdr->length;
 331 
 332         (void) smc_init_ipmi_msg(&req_pkt, SMC_GET_DEVICE_SDR,
 333                 FRUACCESS_MSG_ID, 6, datap, DEFAULT_SEQN,
 334                 sdr->dest, SMC_NETFN_SENSOR_REQ, sdr->lun);
 335 
 336         if (smc_send_msg(DEFAULT_FD, &req_pkt, &res_pkt,
 337                 POLL_TIMEOUT) != SMC_SUCCESS) {
 338                 return (-1);
 339         }
 340         bzero(sdr->buffer, SDR_BUFFER_LEN_MAX);
 341         (void) memcpy(sdr->buffer, res_pkt.data, res_pkt.hdr.len);
 342         return (0);
 343 }